Add `CommaSeparatedStrings` datatype (#274)

* Add CommaSeparatedStrings datatype

* Version 0.9.9
This commit is contained in:
Tom Christie 2018-12-14 16:22:31 +00:00 committed by GitHub
parent 96c09044e8
commit fe2b926009
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 60 additions and 4 deletions

View File

@ -9,7 +9,7 @@ that is not committed to source control.
```python
from starlette.applications import Starlette
from starlette.config import Config
from starlette.datastructures import DatabaseURL, Secret
from starlette.datastructures import CommaSeparatedStrings, DatabaseURL, Secret
# Config will be read from environment variables and/or ".env" files.
config = Config(".env")
@ -17,6 +17,7 @@ config = Config(".env")
DEBUG = config('DEBUG', cast=bool, default=False)
DATABASE_URL = config('DATABASE_URL', cast=DatabaseURL)
SECRET_KEY = config('SECRET_KEY', cast=Secret)
ALLOWED_HOSTS = config('ALLOWED_HOSTS', cast=CommaSeparatedStrings)
app = Starlette()
app.debug = DEBUG
@ -31,6 +32,7 @@ app.debug = DEBUG
DEBUG=True
DATABASE_URL=postgresql://localhost/myproject
SECRET_KEY=43n080musdfjt54t-09sdgr
ALLOWED_HOSTS="127.0.0.1", "localhost"
```
## Configuration precedence
@ -45,8 +47,8 @@ If none of those match, then `config(...)` will raise an error.
## Secrets
For sensitive keys, the `Secret` class is useful, since it prevents the value from
leaking out into tracebacks or logging.
For sensitive keys, the `Secret` class is useful, since it helps minimize
occasions where the value it holds could leak out into tracebacks or logging.
To get the value of a `Secret` instance, you must explicitly cast it to a string.
You should only do this at the point at which the value is used.

View File

@ -1 +1 @@
__version__ = "0.9.8"
__version__ = "0.9.9"

View File

@ -1,5 +1,7 @@
import typing
from collections import namedtuple
from collections.abc import Sequence
from shlex import shlex
from urllib.parse import ParseResult, parse_qsl, urlencode, urlparse
from starlette.types import Scope
@ -195,6 +197,33 @@ class Secret:
return self._value
class CommaSeparatedStrings(Sequence):
def __init__(self, value: typing.Union[str, typing.Sequence[str]]):
if isinstance(value, str):
splitter = shlex(value, posix=True)
splitter.whitespace = ","
splitter.whitespace_split = True
self._items = [item.strip() for item in splitter]
else:
self._items = list(value)
def __len__(self) -> int:
return len(self._items)
def __getitem__(self, index: typing.Union[int, slice]) -> typing.Any:
return self._items[index]
def __iter__(self) -> typing.Iterator[str]:
return iter(self._items)
def __repr__(self) -> str:
list_repr = repr([item for item in self])
return "%s(%s)" % (self.__class__.__name__, list_repr)
def __str__(self) -> str:
return ", ".join([repr(item) for item in self])
class QueryParams(typing.Mapping[str, str]):
"""
An immutable multidict.

View File

@ -1,5 +1,6 @@
from starlette.datastructures import (
URL,
CommaSeparatedStrings,
DatabaseURL,
Headers,
MutableHeaders,
@ -59,6 +60,30 @@ def test_database_url():
assert u.driver == "asyncpg"
def test_csv():
csv = CommaSeparatedStrings('"localhost", "127.0.0.1", 0.0.0.0')
assert list(csv) == ["localhost", "127.0.0.1", "0.0.0.0"]
assert repr(csv) == "CommaSeparatedStrings(['localhost', '127.0.0.1', '0.0.0.0'])"
assert str(csv) == "'localhost', '127.0.0.1', '0.0.0.0'"
assert csv[0] == "localhost"
assert len(csv) == 3
csv = CommaSeparatedStrings("'localhost', '127.0.0.1', 0.0.0.0")
assert list(csv) == ["localhost", "127.0.0.1", "0.0.0.0"]
assert repr(csv) == "CommaSeparatedStrings(['localhost', '127.0.0.1', '0.0.0.0'])"
assert str(csv) == "'localhost', '127.0.0.1', '0.0.0.0'"
csv = CommaSeparatedStrings("localhost, 127.0.0.1, 0.0.0.0")
assert list(csv) == ["localhost", "127.0.0.1", "0.0.0.0"]
assert repr(csv) == "CommaSeparatedStrings(['localhost', '127.0.0.1', '0.0.0.0'])"
assert str(csv) == "'localhost', '127.0.0.1', '0.0.0.0'"
csv = CommaSeparatedStrings(["localhost", "127.0.0.1", "0.0.0.0"])
assert list(csv) == ["localhost", "127.0.0.1", "0.0.0.0"]
assert repr(csv) == "CommaSeparatedStrings(['localhost', '127.0.0.1', '0.0.0.0'])"
assert str(csv) == "'localhost', '127.0.0.1', '0.0.0.0'"
def test_url_from_scope():
u = URL(
scope={"path": "/path/to/somewhere", "query_string": b"abc=123", "headers": []}