Skip to content
Snippets Groups Projects
Commit 4792c0e3 authored by Fynn Becker's avatar Fynn Becker :crab:
Browse files

Add black and isort and apply formatting

parent 12272d52
Branches
Tags
No related merge requests found
[flake8]
max-line-length = 88
max-doc-length = 88
...@@ -16,13 +16,13 @@ default_app_config = "postgrestutils.apps.PostgrestUtilsConfig" ...@@ -16,13 +16,13 @@ default_app_config = "postgrestutils.apps.PostgrestUtilsConfig"
REPR_OUTPUT_SIZE = 20 REPR_OUTPUT_SIZE = 20
Count = enum.Enum( Count = enum.Enum(
'Count', "Count",
( (
('NONE', None), ("NONE", None),
('EXACT', 'exact'), ("EXACT", "exact"),
('PLANNED', 'planned'), ("PLANNED", "planned"),
('ESTIMATED', 'estimated') ("ESTIMATED", "estimated"),
) ),
) )
DEFAULT_SCHEMA = object() DEFAULT_SCHEMA = object()
...@@ -43,13 +43,14 @@ class Session: ...@@ -43,13 +43,14 @@ class Session:
some of these may be overridden on a per-request basis by using the `.get()` some of these may be overridden on a per-request basis by using the `.get()`
or `.filter()` methods. or `.filter()` methods.
""" """
def __init__( def __init__(
self, self,
base_uri: Optional[str] = None, base_uri: Optional[str] = None,
token: Optional[str] = None, token: Optional[str] = None,
schema: Optional[str] = None, schema: Optional[str] = None,
parse_dt: bool = True, parse_dt: bool = True,
count: Count = Count.NONE count: Count = Count.NONE,
): ):
""" """
:param base_uri: base uri of the PostgREST instance to use :param base_uri: base uri of the PostgREST instance to use
...@@ -104,7 +105,7 @@ class Session: ...@@ -104,7 +105,7 @@ class Session:
True, True,
parse_dt if parse_dt is not None else self.parse_dt, parse_dt if parse_dt is not None else self.parse_dt,
count if count is not None else self.count, count if count is not None else self.count,
**kwargs **kwargs,
) )
# populate the cache # populate the cache
# will raise ObjectDoesNotExist/MultipleObjectsReturned if no or # will raise ObjectDoesNotExist/MultipleObjectsReturned if no or
...@@ -120,7 +121,7 @@ class Session: ...@@ -120,7 +121,7 @@ class Session:
count: Optional[Count] = None, count: Optional[Count] = None,
schema: Optional[str] = None, schema: Optional[str] = None,
**kwargs **kwargs
) -> 'JsonResultSet': ) -> "JsonResultSet":
""" """
:param endpoint: specifies which endpoint to request :param endpoint: specifies which endpoint to request
:param parse_dt: whether to parse datetime strings as returned by :param parse_dt: whether to parse datetime strings as returned by
...@@ -137,20 +138,20 @@ class Session: ...@@ -137,20 +138,20 @@ class Session:
False, False,
parse_dt if parse_dt is not None else self.parse_dt, parse_dt if parse_dt is not None else self.parse_dt,
count if count is not None else self.count, count if count is not None else self.count,
**kwargs **kwargs,
) )
def _configure_session_defaults(self): def _configure_session_defaults(self):
self.session.headers['Accept'] = 'application/json' self.session.headers["Accept"] = "application/json"
if self.token: if self.token:
self.session.headers['Authorization'] = 'Bearer {}'.format(self.token) self.session.headers["Authorization"] = "Bearer {}".format(self.token)
if self.schema is not None: if self.schema is not None:
self.session.headers['Accept-Profile'] = self.schema self.session.headers["Accept-Profile"] = self.schema
def _set_schema_header(self, schema, kwargs: dict): def _set_schema_header(self, schema, kwargs: dict):
if schema is DEFAULT_SCHEMA: if schema is DEFAULT_SCHEMA:
schema = None schema = None
kwargs.setdefault('headers', dict())['Accept-Profile'] = schema kwargs.setdefault("headers", dict())["Accept-Profile"] = schema
class JsonResultSet: class JsonResultSet:
...@@ -161,7 +162,16 @@ class JsonResultSet: ...@@ -161,7 +162,16 @@ class JsonResultSet:
to ensure pythonic behavior. to ensure pythonic behavior.
Check the README for more detailed information. Check the README for more detailed information.
""" """
def __init__(self, client: Session, endpoint: str, singular: bool, parse_dt: bool, count: Count, **kwargs):
def __init__(
self,
client: Session,
endpoint: str,
singular: bool,
parse_dt: bool,
count: Count,
**kwargs
):
self._len_cache = None # type: Optional[int] self._len_cache = None # type: Optional[int]
self._result_cache = None # type: Optional[list] self._result_cache = None # type: Optional[list]
...@@ -176,7 +186,7 @@ class JsonResultSet: ...@@ -176,7 +186,7 @@ class JsonResultSet:
data = list(self[: REPR_OUTPUT_SIZE + 1]) data = list(self[: REPR_OUTPUT_SIZE + 1])
if len(data) > REPR_OUTPUT_SIZE: if len(data) > REPR_OUTPUT_SIZE:
data[-1] = "...(remaining elements truncated)..." data[-1] = "...(remaining elements truncated)..."
return '<{} {}>'.format(self.__class__.__name__, data) return "<{} {}>".format(self.__class__.__name__, data)
def __iter__(self): def __iter__(self):
self._fetch_all() self._fetch_all()
...@@ -201,19 +211,30 @@ class JsonResultSet: ...@@ -201,19 +211,30 @@ class JsonResultSet:
if not isinstance(key, (int, slice)): if not isinstance(key, (int, slice)):
raise TypeError( raise TypeError(
"{self.__class__.__name__} indices must be integers or slices, not {key.__class__.__name__}".format( "{self.__class__.__name__} indices must be integers or slices, not {key.__class__.__name__}".format(
self=self, self=self, key=key
key=key )
)
if (isinstance(key, int) and key < 0) or (
isinstance(key, slice)
and (
(key.start is not None and key.start < 0)
or (key.stop is not None and key.stop < 0)
)
):
raise ValueError(
"{self.__class__.__name__} does not support negative indexing".format(
self=self
) )
) )
if ((isinstance(key, int) and key < 0) or
(isinstance(key, slice) and ((key.start is not None and key.start < 0) or
(key.stop is not None and key.stop < 0)))):
raise ValueError("{self.__class__.__name__} does not support negative indexing".format(self=self))
if isinstance(key, slice) and key.step is not None: if isinstance(key, slice) and key.step is not None:
raise ValueError("{self.__class__.__name__} does not support stepping".format(self=self)) raise ValueError(
"{self.__class__.__name__} does not support stepping".format(self=self)
)
# cache is not populated and unbounded slice is requested, i.e. res[:] # cache is not populated and unbounded slice is requested, i.e. res[:]
if isinstance(key, slice) and all(e is None for e in (self._result_cache, key.start, key.stop)): if isinstance(key, slice) and all(
e is None for e in (self._result_cache, key.start, key.stop)
):
self._fetch_all() self._fetch_all()
if self._result_cache is not None: if self._result_cache is not None:
...@@ -223,9 +244,13 @@ class JsonResultSet: ...@@ -223,9 +244,13 @@ class JsonResultSet:
start = key.start if key.start is not None else 0 start = key.start if key.start is not None else 0
if key.stop is not None and key.stop <= start: if key.stop is not None and key.stop <= start:
return list() return list()
range = '{start}-{stop}'.format(start=start, stop=key.stop - 1 if key.stop is not None else '') range = "{start}-{stop}".format(
start=start, stop=key.stop - 1 if key.stop is not None else ""
)
return self._fetch_range(range) return self._fetch_range(range)
return self._fetch_range('{0}-{0}'.format(key))[0] # single element requested, return dict return self._fetch_range("{0}-{0}".format(key))[
0
] # single element requested, return dict
def refresh_from_pgrest(self): def refresh_from_pgrest(self):
"""Lazily refresh data from PostgREST.""" """Lazily refresh data from PostgREST."""
...@@ -240,14 +265,18 @@ class JsonResultSet: ...@@ -240,14 +265,18 @@ class JsonResultSet:
""" """
if self._len_cache is None: if self._len_cache is None:
request_kwargs = copy.deepcopy(self.request_kwargs) request_kwargs = copy.deepcopy(self.request_kwargs)
request_kwargs.setdefault('headers', dict())['Prefer'] = 'count={}'.format(self.count.value) request_kwargs.setdefault("headers", dict())["Prefer"] = "count={}".format(
request_kwargs['headers']['Range-Unit'] = 'items' self.count.value
)
request_kwargs["headers"]["Range-Unit"] = "items"
# Have to request something so just fetch the first item # Have to request something so just fetch the first item
request_kwargs['headers']['Range'] = '0-0' request_kwargs["headers"]["Range"] = "0-0"
resp = self.client.session.get(urljoin(self.client.base_uri, self.endpoint), **request_kwargs) resp = self.client.session.get(
urljoin(self.client.base_uri, self.endpoint), **request_kwargs
)
count = int(resp.headers['Content-Range'].split('/')[-1]) count = int(resp.headers["Content-Range"].split("/")[-1])
self._len_cache = count self._len_cache = count
# If the request yields only one element anyway, might as well cache # If the request yields only one element anyway, might as well cache
...@@ -266,9 +295,13 @@ class JsonResultSet: ...@@ -266,9 +295,13 @@ class JsonResultSet:
request_kwargs = copy.deepcopy(self.request_kwargs) request_kwargs = copy.deepcopy(self.request_kwargs)
if self.singular: if self.singular:
request_kwargs.setdefault('headers', dict())['Accept'] = 'application/vnd.pgrst.object+json' request_kwargs.setdefault("headers", dict())[
"Accept"
] = "application/vnd.pgrst.object+json"
resp = self.client.session.get(urljoin(self.client.base_uri, self.endpoint), **request_kwargs) resp = self.client.session.get(
urljoin(self.client.base_uri, self.endpoint), **request_kwargs
)
self._result_cache = self._parse_response(resp) self._result_cache = self._parse_response(resp)
# fetched all elements anyway, caching their length is very cheap # fetched all elements anyway, caching their length is very cheap
...@@ -281,10 +314,12 @@ class JsonResultSet: ...@@ -281,10 +314,12 @@ class JsonResultSet:
more information. more information.
""" """
request_kwargs = copy.deepcopy(self.request_kwargs) request_kwargs = copy.deepcopy(self.request_kwargs)
request_kwargs.setdefault('headers', dict())['Range-Unit'] = 'items' request_kwargs.setdefault("headers", dict())["Range-Unit"] = "items"
request_kwargs['headers']['Range'] = range request_kwargs["headers"]["Range"] = range
resp = self.client.session.get(urljoin(self.client.base_uri, self.endpoint), **request_kwargs) resp = self.client.session.get(
urljoin(self.client.base_uri, self.endpoint), **request_kwargs
)
return self._parse_response(resp) return self._parse_response(resp)
def _parse_response(self, resp): def _parse_response(self, resp):
...@@ -308,7 +343,13 @@ class JsonResultSet: ...@@ -308,7 +343,13 @@ class JsonResultSet:
raise detailed from e raise detailed from e
# fall back to raising a generic HTTPError exception # fall back to raising a generic HTTPError exception
raise type(e)(resp.status_code, resp.reason, resp.text, response=resp, request=e.request) raise type(e)(
resp.status_code,
resp.reason,
resp.text,
response=resp,
request=e.request,
)
if self.parse_dt: if self.parse_dt:
json_result = resp.json(object_hook=datetime_parser) json_result = resp.json(object_hook=datetime_parser)
...@@ -325,7 +366,7 @@ class JsonResultSet: ...@@ -325,7 +366,7 @@ class JsonResultSet:
observed row count. observed row count.
""" """
detail_regex = re.compile( detail_regex = re.compile(
r'Results contain (?P<row_count>\d+) rows, application/vnd\.pgrst\.object\+json requires 1 row' r"Results contain (?P<row_count>\d+) rows, application/vnd\.pgrst\.object\+json requires 1 row"
) )
try: try:
json = resp.json() json = resp.json()
...@@ -335,9 +376,9 @@ class JsonResultSet: ...@@ -335,9 +376,9 @@ class JsonResultSet:
# more insights. # more insights.
logger.warning("Unparsable 406: {}".format(resp.text)) logger.warning("Unparsable 406: {}".format(resp.text))
else: else:
result = re.match(detail_regex, json['details']) result = re.match(detail_regex, json["details"])
if result is not None: if result is not None:
row_count = int(result.group('row_count')) row_count = int(result.group("row_count"))
if row_count == 0: if row_count == 0:
raise ObjectDoesNotExist(json) raise ObjectDoesNotExist(json)
else: else:
......
...@@ -9,8 +9,7 @@ from datetime import datetime ...@@ -9,8 +9,7 @@ from datetime import datetime
from typing import Union from typing import Union
from django.conf import settings from django.conf import settings
from django.utils import dateparse from django.utils import dateparse, timezone as django_tz
from django.utils import timezone as django_tz
import postgrestutils import postgrestutils
...@@ -20,19 +19,17 @@ from .signals import user_account_fetched ...@@ -20,19 +19,17 @@ from .signals import user_account_fetched
def autofetch(sender, **kwargs): def autofetch(sender, **kwargs):
"""Fetch user account on login based on the AUTOFETCH configuration""" """Fetch user account on login based on the AUTOFETCH configuration"""
payload = { payload = {"select": app_settings.AUTOFETCH}
'select': app_settings.AUTOFETCH
}
if settings.DEBUG: if settings.DEBUG:
# prod uuids != dev uuids, fall back on matching accounts by username # prod uuids != dev uuids, fall back on matching accounts by username
payload['username'] = 'eq.{}'.format(kwargs['user'].get_username()) payload["username"] = "eq.{}".format(kwargs["user"].get_username())
else: else:
payload['auth_provider_uid'] = 'eq.{}'.format(kwargs['user'].sso_mapping.uuid) payload["auth_provider_uid"] = "eq.{}".format(kwargs["user"].sso_mapping.uuid)
with postgrestutils.Session() as s: with postgrestutils.Session() as s:
account = s.get('account', params=payload) account = s.get("account", params=payload)
user_account_fetched.send(sender=None, request=kwargs['request'], account=account) user_account_fetched.send(sender=None, request=kwargs["request"], account=account)
def _try_django_parse_dt(value: str) -> Union[datetime, str]: def _try_django_parse_dt(value: str) -> Union[datetime, str]:
......
...@@ -16,6 +16,7 @@ for k in list(globals().keys()): # list prevents errors on changes ...@@ -16,6 +16,7 @@ for k in list(globals().keys()): # list prevents errors on changes
if _DJANGO: if _DJANGO:
from django.conf import settings as django_settings from django.conf import settings as django_settings
from django.core.exceptions import ImproperlyConfigured from django.core.exceptions import ImproperlyConfigured
try: try:
new_value = getattr(django_settings, "POSTGREST_UTILS_" + k) new_value = getattr(django_settings, "POSTGREST_UTILS_" + k)
globals()[k] = getattr(django_settings, "POSTGREST_UTILS_" + k) globals()[k] = getattr(django_settings, "POSTGREST_UTILS_" + k)
...@@ -25,6 +26,7 @@ for k in list(globals().keys()): # list prevents errors on changes ...@@ -25,6 +26,7 @@ for k in list(globals().keys()): # list prevents errors on changes
pass # django is installed and used but the setting is not present pass # django is installed and used but the setting is not present
else: else:
import os import os
try: try:
globals()[k] = os.environ["POSTGREST_UTILS_" + k] globals()[k] = os.environ["POSTGREST_UTILS_" + k]
except KeyError: except KeyError:
......
import django.dispatch import django.dispatch
user_account_fetched = django.dispatch.Signal(providing_args=['request', 'account']) user_account_fetched = django.dispatch.Signal(providing_args=["request", "account"])
...@@ -12,7 +12,8 @@ if _DJANGO: ...@@ -12,7 +12,8 @@ if _DJANGO:
logger = logging.getLogger("postgrestutils") logger = logging.getLogger("postgrestutils")
# this regex matches postgres' JSON formatting for timestamps # this regex matches postgres' JSON formatting for timestamps
JSON_TIMESTAMP_REGEX = re.compile(r"(?P<year>\d{4})-" JSON_TIMESTAMP_REGEX = re.compile(
r"(?P<year>\d{4})-"
r"(?P<month>\d{2})-" r"(?P<month>\d{2})-"
r"(?P<day>\d{2})" r"(?P<day>\d{2})"
r"T(?P<hour>\d{2}):" r"T(?P<hour>\d{2}):"
...@@ -21,7 +22,8 @@ JSON_TIMESTAMP_REGEX = re.compile(r"(?P<year>\d{4})-" ...@@ -21,7 +22,8 @@ JSON_TIMESTAMP_REGEX = re.compile(r"(?P<year>\d{4})-"
r"(?P<microsecond>\d{1,6})" r"(?P<microsecond>\d{1,6})"
r"((?P<offsetsign>[+-])" r"((?P<offsetsign>[+-])"
r"(?P<offsethours>\d{2}):" r"(?P<offsethours>\d{2}):"
r"(?P<offsetminutes>\d{2}))?$") r"(?P<offsetminutes>\d{2}))?$"
)
def _clean_parts(parts: Dict[str, str]): def _clean_parts(parts: Dict[str, str]):
...@@ -44,12 +46,17 @@ def _try_python_parse_dt(value: str) -> Union[datetime, str]: ...@@ -44,12 +46,17 @@ def _try_python_parse_dt(value: str) -> Union[datetime, str]:
match = JSON_TIMESTAMP_REGEX.match(value) match = JSON_TIMESTAMP_REGEX.match(value)
if match: if match:
parts = _clean_parts(match.groupdict()) parts = _clean_parts(match.groupdict())
if parts.get('offsetsign') and parts.get('offsethours') and parts.get('offsetminutes'): if (
sign = -1 if parts.pop('offsetsign', '+') == '-' else 1 parts.get("offsetsign")
and parts.get("offsethours")
and parts.get("offsetminutes")
):
sign = -1 if parts.pop("offsetsign", "+") == "-" else 1
tz = timezone( tz = timezone(
offset=sign * timedelta( offset=sign
hours=int(parts.pop('offsethours')), * timedelta(
minutes=int(parts.pop('offsetminutes')) hours=int(parts.pop("offsethours")),
minutes=int(parts.pop("offsetminutes")),
) )
) )
parsed_dt = datetime(**parts).replace(tzinfo=tz).astimezone() parsed_dt = datetime(**parts).replace(tzinfo=tz).astimezone()
......
[tool.black]
line-length = 88
target-version = ["py35"]
[tool.isort]
profile = "black"
combine_as_imports = true
default_section = "THIRDPARTY"
known_first_party = "postgrestutils"
known_django = "django"
sections = "FUTURE,STDLIB,DJANGO,THIRDPARTY,FIRSTPARTY,LOCALFOLDER"
[flake8]
max-line-length = 119
exclude = **/settings/*
[isort]
combine_as_import = true
default_section = THIRDPARTY
include_trailing_comma = true
known_first_party = postgrestutils
known_django = django
sections = FUTURE,STDLIB,DJANGO,THIRDPARTY,FIRSTPARTY,LOCALFOLDER
line_length = 79
multi_line_output = 5
not_skip = __init__.py
...@@ -9,33 +9,27 @@ os.chdir(os.path.normpath(os.path.join(os.path.abspath(__file__), os.pardir))) ...@@ -9,33 +9,27 @@ os.chdir(os.path.normpath(os.path.join(os.path.abspath(__file__), os.pardir)))
setup( setup(
name='postgrestutils', name="postgrestutils",
version='1.0.0', version="1.0.0",
packages=find_packages(), packages=find_packages(),
include_package_data=True, include_package_data=True,
license='BSD', license="BSD",
description='Some helpers to use our postgREST API(s)', description="Some helpers to use our postgREST API(s)",
long_description=README, long_description=README,
url='https://lab.it.hs-hannover.de/tools/postgrestutils', url="https://lab.it.hs-hannover.de/tools/postgrestutils",
author='Fynn Becker', author="Fynn Becker",
author_email='fynn.becker@hs-hannover.de', author_email="fynn.becker@hs-hannover.de",
zip_safe=False, zip_safe=False,
install_requires=[ install_requires=["requests>=2.19.1,<3.0.0"],
'requests>=2.19.1,<3.0.0' extras_require={"dev": ["requests-mock"]},
],
extras_require={
'dev': [
'requests-mock'
]
},
classifiers=[ classifiers=[
'Environment :: Web Environment', "Environment :: Web Environment",
'Intended Audience :: Developers', "Intended Audience :: Developers",
'License :: OSI Approved :: BSD License', "License :: OSI Approved :: BSD License",
'Operating System :: OS Independent', "Operating System :: OS Independent",
'Programming Language :: Python', "Programming Language :: Python",
'Programming Language :: Python :: 3.4', "Programming Language :: Python :: 3.4",
'Topic :: Internet :: WWW/HTTP', "Topic :: Internet :: WWW/HTTP",
'Topic :: Internet :: WWW/HTTP :: Dynamic Content', "Topic :: Internet :: WWW/HTTP :: Dynamic Content",
], ],
) )
This diff is collapsed.
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment