From bc62db181c9e91ddc169cd51debf5638fdbe66ab Mon Sep 17 00:00:00 2001 From: Ivan Grebenshchikov Date: Thu, 8 Jun 2023 22:37:42 +0200 Subject: [PATCH] LITE-27567 Add black * Double quotes replaced with single quotes --- README.md | 5 +- dj_rql/_dataclasses.py | 5 +- dj_rql/constants.py | 3 +- dj_rql/drf/backend.py | 22 +- dj_rql/drf/compat.py | 9 +- dj_rql/drf/paginations.py | 18 +- dj_rql/drf/serializers.py | 9 +- dj_rql/filter_cls.py | 335 +++++++++------ .../management/commands/generate_rql_class.py | 32 +- dj_rql/openapi.py | 4 +- dj_rql/transformer.py | 26 +- dj_rql/utils.py | 8 +- poetry.lock | 405 +++++++++++------- pyproject.toml | 7 + tests/constants.py | 36 +- tests/dj_rf/filters.py | 382 +++++++++-------- tests/dj_rf/models.py | 15 +- .../test_commands/test_generate_rql_class.py | 32 +- tests/test_drf/test_common_drf_backend.py | 3 +- tests/test_drf/test_django_filters_backend.py | 378 +++++++++------- tests/test_drf/test_dynamic_filter.py | 6 +- tests/test_drf/test_pagination.py | 3 +- tests/test_drf/test_serializers.py | 8 +- tests/test_filter_cls/conftest.py | 1 + tests/test_filter_cls/test_apply_filters.py | 175 ++++---- .../test_filter_cls/test_fields_filtering.py | 117 +++-- tests/test_filter_cls/test_initialization.py | 218 ++++++---- tests/test_filter_cls/test_select.py | 6 +- tests/test_qs.py | 3 +- tests/test_utils.py | 5 +- 30 files changed, 1390 insertions(+), 886 deletions(-) diff --git a/README.md b/README.md index d35a9ea..06da1b4 100644 --- a/README.md +++ b/README.md @@ -275,8 +275,9 @@ Development 1. Python 3.8+ 2. Install poetry: `pip install poetry` 3. Install dependencies: `poetry install` -4. We use `isort` library to order and format our imports, and we check it using `flake8-isort` library (automatically on `flake8` run). -For convenience you may run `isort .` to order imports. +4. We use `isort` library to order and format our imports, and `black` - to format the code. +We check it using `flake8-isort` and `flake8-black` libraries (automatically on `flake8` run). +For convenience you may run `isort . && black .` to format the code. 5. Run flake8: `poetry run flake8` Testing diff --git a/dj_rql/_dataclasses.py b/dj_rql/_dataclasses.py index 175873d..cef5624 100644 --- a/dj_rql/_dataclasses.py +++ b/dj_rql/_dataclasses.py @@ -21,8 +21,9 @@ def __init__(self, queryset, select_data, filter_tree, filter_node=None, filter_ class FilterArgs: - def __init__(self, filter_name, operator, str_value, list_operator=None, - namespace=None, **kwargs): + def __init__( + self, filter_name, operator, str_value, list_operator=None, namespace=None, **kwargs + ): """ :param str filter_name: Full filter name (f.e. ns1.ns2.filter1) :param str operator: RQL operator (f.e. eq, like, etc.) diff --git a/dj_rql/constants.py b/dj_rql/constants.py index 71d524a..5aa8700 100644 --- a/dj_rql/constants.py +++ b/dj_rql/constants.py @@ -48,7 +48,8 @@ class FilterTypes(FT): def field_filter_type(cls, field): return next( ( - filter_type for base_cls, filter_type in cls.mapper + filter_type + for base_cls, filter_type in cls.mapper if issubclass(field.__class__, base_cls) ), cls.STRING, diff --git a/dj_rql/drf/backend.py b/dj_rql/drf/backend.py index 603a5aa..d85f131 100644 --- a/dj_rql/drf/backend.py +++ b/dj_rql/drf/backend.py @@ -46,6 +46,7 @@ def get_rql_filter_class(self): return ModelFilterClass ``` """ + OPENAPI_RETRIEVE_SPECIFICATION = False _CACHES = {} @@ -59,11 +60,13 @@ def filter_queryset(self, request, queryset, view): filter_instance = self._get_filter_instance(filter_class, queryset, view) query = self.get_query(filter_instance, request, view) - can_query_be_cached = all(( - filter_class.QUERIES_CACHE_BACKEND, - filter_class.QUERIES_CACHE_SIZE, - request.method in ('GET', 'HEAD', 'OPTIONS'), - )) + can_query_be_cached = all( + ( + filter_class.QUERIES_CACHE_BACKEND, + filter_class.QUERIES_CACHE_SIZE, + request.method in ('GET', 'HEAD', 'OPTIONS'), + ), + ) if can_query_be_cached: # We must use the combination of queryset and query to make a cache key as # queryset can already contain some filters (e.x. based on authentication) @@ -117,7 +120,8 @@ def get_query(cls, filter_instance, request, view): def _get_or_init_cache(cls, filter_class, view): qual_name = cls._get_filter_cls_qual_name(view, filter_class) return cls._CACHES.setdefault( - qual_name, filter_class.QUERIES_CACHE_BACKEND(int(filter_class.QUERIES_CACHE_SIZE)), + qual_name, + filter_class.QUERIES_CACHE_BACKEND(int(filter_class.QUERIES_CACHE_SIZE)), ) @classmethod @@ -135,6 +139,8 @@ def _get_filter_instance(cls, filter_class, queryset, view): @staticmethod def _get_filter_cls_qual_name(view, filter_class): return '{0}.{1}+{2}.{3}'.format( - view.__class__.__module__, view.__class__.__name__, - filter_class.__module__, filter_class.__name__, + view.__class__.__module__, + view.__class__.__name__, + filter_class.__module__, + filter_class.__name__, ) diff --git a/dj_rql/drf/compat.py b/dj_rql/drf/compat.py index a82bfca..3e205d4 100644 --- a/dj_rql/drf/compat.py +++ b/dj_rql/drf/compat.py @@ -28,6 +28,7 @@ class CompatibilityRQLFilterBackend(RQLFilterBackend): filter backend (without raising API version number or losing compatibility), this base compatibility DRF backend must be inherited from. """ + @classmethod def get_query(cls, filter_instance, request, view): try: @@ -70,6 +71,7 @@ class DjangoFiltersRQLFilterBackend(CompatibilityRQLFilterBackend): * ?&& syntax in queries; * etc. """ + RESERVED_ORDERING_WORDS = {'order_by', 'ordering'} _POSSIBLE_DF_LOOKUPS = DJL.all() @@ -161,7 +163,9 @@ def get_rql_query(cls, filter_instance, request, query_string): one_filter_value_pairs = [] for value in request.query_params.getlist(filter_name): name_value_pair = cls._get_one_filter_value_pair( - filter_instance, filter_name, value, + filter_instance, + filter_name, + value, ) if name_value_pair is not None: one_filter_value_pairs.append(name_value_pair) @@ -202,7 +206,8 @@ def _convert_filter_to_rql(cls, filter_name, value): if lookup == DJL.IN: return 'in({0},({1}))'.format( - filter_base, ','.join(cls._add_quotes_to_value(v) for v in value.split(',') if v), + filter_base, + ','.join(cls._add_quotes_to_value(v) for v in value.split(',') if v), ) if lookup == DJL.NULL: diff --git a/dj_rql/drf/paginations.py b/dj_rql/drf/paginations.py index 431960b..c177546 100644 --- a/dj_rql/drf/paginations.py +++ b/dj_rql/drf/paginations.py @@ -13,7 +13,7 @@ class RQLLimitOffsetPagination(LimitOffsetPagination): - """ RQL limit offset pagination. """ + """RQL limit offset pagination.""" def __init__(self, *args, **kwargs): super(RQLLimitOffsetPagination, self).__init__(*args, **kwargs) @@ -37,9 +37,11 @@ def paginate_queryset(self, queryset, request, view=None): try: self._rql_limit, self._rql_offset = RQLLimitOffsetTransformer().transform(rql_ast) except LarkError: - raise RQLFilterParsingError(details={ - 'error': 'Limit and offset are set incorrectly.', - }) + raise RQLFilterParsingError( + details={ + 'error': 'Limit and offset are set incorrectly.', + }, + ) self.limit = self.get_limit(request) if self.limit == 0: @@ -62,7 +64,7 @@ def paginate_queryset(self, queryset, request, view=None): if self.limit + self.offset > self.count: self.limit = self.count - self.offset - return list(queryset[self.offset:self.offset + self.limit]) + return list(queryset[self.offset : self.offset + self.limit]) def get_limit(self, *args): if self._rql_limit is not None: @@ -82,7 +84,7 @@ def get_offset(self, *args): class RQLContentRangeLimitOffsetPagination(RQLLimitOffsetPagination): - """ RQL RFC2616 limit offset pagination. + """RQL RFC2616 limit offset pagination. Examples: ``` @@ -96,6 +98,8 @@ class RQLContentRangeLimitOffsetPagination(RQLLimitOffsetPagination): def get_paginated_response(self, data): length = len(data) - 1 if data else 0 content_range = 'items {0}-{1}/{2}'.format( - self.offset, self.offset + length, self.count, + self.offset, + self.offset + length, + self.count, ) return Response(data, headers={'Content-Range': content_range}) diff --git a/dj_rql/drf/serializers.py b/dj_rql/drf/serializers.py index 640b7e9..eaab7c3 100644 --- a/dj_rql/drf/serializers.py +++ b/dj_rql/drf/serializers.py @@ -19,7 +19,7 @@ def apply_rql_select(self): for field_name, is_included in rql_select['select'].items(): split_field_name = field_name.split('.') - is_current_level_field = (len(split_field_name) == 1) + is_current_level_field = len(split_field_name) == 1 current_depth_field_name = split_field_name[0] if is_current_level_field: @@ -48,8 +48,11 @@ def _get_deeper_rql_select(self): def _get_field_rql_select(self, field): take_parent = bool( - field.parent and getattr(field.parent, 'many', False) and isinstance( - field, field.parent.child.__class__, + field.parent + and getattr(field.parent, 'many', False) + and isinstance( + field, + field.parent.child.__class__, ), ) if take_parent: diff --git a/dj_rql/filter_cls.py b/dj_rql/filter_cls.py index 76d984b..579075b 100644 --- a/dj_rql/filter_cls.py +++ b/dj_rql/filter_cls.py @@ -169,9 +169,11 @@ def build_q_for_custom_filter(self, data: FilterArgs) -> Q: Raises: RQLFilterParsingError: Filter logic is not implemented. """ - raise RQLFilterParsingError(details={ - 'error': 'Filter logic is not implemented: {0}.'.format(data.filter_name), - }) + raise RQLFilterParsingError( + details={ + 'error': 'Filter logic is not implemented: {0}.'.format(data.filter_name), + }, + ) def build_name_for_custom_ordering(self, filter_name: str) -> str: """Builder for ordering name of custom filter. @@ -185,9 +187,11 @@ def build_name_for_custom_ordering(self, filter_name: str) -> str: Raises: RQLFilterParsingError: Ordering logic is not implemented. """ - raise RQLFilterParsingError(details={ - 'error': 'Ordering logic is not implemented: {0}.'.format(filter_name), - }) + raise RQLFilterParsingError( + details={ + 'error': 'Ordering logic is not implemented: {0}.'.format(filter_name), + }, + ) def optimize_field(self, data: OptimizationArgs): """This method can be overridden to apply complex DB optimization logic. @@ -305,9 +309,11 @@ def build_q_for_filter(self, data: FilterArgs) -> Q: filter_basename, namespace = data.filter_basename, data.namespace if namespace and filter_basename == RQL_SEARCH_PARAM: - raise RQLFilterLookupError(details={ - 'error': f'Filter "{filter_basename}" can be applied only on top level.', - }) + raise RQLFilterLookupError( + details={ + 'error': f'Filter "{filter_basename}" can be applied only on top level.', + }, + ) if filter_name == RQL_SEARCH_PARAM: return self._build_q_for_search(operator, str_value) @@ -328,19 +334,31 @@ def build_q_for_filter(self, data: FilterArgs) -> Q: list_filter_lookup = FilterLookups.OUT if list_filter_lookup not in available_lookups: - raise RQLFilterLookupError(**self._get_error_details( - filter_name, list_filter_lookup, str_value, - )) + raise RQLFilterLookupError( + **self._get_error_details( + filter_name, + list_filter_lookup, + str_value, + ) + ) null_values = base_item.get('null_values', set()) filter_lookup = self._get_filter_lookup( - filter_name, operator, str_value, available_lookups, null_values, + filter_name, + operator, + str_value, + available_lookups, + null_values, ) django_field = base_item.get('field') if django_field and isinstance(django_field, SelectField): - raise RQLFilterLookupError(**self._get_error_details( - filter_name, filter_lookup, str_value, - )) + raise RQLFilterLookupError( + **self._get_error_details( + filter_name, + filter_lookup, + str_value, + ) + ) django_lookup = self._get_django_lookup(filter_lookup, str_value, null_values) @@ -349,19 +367,26 @@ def build_q_for_filter(self, data: FilterArgs) -> Q: typed_value = None if django_field is not None: typed_value = self._get_typed_value( - filter_name, filter_lookup, str_value, django_field, - use_repr, null_values, django_lookup, + filter_name, + filter_lookup, + str_value, + django_field, + use_repr, + null_values, + django_lookup, ) if base_item.get('custom'): - return self.build_q_for_custom_filter(FilterArgs( - filter_name, - operator, - str_value, - list_operator=list_operator, - filter_lookup=filter_lookup, - django_lookup=django_lookup, - )) + return self.build_q_for_custom_filter( + FilterArgs( + filter_name, + operator, + str_value, + list_operator=list_operator, + filter_lookup=filter_lookup, + django_lookup=django_lookup, + ), + ) if not isinstance(filter_item, iterable_types): return self._build_django_q(filter_item, django_lookup, filter_lookup, typed_value) @@ -416,9 +441,11 @@ def _build_select_data_for_inclusion(self, filter_name, inclusions, exclusions): for index, part in enumerate(filter_name_parts): if part not in select_tree: - raise RQLFilterParsingError(details={ - 'error': 'Bad select filter: {0}.'.format(filter_name), - }) + raise RQLFilterParsingError( + details={ + 'error': 'Bad select filter: {0}.'.format(filter_name), + }, + ) current_part = '{0}.{1}'.format(parent_parts, part) if parent_parts else part @@ -445,9 +472,11 @@ def _build_select_data_for_exclusions(self, exclude_select, inclusions, exclusio for filter_name in real_exclude_select: if filter_name in inclusions: - raise RQLFilterParsingError(details={ - 'error': 'Bad select filter: incompatible properties.', - }) + raise RQLFilterParsingError( + details={ + 'error': 'Bad select filter: incompatible properties.', + }, + ) select_tree = self.select_tree filter_name_parts = filter_name.split('.') @@ -455,9 +484,11 @@ def _build_select_data_for_exclusions(self, exclude_select, inclusions, exclusio for index, part in enumerate(filter_name_parts): if part not in select_tree: - raise RQLFilterParsingError(details={ - 'error': 'Bad select filter: -{0}.'.format(filter_name), - }) + raise RQLFilterParsingError( + details={ + 'error': 'Bad select filter: -{0}.'.format(filter_name), + }, + ) if index != last_filter_name_part_index: select_tree = select_tree[part]['fields'] @@ -470,7 +501,7 @@ def _build_select_data_for_exclusions(self, exclude_select, inclusions, exclusio def _prepare_selects(select): include_select, exclude_select = [], set() for s_prop in select: - is_included = (s_prop[0] != RQL_MINUS) + is_included = s_prop[0] != RQL_MINUS filter_name = s_prop[1:] if s_prop[0] in (RQL_MINUS, RQL_PLUS) else s_prop if is_included: @@ -482,9 +513,11 @@ def _prepare_selects(select): def _build_q_for_search(self, operator, str_value): if operator != ComparisonOperators.EQ: - raise RQLFilterParsingError(details={ - 'error': 'Bad search filter: {0}.'.format(operator), - }) + raise RQLFilterParsingError( + details={ + 'error': 'Bad search filter: {0}.'.format(operator), + }, + ) unquoted_value = self.remove_quotes(str_value) if not unquoted_value: @@ -498,9 +531,13 @@ def _build_q_for_search(self, operator, str_value): q = self._build_q_for_extended_search(unquoted_value) for filter_name in self.search_filters: - q |= self.build_q_for_filter(FilterArgs( - filter_name, SearchOperators.I_LIKE, unquoted_value, - )) + q |= self.build_q_for_filter( + FilterArgs( + filter_name, + SearchOperators.I_LIKE, + unquoted_value, + ), + ) return q @@ -510,7 +547,8 @@ def _build_q_for_extended_search(self, str_value): for django_orm_route in self.EXTENDED_SEARCH_ORM_ROUTES: django_lookup = self._get_searching_django_lookup( - extended_search_filter_lookup, str_value, + extended_search_filter_lookup, + str_value, ) typed_value = self._get_searching_typed_value(django_lookup, str_value) q |= self._build_django_q( @@ -565,25 +603,31 @@ def _apply_ordering(self, qs, properties): return qs if len(properties) > 1: - raise RQLFilterParsingError(details={ - 'error': 'Bad ordering filter: query can contain only one ordering operation.', - }) + raise RQLFilterParsingError( + details={ + 'error': 'Bad ordering filter: query can contain only one ordering operation.', + }, + ) if len(properties[0]) > self.MAX_ORDERING_LENGTH_IN_QUERY: - raise RQLFilterParsingError(details={ - 'error': 'Bad ordering filter: max allowed number is {n}.'.format( - n=self.MAX_ORDERING_LENGTH_IN_QUERY, - ), - }) + raise RQLFilterParsingError( + details={ + 'error': 'Bad ordering filter: max allowed number is {n}.'.format( + n=self.MAX_ORDERING_LENGTH_IN_QUERY, + ), + }, + ) ordering_fields = [] perm = [] for prop in properties[0]: filter_name, sign = self._get_filter_name_with_sign_for_ordering(prop) if filter_name not in self.ordering_filters: - raise RQLFilterParsingError(details={ - 'error': 'Bad ordering filter: {0}.'.format(filter_name), - }) + raise RQLFilterParsingError( + details={ + 'error': 'Bad ordering filter: {0}.'.format(filter_name), + }, + ) perm.append('{0}{1}'.format(sign, filter_name)) filters = self.filters[filter_name] @@ -598,9 +642,11 @@ def _apply_ordering(self, qs, properties): perms = self.allowed_ordering_permutations if perms and tuple(perm) not in perms: - raise RQLFilterParsingError(details={ - 'error': 'Bad ordering filter: permutation not allowed.', - }) + raise RQLFilterParsingError( + details={ + 'error': 'Bad ordering filter: permutation not allowed.', + }, + ) return qs.order_by(*ordering_fields) @@ -622,7 +668,7 @@ def _get_filter_ordering_name(self, filter_item, filter_name): return filter_item['orm_route'] def _build_filters(self, filters, **kwargs): - """ Converter of provided nested filter configuration to linear inner representation. """ + """Converter of provided nested filter configuration to linear inner representation.""" filter_route = kwargs.get('filter_route', '') orm_route = kwargs.get('orm_route', '') orm_model = kwargs.get('orm_model') @@ -642,7 +688,8 @@ def _build_filters(self, filters, **kwargs): field_orm_route = '{0}{1}'.format(orm_route, item) field = self._get_field(_model, item) self._add_filter_item( - field_filter_route, self._build_mapped_item(field, field_orm_route), + field_filter_route, + self._build_mapped_item(field, field_orm_route), ) self._fill_select_tree(item, field_filter_route, select_tree, parent_qs=parent_qs) continue @@ -650,7 +697,8 @@ def _build_filters(self, filters, **kwargs): if 'namespace' in item: for option in ('filter', 'dynamic', 'custom'): e = "{0}: '{1}' is not supported by namespaces.".format( - item['namespace'], option, + item['namespace'], + option, ) assert option not in item, e @@ -659,9 +707,13 @@ def _build_filters(self, filters, **kwargs): orm_field_name = item.get('source', namespace) related_orm_route = '{0}{1}__'.format(orm_route, orm_field_name) - related_model = self._get_field_related_model(self._get_field( - _model, orm_field_name, get_related=True, - )) + related_model = self._get_field_related_model( + self._get_field( + _model, + orm_field_name, + get_related=True, + ), + ) qs = item.get('qs') tree, p_qs = self._fill_select_tree( @@ -690,14 +742,16 @@ def _build_filters(self, filters, **kwargs): field_filter_route = '{0}{1}'.format(filter_route, filter_name) self._fill_select_tree( - filter_name, field_filter_route, select_tree, + filter_name, + field_filter_route, + select_tree, hidden=item.get('hidden', False), qs=item.get('qs'), parent_qs=parent_qs, ) if item.get('custom', False): - assert 'lookups' in item, "Custom filters must specify possible lookups." + assert 'lookups' in item, 'Custom filters must specify possible lookups.' self._add_filter_item(field_filter_route, item) self._register_ordering_and_search(item, field_filter_route) @@ -706,11 +760,20 @@ def _build_filters(self, filters, **kwargs): self._check_use_repr(item, field_filter_route) self._check_dynamic(item, field_filter_route, filter_route) self._build_filters_for_common_item( - item, field_filter_route, orm_route, _model, distinct, + item, + field_filter_route, + orm_route, + _model, + distinct, ) def _build_filters_for_common_item( - self, item, field_filter_route, orm_route, orm_model, distinct, + self, + item, + field_filter_route, + orm_route, + orm_model, + distinct, ): filter_name = item['filter'] field = item.get('field') @@ -738,9 +801,16 @@ def _build_filters_for_common_item( self._add_filter_item(field_filter_route, items) self._register_ordering_and_search(item, field_filter_route) - def _fill_select_tree(self, f_name, full_f_name, select_tree, - namespace=False, hidden=False, qs=None, parent_qs=None): - + def _fill_select_tree( + self, + f_name, + full_f_name, + select_tree, + namespace=False, + hidden=False, + qs=None, + parent_qs=None, + ): if not self.SELECT: return select_tree, None @@ -763,13 +833,16 @@ def _fill_select_tree(self, f_name, full_f_name, select_tree, path = '' for index, filter_name_part in enumerate(filter_name_parts): path += filter_name_part - current_select_tree.setdefault(filter_name_part, { - 'hidden': hidden, - 'fields': {}, - 'namespace': namespace or (index != last_filter_name_part_index), - 'qs': changed_qs, - 'path': path if is_logical_namespace else full_f_name, - }) + current_select_tree.setdefault( + filter_name_part, + { + 'hidden': hidden, + 'fields': {}, + 'namespace': namespace or (index != last_filter_name_part_index), + 'qs': changed_qs, + 'path': path if is_logical_namespace else full_f_name, + }, + ) current_select_tree = current_select_tree[filter_name_part]['fields'] path += '.' @@ -813,8 +886,7 @@ def _validate_and_store_allowed_ordering_permutations(self): assert filter_name in self.ordering_filters, e self.allowed_ordering_permutations = { - tuple(s[1:] if s[0] == '+' else s for s in t) - for t in perms + tuple(s[1:] if s[0] == '+' else s for s in t) for t in perms } @classmethod @@ -901,17 +973,25 @@ def _get_filter_lookup(cls, filter_name, operator, str_value, available_lookups, if str_value in null_values: null_lookups = {FilterLookups.EQ, FilterLookups.NE} if (FilterLookups.NULL not in available_lookups) or (filter_lookup not in null_lookups): - raise RQLFilterLookupError(**cls._get_error_details( - filter_name, filter_lookup, str_value, - )) + raise RQLFilterLookupError( + **cls._get_error_details( + filter_name, + filter_lookup, + str_value, + ) + ) if str_value == RQL_EMPTY: available_lookups = {FilterLookups.EQ, FilterLookups.NE} if filter_lookup not in available_lookups: - raise RQLFilterLookupError(**cls._get_error_details( - filter_name, filter_lookup, str_value, - )) + raise RQLFilterLookupError( + **cls._get_error_details( + filter_name, + filter_lookup, + str_value, + ) + ) return filter_lookup @@ -957,8 +1037,16 @@ def _get_searching_django_lookup(cls, filter_lookup, str_value): return getattr(DjangoLookups, '{0}{1}'.format(prefix, pattern)) @classmethod - def _get_typed_value(cls, filter_name, filter_lookup, str_value, django_field, - use_repr, null_values, django_lookup): + def _get_typed_value( + cls, + filter_name, + filter_lookup, + str_value, + django_field, + use_repr, + null_values, + django_lookup, + ): if str_value in null_values: return True @@ -969,17 +1057,24 @@ def _get_typed_value(cls, filter_name, filter_lookup, str_value, django_field, typed_value = cls._convert_value(django_field, str_value, use_repr=use_repr) return typed_value except (ValueError, TypeError, decimal.InvalidOperation): - raise RQLFilterValueError(**cls._get_error_details( - filter_name, filter_lookup, str_value, - )) + raise RQLFilterValueError( + **cls._get_error_details( + filter_name, + filter_lookup, + str_value, + ) + ) @classmethod def _reflect_like_value(cls, str_value): star_replacer = uuid4().hex - return '\\'.join( - v.replace(r'\{0}'.format(RQL_ANY_SYMBOL), star_replacer) - for v in cls.remove_quotes(str_value).split(r'\\') - ), star_replacer + return ( + '\\'.join( + v.replace(r'\{0}'.format(RQL_ANY_SYMBOL), star_replacer) + for v in cls.remove_quotes(str_value).split(r'\\') + ), + star_replacer, + ) @classmethod def _get_searching_typed_value(cls, django_lookup, str_value): @@ -999,7 +1094,8 @@ def _get_searching_typed_value(cls, django_lookup, str_value): new_val = new_val[1:] if val[0] == RQL_ANY_SYMBOL else '^{0}'.format(new_val) new_val = new_val[:-1] if val[-1] == RQL_ANY_SYMBOL else '{0}$'.format(new_val) return new_val.replace(RQL_ANY_SYMBOL, any_symbol_regex).replace( - star_replacer, RQL_ANY_SYMBOL, + star_replacer, + RQL_ANY_SYMBOL, ) @staticmethod @@ -1011,10 +1107,7 @@ def _escape_regex_special_symbols(str_value): or misinterpreted (symbols range). Regex is not supported by RQL so we can safely escape redundant special symbols. """ - return ( - re.escape(str_value) - .replace(r'\{0}'.format(RQL_ANY_SYMBOL), RQL_ANY_SYMBOL) - ) + return re.escape(str_value).replace(r'\{0}'.format(RQL_ANY_SYMBOL), RQL_ANY_SYMBOL) @classmethod def _convert_value(cls, django_field, str_value, use_repr=False): @@ -1054,7 +1147,7 @@ def _convert_value(cls, django_field, str_value, use_repr=False): def _convert_decimal_value(cls, value, field): if '.' in value: integer_part, fractional_part = value.split('.', 1) - value = integer_part + '.' + fractional_part[:cls._get_decimal_field_precision(field)] + value = integer_part + '.' + fractional_part[: cls._get_decimal_field_precision(field)] return decimal.Decimal(value) @@ -1095,9 +1188,7 @@ def _get_choices_field_db_value(cls, value, choices, filter_type, use_repr): # F.e.: `choices=((0, 'v0'), (1, 'v1'))` can be filtered by 'v1' if `use_repr=True` or # by '1' if `use_repr=False` if isinstance(choices[0], tuple): - iterator = iter( - choice[0] for choice in choices if str(choice[int(use_repr)]) == value - ) + iterator = iter(choice[0] for choice in choices if str(choice[int(use_repr)]) == value) else: iterator = iter(choice for choice in choices if choice == value) try: @@ -1110,9 +1201,7 @@ def _get_choices_field_db_value(cls, value, choices, filter_type, use_repr): def _get_choice_class_db_value(cls, value, choices, filter_type, use_repr): if use_repr: try: - db_value = next( - db_value for db_value, value_repr in choices if value_repr == value - ) + db_value = next(db_value for db_value, value_repr in choices if value_repr == value) return db_value except StopIteration: raise ValueError @@ -1184,7 +1273,7 @@ def _check_use_repr(filter_item, filter_name): def _check_dynamic(filter_item, filter_name, filter_route): field = filter_item.get('field') if filter_item.get('dynamic', False): - e = "{0}: dynamic filters are not supported in namespaces.".format(filter_name) + e = '{0}: dynamic filters are not supported in namespaces.'.format(filter_name) assert filter_route == '', e e = "{0}: dynamic filters must have 'field' set.".format(filter_name) @@ -1218,11 +1307,9 @@ def _get_init_filters(self): 'search': FilterTypes.field_filter_type(f) == FilterTypes.STRING, } for f in self.MODEL._meta.get_fields() - if isinstance(f, SUPPORTED_FIELD_TYPES) and ( - f.name not in self.EXCLUDE_FILTERS - ) and ( - f.name not in described_filters - ) + if isinstance(f, SUPPORTED_FIELD_TYPES) + and (f.name not in self.EXCLUDE_FILTERS) + and (f.name not in described_filters) ) return described_filters + filters @@ -1285,11 +1372,13 @@ def _iter_model_to_get_filters(self, depth, model_data): model_related_models.append(relation_data + (rel_f_name,)) continue - namespace.append({ - 'filter': field.name, - 'ordering': True, - 'search': FilterTypes.field_filter_type(field) == FilterTypes.STRING, - }) + namespace.append( + { + 'filter': field.name, + 'ordering': True, + 'search': FilterTypes.field_filter_type(field) == FilterTypes.STRING, + }, + ) return [i for i in model_related_models if i[0] not in through_models] @@ -1301,11 +1390,13 @@ def _add_relation_to_iterated_models(self, depth, field, namespace): namespace_filters = [] if depth < self.DEPTH: - namespace.append({ - 'namespace': field.name, - 'filters': namespace_filters, - 'qs': self._get_field_optimization(field), - }) + namespace.append( + { + 'namespace': field.name, + 'filters': namespace_filters, + 'qs': self._get_field_optimization(field), + }, + ) return field.related_model, namespace_filters, circular_related_name diff --git a/dj_rql/management/commands/generate_rql_class.py b/dj_rql/management/commands/generate_rql_class.py index 639ce09..e850d48 100644 --- a/dj_rql/management/commands/generate_rql_class.py +++ b/dj_rql/management/commands/generate_rql_class.py @@ -89,16 +89,28 @@ def _get_field_optimization(self, field): return "NPR('{0}')".format(field.name) filters = Cls(model._default_manager.all()).init_filters - filters_str = json.dumps(filters, sort_keys=False, indent=4).replace( - '"ordering": true', '"ordering": True', - ).replace( - '"ordering": false', '"ordering": False', - ).replace( - '"search": true', '"search": True', - ).replace( - '"search": false', '"search": False', - ).replace( - '"qs": null', '"qs": None', + filters_str = ( + json.dumps(filters, sort_keys=False, indent=4) + .replace( + '"ordering": true', + '"ordering": True', + ) + .replace( + '"ordering": false', + '"ordering": False', + ) + .replace( + '"search": true', + '"search": True', + ) + .replace( + '"search": false', + '"search": False', + ) + .replace( + '"qs": null', + '"qs": None', + ) ) filters_str = re.sub(r"\"((NPR|NSR)\('\w+?'\))\"", r'\1', filters_str) diff --git a/dj_rql/openapi.py b/dj_rql/openapi.py index 7f12c25..8b1b2a9 100644 --- a/dj_rql/openapi.py +++ b/dj_rql/openapi.py @@ -89,7 +89,9 @@ def _render_null_inplace(cls, base, filter_item, filter_instance): if FilterLookups.NULL in filter_item.get('lookups', {}) and null_values != {RQL_NULL}: return cls._render_common_key_inplace( - base, 'null', ', '.join(sorted(null_values)), + base, + 'null', + ', '.join(sorted(null_values)), ) return base diff --git a/dj_rql/transformer.py b/dj_rql/transformer.py index 14501de..c841037 100644 --- a/dj_rql/transformer.py +++ b/dj_rql/transformer.py @@ -16,7 +16,7 @@ class RQLToDjangoORMTransformer(BaseRQLTransformer): - """ Parsed RQL AST tree transformer to Django ORM Query. + """Parsed RQL AST tree transformer to Django ORM Query. Notes: Grammar-Function name mapping is made automatically by Lark. @@ -25,6 +25,7 @@ class RQLToDjangoORMTransformer(BaseRQLTransformer): They are applied later in FilterCls. This is done on purpose, because transformer knows nothing about the mappings between filter names and orm fields. """ + NAMESPACE_PROVIDERS = ('comp', 'listing') NAMESPACE_FILLERS = ('prop',) NAMESPACE_ACTIVATORS = ('tuple',) @@ -50,9 +51,9 @@ def _push_namespace(self, tree): self._namespace.append(None) elif tree.data in self.NAMESPACE_ACTIVATORS: self._active_namespace = len(self._namespace) - elif (tree.data in self.NAMESPACE_FILLERS - and self._namespace - and self._namespace[-1] is None): + elif ( + tree.data in self.NAMESPACE_FILLERS and self._namespace and self._namespace[-1] is None + ): self._namespace[-1] = self._get_value(tree) def _pop_namespace(self, tree): @@ -62,7 +63,7 @@ def _pop_namespace(self, tree): self._active_namespace -= 1 def _get_current_namespace(self): - return self._namespace[:self._active_namespace] + return self._namespace[: self._active_namespace] def _transform_tree(self, tree): self._push_namespace(tree) @@ -140,10 +141,14 @@ def listing(self, args): else: field_q = ~value else: - field_q = self._filter_cls_instance.build_q_for_filter(FilterArgs( - prop, f_op, value, - list_operator=operation, - )) + field_q = self._filter_cls_instance.build_q_for_filter( + FilterArgs( + prop, + f_op, + value, + list_operator=operation, + ), + ) if operation == ListOperators.IN: q |= field_q else: @@ -185,7 +190,8 @@ def select(self, args): class RQLLimitOffsetTransformer(BaseRQLTransformer): - """ Parsed RQL AST tree transformer to (limit, offset) tuple for limit offset pagination. """ + """Parsed RQL AST tree transformer to (limit, offset) tuple for limit offset pagination.""" + def __init__(self): self.limit = None self.offset = None diff --git a/dj_rql/utils.py b/dj_rql/utils.py index 373f9bf..e1bf61a 100644 --- a/dj_rql/utils.py +++ b/dj_rql/utils.py @@ -4,7 +4,7 @@ def assert_filter_cls(filter_cls, filters, ordering_filters, search_filters): - """ Helper function for testing of custom view rql filter classes. + """Helper function for testing of custom view rql filter classes. Args: filter_cls (cls): Custom RQL Filter. @@ -32,7 +32,7 @@ def _is_filter_subset(main_dct, subset_dct): _is_filter_subset(main_dct_value, value) except AssertionError as e: raise AssertionError( - "Wrong filter `{0}` configuration: {1}".format(key, str(e)), + 'Wrong filter `{0}` configuration: {1}'.format(key, str(e)), ) elif isinstance(value, list): @@ -43,9 +43,9 @@ def _is_filter_subset(main_dct, subset_dct): _is_filter_subset(m_dict, s_dict) else: - assert main_dct[key] == value, "{0} != {1}".format(main_dct[key], value) + assert main_dct[key] == value, '{0} != {1}'.format(main_dct[key], value) - e = "assertion data must contain `orm_route` and `lookups`." + e = 'assertion data must contain `orm_route` and `lookups`.' assert {'orm_route', 'lookups'}.issubset(subset_keys), e assert 'field' in main_keys diff --git a/poetry.lock b/poetry.lock index 5d0e3e7..8cb6f26 100644 --- a/poetry.lock +++ b/poetry.lock @@ -63,6 +63,55 @@ files = [ [package.extras] tzdata = ["tzdata"] +[[package]] +name = "black" +version = "23.3.0" +description = "The uncompromising code formatter." +optional = false +python-versions = ">=3.7" +files = [ + {file = "black-23.3.0-cp310-cp310-macosx_10_16_arm64.whl", hash = "sha256:0945e13506be58bf7db93ee5853243eb368ace1c08a24c65ce108986eac65915"}, + {file = "black-23.3.0-cp310-cp310-macosx_10_16_universal2.whl", hash = "sha256:67de8d0c209eb5b330cce2469503de11bca4085880d62f1628bd9972cc3366b9"}, + {file = "black-23.3.0-cp310-cp310-macosx_10_16_x86_64.whl", hash = "sha256:7c3eb7cea23904399866c55826b31c1f55bbcd3890ce22ff70466b907b6775c2"}, + {file = "black-23.3.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:32daa9783106c28815d05b724238e30718f34155653d4d6e125dc7daec8e260c"}, + {file = "black-23.3.0-cp310-cp310-win_amd64.whl", hash = "sha256:35d1381d7a22cc5b2be2f72c7dfdae4072a3336060635718cc7e1ede24221d6c"}, + {file = "black-23.3.0-cp311-cp311-macosx_10_16_arm64.whl", hash = "sha256:a8a968125d0a6a404842fa1bf0b349a568634f856aa08ffaff40ae0dfa52e7c6"}, + {file = "black-23.3.0-cp311-cp311-macosx_10_16_universal2.whl", hash = "sha256:c7ab5790333c448903c4b721b59c0d80b11fe5e9803d8703e84dcb8da56fec1b"}, + {file = "black-23.3.0-cp311-cp311-macosx_10_16_x86_64.whl", hash = "sha256:a6f6886c9869d4daae2d1715ce34a19bbc4b95006d20ed785ca00fa03cba312d"}, + {file = "black-23.3.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:6f3c333ea1dd6771b2d3777482429864f8e258899f6ff05826c3a4fcc5ce3f70"}, + {file = "black-23.3.0-cp311-cp311-win_amd64.whl", hash = "sha256:11c410f71b876f961d1de77b9699ad19f939094c3a677323f43d7a29855fe326"}, + {file = "black-23.3.0-cp37-cp37m-macosx_10_16_x86_64.whl", hash = "sha256:1d06691f1eb8de91cd1b322f21e3bfc9efe0c7ca1f0e1eb1db44ea367dff656b"}, + {file = "black-23.3.0-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:50cb33cac881766a5cd9913e10ff75b1e8eb71babf4c7104f2e9c52da1fb7de2"}, + {file = "black-23.3.0-cp37-cp37m-win_amd64.whl", hash = "sha256:e114420bf26b90d4b9daa597351337762b63039752bdf72bf361364c1aa05925"}, + {file = "black-23.3.0-cp38-cp38-macosx_10_16_arm64.whl", hash = "sha256:48f9d345675bb7fbc3dd85821b12487e1b9a75242028adad0333ce36ed2a6d27"}, + {file = "black-23.3.0-cp38-cp38-macosx_10_16_universal2.whl", hash = "sha256:714290490c18fb0126baa0fca0a54ee795f7502b44177e1ce7624ba1c00f2331"}, + {file = "black-23.3.0-cp38-cp38-macosx_10_16_x86_64.whl", hash = "sha256:064101748afa12ad2291c2b91c960be28b817c0c7eaa35bec09cc63aa56493c5"}, + {file = "black-23.3.0-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:562bd3a70495facf56814293149e51aa1be9931567474993c7942ff7d3533961"}, + {file = "black-23.3.0-cp38-cp38-win_amd64.whl", hash = "sha256:e198cf27888ad6f4ff331ca1c48ffc038848ea9f031a3b40ba36aced7e22f2c8"}, + {file = "black-23.3.0-cp39-cp39-macosx_10_16_arm64.whl", hash = "sha256:3238f2aacf827d18d26db07524e44741233ae09a584273aa059066d644ca7b30"}, + {file = "black-23.3.0-cp39-cp39-macosx_10_16_universal2.whl", hash = "sha256:f0bd2f4a58d6666500542b26354978218a9babcdc972722f4bf90779524515f3"}, + {file = "black-23.3.0-cp39-cp39-macosx_10_16_x86_64.whl", hash = "sha256:92c543f6854c28a3c7f39f4d9b7694f9a6eb9d3c5e2ece488c327b6e7ea9b266"}, + {file = "black-23.3.0-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3a150542a204124ed00683f0db1f5cf1c2aaaa9cc3495b7a3b5976fb136090ab"}, + {file = "black-23.3.0-cp39-cp39-win_amd64.whl", hash = "sha256:6b39abdfb402002b8a7d030ccc85cf5afff64ee90fa4c5aebc531e3ad0175ddb"}, + {file = "black-23.3.0-py3-none-any.whl", hash = "sha256:ec751418022185b0c1bb7d7736e6933d40bbb14c14a0abcf9123d1b159f98dd4"}, + {file = "black-23.3.0.tar.gz", hash = "sha256:1c7b8d606e728a41ea1ccbd7264677e494e87cf630e399262ced92d4a8dac940"}, +] + +[package.dependencies] +click = ">=8.0.0" +mypy-extensions = ">=0.4.3" +packaging = ">=22.0" +pathspec = ">=0.9.0" +platformdirs = ">=2" +tomli = {version = ">=1.1.0", markers = "python_version < \"3.11\""} +typing-extensions = {version = ">=3.10.0.0", markers = "python_version < \"3.10\""} + +[package.extras] +colorama = ["colorama (>=0.4.3)"] +d = ["aiohttp (>=3.7.4)"] +jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] +uvloop = ["uvloop (>=0.15.2)"] + [[package]] name = "cachetools" version = "5.3.1" @@ -284,13 +333,13 @@ toml = ["tomli"] [[package]] name = "django" -version = "4.2.1" +version = "4.2.2" description = "A high-level Python web framework that encourages rapid development and clean, pragmatic design." optional = false python-versions = ">=3.8" files = [ - {file = "Django-4.2.1-py3-none-any.whl", hash = "sha256:066b6debb5ac335458d2a713ed995570536c8b59a580005acb0732378d5eb1ee"}, - {file = "Django-4.2.1.tar.gz", hash = "sha256:7efa6b1f781a6119a10ac94b4794ded90db8accbe7802281cd26f8664ffed59c"}, + {file = "Django-4.2.2-py3-none-any.whl", hash = "sha256:672b3fa81e1f853bb58be1b51754108ab4ffa12a77c06db86aa8df9ed0c46fe5"}, + {file = "Django-4.2.2.tar.gz", hash = "sha256:2a6b6fbff5b59dd07bef10bcb019bee2ea97a30b2a656d51346596724324badf"}, ] [package.dependencies] @@ -373,6 +422,25 @@ mccabe = ">=0.7.0,<0.8.0" pycodestyle = ">=2.9.0,<2.10.0" pyflakes = ">=2.5.0,<2.6.0" +[[package]] +name = "flake8-black" +version = "0.3.6" +description = "flake8 plugin to call black as a code style validator" +optional = false +python-versions = ">=3.7" +files = [ + {file = "flake8-black-0.3.6.tar.gz", hash = "sha256:0dfbca3274777792a5bcb2af887a4cad72c72d0e86c94e08e3a3de151bb41c34"}, + {file = "flake8_black-0.3.6-py3-none-any.whl", hash = "sha256:fe8ea2eca98d8a504f22040d9117347f6b367458366952862ac3586e7d4eeaca"}, +] + +[package.dependencies] +black = ">=22.1.0" +flake8 = ">=3" +tomli = {version = "*", markers = "python_version < \"3.11\""} + +[package.extras] +develop = ["build", "twine"] + [[package]] name = "flake8-broken-line" version = "0.6.0" @@ -638,61 +706,61 @@ testing = ["coverage", "pyyaml"] [[package]] name = "markupsafe" -version = "2.1.2" +version = "2.1.3" description = "Safely add untrusted strings to HTML/XML markup." optional = false python-versions = ">=3.7" files = [ - {file = "MarkupSafe-2.1.2-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:665a36ae6f8f20a4676b53224e33d456a6f5a72657d9c83c2aa00765072f31f7"}, - {file = "MarkupSafe-2.1.2-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:340bea174e9761308703ae988e982005aedf427de816d1afe98147668cc03036"}, - {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:22152d00bf4a9c7c83960521fc558f55a1adbc0631fbb00a9471e097b19d72e1"}, - {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:28057e985dace2f478e042eaa15606c7efccb700797660629da387eb289b9323"}, - {file = "MarkupSafe-2.1.2-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca244fa73f50a800cf8c3ebf7fd93149ec37f5cb9596aa8873ae2c1d23498601"}, - {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:d9d971ec1e79906046aa3ca266de79eac42f1dbf3612a05dc9368125952bd1a1"}, - {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:7e007132af78ea9df29495dbf7b5824cb71648d7133cf7848a2a5dd00d36f9ff"}, - {file = "MarkupSafe-2.1.2-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:7313ce6a199651c4ed9d7e4cfb4aa56fe923b1adf9af3b420ee14e6d9a73df65"}, - {file = "MarkupSafe-2.1.2-cp310-cp310-win32.whl", hash = "sha256:c4a549890a45f57f1ebf99c067a4ad0cb423a05544accaf2b065246827ed9603"}, - {file = "MarkupSafe-2.1.2-cp310-cp310-win_amd64.whl", hash = "sha256:835fb5e38fd89328e9c81067fd642b3593c33e1e17e2fdbf77f5676abb14a156"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2ec4f2d48ae59bbb9d1f9d7efb9236ab81429a764dedca114f5fdabbc3788013"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:608e7073dfa9e38a85d38474c082d4281f4ce276ac0010224eaba11e929dd53a"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:65608c35bfb8a76763f37036547f7adfd09270fbdbf96608be2bead319728fcd"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f2bfb563d0211ce16b63c7cb9395d2c682a23187f54c3d79bfec33e6705473c6"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:da25303d91526aac3672ee6d49a2f3db2d9502a4a60b55519feb1a4c7714e07d"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:9cad97ab29dfc3f0249b483412c85c8ef4766d96cdf9dcf5a1e3caa3f3661cf1"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:085fd3201e7b12809f9e6e9bc1e5c96a368c8523fad5afb02afe3c051ae4afcc"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:1bea30e9bf331f3fef67e0a3877b2288593c98a21ccb2cf29b74c581a4eb3af0"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-win32.whl", hash = "sha256:7df70907e00c970c60b9ef2938d894a9381f38e6b9db73c5be35e59d92e06625"}, - {file = "MarkupSafe-2.1.2-cp311-cp311-win_amd64.whl", hash = "sha256:e55e40ff0cc8cc5c07996915ad367fa47da6b3fc091fdadca7f5403239c5fec3"}, - {file = "MarkupSafe-2.1.2-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:a6e40afa7f45939ca356f348c8e23048e02cb109ced1eb8420961b2f40fb373a"}, - {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cf877ab4ed6e302ec1d04952ca358b381a882fbd9d1b07cccbfd61783561f98a"}, - {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:63ba06c9941e46fa389d389644e2d8225e0e3e5ebcc4ff1ea8506dce646f8c8a"}, - {file = "MarkupSafe-2.1.2-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f1cd098434e83e656abf198f103a8207a8187c0fc110306691a2e94a78d0abb2"}, - {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:55f44b440d491028addb3b88f72207d71eeebfb7b5dbf0643f7c023ae1fba619"}, - {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:a6f2fcca746e8d5910e18782f976489939d54a91f9411c32051b4aab2bd7c513"}, - {file = "MarkupSafe-2.1.2-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:0b462104ba25f1ac006fdab8b6a01ebbfbce9ed37fd37fd4acd70c67c973e460"}, - {file = "MarkupSafe-2.1.2-cp37-cp37m-win32.whl", hash = "sha256:7668b52e102d0ed87cb082380a7e2e1e78737ddecdde129acadb0eccc5423859"}, - {file = "MarkupSafe-2.1.2-cp37-cp37m-win_amd64.whl", hash = "sha256:6d6607f98fcf17e534162f0709aaad3ab7a96032723d8ac8750ffe17ae5a0666"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:a806db027852538d2ad7555b203300173dd1b77ba116de92da9afbc3a3be3eed"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:a4abaec6ca3ad8660690236d11bfe28dfd707778e2442b45addd2f086d6ef094"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f03a532d7dee1bed20bc4884194a16160a2de9ffc6354b3878ec9682bb623c54"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4cf06cdc1dda95223e9d2d3c58d3b178aa5dacb35ee7e3bbac10e4e1faacb419"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:22731d79ed2eb25059ae3df1dfc9cb1546691cc41f4e3130fe6bfbc3ecbbecfa"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:f8ffb705ffcf5ddd0e80b65ddf7bed7ee4f5a441ea7d3419e861a12eaf41af58"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:8db032bf0ce9022a8e41a22598eefc802314e81b879ae093f36ce9ddf39ab1ba"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:2298c859cfc5463f1b64bd55cb3e602528db6fa0f3cfd568d3605c50678f8f03"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-win32.whl", hash = "sha256:50c42830a633fa0cf9e7d27664637532791bfc31c731a87b202d2d8ac40c3ea2"}, - {file = "MarkupSafe-2.1.2-cp38-cp38-win_amd64.whl", hash = "sha256:bb06feb762bade6bf3c8b844462274db0c76acc95c52abe8dbed28ae3d44a147"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:99625a92da8229df6d44335e6fcc558a5037dd0a760e11d84be2260e6f37002f"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:8bca7e26c1dd751236cfb0c6c72d4ad61d986e9a41bbf76cb445f69488b2a2bd"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:40627dcf047dadb22cd25ea7ecfe9cbf3bbbad0482ee5920b582f3809c97654f"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:40dfd3fefbef579ee058f139733ac336312663c6706d1163b82b3003fb1925c4"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:090376d812fb6ac5f171e5938e82e7f2d7adc2b629101cec0db8b267815c85e2"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:2e7821bffe00aa6bd07a23913b7f4e01328c3d5cc0b40b36c0bd81d362faeb65"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:c0a33bc9f02c2b17c3ea382f91b4db0e6cde90b63b296422a939886a7a80de1c"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:b8526c6d437855442cdd3d87eede9c425c4445ea011ca38d937db299382e6fa3"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-win32.whl", hash = "sha256:137678c63c977754abe9086a3ec011e8fd985ab90631145dfb9294ad09c102a7"}, - {file = "MarkupSafe-2.1.2-cp39-cp39-win_amd64.whl", hash = "sha256:0576fe974b40a400449768941d5d0858cc624e3249dfd1e0c33674e5c7ca7aed"}, - {file = "MarkupSafe-2.1.2.tar.gz", hash = "sha256:abcabc8c2b26036d62d4c746381a6f7cf60aafcc653198ad678306986b09450d"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_universal2.whl", hash = "sha256:cd0f502fe016460680cd20aaa5a76d241d6f35a1c3350c474bac1273803893fa"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:e09031c87a1e51556fdcb46e5bd4f59dfb743061cf93c4d6831bf894f125eb57"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:68e78619a61ecf91e76aa3e6e8e33fc4894a2bebe93410754bd28fce0a8a4f9f"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:65c1a9bcdadc6c28eecee2c119465aebff8f7a584dd719facdd9e825ec61ab52"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:525808b8019e36eb524b8c68acdd63a37e75714eac50e988180b169d64480a00"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:962f82a3086483f5e5f64dbad880d31038b698494799b097bc59c2edf392fce6"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:aa7bd130efab1c280bed0f45501b7c8795f9fdbeb02e965371bbef3523627779"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:c9c804664ebe8f83a211cace637506669e7890fec1b4195b505c214e50dd4eb7"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-win32.whl", hash = "sha256:10bbfe99883db80bdbaff2dcf681dfc6533a614f700da1287707e8a5d78a8431"}, + {file = "MarkupSafe-2.1.3-cp310-cp310-win_amd64.whl", hash = "sha256:1577735524cdad32f9f694208aa75e422adba74f1baee7551620e43a3141f559"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:ad9e82fb8f09ade1c3e1b996a6337afac2b8b9e365f926f5a61aacc71adc5b3c"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3c0fae6c3be832a0a0473ac912810b2877c8cb9d76ca48de1ed31e1c68386575"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b076b6226fb84157e3f7c971a47ff3a679d837cf338547532ab866c57930dbee"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:bfce63a9e7834b12b87c64d6b155fdd9b3b96191b6bd334bf37db7ff1fe457f2"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:338ae27d6b8745585f87218a3f23f1512dbf52c26c28e322dbe54bcede54ccb9"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:e4dd52d80b8c83fdce44e12478ad2e85c64ea965e75d66dbeafb0a3e77308fcc"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:df0be2b576a7abbf737b1575f048c23fb1d769f267ec4358296f31c2479db8f9"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:5bbe06f8eeafd38e5d0a4894ffec89378b6c6a625ff57e3028921f8ff59318ac"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-win32.whl", hash = "sha256:dd15ff04ffd7e05ffcb7fe79f1b98041b8ea30ae9234aed2a9168b5797c3effb"}, + {file = "MarkupSafe-2.1.3-cp311-cp311-win_amd64.whl", hash = "sha256:134da1eca9ec0ae528110ccc9e48041e0828d79f24121a1a146161103c76e686"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:8e254ae696c88d98da6555f5ace2279cf7cd5b3f52be2b5cf97feafe883b58d2"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cb0932dc158471523c9637e807d9bfb93e06a95cbf010f1a38b98623b929ef2b"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9402b03f1a1b4dc4c19845e5c749e3ab82d5078d16a2a4c2cd2df62d57bb0707"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:ca379055a47383d02a5400cb0d110cef0a776fc644cda797db0c5696cfd7e18e"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:b7ff0f54cb4ff66dd38bebd335a38e2c22c41a8ee45aa608efc890ac3e3931bc"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:c011a4149cfbcf9f03994ec2edffcb8b1dc2d2aede7ca243746df97a5d41ce48"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:56d9f2ecac662ca1611d183feb03a3fa4406469dafe241673d521dd5ae92a155"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-win32.whl", hash = "sha256:8758846a7e80910096950b67071243da3e5a20ed2546e6392603c096778d48e0"}, + {file = "MarkupSafe-2.1.3-cp37-cp37m-win_amd64.whl", hash = "sha256:787003c0ddb00500e49a10f2844fac87aa6ce977b90b0feaaf9de23c22508b24"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_universal2.whl", hash = "sha256:2ef12179d3a291be237280175b542c07a36e7f60718296278d8593d21ca937d4"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:2c1b19b3aaacc6e57b7e25710ff571c24d6c3613a45e905b1fde04d691b98ee0"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8afafd99945ead6e075b973fefa56379c5b5c53fd8937dad92c662da5d8fd5ee"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8c41976a29d078bb235fea9b2ecd3da465df42a562910f9022f1a03107bd02be"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d080e0a5eb2529460b30190fcfcc4199bd7f827663f858a226a81bc27beaa97e"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:69c0f17e9f5a7afdf2cc9fb2d1ce6aabdb3bafb7f38017c0b77862bcec2bbad8"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:504b320cd4b7eff6f968eddf81127112db685e81f7e36e75f9f84f0df46041c3"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:42de32b22b6b804f42c5d98be4f7e5e977ecdd9ee9b660fda1a3edf03b11792d"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-win32.whl", hash = "sha256:ceb01949af7121f9fc39f7d27f91be8546f3fb112c608bc4029aef0bab86a2a5"}, + {file = "MarkupSafe-2.1.3-cp38-cp38-win_amd64.whl", hash = "sha256:1b40069d487e7edb2676d3fbdb2b0829ffa2cd63a2ec26c4938b2d34391b4ecc"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_universal2.whl", hash = "sha256:8023faf4e01efadfa183e863fefde0046de576c6f14659e8782065bcece22198"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:6b2b56950d93e41f33b4223ead100ea0fe11f8e6ee5f641eb753ce4b77a7042b"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:9dcdfd0eaf283af041973bff14a2e143b8bd64e069f4c383416ecd79a81aab58"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:05fb21170423db021895e1ea1e1f3ab3adb85d1c2333cbc2310f2a26bc77272e"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:282c2cb35b5b673bbcadb33a585408104df04f14b2d9b01d4c345a3b92861c2c"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:ab4a0df41e7c16a1392727727e7998a467472d0ad65f3ad5e6e765015df08636"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:7ef3cb2ebbf91e330e3bb937efada0edd9003683db6b57bb108c4001f37a02ea"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:0a4e4a1aff6c7ac4cd55792abf96c915634c2b97e3cc1c7129578aa68ebd754e"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-win32.whl", hash = "sha256:fec21693218efe39aa7f8599346e90c705afa52c5b31ae019b2e57e8f6542bb2"}, + {file = "MarkupSafe-2.1.3-cp39-cp39-win_amd64.whl", hash = "sha256:3fd4abcb888d15a94f32b75d8fd18ee162ca0c064f35b11134be77050296d6ba"}, + {file = "MarkupSafe-2.1.3.tar.gz", hash = "sha256:af598ed32d6ae86f1b747b82783958b1a4ab8f617b06fe68795c7f026abbdcad"}, ] [[package]] @@ -866,6 +934,17 @@ files = [ griffe = ">=0.24" mkdocstrings = ">=0.19" +[[package]] +name = "mypy-extensions" +version = "1.0.0" +description = "Type system extensions for programs checked with the mypy type checker." +optional = false +python-versions = ">=3.5" +files = [ + {file = "mypy_extensions-1.0.0-py3-none-any.whl", hash = "sha256:4392f6c0eb8a5668a69e23d168ffa70f0be9ccfd32b5cc2d26a34ae5b844552d"}, + {file = "mypy_extensions-1.0.0.tar.gz", hash = "sha256:75dbf8955dc00442a438fc4d0666508a9a97b6bd41aa2f0ffe9d2f2725af0782"}, +] + [[package]] name = "packaging" version = "23.1" @@ -877,6 +956,32 @@ files = [ {file = "packaging-23.1.tar.gz", hash = "sha256:a392980d2b6cffa644431898be54b0045151319d1e7ec34f0cfed48767dd334f"}, ] +[[package]] +name = "pathspec" +version = "0.11.1" +description = "Utility library for gitignore style pattern matching of file paths." +optional = false +python-versions = ">=3.7" +files = [ + {file = "pathspec-0.11.1-py3-none-any.whl", hash = "sha256:d8af70af76652554bd134c22b3e8a1cc46ed7d91edcdd721ef1a0c51a84a5293"}, + {file = "pathspec-0.11.1.tar.gz", hash = "sha256:2798de800fa92780e33acca925945e9a19a133b715067cf165b8866c15a31687"}, +] + +[[package]] +name = "platformdirs" +version = "3.5.1" +description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." +optional = false +python-versions = ">=3.7" +files = [ + {file = "platformdirs-3.5.1-py3-none-any.whl", hash = "sha256:e2378146f1964972c03c085bb5662ae80b2b8c06226c54b2ff4aa9483e8a13a5"}, + {file = "platformdirs-3.5.1.tar.gz", hash = "sha256:412dae91f52a6f84830f39a8078cecd0e866cb72294a5c66808e74d5e88d251f"}, +] + +[package.extras] +docs = ["furo (>=2023.3.27)", "proselint (>=0.13)", "sphinx (>=6.2.1)", "sphinx-autodoc-typehints (>=1.23,!=1.23.4)"] +test = ["appdirs (==1.4.4)", "covdefaults (>=2.3)", "pytest (>=7.3.1)", "pytest-cov (>=4)", "pytest-mock (>=3.10)"] + [[package]] name = "pluggy" version = "1.0.0" @@ -1123,99 +1228,99 @@ pyyaml = "*" [[package]] name = "regex" -version = "2023.5.5" +version = "2023.6.3" description = "Alternative regular expression module, to replace re." optional = false python-versions = ">=3.6" files = [ - {file = "regex-2023.5.5-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:48c9ec56579d4ba1c88f42302194b8ae2350265cb60c64b7b9a88dcb7fbde309"}, - {file = "regex-2023.5.5-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:02f4541550459c08fdd6f97aa4e24c6f1932eec780d58a2faa2068253df7d6ff"}, - {file = "regex-2023.5.5-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:53e22e4460f0245b468ee645156a4f84d0fc35a12d9ba79bd7d79bdcd2f9629d"}, - {file = "regex-2023.5.5-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:4b870b6f632fc74941cadc2a0f3064ed8409e6f8ee226cdfd2a85ae50473aa94"}, - {file = "regex-2023.5.5-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:171c52e320fe29260da550d81c6b99f6f8402450dc7777ef5ced2e848f3b6f8f"}, - {file = "regex-2023.5.5-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:aad5524c2aedaf9aa14ef1bc9327f8abd915699dea457d339bebbe2f0d218f86"}, - {file = "regex-2023.5.5-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5a0f874ee8c0bc820e649c900243c6d1e6dc435b81da1492046716f14f1a2a96"}, - {file = "regex-2023.5.5-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:e645c757183ee0e13f0bbe56508598e2d9cd42b8abc6c0599d53b0d0b8dd1479"}, - {file = "regex-2023.5.5-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:a4c5da39bca4f7979eefcbb36efea04471cd68db2d38fcbb4ee2c6d440699833"}, - {file = "regex-2023.5.5-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:5e3f4468b8c6fd2fd33c218bbd0a1559e6a6fcf185af8bb0cc43f3b5bfb7d636"}, - {file = "regex-2023.5.5-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:59e4b729eae1a0919f9e4c0fc635fbcc9db59c74ad98d684f4877be3d2607dd6"}, - {file = "regex-2023.5.5-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:ba73a14e9c8f9ac409863543cde3290dba39098fc261f717dc337ea72d3ebad2"}, - {file = "regex-2023.5.5-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:0bbd5dcb19603ab8d2781fac60114fb89aee8494f4505ae7ad141a3314abb1f9"}, - {file = "regex-2023.5.5-cp310-cp310-win32.whl", hash = "sha256:40005cbd383438aecf715a7b47fe1e3dcbc889a36461ed416bdec07e0ef1db66"}, - {file = "regex-2023.5.5-cp310-cp310-win_amd64.whl", hash = "sha256:59597cd6315d3439ed4b074febe84a439c33928dd34396941b4d377692eca810"}, - {file = "regex-2023.5.5-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:8f08276466fedb9e36e5193a96cb944928301152879ec20c2d723d1031cd4ddd"}, - {file = "regex-2023.5.5-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:cd46f30e758629c3ee91713529cfbe107ac50d27110fdcc326a42ce2acf4dafc"}, - {file = "regex-2023.5.5-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f2910502f718828cecc8beff004917dcf577fc5f8f5dd40ffb1ea7612124547b"}, - {file = "regex-2023.5.5-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:445d6f4fc3bd9fc2bf0416164454f90acab8858cd5a041403d7a11e3356980e8"}, - {file = "regex-2023.5.5-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:18196c16a584619c7c1d843497c069955d7629ad4a3fdee240eb347f4a2c9dbe"}, - {file = "regex-2023.5.5-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:33d430a23b661629661f1fe8395be2004006bc792bb9fc7c53911d661b69dd7e"}, - {file = "regex-2023.5.5-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:72a28979cc667e5f82ef433db009184e7ac277844eea0f7f4d254b789517941d"}, - {file = "regex-2023.5.5-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:f764e4dfafa288e2eba21231f455d209f4709436baeebb05bdecfb5d8ddc3d35"}, - {file = "regex-2023.5.5-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:23d86ad2121b3c4fc78c58f95e19173790e22ac05996df69b84e12da5816cb17"}, - {file = "regex-2023.5.5-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:690a17db524ee6ac4a27efc5406530dd90e7a7a69d8360235323d0e5dafb8f5b"}, - {file = "regex-2023.5.5-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:1ecf3dcff71f0c0fe3e555201cbe749fa66aae8d18f80d2cc4de8e66df37390a"}, - {file = "regex-2023.5.5-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:811040d7f3dd9c55eb0d8b00b5dcb7fd9ae1761c454f444fd9f37fe5ec57143a"}, - {file = "regex-2023.5.5-cp311-cp311-win32.whl", hash = "sha256:c8c143a65ce3ca42e54d8e6fcaf465b6b672ed1c6c90022794a802fb93105d22"}, - {file = "regex-2023.5.5-cp311-cp311-win_amd64.whl", hash = "sha256:586a011f77f8a2da4b888774174cd266e69e917a67ba072c7fc0e91878178a80"}, - {file = "regex-2023.5.5-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:b6365703e8cf1644b82104cdd05270d1a9f043119a168d66c55684b1b557d008"}, - {file = "regex-2023.5.5-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a56c18f21ac98209da9c54ae3ebb3b6f6e772038681d6cb43b8d53da3b09ee81"}, - {file = "regex-2023.5.5-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:b8b942d8b3ce765dbc3b1dad0a944712a89b5de290ce8f72681e22b3c55f3cc8"}, - {file = "regex-2023.5.5-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:844671c9c1150fcdac46d43198364034b961bd520f2c4fdaabfc7c7d7138a2dd"}, - {file = "regex-2023.5.5-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c2ce65bdeaf0a386bb3b533a28de3994e8e13b464ac15e1e67e4603dd88787fa"}, - {file = "regex-2023.5.5-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:fee0016cc35a8a91e8cc9312ab26a6fe638d484131a7afa79e1ce6165328a135"}, - {file = "regex-2023.5.5-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:18f05d14f14a812fe9723f13afafefe6b74ca042d99f8884e62dbd34dcccf3e2"}, - {file = "regex-2023.5.5-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:941b3f1b2392f0bcd6abf1bc7a322787d6db4e7457be6d1ffd3a693426a755f2"}, - {file = "regex-2023.5.5-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:921473a93bcea4d00295799ab929522fc650e85c6b9f27ae1e6bb32a790ea7d3"}, - {file = "regex-2023.5.5-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:e2205a81f815b5bb17e46e74cc946c575b484e5f0acfcb805fb252d67e22938d"}, - {file = "regex-2023.5.5-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:385992d5ecf1a93cb85adff2f73e0402dd9ac29b71b7006d342cc920816e6f32"}, - {file = "regex-2023.5.5-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:890a09cb0a62198bff92eda98b2b507305dd3abf974778bae3287f98b48907d3"}, - {file = "regex-2023.5.5-cp36-cp36m-win32.whl", hash = "sha256:821a88b878b6589c5068f4cc2cfeb2c64e343a196bc9d7ac68ea8c2a776acd46"}, - {file = "regex-2023.5.5-cp36-cp36m-win_amd64.whl", hash = "sha256:7918a1b83dd70dc04ab5ed24c78ae833ae8ea228cef84e08597c408286edc926"}, - {file = "regex-2023.5.5-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:338994d3d4ca4cf12f09822e025731a5bdd3a37aaa571fa52659e85ca793fb67"}, - {file = "regex-2023.5.5-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0a69cf0c00c4d4a929c6c7717fd918414cab0d6132a49a6d8fc3ded1988ed2ea"}, - {file = "regex-2023.5.5-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8f5e06df94fff8c4c85f98c6487f6636848e1dc85ce17ab7d1931df4a081f657"}, - {file = "regex-2023.5.5-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:a8906669b03c63266b6a7693d1f487b02647beb12adea20f8840c1a087e2dfb5"}, - {file = "regex-2023.5.5-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9fda3e50abad8d0f48df621cf75adc73c63f7243cbe0e3b2171392b445401550"}, - {file = "regex-2023.5.5-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:5ac2b7d341dc1bd102be849d6dd33b09701223a851105b2754339e390be0627a"}, - {file = "regex-2023.5.5-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:fb2b495dd94b02de8215625948132cc2ea360ae84fe6634cd19b6567709c8ae2"}, - {file = "regex-2023.5.5-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:aa7d032c1d84726aa9edeb6accf079b4caa87151ca9fabacef31fa028186c66d"}, - {file = "regex-2023.5.5-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:3d45864693351c15531f7e76f545ec35000d50848daa833cead96edae1665559"}, - {file = "regex-2023.5.5-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:21e90a288e6ba4bf44c25c6a946cb9b0f00b73044d74308b5e0afd190338297c"}, - {file = "regex-2023.5.5-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:10250a093741ec7bf74bcd2039e697f519b028518f605ff2aa7ac1e9c9f97423"}, - {file = "regex-2023.5.5-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:6b8d0c153f07a953636b9cdb3011b733cadd4178123ef728ccc4d5969e67f3c2"}, - {file = "regex-2023.5.5-cp37-cp37m-win32.whl", hash = "sha256:10374c84ee58c44575b667310d5bbfa89fb2e64e52349720a0182c0017512f6c"}, - {file = "regex-2023.5.5-cp37-cp37m-win_amd64.whl", hash = "sha256:9b320677521aabf666cdd6e99baee4fb5ac3996349c3b7f8e7c4eee1c00dfe3a"}, - {file = "regex-2023.5.5-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:afb1c70ec1e594a547f38ad6bf5e3d60304ce7539e677c1429eebab115bce56e"}, - {file = "regex-2023.5.5-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:cf123225945aa58b3057d0fba67e8061c62d14cc8a4202630f8057df70189051"}, - {file = "regex-2023.5.5-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a99757ad7fe5c8a2bb44829fc57ced11253e10f462233c1255fe03888e06bc19"}, - {file = "regex-2023.5.5-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:a623564d810e7a953ff1357f7799c14bc9beeab699aacc8b7ab7822da1e952b8"}, - {file = "regex-2023.5.5-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ced02e3bd55e16e89c08bbc8128cff0884d96e7f7a5633d3dc366b6d95fcd1d6"}, - {file = "regex-2023.5.5-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d1cbe6b5be3b9b698d8cc4ee4dee7e017ad655e83361cd0ea8e653d65e469468"}, - {file = "regex-2023.5.5-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:4a6e4b0e0531223f53bad07ddf733af490ba2b8367f62342b92b39b29f72735a"}, - {file = "regex-2023.5.5-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2e9c4f778514a560a9c9aa8e5538bee759b55f6c1dcd35613ad72523fd9175b8"}, - {file = "regex-2023.5.5-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:256f7f4c6ba145f62f7a441a003c94b8b1af78cee2cccacfc1e835f93bc09426"}, - {file = "regex-2023.5.5-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:bd7b68fd2e79d59d86dcbc1ccd6e2ca09c505343445daaa4e07f43c8a9cc34da"}, - {file = "regex-2023.5.5-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:4a5059bd585e9e9504ef9c07e4bc15b0a621ba20504388875d66b8b30a5c4d18"}, - {file = "regex-2023.5.5-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:6893544e06bae009916a5658ce7207e26ed17385149f35a3125f5259951f1bbe"}, - {file = "regex-2023.5.5-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:c64d5abe91a3dfe5ff250c6bb267ef00dbc01501518225b45a5f9def458f31fb"}, - {file = "regex-2023.5.5-cp38-cp38-win32.whl", hash = "sha256:7923470d6056a9590247ff729c05e8e0f06bbd4efa6569c916943cb2d9b68b91"}, - {file = "regex-2023.5.5-cp38-cp38-win_amd64.whl", hash = "sha256:4035d6945cb961c90c3e1c1ca2feb526175bcfed44dfb1cc77db4fdced060d3e"}, - {file = "regex-2023.5.5-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:50fd2d9b36938d4dcecbd684777dd12a407add4f9f934f235c66372e630772b0"}, - {file = "regex-2023.5.5-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:d19e57f888b00cd04fc38f5e18d0efbd91ccba2d45039453ab2236e6eec48d4d"}, - {file = "regex-2023.5.5-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd966475e963122ee0a7118ec9024388c602d12ac72860f6eea119a3928be053"}, - {file = "regex-2023.5.5-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:db09e6c18977a33fea26fe67b7a842f706c67cf8bda1450974d0ae0dd63570df"}, - {file = "regex-2023.5.5-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6164d4e2a82f9ebd7752a06bd6c504791bedc6418c0196cd0a23afb7f3e12b2d"}, - {file = "regex-2023.5.5-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:84397d3f750d153ebd7f958efaa92b45fea170200e2df5e0e1fd4d85b7e3f58a"}, - {file = "regex-2023.5.5-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:9c3efee9bb53cbe7b285760c81f28ac80dc15fa48b5fe7e58b52752e642553f1"}, - {file = "regex-2023.5.5-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:144b5b017646b5a9392a5554a1e5db0000ae637be4971c9747566775fc96e1b2"}, - {file = "regex-2023.5.5-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:1189fbbb21e2c117fda5303653b61905aeeeea23de4a94d400b0487eb16d2d60"}, - {file = "regex-2023.5.5-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:f83fe9e10f9d0b6cf580564d4d23845b9d692e4c91bd8be57733958e4c602956"}, - {file = "regex-2023.5.5-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:72aa4746993a28c841e05889f3f1b1e5d14df8d3daa157d6001a34c98102b393"}, - {file = "regex-2023.5.5-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:de2f780c3242ea114dd01f84848655356af4dd561501896c751d7b885ea6d3a1"}, - {file = "regex-2023.5.5-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:290fd35219486dfbc00b0de72f455ecdd63e59b528991a6aec9fdfc0ce85672e"}, - {file = "regex-2023.5.5-cp39-cp39-win32.whl", hash = "sha256:732176f5427e72fa2325b05c58ad0b45af341c459910d766f814b0584ac1f9ac"}, - {file = "regex-2023.5.5-cp39-cp39-win_amd64.whl", hash = "sha256:1307aa4daa1cbb23823d8238e1f61292fd07e4e5d8d38a6efff00b67a7cdb764"}, - {file = "regex-2023.5.5.tar.gz", hash = "sha256:7d76a8a1fc9da08296462a18f16620ba73bcbf5909e42383b253ef34d9d5141e"}, + {file = "regex-2023.6.3-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:824bf3ac11001849aec3fa1d69abcb67aac3e150a933963fb12bda5151fe1bfd"}, + {file = "regex-2023.6.3-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:05ed27acdf4465c95826962528f9e8d41dbf9b1aa8531a387dee6ed215a3e9ef"}, + {file = "regex-2023.6.3-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:0b49c764f88a79160fa64f9a7b425620e87c9f46095ef9c9920542ab2495c8bc"}, + {file = "regex-2023.6.3-cp310-cp310-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8e3f1316c2293e5469f8f09dc2d76efb6c3982d3da91ba95061a7e69489a14ef"}, + {file = "regex-2023.6.3-cp310-cp310-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:43e1dd9d12df9004246bacb79a0e5886b3b6071b32e41f83b0acbf293f820ee8"}, + {file = "regex-2023.6.3-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4959e8bcbfda5146477d21c3a8ad81b185cd252f3d0d6e4724a5ef11c012fb06"}, + {file = "regex-2023.6.3-cp310-cp310-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:af4dd387354dc83a3bff67127a124c21116feb0d2ef536805c454721c5d7993d"}, + {file = "regex-2023.6.3-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2239d95d8e243658b8dbb36b12bd10c33ad6e6933a54d36ff053713f129aa536"}, + {file = "regex-2023.6.3-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:890e5a11c97cf0d0c550eb661b937a1e45431ffa79803b942a057c4fb12a2da2"}, + {file = "regex-2023.6.3-cp310-cp310-musllinux_1_1_i686.whl", hash = "sha256:a8105e9af3b029f243ab11ad47c19b566482c150c754e4c717900a798806b222"}, + {file = "regex-2023.6.3-cp310-cp310-musllinux_1_1_ppc64le.whl", hash = "sha256:25be746a8ec7bc7b082783216de8e9473803706723b3f6bef34b3d0ed03d57e2"}, + {file = "regex-2023.6.3-cp310-cp310-musllinux_1_1_s390x.whl", hash = "sha256:3676f1dd082be28b1266c93f618ee07741b704ab7b68501a173ce7d8d0d0ca18"}, + {file = "regex-2023.6.3-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:10cb847aeb1728412c666ab2e2000ba6f174f25b2bdc7292e7dd71b16db07568"}, + {file = "regex-2023.6.3-cp310-cp310-win32.whl", hash = "sha256:dbbbfce33cd98f97f6bffb17801b0576e653f4fdb1d399b2ea89638bc8d08ae1"}, + {file = "regex-2023.6.3-cp310-cp310-win_amd64.whl", hash = "sha256:c5f8037000eb21e4823aa485149f2299eb589f8d1fe4b448036d230c3f4e68e0"}, + {file = "regex-2023.6.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:c123f662be8ec5ab4ea72ea300359023a5d1df095b7ead76fedcd8babbedf969"}, + {file = "regex-2023.6.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:9edcbad1f8a407e450fbac88d89e04e0b99a08473f666a3f3de0fd292badb6aa"}, + {file = "regex-2023.6.3-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:dcba6dae7de533c876255317c11f3abe4907ba7d9aa15d13e3d9710d4315ec0e"}, + {file = "regex-2023.6.3-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:29cdd471ebf9e0f2fb3cac165efedc3c58db841d83a518b082077e612d3ee5df"}, + {file = "regex-2023.6.3-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:12b74fbbf6cbbf9dbce20eb9b5879469e97aeeaa874145517563cca4029db65c"}, + {file = "regex-2023.6.3-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0c29ca1bd61b16b67be247be87390ef1d1ef702800f91fbd1991f5c4421ebae8"}, + {file = "regex-2023.6.3-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:d77f09bc4b55d4bf7cc5eba785d87001d6757b7c9eec237fe2af57aba1a071d9"}, + {file = "regex-2023.6.3-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:ea353ecb6ab5f7e7d2f4372b1e779796ebd7b37352d290096978fea83c4dba0c"}, + {file = "regex-2023.6.3-cp311-cp311-musllinux_1_1_i686.whl", hash = "sha256:10590510780b7541969287512d1b43f19f965c2ece6c9b1c00fc367b29d8dce7"}, + {file = "regex-2023.6.3-cp311-cp311-musllinux_1_1_ppc64le.whl", hash = "sha256:e2fbd6236aae3b7f9d514312cdb58e6494ee1c76a9948adde6eba33eb1c4264f"}, + {file = "regex-2023.6.3-cp311-cp311-musllinux_1_1_s390x.whl", hash = "sha256:6b2675068c8b56f6bfd5a2bda55b8accbb96c02fd563704732fd1c95e2083461"}, + {file = "regex-2023.6.3-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:74419d2b50ecb98360cfaa2974da8689cb3b45b9deff0dcf489c0d333bcc1477"}, + {file = "regex-2023.6.3-cp311-cp311-win32.whl", hash = "sha256:fb5ec16523dc573a4b277663a2b5a364e2099902d3944c9419a40ebd56a118f9"}, + {file = "regex-2023.6.3-cp311-cp311-win_amd64.whl", hash = "sha256:09e4a1a6acc39294a36b7338819b10baceb227f7f7dbbea0506d419b5a1dd8af"}, + {file = "regex-2023.6.3-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:0654bca0cdf28a5956c83839162692725159f4cda8d63e0911a2c0dc76166525"}, + {file = "regex-2023.6.3-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:463b6a3ceb5ca952e66550a4532cef94c9a0c80dc156c4cc343041951aec1697"}, + {file = "regex-2023.6.3-cp36-cp36m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:87b2a5bb5e78ee0ad1de71c664d6eb536dc3947a46a69182a90f4410f5e3f7dd"}, + {file = "regex-2023.6.3-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:6343c6928282c1f6a9db41f5fd551662310e8774c0e5ebccb767002fcf663ca9"}, + {file = "regex-2023.6.3-cp36-cp36m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:b6192d5af2ccd2a38877bfef086d35e6659566a335b1492786ff254c168b1693"}, + {file = "regex-2023.6.3-cp36-cp36m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:74390d18c75054947e4194019077e243c06fbb62e541d8817a0fa822ea310c14"}, + {file = "regex-2023.6.3-cp36-cp36m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:742e19a90d9bb2f4a6cf2862b8b06dea5e09b96c9f2df1779e53432d7275331f"}, + {file = "regex-2023.6.3-cp36-cp36m-musllinux_1_1_aarch64.whl", hash = "sha256:8abbc5d54ea0ee80e37fef009e3cec5dafd722ed3c829126253d3e22f3846f1e"}, + {file = "regex-2023.6.3-cp36-cp36m-musllinux_1_1_i686.whl", hash = "sha256:c2b867c17a7a7ae44c43ebbeb1b5ff406b3e8d5b3e14662683e5e66e6cc868d3"}, + {file = "regex-2023.6.3-cp36-cp36m-musllinux_1_1_ppc64le.whl", hash = "sha256:d831c2f8ff278179705ca59f7e8524069c1a989e716a1874d6d1aab6119d91d1"}, + {file = "regex-2023.6.3-cp36-cp36m-musllinux_1_1_s390x.whl", hash = "sha256:ee2d1a9a253b1729bb2de27d41f696ae893507c7db224436abe83ee25356f5c1"}, + {file = "regex-2023.6.3-cp36-cp36m-musllinux_1_1_x86_64.whl", hash = "sha256:61474f0b41fe1a80e8dfa70f70ea1e047387b7cd01c85ec88fa44f5d7561d787"}, + {file = "regex-2023.6.3-cp36-cp36m-win32.whl", hash = "sha256:0b71e63226e393b534105fcbdd8740410dc6b0854c2bfa39bbda6b0d40e59a54"}, + {file = "regex-2023.6.3-cp36-cp36m-win_amd64.whl", hash = "sha256:bbb02fd4462f37060122e5acacec78e49c0fbb303c30dd49c7f493cf21fc5b27"}, + {file = "regex-2023.6.3-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:b862c2b9d5ae38a68b92e215b93f98d4c5e9454fa36aae4450f61dd33ff48487"}, + {file = "regex-2023.6.3-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:976d7a304b59ede34ca2921305b57356694f9e6879db323fd90a80f865d355a3"}, + {file = "regex-2023.6.3-cp37-cp37m-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:83320a09188e0e6c39088355d423aa9d056ad57a0b6c6381b300ec1a04ec3d16"}, + {file = "regex-2023.6.3-cp37-cp37m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:9427a399501818a7564f8c90eced1e9e20709ece36be701f394ada99890ea4b3"}, + {file = "regex-2023.6.3-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:7178bbc1b2ec40eaca599d13c092079bf529679bf0371c602edaa555e10b41c3"}, + {file = "regex-2023.6.3-cp37-cp37m-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:837328d14cde912af625d5f303ec29f7e28cdab588674897baafaf505341f2fc"}, + {file = "regex-2023.6.3-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:2d44dc13229905ae96dd2ae2dd7cebf824ee92bc52e8cf03dcead37d926da019"}, + {file = "regex-2023.6.3-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:d54af539295392611e7efbe94e827311eb8b29668e2b3f4cadcfe6f46df9c777"}, + {file = "regex-2023.6.3-cp37-cp37m-musllinux_1_1_i686.whl", hash = "sha256:7117d10690c38a622e54c432dfbbd3cbd92f09401d622902c32f6d377e2300ee"}, + {file = "regex-2023.6.3-cp37-cp37m-musllinux_1_1_ppc64le.whl", hash = "sha256:bb60b503ec8a6e4e3e03a681072fa3a5adcbfa5479fa2d898ae2b4a8e24c4591"}, + {file = "regex-2023.6.3-cp37-cp37m-musllinux_1_1_s390x.whl", hash = "sha256:65ba8603753cec91c71de423a943ba506363b0e5c3fdb913ef8f9caa14b2c7e0"}, + {file = "regex-2023.6.3-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:271f0bdba3c70b58e6f500b205d10a36fb4b58bd06ac61381b68de66442efddb"}, + {file = "regex-2023.6.3-cp37-cp37m-win32.whl", hash = "sha256:9beb322958aaca059f34975b0df135181f2e5d7a13b84d3e0e45434749cb20f7"}, + {file = "regex-2023.6.3-cp37-cp37m-win_amd64.whl", hash = "sha256:fea75c3710d4f31389eed3c02f62d0b66a9da282521075061ce875eb5300cf23"}, + {file = "regex-2023.6.3-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:8f56fcb7ff7bf7404becdfc60b1e81a6d0561807051fd2f1860b0d0348156a07"}, + {file = "regex-2023.6.3-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:d2da3abc88711bce7557412310dfa50327d5769a31d1c894b58eb256459dc289"}, + {file = "regex-2023.6.3-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a99b50300df5add73d307cf66abea093304a07eb017bce94f01e795090dea87c"}, + {file = "regex-2023.6.3-cp38-cp38-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:5708089ed5b40a7b2dc561e0c8baa9535b77771b64a8330b684823cfd5116036"}, + {file = "regex-2023.6.3-cp38-cp38-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:687ea9d78a4b1cf82f8479cab23678aff723108df3edeac098e5b2498879f4a7"}, + {file = "regex-2023.6.3-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4d3850beab9f527f06ccc94b446c864059c57651b3f911fddb8d9d3ec1d1b25d"}, + {file = "regex-2023.6.3-cp38-cp38-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:e8915cc96abeb8983cea1df3c939e3c6e1ac778340c17732eb63bb96247b91d2"}, + {file = "regex-2023.6.3-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:841d6e0e5663d4c7b4c8099c9997be748677d46cbf43f9f471150e560791f7ff"}, + {file = "regex-2023.6.3-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:9edce5281f965cf135e19840f4d93d55b3835122aa76ccacfd389e880ba4cf82"}, + {file = "regex-2023.6.3-cp38-cp38-musllinux_1_1_i686.whl", hash = "sha256:b956231ebdc45f5b7a2e1f90f66a12be9610ce775fe1b1d50414aac1e9206c06"}, + {file = "regex-2023.6.3-cp38-cp38-musllinux_1_1_ppc64le.whl", hash = "sha256:36efeba71c6539d23c4643be88295ce8c82c88bbd7c65e8a24081d2ca123da3f"}, + {file = "regex-2023.6.3-cp38-cp38-musllinux_1_1_s390x.whl", hash = "sha256:cf67ca618b4fd34aee78740bea954d7c69fdda419eb208c2c0c7060bb822d747"}, + {file = "regex-2023.6.3-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:b4598b1897837067a57b08147a68ac026c1e73b31ef6e36deeeb1fa60b2933c9"}, + {file = "regex-2023.6.3-cp38-cp38-win32.whl", hash = "sha256:f415f802fbcafed5dcc694c13b1292f07fe0befdb94aa8a52905bd115ff41e88"}, + {file = "regex-2023.6.3-cp38-cp38-win_amd64.whl", hash = "sha256:d4f03bb71d482f979bda92e1427f3ec9b220e62a7dd337af0aa6b47bf4498f72"}, + {file = "regex-2023.6.3-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ccf91346b7bd20c790310c4147eee6ed495a54ddb6737162a36ce9dbef3e4751"}, + {file = "regex-2023.6.3-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:b28f5024a3a041009eb4c333863d7894d191215b39576535c6734cd88b0fcb68"}, + {file = "regex-2023.6.3-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e0bb18053dfcfed432cc3ac632b5e5e5c5b7e55fb3f8090e867bfd9b054dbcbf"}, + {file = "regex-2023.6.3-cp39-cp39-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:9a5bfb3004f2144a084a16ce19ca56b8ac46e6fd0651f54269fc9e230edb5e4a"}, + {file = "regex-2023.6.3-cp39-cp39-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:5c6b48d0fa50d8f4df3daf451be7f9689c2bde1a52b1225c5926e3f54b6a9ed1"}, + {file = "regex-2023.6.3-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:051da80e6eeb6e239e394ae60704d2b566aa6a7aed6f2890a7967307267a5dc6"}, + {file = "regex-2023.6.3-cp39-cp39-manylinux_2_5_i686.manylinux1_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a4c3b7fa4cdaa69268748665a1a6ff70c014d39bb69c50fda64b396c9116cf77"}, + {file = "regex-2023.6.3-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:457b6cce21bee41ac292d6753d5e94dcbc5c9e3e3a834da285b0bde7aa4a11e9"}, + {file = "regex-2023.6.3-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:aad51907d74fc183033ad796dd4c2e080d1adcc4fd3c0fd4fd499f30c03011cd"}, + {file = "regex-2023.6.3-cp39-cp39-musllinux_1_1_i686.whl", hash = "sha256:0385e73da22363778ef2324950e08b689abdf0b108a7d8decb403ad7f5191938"}, + {file = "regex-2023.6.3-cp39-cp39-musllinux_1_1_ppc64le.whl", hash = "sha256:c6a57b742133830eec44d9b2290daf5cbe0a2f1d6acee1b3c7b1c7b2f3606df7"}, + {file = "regex-2023.6.3-cp39-cp39-musllinux_1_1_s390x.whl", hash = "sha256:3e5219bf9e75993d73ab3d25985c857c77e614525fac9ae02b1bebd92f7cecac"}, + {file = "regex-2023.6.3-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:e5087a3c59eef624a4591ef9eaa6e9a8d8a94c779dade95d27c0bc24650261cd"}, + {file = "regex-2023.6.3-cp39-cp39-win32.whl", hash = "sha256:20326216cc2afe69b6e98528160b225d72f85ab080cbdf0b11528cbbaba2248f"}, + {file = "regex-2023.6.3-cp39-cp39-win_amd64.whl", hash = "sha256:bdff5eab10e59cf26bc479f565e25ed71a7d041d1ded04ccf9aee1d9f208487a"}, + {file = "regex-2023.6.3.tar.gz", hash = "sha256:72d1a25bf36d2050ceb35b517afe13864865268dfb45910e2e17a84be6cbfeb0"}, ] [[package]] @@ -1309,13 +1414,13 @@ files = [ [[package]] name = "typing-extensions" -version = "4.6.2" +version = "4.6.3" description = "Backported and Experimental Type Hints for Python 3.7+" optional = false python-versions = ">=3.7" files = [ - {file = "typing_extensions-4.6.2-py3-none-any.whl", hash = "sha256:3a8b36f13dd5fdc5d1b16fe317f5668545de77fa0b8e02006381fd49d731ab98"}, - {file = "typing_extensions-4.6.2.tar.gz", hash = "sha256:06006244c70ac8ee83fa8282cb188f697b8db25bc8b4df07be1873c43897060c"}, + {file = "typing_extensions-4.6.3-py3-none-any.whl", hash = "sha256:88a4153d8505aabbb4e13aacb7c486c2b4a33ca3b3f807914a9b4c844c471c26"}, + {file = "typing_extensions-4.6.3.tar.gz", hash = "sha256:d91d5919357fe7f681a9f2b5b4cb2a5f1ef0a1e9f59c4d8ff0d3491e05c0ffd5"}, ] [[package]] @@ -1342,13 +1447,13 @@ files = [ [[package]] name = "urllib3" -version = "2.0.2" +version = "2.0.3" description = "HTTP library with thread-safe connection pooling, file post, and more." optional = false python-versions = ">=3.7" files = [ - {file = "urllib3-2.0.2-py3-none-any.whl", hash = "sha256:d055c2f9d38dc53c808f6fdc8eab7360b6fdbbde02340ed25cfbcd817c62469e"}, - {file = "urllib3-2.0.2.tar.gz", hash = "sha256:61717a1095d7e155cdb737ac7bb2f4324a858a1e2e6466f6d03ff630ca68d3cc"}, + {file = "urllib3-2.0.3-py3-none-any.whl", hash = "sha256:48e7fafa40319d358848e1bc6809b208340fafe2096f1725d05d67443d0483d1"}, + {file = "urllib3-2.0.3.tar.gz", hash = "sha256:bee28b5e56addb8226c96f7f13ac28cb4c301dd5ea8a6ca179c0b9835e032825"}, ] [package.extras] @@ -1414,4 +1519,4 @@ testing = ["big-O", "flake8 (<5)", "jaraco.functools", "jaraco.itertools", "more [metadata] lock-version = "2.0" python-versions = ">=3.8,<4" -content-hash = "19beb407270a4f143ae5bd0f560c3f21cab0a003955718d39fb0123b761656ab" +content-hash = "76ba767826199fca93a43daa00b67630518618b5d9d769eedd23bc538c7c9173" diff --git a/pyproject.toml b/pyproject.toml index a84ec84..db0dac8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -39,11 +39,13 @@ django = ">=3.2" lib-rql = "^2.0" [tool.poetry.group.test.dependencies] +black = ">=23.3" pytest = ">=7.2.0,<8" pytest-cov = ">=2.10.1,<5" pytest-mock = "^3.3.1" coverage = "^7" flake8 = ">=3.8,<6" +flake8-black = ">=0.3" flake8-bugbear = ">=20,<23" flake8-cognitive-complexity = "^0.1" flake8-commas = "~2.1" @@ -117,4 +119,9 @@ exclude = [ show-source = true max-line-length = 100 max-cognitive-complexity = 20 +select = "B" ignore = ["FI1", "W503", "W605"] + +[tool.black] +line_length = 100 +skip-string-normalization = true diff --git a/tests/constants.py b/tests/constants.py index 7add1e3..bb09ce8 100644 --- a/tests/constants.py +++ b/tests/constants.py @@ -13,17 +13,39 @@ OK_PROPS = ( - 'p', 'p1', 'prop.p2', 'u_p', 'p1__p2__p3', - ComparisonOperators.LT, LogicalOperators.AND, LogicalOperators.NOT, ListOperators.OUT, + 'p', + 'p1', + 'prop.p2', + 'u_p', + 'p1__p2__p3', + ComparisonOperators.LT, + LogicalOperators.AND, + LogicalOperators.NOT, + ListOperators.OUT, SearchOperators.LIKE, ) OK_VALUES = [ - 'value', 'PRD-000-000' "0", '""', "''", '-3.23', '+2', '"text , t lt"', 'val*', - '2014-01-21T19:31:58+03:00', '2015-02-12', '"eq(1,2)"', 'email@example.com', - r'\*', 'текст', - ComparisonOperators.NE, LogicalOperators.OR, LogicalOperators.NOT, ListOperators.IN, + 'value', + 'PRD-000-000' '0', + '""', + "''", + '-3.23', + '+2', + '"text , t lt"', + 'val*', + '2014-01-21T19:31:58+03:00', + '2015-02-12', + '"eq(1,2)"', + 'email@example.com', + r'\*', + 'текст', + ComparisonOperators.NE, + LogicalOperators.OR, + LogicalOperators.NOT, + ListOperators.IN, SearchOperators.I_LIKE, - RQL_NULL, RQL_EMPTY, + RQL_NULL, + RQL_EMPTY, ] FAIL_PROPS = ['', '=', 't t', '"p"', '23', '1p', 'v*', '+v', 'текст'] diff --git a/tests/dj_rf/filters.py b/tests/dj_rf/filters.py index 62d0aae..2dc6b8a 100644 --- a/tests/dj_rf/filters.py +++ b/tests/dj_rf/filters.py @@ -23,185 +23,226 @@ from tests.dj_rf.models import Book -AUTHOR_FILTERS = ['is_male', { - 'filter': 'email', - 'ordering': True, - 'search': True, -}, { - 'namespace': 'publisher', - 'filters': ['id'], - 'distinct': False, - 'qs': NSR('publisher'), -}] +AUTHOR_FILTERS = [ + 'is_male', + { + 'filter': 'email', + 'ordering': True, + 'search': True, + }, + { + 'namespace': 'publisher', + 'filters': ['id'], + 'distinct': False, + 'qs': NSR('publisher'), + }, +] -PAGE_FILTERS = [{ - 'filter': 'number', - 'lookups': {FilterLookups.EQ, FilterLookups.NE}, -}, { - 'filter': 'id', - 'source': 'uuid', -}] +PAGE_FILTERS = [ + { + 'filter': 'number', + 'lookups': {FilterLookups.EQ, FilterLookups.NE}, + }, + { + 'filter': 'id', + 'source': 'uuid', + }, +] class BooksFilterClass(RQLFilterClass): MODEL = Book QUERIES_CACHE_BACKEND = LFUCache - FILTERS = ['id', { - 'filter': 'title', - 'null_values': {RQL_NULL, 'NULL_ID'}, - 'search': True, - }, 'current_price', 'written', { - 'filter': 'status', - 'distinct': True, - 'openapi': { - 'required': True, - }, - 'hidden': True, - }, { - 'filter': 'author__email', - 'search': True, - 'openapi': { - 'description': 'Author Email', - 'deprecated': True, - }, - }, { - 'filter': 'name', - 'source': 'author__name', - 'distinct': True, - 'openapi': { + FILTERS = [ + 'id', + { + 'filter': 'title', + 'null_values': {RQL_NULL, 'NULL_ID'}, + 'search': True, + }, + 'current_price', + 'written', + { + 'filter': 'status', + 'distinct': True, + 'openapi': { + 'required': True, + }, 'hidden': True, }, - }, { - 'namespace': 'author', - 'filters': AUTHOR_FILTERS, - 'distinct': True, - 'qs': SR('author', 'author__publisher'), - 'hidden': True, - }, { - 'namespace': 'page', - 'source': 'pages', - 'filters': PAGE_FILTERS, - 'qs': PR('pages'), - }, { - 'filter': 'published.at', - 'source': 'published_at', - 'ordering': True, - 'distinct': True, - }, { - 'filter': 'rating.blog', - 'source': 'blog_rating', - 'use_repr': True, - }, { - 'filter': 'rating.blog_int', - 'source': 'blog_rating', - 'use_repr': False, - }, { - 'filter': 'amazon_rating', - 'lookups': {FilterLookups.GE, FilterLookups.LT}, - 'null_values': {'random'}, - 'hidden': True, - }, { - 'filter': 'url', - 'source': 'publishing_url', - 'openapi': { - 'type': 'string', - }, - }, { - 'filter': 'd_id', - 'sources': ['id', 'author__id'], - 'ordering': True, - }, { - 'filter': 'custom_filter', - 'distinct': True, - 'custom': True, - 'lookups': {FilterLookups.I_LIKE}, - - 'custom_data': [1], - }, { - 'filter': 'int_choice_field', - 'ordering': True, - }, { - 'filter': 'int_choice_field_repr', - 'source': 'int_choice_field', - 'use_repr': True, - 'lookups': {FilterLookups.EQ, FilterLookups.NE}, - }, { - 'filter': 'str_choice_field', - 'search': True, - }, { - 'filter': 'str_choice_field_repr', - 'source': 'str_choice_field', - 'use_repr': True, - 'lookups': {FilterLookups.EQ, FilterLookups.NE}, - }, { - 'filter': 'has_list_lookup', - 'custom': True, - 'lookups': {FilterLookups.EQ, FilterLookups.NE, FilterLookups.IN, FilterLookups.OUT}, - }, { - 'filter': 'no_list_lookup', - 'custom': True, - 'lookups': {FilterLookups.EQ}, - }, { - 'filter': 't__in', - 'source': 'title', - 'openapi': { - 'type': 'custom', - 'format': 'custom', - }, - }, 'github_stars', { - 'filter': 'ordering_filter', - 'custom': True, - 'ordering': True, - 'lookups': {FilterLookups.EQ}, - }, { - 'filter': 'fsm', - 'source': 'fsm_field', - 'ordering': True, - 'search': True, - }, { - 'filter': 'anno_int', - 'dynamic': True, - 'field': IntegerField(), - 'lookups': {FilterLookups.EQ}, - 'ordering': True, - }, { - 'filter': 'anno_str', - 'dynamic': True, - 'field': CharField(), - 'search': True, - }, { - 'filter': 'anno_int_ref', - 'dynamic': True, - 'field': IntegerField(), - 'source': 'anno_int', - 'ordering': True, - }, { - 'filter': 'anno_auto', - 'dynamic': True, - 'field': AutoField(null=True), - 'qs': AN(anno_auto=F('id')), - }, { - 'filter': 'anno_title_non_dynamic', - 'dynamic': False, - 'source': 'title', - }, { - 'filter': 'anno_title_dynamic', - 'dynamic': True, - 'source': 'title', - 'field': CharField(), - 'search': True, - }, { - 'namespace': 'author_publisher', - 'source': 'author__publisher', - 'filters': ['id'], - }, { - 'filter': 'select_author', - 'dynamic': True, - 'field': SelectField(), - 'hidden': True, - 'qs': SR('author'), - }] + { + 'filter': 'author__email', + 'search': True, + 'openapi': { + 'description': 'Author Email', + 'deprecated': True, + }, + }, + { + 'filter': 'name', + 'source': 'author__name', + 'distinct': True, + 'openapi': { + 'hidden': True, + }, + }, + { + 'namespace': 'author', + 'filters': AUTHOR_FILTERS, + 'distinct': True, + 'qs': SR('author', 'author__publisher'), + 'hidden': True, + }, + { + 'namespace': 'page', + 'source': 'pages', + 'filters': PAGE_FILTERS, + 'qs': PR('pages'), + }, + { + 'filter': 'published.at', + 'source': 'published_at', + 'ordering': True, + 'distinct': True, + }, + { + 'filter': 'rating.blog', + 'source': 'blog_rating', + 'use_repr': True, + }, + { + 'filter': 'rating.blog_int', + 'source': 'blog_rating', + 'use_repr': False, + }, + { + 'filter': 'amazon_rating', + 'lookups': {FilterLookups.GE, FilterLookups.LT}, + 'null_values': {'random'}, + 'hidden': True, + }, + { + 'filter': 'url', + 'source': 'publishing_url', + 'openapi': { + 'type': 'string', + }, + }, + { + 'filter': 'd_id', + 'sources': ['id', 'author__id'], + 'ordering': True, + }, + { + 'filter': 'custom_filter', + 'distinct': True, + 'custom': True, + 'lookups': {FilterLookups.I_LIKE}, + 'custom_data': [1], + }, + { + 'filter': 'int_choice_field', + 'ordering': True, + }, + { + 'filter': 'int_choice_field_repr', + 'source': 'int_choice_field', + 'use_repr': True, + 'lookups': {FilterLookups.EQ, FilterLookups.NE}, + }, + { + 'filter': 'str_choice_field', + 'search': True, + }, + { + 'filter': 'str_choice_field_repr', + 'source': 'str_choice_field', + 'use_repr': True, + 'lookups': {FilterLookups.EQ, FilterLookups.NE}, + }, + { + 'filter': 'has_list_lookup', + 'custom': True, + 'lookups': {FilterLookups.EQ, FilterLookups.NE, FilterLookups.IN, FilterLookups.OUT}, + }, + { + 'filter': 'no_list_lookup', + 'custom': True, + 'lookups': {FilterLookups.EQ}, + }, + { + 'filter': 't__in', + 'source': 'title', + 'openapi': { + 'type': 'custom', + 'format': 'custom', + }, + }, + 'github_stars', + { + 'filter': 'ordering_filter', + 'custom': True, + 'ordering': True, + 'lookups': {FilterLookups.EQ}, + }, + { + 'filter': 'fsm', + 'source': 'fsm_field', + 'ordering': True, + 'search': True, + }, + { + 'filter': 'anno_int', + 'dynamic': True, + 'field': IntegerField(), + 'lookups': {FilterLookups.EQ}, + 'ordering': True, + }, + { + 'filter': 'anno_str', + 'dynamic': True, + 'field': CharField(), + 'search': True, + }, + { + 'filter': 'anno_int_ref', + 'dynamic': True, + 'field': IntegerField(), + 'source': 'anno_int', + 'ordering': True, + }, + { + 'filter': 'anno_auto', + 'dynamic': True, + 'field': AutoField(null=True), + 'qs': AN(anno_auto=F('id')), + }, + { + 'filter': 'anno_title_non_dynamic', + 'dynamic': False, + 'source': 'title', + }, + { + 'filter': 'anno_title_dynamic', + 'dynamic': True, + 'source': 'title', + 'field': CharField(), + 'search': True, + }, + { + 'namespace': 'author_publisher', + 'source': 'author__publisher', + 'filters': ['id'], + }, + { + 'filter': 'select_author', + 'dynamic': True, + 'field': SelectField(), + 'hidden': True, + 'qs': SR('author'), + }, + ] class SelectBooksFilterClass(BooksFilterClass): @@ -211,7 +252,6 @@ class SelectBooksFilterClass(BooksFilterClass): class SelectDetailedBooksFilterClass(SelectBooksFilterClass): - def __make_filters(): result = deepcopy(BooksFilterClass.FILTERS) result[4]['hidden'] = False # status diff --git a/tests/dj_rf/models.py b/tests/dj_rf/models.py index b6c3733..647c0f9 100644 --- a/tests/dj_rf/models.py +++ b/tests/dj_rf/models.py @@ -27,7 +27,10 @@ class Author(models.Model): is_male = models.NullBooleanField() publisher = models.ForeignKey( - Publisher, related_name='authors', on_delete=models.SET_NULL, null=True, + Publisher, + related_name='authors', + on_delete=models.SET_NULL, + null=True, ) fk1 = models.ForeignKey(RandomFk, on_delete=models.SET_NULL, null=True) @@ -111,7 +114,10 @@ class ReverseFKRelated(models.Model): class ReverseOneToOneRelated(models.Model): auto = models.OneToOneField( - AutoMain, null=True, on_delete=models.SET_NULL, related_name='reverse_OtO', + AutoMain, + null=True, + on_delete=models.SET_NULL, + related_name='reverse_OtO', ) @@ -121,7 +127,10 @@ class ReverseManyToManyRelated(models.Model): class ReverseManyToManyTroughRelated(models.Model): auto = models.ManyToManyField( - AutoMain, through='Through', through_fields=('mtm', 'auto'), related_name='reverse_MtM', + AutoMain, + through='Through', + through_fields=('mtm', 'auto'), + related_name='reverse_MtM', ) diff --git a/tests/test_commands/test_generate_rql_class.py b/tests/test_commands/test_generate_rql_class.py index 3ef90ce..f5c7c66 100644 --- a/tests/test_commands/test_generate_rql_class.py +++ b/tests/test_commands/test_generate_rql_class.py @@ -17,6 +17,7 @@ def test_default(): f.write(code) from tests.test_commands._generated_filters1 import AutoMainFilters + assert AutoMainFilters.MODEL == AutoMain assert AutoMainFilters.FILTERS assert AutoMainFilters.SELECT is True @@ -39,31 +40,36 @@ def test_overridden_args(): f.write(code) from tests.test_commands._generated_filters2 import PublisherFilters + assert PublisherFilters.MODEL == Publisher assert PublisherFilters.FILTERS == [ { - "filter": "id", - "ordering": True, - "search": False, + 'filter': 'id', + 'ordering': True, + 'search': False, }, { - "filter": "name", - "ordering": True, - "search": True, + 'filter': 'name', + 'ordering': True, + 'search': True, }, { - "namespace": "fk1", - "filters": [ + 'namespace': 'fk1', + 'filters': [ { - "filter": "id", - "ordering": True, - "search": False, + 'filter': 'id', + 'ordering': True, + 'search': False, }, ], - "qs": None, + 'qs': None, }, ] assert PublisherFilters.SELECT is False assert PublisherFilters.EXCLUDE_FILTERS == [ - 'authors', 'fk1.publisher', 'fk1.author', 'fk2', 'invalid', + 'authors', + 'fk1.publisher', + 'fk1.author', + 'fk2', + 'invalid', ] diff --git a/tests/test_drf/test_common_drf_backend.py b/tests/test_drf/test_common_drf_backend.py index a2951a5..bed40e4 100644 --- a/tests/test_drf/test_common_drf_backend.py +++ b/tests/test_drf/test_common_drf_backend.py @@ -195,8 +195,7 @@ def test_query_cache(api_client, clear_cache, django_assert_num_queries): assert cache.maxsize == 100 cache = caches[ - 'tests.dj_rf.view.DynamicFilterClsViewSet' - '+tests.dj_rf.filters.SelectBooksFilterClass' + 'tests.dj_rf.view.DynamicFilterClsViewSet' '+tests.dj_rf.filters.SelectBooksFilterClass' ] assert isinstance(cache, LRUCache) assert cache.currsize == 3 diff --git a/tests/test_drf/test_django_filters_backend.py b/tests/test_drf/test_django_filters_backend.py index cc539d8..7f19252 100644 --- a/tests/test_drf/test_django_filters_backend.py +++ b/tests/test_drf/test_django_filters_backend.py @@ -30,41 +30,44 @@ def test_compatibility_modify_initial_query(backend): assert backend.modify_initial_query(None, None, 'q') == 'q' -@pytest.mark.parametrize('query,expected', ( - ('', False), - ('&', True), - ('k=v&&', True), - ('k=True', True), - ('author.is_male=True', True), - ('&k&', True), - ('k=v', False), - ('order_by=v', True), - ('k=v%20v', True), - ('k__in=v', True), - ('l1__l2__in=v', True), - ('k__random=v', False), - ('k__in=v,v', True), - ('k__in=ne', True), - ('k__in=ne=v', False), - ('k__in=ne=v""', False), - ('t__in=v', False), - ('order_by=k&k__in=v', True), - ('limit=10,offset=2', False), - ('limit=10,eq(offset,2)', False), - ('limit=10,eq(offset__in,2)', False), - ('limit=10,eq(t__in,b)', False), - ('limit=10;eq(t__in,b)', False), - ('select(books)&k__in=v,v', True), - ('k__in=v,v&select(books)', True), - ('select(books)', False), - ('title__in=v,v', True), - ('limit=10;k__in=2', True), - ('(k=v;k=z)', False), - ('limit=10;k__in=2;k=y)', True), - ('t(email=1)', False), - ('author=t(email=email)', False), - ('k__in=v&t(auhtor=1)', False), -)) +@pytest.mark.parametrize( + 'query,expected', + ( + ('', False), + ('&', True), + ('k=v&&', True), + ('k=True', True), + ('author.is_male=True', True), + ('&k&', True), + ('k=v', False), + ('order_by=v', True), + ('k=v%20v', True), + ('k__in=v', True), + ('l1__l2__in=v', True), + ('k__random=v', False), + ('k__in=v,v', True), + ('k__in=ne', True), + ('k__in=ne=v', False), + ('k__in=ne=v""', False), + ('t__in=v', False), + ('order_by=k&k__in=v', True), + ('limit=10,offset=2', False), + ('limit=10,eq(offset,2)', False), + ('limit=10,eq(offset__in,2)', False), + ('limit=10,eq(t__in,b)', False), + ('limit=10;eq(t__in,b)', False), + ('select(books)&k__in=v,v', True), + ('k__in=v,v&select(books)', True), + ('select(books)', False), + ('title__in=v,v', True), + ('limit=10;k__in=2', True), + ('(k=v;k=z)', False), + ('limit=10;k__in=2;k=y)', True), + ('t(email=1)', False), + ('author=t(email=email)', False), + ('k__in=v&t(auhtor=1)', False), + ), +) def test_old_syntax(mocker, query, expected): request = mocker.MagicMock(query_params=QueryDict(query)) filter_instance = BooksFilterClass(Book.objects.none()) @@ -97,9 +100,13 @@ def test_bad_syntax_query(mocker): assert DjangoFiltersRQLFilterBackend.get_query(filter_instance, request, None) -@pytest.mark.parametrize('query', ( - 'select(books)&k__in=v,v', 'k__in=v,v&select(books)', -)) +@pytest.mark.parametrize( + 'query', + ( + 'select(books)&k__in=v,v', + 'k__in=v,v&select(books)', + ), +) def test_old_syntax_select_remains(query, mocker): expected = ('select(books)&in(k,("v","v"))', 'in(k,("v","v"))&select(books)') request = mocker.MagicMock(query_params=QueryDict(query)) @@ -134,10 +141,13 @@ def test_common_comparison(api_client, clear_cache): @pytest.mark.django_db -@pytest.mark.parametrize('title,count', ( - ('G', 1), - ('', 2), -)) +@pytest.mark.parametrize( + 'title,count', + ( + ('G', 1), + ('', 2), + ), +) def test__in_with_one_value(api_client, clear_cache, title, count): create_books(['G', '']) @@ -155,10 +165,13 @@ def test__in_with_several_values(api_client, clear_cache): @pytest.mark.django_db -@pytest.mark.parametrize('query', ( - 'G,', - ',G', -)) +@pytest.mark.parametrize( + 'query', + ( + 'G,', + ',G', + ), +) def test__in_with_several_values_one_empty_one_invalid(api_client, clear_cache, query): create_books(['G', '']) @@ -176,12 +189,15 @@ def test_double__in_property(api_client, clear_cache): @pytest.mark.django_db -@pytest.mark.parametrize('value,index', ( - ('True', 0), - ('true', 0), - ('False', 1), - ('false', 1), -)) +@pytest.mark.parametrize( + 'value,index', + ( + ('True', 0), + ('true', 0), + ('False', 1), + ('false', 1), + ), +) def test_boolean_value_ok(api_client, clear_cache, value, index): authors = [ Author.objects.create(name='n', is_male=True), @@ -195,26 +211,32 @@ def test_boolean_value_ok(api_client, clear_cache, value, index): @pytest.mark.django_db -@pytest.mark.parametrize('value', ( - '0', - '1', - 'TRUE', - 'other', -)) +@pytest.mark.parametrize( + 'value', + ( + '0', + '1', + 'TRUE', + 'other', + ), +) def test_boolean_value_fail(api_client, clear_cache, value): with pytest.raises(RQLFilterValueError): filter_api(api_client, 'author.is_male={0}'.format(value)) @pytest.mark.django_db -@pytest.mark.parametrize('value,index', ( - ('True', 0), - ('true', 0), - ('1', 0), - ('False', 1), - ('false', 1), - ('0', 1), -)) +@pytest.mark.parametrize( + 'value,index', + ( + ('True', 0), + ('true', 0), + ('1', 0), + ('False', 1), + ('false', 1), + ('0', 1), + ), +) def test__isnull_ok(api_client, clear_cache, value, index): books = create_books([None, 'G']) @@ -224,22 +246,28 @@ def test__isnull_ok(api_client, clear_cache, value, index): @pytest.mark.django_db -@pytest.mark.parametrize('value', ( - '2', - 'TRUE', - 'other', -)) +@pytest.mark.parametrize( + 'value', + ( + '2', + 'TRUE', + 'other', + ), +) def test__isnull_fail(api_client, clear_cache, value): with pytest.raises(RQLFilterParsingError): filter_api(api_client, 'title__isnull={0}'.format(value)) @pytest.mark.django_db -@pytest.mark.parametrize('value,count', ( - ('"G"', 0), - ('g', 0), - ('G', 1), -)) +@pytest.mark.parametrize( + 'value,count', + ( + ('"G"', 0), + ('g', 0), + ('G', 1), + ), +) def test__exact(api_client, clear_cache, value, count): create_book('G') @@ -248,34 +276,44 @@ def test__exact(api_client, clear_cache, value, count): @pytest.mark.django_db -@pytest.mark.parametrize('values,count', ( - (('G', 'G'), 1), - (('G', 'H'), 0), -)) +@pytest.mark.parametrize( + 'values,count', + ( + (('G', 'G'), 1), + (('G', 'H'), 0), + ), +) def test_multiple_choice(api_client, clear_cache, values, count): create_book('G') response = filter_api( - api_client, 'title__exact={0}&title__exact={1}'.format(values[0], values[1]), + api_client, + 'title__exact={0}&title__exact={1}'.format(values[0], values[1]), ) assert_ok_response(response, count) @pytest.mark.django_db -@pytest.mark.parametrize('operator', ( - DjangoLookups.CONTAINS, - DjangoLookups.I_CONTAINS, -)) -@pytest.mark.parametrize('value,count', ( - ('Title', 1), - ('Ti', 1), - ('it', 1), - ('le', 1), - ('"Title"', 0), - ('other', 0), - ('iT', 1), - ('titlE', 1), -)) +@pytest.mark.parametrize( + 'operator', + ( + DjangoLookups.CONTAINS, + DjangoLookups.I_CONTAINS, + ), +) +@pytest.mark.parametrize( + 'value,count', + ( + ('Title', 1), + ('Ti', 1), + ('it', 1), + ('le', 1), + ('"Title"', 0), + ('other', 0), + ('iT', 1), + ('titlE', 1), + ), +) def test__contains(api_client, clear_cache, value, count, operator): create_book('Title') @@ -284,20 +322,26 @@ def test__contains(api_client, clear_cache, value, count, operator): @pytest.mark.django_db -@pytest.mark.parametrize('operator', ( - DjangoLookups.STARTSWITH, - DjangoLookups.I_STARTSWITH, -)) -@pytest.mark.parametrize('value,count', ( - ('Title', 1), - ('Ti', 1), - ('it', 0), - ('le', 0), - ('"Title"', 0), - ('other', 0), - ('iT', 0), - ('titlE', 1), -)) +@pytest.mark.parametrize( + 'operator', + ( + DjangoLookups.STARTSWITH, + DjangoLookups.I_STARTSWITH, + ), +) +@pytest.mark.parametrize( + 'value,count', + ( + ('Title', 1), + ('Ti', 1), + ('it', 0), + ('le', 0), + ('"Title"', 0), + ('other', 0), + ('iT', 0), + ('titlE', 1), + ), +) def test__startswith(api_client, clear_cache, value, count, operator): create_book('Title') @@ -306,20 +350,26 @@ def test__startswith(api_client, clear_cache, value, count, operator): @pytest.mark.django_db -@pytest.mark.parametrize('operator', ( - DjangoLookups.ENDSWITH, - DjangoLookups.I_ENDSWITH, -)) -@pytest.mark.parametrize('value,count', ( - ('Title', 1), - ('Ti', 0), - ('it', 0), - ('le', 1), - ('"Title"', 0), - ('other', 0), - ('iT', 0), - ('titlE', 1), -)) +@pytest.mark.parametrize( + 'operator', + ( + DjangoLookups.ENDSWITH, + DjangoLookups.I_ENDSWITH, + ), +) +@pytest.mark.parametrize( + 'value,count', + ( + ('Title', 1), + ('Ti', 0), + ('it', 0), + ('le', 1), + ('"Title"', 0), + ('other', 0), + ('iT', 0), + ('titlE', 1), + ), +) def test__endswith(api_client, clear_cache, value, count, operator): create_book('Title') @@ -328,38 +378,47 @@ def test__endswith(api_client, clear_cache, value, count, operator): @pytest.mark.django_db -@pytest.mark.parametrize('lookup', ( - DjangoLookups.REGEX, - DjangoLookups.I_REGEX, -)) +@pytest.mark.parametrize( + 'lookup', + ( + DjangoLookups.REGEX, + DjangoLookups.I_REGEX, + ), +) def test__regex(api_client, clear_cache, lookup): with pytest.raises(RQLFilterParsingError): filter_api(api_client, 'title__{0}=true'.format(lookup)) @pytest.mark.django_db -@pytest.mark.parametrize('lookup', ( - 'day', - 'week', - 'month', - 'time', - 'hour', - 'minute', - 'second', - 'day__gt', -)) +@pytest.mark.parametrize( + 'lookup', + ( + 'day', + 'week', + 'month', + 'time', + 'hour', + 'minute', + 'second', + 'day__gt', + ), +) def test__day_week_etc(api_client, clear_cache, lookup): response = filter_api(api_client, 'title__{0}=2020'.format(lookup)) assert_ok_response(response, 0) @pytest.mark.django_db -@pytest.mark.parametrize('lookup,p_value,n_value', ( - (DjangoLookups.GT, 1, 5), - (DjangoLookups.GTE, 5, 10), - (DjangoLookups.LT, 10, 5), - (DjangoLookups.LTE, 10, 1), -)) +@pytest.mark.parametrize( + 'lookup,p_value,n_value', + ( + (DjangoLookups.GT, 1, 5), + (DjangoLookups.GTE, 5, 10), + (DjangoLookups.LT, 10, 5), + (DjangoLookups.LTE, 10, 1), + ), +) def test__gt_ge_lt_le(api_client, clear_cache, lookup, p_value, n_value): Book.objects.create(github_stars=5) @@ -371,10 +430,13 @@ def test__gt_ge_lt_le(api_client, clear_cache, lookup, p_value, n_value): @pytest.mark.django_db -@pytest.mark.parametrize('ordering_term', ( - 'order_by', - 'ordering', -)) +@pytest.mark.parametrize( + 'ordering_term', + ( + 'order_by', + 'ordering', + ), +) def test_order_ok(api_client, clear_cache, ordering_term): same_email = 'a@m.com' authors = [ @@ -391,10 +453,13 @@ def test_order_ok(api_client, clear_cache, ordering_term): @pytest.mark.django_db -@pytest.mark.parametrize('ordering_term', ( - 'order_by', - 'ordering', -)) +@pytest.mark.parametrize( + 'ordering_term', + ( + 'order_by', + 'ordering', + ), +) def test_order_fail(api_client, clear_cache, ordering_term): with pytest.raises(RQLFilterParsingError): filter_api(api_client, '{0}=invalid'.format(ordering_term)) @@ -432,10 +497,13 @@ def test_empty_property(api_client, clear_cache): @pytest.mark.django_db -@pytest.mark.parametrize('prop', ( - 'title', - 'title__exact', -)) +@pytest.mark.parametrize( + 'prop', + ( + 'title', + 'title__exact', + ), +) def test_empty_value(api_client, clear_cache, prop): create_book() diff --git a/tests/test_drf/test_dynamic_filter.py b/tests/test_drf/test_dynamic_filter.py index e456b24..9439157 100644 --- a/tests/test_drf/test_dynamic_filter.py +++ b/tests/test_drf/test_dynamic_filter.py @@ -30,8 +30,7 @@ def test_detail_exclude_fields(api_client, clear_cache): book = Book.objects.create(author=author, status=Book.PLANNING, amazon_rating=5.0) response = api_client.get( - reverse('dynamicfiltercls-detail', [book.pk]) - + '?select(-author,-status,-amazon_rating)', + reverse('dynamicfiltercls-detail', [book.pk]) + '?select(-author,-status,-amazon_rating)', ) assert response.status_code == HTTP_200_OK @@ -61,8 +60,7 @@ def test_list_include_fields(api_client, clear_cache): Book.objects.create(author=author, status=Book.PLANNING, amazon_rating=5.0) response = api_client.get( - reverse('dynamicfiltercls-list') - + '?select(author,status,amazon_rating)', + reverse('dynamicfiltercls-list') + '?select(author,status,amazon_rating)', ) assert response.status_code == HTTP_200_OK diff --git a/tests/test_drf/test_pagination.py b/tests/test_drf/test_pagination.py index 58082c2..5dcc7be 100644 --- a/tests/test_drf/test_pagination.py +++ b/tests/test_drf/test_pagination.py @@ -16,7 +16,8 @@ class TestRQLLimitOffsetPagination(TestCase): - """ An adopted copy of DRF pagination test case. """ + """An adopted copy of DRF pagination test case.""" + def setUp(self): class ExamplePagination(RQLContentRangeLimitOffsetPagination): default_limit = 10 diff --git a/tests/test_drf/test_serializers.py b/tests/test_drf/test_serializers.py index 2481aa3..816a46b 100644 --- a/tests/test_drf/test_serializers.py +++ b/tests/test_drf/test_serializers.py @@ -53,9 +53,11 @@ class Request: 'id': publisher.id, }, }, - 'pages': [{ - 'id': str(page.uuid), - }], + 'pages': [ + { + 'id': str(page.uuid), + }, + ], 'status': book.status, 'amazon_rating': book.amazon_rating, } == data diff --git a/tests/test_filter_cls/conftest.py b/tests/test_filter_cls/conftest.py index d3a745a..e3a775f 100644 --- a/tests/test_filter_cls/conftest.py +++ b/tests/test_filter_cls/conftest.py @@ -28,4 +28,5 @@ def _generate_books(count=2): book_qs = apply_annotations(Book.objects.order_by('id')) books = list(book_qs) return books + return _generate_books diff --git a/tests/test_filter_cls/test_apply_filters.py b/tests/test_filter_cls/test_apply_filters.py index f20a6f7..cbc2f44 100644 --- a/tests/test_filter_cls/test_apply_filters.py +++ b/tests/test_filter_cls/test_apply_filters.py @@ -146,9 +146,12 @@ def test_nested_logic(): def apply_listing_filters(operator, *values): - return apply_filters('{operator}(id,({values}))'.format( - operator=operator, values=','.join(tuple(values)), - )) + return apply_filters( + '{operator}(id,({values}))'.format( + operator=operator, + values=','.join(tuple(values)), + ), + ) apply_in_listing_filters = partial(apply_listing_filters, ListOperators.IN) @@ -175,23 +178,26 @@ def test_out(generate_books): @pytest.mark.filterwarnings('ignore::RuntimeWarning') @pytest.mark.django_db -@pytest.mark.parametrize('filter_string', ( - 't(author.email={email},title=null())', - 't(search={email})', - 't(ge(published.at,{published_at}))', - 't(author.publisher.id={publisher_id})', - 'title=null()&t(author.email={email})', - 't(author=t(email={email}))', - 'author=t(email={email},is_male=true)', - 'author=t(publisher=t(id={publisher_id}))', - 'author=t(email={email},ne(is_male,false))', - 'ne(author,t(email={second_book_email},is_male=true))', - 'and(author=t(email={email}),author=t(is_male=true))', - 'and(title=null(),author=t(is_male=true,publisher=t(id={publisher_id})))', - 'in(author.email,({email}))', - 'in(author,(t(publisher.id=null()),t(email={email})))', - 'out(author,(t(email={second_book_email})))', -)) +@pytest.mark.parametrize( + 'filter_string', + ( + 't(author.email={email},title=null())', + 't(search={email})', + 't(ge(published.at,{published_at}))', + 't(author.publisher.id={publisher_id})', + 'title=null()&t(author.email={email})', + 't(author=t(email={email}))', + 'author=t(email={email},is_male=true)', + 'author=t(publisher=t(id={publisher_id}))', + 'author=t(email={email},ne(is_male,false))', + 'ne(author,t(email={second_book_email},is_male=true))', + 'and(author=t(email={email}),author=t(is_male=true))', + 'and(title=null(),author=t(is_male=true,publisher=t(id={publisher_id})))', + 'in(author.email,({email}))', + 'in(author,(t(publisher.id=null()),t(email={email})))', + 'out(author,(t(email={second_book_email})))', + ), +) def test_tuple(generate_books, filter_string): books = generate_books() comp_filter = filter_string.format( @@ -204,41 +210,46 @@ def test_tuple(generate_books, filter_string): @pytest.mark.django_db -@pytest.mark.parametrize('filter_string', ( - 'author=t(like=1)', - 'author=t(ilike=1)', - 'author=t(in=1)', - 'author=t(out=1)', - 'author=t(eq=1)', - 'author=t(ne=1)', - 'author=t(and=1)', - 'author=t(or=1)', - 'author=t(limit=1)', - 'author=t(offset=1)', -)) +@pytest.mark.parametrize( + 'filter_string', + ( + 'author=t(like=1)', + 'author=t(ilike=1)', + 'author=t(in=1)', + 'author=t(out=1)', + 'author=t(eq=1)', + 'author=t(ne=1)', + 'author=t(and=1)', + 'author=t(or=1)', + 'author=t(limit=1)', + 'author=t(offset=1)', + ), +) def test_tuple_syntax_terms_not_fail(generate_books, filter_string): books = generate_books() assert apply_filters(filter_string) == books -@pytest.mark.parametrize('filter_string', ( - 't()', - 't(1=1)', - 'author=t(t(t(name=1))' - 'author=t(male)', - 'author=t(test=in(male,(true,false)))', - 'in(t(is_male=true),(author))', - 'select(t(author.publisher))', - 'author=t(limit(email))', - 'author=t(offset(email))', - 'author=t(select(email))', - 'author=t(ordering(email))', - 'author=t(and(a=1,b=2))', - 'author=t(or(a=1,b=2))', - 'author=t(not(a=1))', - 'author=t(search(x,term))', - 'auhtor=t(select(+test))', -)) +@pytest.mark.parametrize( + 'filter_string', + ( + 't()', + 't(1=1)', + 'author=t(t(t(name=1))' 'author=t(male)', + 'author=t(test=in(male,(true,false)))', + 'in(t(is_male=true),(author))', + 'select(t(author.publisher))', + 'author=t(limit(email))', + 'author=t(offset(email))', + 'author=t(select(email))', + 'author=t(ordering(email))', + 'author=t(and(a=1,b=2))', + 'author=t(or(a=1,b=2))', + 'author=t(not(a=1))', + 'author=t(search(x,term))', + 'auhtor=t(select(+test))', + ), +) def test_tuple_parse_error(filter_string): with pytest.raises(RQLFilterParsingError) as e: apply_filters(filter_string) @@ -360,13 +371,19 @@ def test_too_much_ordering_filters(): @pytest.mark.django_db -@pytest.mark.parametrize('perms, q', ( - ({('author.email',)}, '-published.at'), - ({('-author.email',)}, 'author.email'), - ({('d_id', '-d_id')}, 'd_id,author.email'), - ({('d_id', 'author.email')}, 'd_id'), - ({('d_id', 'author.email'), ('author.email', '-published.at')}, 'author.email,published.at'), -)) +@pytest.mark.parametrize( + 'perms, q', + ( + ({('author.email',)}, '-published.at'), + ({('-author.email',)}, 'author.email'), + ({('d_id', '-d_id')}, 'd_id,author.email'), + ({('d_id', 'author.email')}, 'd_id'), + ( + {('d_id', 'author.email'), ('author.email', '-published.at')}, + 'author.email,published.at', + ), + ), +) def test_bad_ordering_permutation(perms, q): class CustomCls(BooksFilterClass): ALLOWED_ORDERING_PERMUTATIONS_IN_QUERY = perms @@ -451,12 +468,14 @@ def assert_ordering(self, filter_name, expected): def test_custom_filter_search_ok(mocker): class CustomCls(RQLFilterClass): MODEL = Book - FILTERS = [{ - 'filter': 'search_filter', - 'custom': True, - 'search': True, - 'lookups': {FilterLookups.I_LIKE}, - }] + FILTERS = [ + { + 'filter': 'search_filter', + 'custom': True, + 'search': True, + 'lookups': {FilterLookups.I_LIKE}, + }, + ] def assert_search(self, value, expected): assert list(self.apply_filters('search={0}'.format(value))[1]) == expected @@ -464,14 +483,19 @@ def assert_search(self, value, expected): @classmethod def side_effect(cls, data): django_lookup = data.django_lookup - return Q(**{ - 'title__{0}'.format(django_lookup): cls._get_searching_typed_value( - django_lookup, data.str_value, - ), - }) + return Q( + **{ + 'title__{0}'.format(django_lookup): cls._get_searching_typed_value( + django_lookup, + data.str_value, + ), + } + ) build_q_for_custom_filter_patch = mocker.patch.object( - CustomCls, 'build_q_for_custom_filter', side_effect=CustomCls.side_effect, + CustomCls, + 'build_q_for_custom_filter', + side_effect=CustomCls.side_effect, ) books = [ @@ -502,6 +526,7 @@ class CustomWithFieldCLS(RQLFilterClass): def build_q_for_custom_filter(self, data): if data.filter_name == 'int_field': from django.db.models import Q + return Q(**{f'int_choice_field__{data.django_lookup}': data.str_value}) return super().build_q_for_custom_filter(data) @@ -541,11 +566,13 @@ def build_q_for_custom_filter(self, data): def test_dynamic_no_annotation(): class CustomCls(RQLFilterClass): MODEL = Book - FILTERS = [{ - 'filter': 'anno', - 'dynamic': True, - 'field': IntegerField(), - }] + FILTERS = [ + { + 'filter': 'anno', + 'dynamic': True, + 'field': IntegerField(), + }, + ] # We want to be error unhandled in this case with pytest.raises(FieldError): diff --git a/tests/test_filter_cls/test_fields_filtering.py b/tests/test_filter_cls/test_fields_filtering.py index ba2b7d3..257aa34 100644 --- a/tests/test_filter_cls/test_fields_filtering.py +++ b/tests/test_filter_cls/test_fields_filtering.py @@ -97,10 +97,16 @@ def test_current_price(): assert filter_field(filter_name, CO.EQ, 2) == [] assert filter_field(filter_name, CO.GT, books[1].current_price) == [books[0], books[2]] assert filter_field(filter_name, CO.NE, books[1].current_price) == [ - books[0], books[2], books[3], books[4], + books[0], + books[2], + books[3], + books[4], ] assert filter_field(filter_name, CO.LE, books[0].current_price) == [ - books[0], books[1], books[3], books[4], + books[0], + books[1], + books[3], + books[4], ] @@ -404,38 +410,64 @@ def test_boolean_field_fail(filter_name, bad_value): assert_filter_field_value_error(filter_name, CO.EQ, bad_value) -@pytest.mark.parametrize('bad_value', [ - '2019-02-12T10:02:00', '0', 'date', '2019:02:12', '2019-27-1', -]) +@pytest.mark.parametrize( + 'bad_value', + [ + '2019-02-12T10:02:00', + '0', + 'date', + '2019:02:12', + '2019-27-1', + ], +) @pytest.mark.parametrize('filter_name', ['written']) def test_date_field_fail(filter_name, bad_value): assert_filter_field_value_error(filter_name, CO.EQ, bad_value) -@pytest.mark.parametrize('bad_value', [ - '0', 'date', '2019-02-12T27:00:00', '2019-02-12T21:00:00K', -]) +@pytest.mark.parametrize( + 'bad_value', + [ + '0', + 'date', + '2019-02-12T27:00:00', + '2019-02-12T21:00:00K', + ], +) @pytest.mark.parametrize('filter_name', ['published.at']) def test_datetime_field_fail(filter_name, bad_value): assert_filter_field_value_error(filter_name, CO.EQ, bad_value) @pytest.mark.parametrize('bad_operator', [CO.GT, CO.LE]) -@pytest.mark.parametrize('filter_name,value', [ - ('amazon_rating', '1.23'), ('page.number', '5'), - ('int_choice_field_repr', 'I'), ('str_choice_field_repr', 'I'), - ('select_author', 'value'), -]) +@pytest.mark.parametrize( + 'filter_name,value', + [ + ('amazon_rating', '1.23'), + ('page.number', '5'), + ('int_choice_field_repr', 'I'), + ('str_choice_field_repr', 'I'), + ('select_author', 'value'), + ], +) def test_field_lookup_fail(filter_name, value, bad_operator): assert_filter_field_lookup_error(filter_name, bad_operator, value) -@pytest.mark.parametrize('filter_name,bad_value', [ - ('status', 'invalid'), ('rating.blog', 'invalid'), ('rating.blog_int', '-1'), - ('int_choice_field', 0), ('int_choice_field', 'invalid'), - ('int_choice_field_repr', 0), ('int_choice_field_repr', 'invalid'), - ('str_choice_field', 'zero'), ('str_choice_field_repr', 'zero'), -]) +@pytest.mark.parametrize( + 'filter_name,bad_value', + [ + ('status', 'invalid'), + ('rating.blog', 'invalid'), + ('rating.blog_int', '-1'), + ('int_choice_field', 0), + ('int_choice_field', 'invalid'), + ('int_choice_field_repr', 0), + ('int_choice_field_repr', 'invalid'), + ('str_choice_field', 'zero'), + ('str_choice_field_repr', 'zero'), + ], +) def test_bad_choice_fail(filter_name, bad_value): assert_filter_field_value_error(filter_name, CO.EQ, bad_value) @@ -459,28 +491,31 @@ def test_empty_value_fail(filter_name): assert_filter_field_value_error(filter_name, CO.EQ, RQL_EMPTY) -@pytest.mark.parametrize('value,db_lookup,db_value', [ - ('value', DjangoLookups.EXACT, 'value'), - ('*value', DjangoLookups.ENDSWITH, 'value'), - ('value*', DjangoLookups.STARTSWITH, 'value'), - ('*value*', DjangoLookups.CONTAINS, 'value'), - ('val*ue', DjangoLookups.REGEX, '^val(.*)ue$'), - ('val*ue*', DjangoLookups.REGEX, '^val(.*)ue'), - ('*val*ue', DjangoLookups.REGEX, 'val(.*)ue$'), - ('*val*ue*', DjangoLookups.REGEX, 'val(.*)ue'), - ('*', DjangoLookups.REGEX, '(.*)'), - (r'value\*', DjangoLookups.EXACT, 'value*'), - (r'value\\*', DjangoLookups.STARTSWITH, 'value\\'), - (r'value\\\*', DjangoLookups.EXACT, r'value\*'), - (r'value\**', DjangoLookups.STARTSWITH, 'value*'), - (r'*\*\*value', DjangoLookups.ENDSWITH, '**value'), - (r'*val\*ue*', DjangoLookups.CONTAINS, 'val*ue'), - (r'va\*l*\*ue*', DjangoLookups.REGEX, '^va*l(.*)*ue'), - ('val*[ue}*', DjangoLookups.REGEX, r'^val(.*)\[ue\}'), - ('val*ue)*', DjangoLookups.REGEX, r'^val(.*)ue\)'), - ('*val*ue{2*', DjangoLookups.REGEX, r'val(.*)ue\{2'), - ('val*ue{2}*', DjangoLookups.REGEX, r'^val(.*)ue\{2\}'), -]) +@pytest.mark.parametrize( + 'value,db_lookup,db_value', + [ + ('value', DjangoLookups.EXACT, 'value'), + ('*value', DjangoLookups.ENDSWITH, 'value'), + ('value*', DjangoLookups.STARTSWITH, 'value'), + ('*value*', DjangoLookups.CONTAINS, 'value'), + ('val*ue', DjangoLookups.REGEX, '^val(.*)ue$'), + ('val*ue*', DjangoLookups.REGEX, '^val(.*)ue'), + ('*val*ue', DjangoLookups.REGEX, 'val(.*)ue$'), + ('*val*ue*', DjangoLookups.REGEX, 'val(.*)ue'), + ('*', DjangoLookups.REGEX, '(.*)'), + (r'value\*', DjangoLookups.EXACT, 'value*'), + (r'value\\*', DjangoLookups.STARTSWITH, 'value\\'), + (r'value\\\*', DjangoLookups.EXACT, r'value\*'), + (r'value\**', DjangoLookups.STARTSWITH, 'value*'), + (r'*\*\*value', DjangoLookups.ENDSWITH, '**value'), + (r'*val\*ue*', DjangoLookups.CONTAINS, 'val*ue'), + (r'va\*l*\*ue*', DjangoLookups.REGEX, '^va*l(.*)*ue'), + ('val*[ue}*', DjangoLookups.REGEX, r'^val(.*)\[ue\}'), + ('val*ue)*', DjangoLookups.REGEX, r'^val(.*)ue\)'), + ('*val*ue{2*', DjangoLookups.REGEX, r'val(.*)ue\{2'), + ('val*ue{2}*', DjangoLookups.REGEX, r'^val(.*)ue\{2\}'), + ], +) def test_searching_q_ok(value, db_lookup, db_value): cls = BooksFilterClass(book_qs) diff --git a/tests/test_filter_cls/test_initialization.py b/tests/test_filter_cls/test_initialization.py index 3803b22..5124382 100644 --- a/tests/test_filter_cls/test_initialization.py +++ b/tests/test_filter_cls/test_initialization.py @@ -26,10 +26,14 @@ def test_building_filters(): expected_sub_dct = { 'id': {'orm_route': 'id', 'lookups': FL.numeric()}, 'title': { - 'orm_route': 'title', 'lookups': FL.string(), 'null_values': {RQL_NULL, 'NULL_ID'}, + 'orm_route': 'title', + 'lookups': FL.string(), + 'null_values': {RQL_NULL, 'NULL_ID'}, }, 'current_price': { - 'orm_route': 'current_price', 'lookups': FL.numeric(), 'null_values': {RQL_NULL}, + 'orm_route': 'current_price', + 'lookups': FL.numeric(), + 'null_values': {RQL_NULL}, }, 'written': {'orm_route': 'written', 'lookups': FL.numeric()}, 'status': {'orm_route': 'status', 'lookups': non_null_string_lookups}, @@ -45,13 +49,18 @@ def test_building_filters(): 'page.id': {'orm_route': 'pages__uuid', 'lookups': FL.string()}, 'published.at': {'orm_route': 'published_at', 'lookups': FL.numeric()}, 'rating.blog': { - 'orm_route': 'blog_rating', 'lookups': FL.numeric(), 'use_repr': True, + 'orm_route': 'blog_rating', + 'lookups': FL.numeric(), + 'use_repr': True, }, 'rating.blog_int': { - 'orm_route': 'blog_rating', 'lookups': FL.numeric(), 'use_repr': False, + 'orm_route': 'blog_rating', + 'lookups': FL.numeric(), + 'use_repr': False, }, 'amazon_rating': { - 'orm_route': 'amazon_rating', 'lookups': {FL.GE, FL.LT}, + 'orm_route': 'amazon_rating', + 'lookups': {FL.GE, FL.LT}, }, 'url': {'orm_route': 'publishing_url', 'lookups': FL.string()}, 'd_id': [ @@ -60,16 +69,22 @@ def test_building_filters(): ], 'custom_filter': {'custom': True, 'custom_data': [1], 'lookups': {FL.I_LIKE}}, 'int_choice_field': { - 'orm_route': 'int_choice_field', 'lookups': non_null_numeric_lookups, + 'orm_route': 'int_choice_field', + 'lookups': non_null_numeric_lookups, }, 'int_choice_field_repr': { - 'orm_route': 'int_choice_field', 'lookups': {FL.EQ, FL.NE}, 'use_repr': True, + 'orm_route': 'int_choice_field', + 'lookups': {FL.EQ, FL.NE}, + 'use_repr': True, }, 'str_choice_field': { - 'orm_route': 'str_choice_field', 'lookups': non_null_string_lookups, + 'orm_route': 'str_choice_field', + 'lookups': non_null_string_lookups, }, 'str_choice_field_repr': { - 'orm_route': 'str_choice_field', 'lookups': {FL.EQ, FL.NE}, 'use_repr': True, + 'orm_route': 'str_choice_field', + 'lookups': {FL.EQ, FL.NE}, + 'use_repr': True, }, 'has_list_lookup': {'custom': True, 'lookups': {FL.EQ, FL.IN, FL.OUT}}, 'no_list_lookup': {'custom': True, 'lookups': {FL.EQ}}, @@ -136,13 +151,16 @@ class Cls(BooksFilterClass): assert str(e.value) == 'Max ordering length must be integer.' -@pytest.mark.parametrize('v', ( - 5, - [['name']], - {'name'}, - {(5,)}, - {('x',), ('y', None)}, -)) +@pytest.mark.parametrize( + 'v', + ( + 5, + [['name']], + {'name'}, + {(5,)}, + {('x',), ('y', None)}, + ), +) def test_wrong_ordering_permutations_setup(v): class Cls(BooksFilterClass): ALLOWED_ORDERING_PERMUTATIONS_IN_QUERY = v @@ -154,15 +172,18 @@ class Cls(BooksFilterClass): assert str(e.value) == expected -@pytest.mark.parametrize('v, expected', ( - ({('x',)}, 'Wrong configuration of allowed ordering permutations: x.'), - ({('-d_id', '+')}, 'Wrong configuration of allowed ordering permutations: +.'), - ({('',)}, 'Wrong configuration of allowed ordering permutations: .'), +@pytest.mark.parametrize( + 'v, expected', ( - {('author.email', '+published.at'), ('fsm', '-title')}, - 'Wrong configuration of allowed ordering permutations: -title.', + ({('x',)}, 'Wrong configuration of allowed ordering permutations: x.'), + ({('-d_id', '+')}, 'Wrong configuration of allowed ordering permutations: +.'), + ({('',)}, 'Wrong configuration of allowed ordering permutations: .'), + ( + {('author.email', '+published.at'), ('fsm', '-title')}, + 'Wrong configuration of allowed ordering permutations: -title.', + ), ), -)) +) def test_wrong_ordering_permutations_filter_name_provided(v, expected): class Cls(BooksFilterClass): ALLOWED_ORDERING_PERMUTATIONS_IN_QUERY = v @@ -218,10 +239,12 @@ class Cls(RQLFilterClass): def test_reserved_filter_name_is_used(filter_name): class Cls(RQLFilterClass): MODEL = Author - FILTERS = [{ - 'filter': filter_name, - 'source': 'id', - }] + FILTERS = [ + { + 'filter': filter_name, + 'source': 'id', + }, + ] with pytest.raises(AssertionError) as e: Cls(empty_qs) @@ -231,12 +254,14 @@ class Cls(RQLFilterClass): def test_bad_use_repr_and_ordering(): class Cls(RQLFilterClass): MODEL = Book - FILTERS = [{ - 'filter': 'rating.blog', - 'source': 'blog_rating', - 'use_repr': True, - 'ordering': True, - }] + FILTERS = [ + { + 'filter': 'rating.blog', + 'source': 'blog_rating', + 'use_repr': True, + 'ordering': True, + }, + ] with pytest.raises(AssertionError) as e: Cls(empty_qs) @@ -246,11 +271,13 @@ class Cls(RQLFilterClass): def test_bad_use_repr_and_search(): class Cls(RQLFilterClass): MODEL = Book - FILTERS = [{ - 'filter': 'str_choice_field', - 'use_repr': True, - 'search': True, - }] + FILTERS = [ + { + 'filter': 'str_choice_field', + 'use_repr': True, + 'search': True, + }, + ] with pytest.raises(AssertionError) as e: Cls(empty_qs) @@ -261,10 +288,12 @@ class Cls(RQLFilterClass): def test_bad_option_in_namespace(option): class Cls(RQLFilterClass): MODEL = Book - FILTERS = [{ - 'namespace': 'title', - option: True, - }] + FILTERS = [ + { + 'namespace': 'title', + option: True, + }, + ] with pytest.raises(AssertionError) as e: Cls(empty_qs) @@ -274,9 +303,11 @@ class Cls(RQLFilterClass): def test_bad_item_structure(): class Cls(RQLFilterClass): MODEL = Book - FILTERS = [{ - 'source': 'title', - }] + FILTERS = [ + { + 'source': 'title', + }, + ] with pytest.raises(AssertionError) as e: Cls(empty_qs) @@ -286,26 +317,32 @@ class Cls(RQLFilterClass): def test_bad_dynamic_in_namespace(): class Cls(RQLFilterClass): MODEL = Book - FILTERS = [{ - 'namespace': 'author', - 'filters': [{ - 'filter': 'a', - 'dynamic': True, - }], - }] + FILTERS = [ + { + 'namespace': 'author', + 'filters': [ + { + 'filter': 'a', + 'dynamic': True, + }, + ], + }, + ] with pytest.raises(AssertionError) as e: Cls(empty_qs) - assert str(e.value) == "author.a: dynamic filters are not supported in namespaces." + assert str(e.value) == 'author.a: dynamic filters are not supported in namespaces.' def test_dynamic_field_not_set(): class Cls(RQLFilterClass): MODEL = Book - FILTERS = [{ - 'filter': 'title', - 'dynamic': True, - }] + FILTERS = [ + { + 'filter': 'title', + 'dynamic': True, + }, + ] with pytest.raises(AssertionError) as e: Cls(empty_qs) @@ -315,15 +352,18 @@ class Cls(RQLFilterClass): def test_bad_dynamic_set(): class Cls(RQLFilterClass): MODEL = Book - FILTERS = [{ - 'filter': 'custom', - 'custom': True, - 'field': True, - 'lookups': {FL.EQ}, - }, { - 'filter': 'common', - 'field': True, - }] + FILTERS = [ + { + 'filter': 'custom', + 'custom': True, + 'field': True, + 'lookups': {FL.EQ}, + }, + { + 'filter': 'common', + 'field': True, + }, + ] with pytest.raises(AssertionError) as e: Cls(empty_qs) @@ -333,10 +373,12 @@ class Cls(RQLFilterClass): def test_bad_search(): class Cls(RQLFilterClass): MODEL = Book - FILTERS = [{ - 'filter': 'id', - 'search': True, - }] + FILTERS = [ + { + 'filter': 'id', + 'search': True, + }, + ] with pytest.raises(AssertionError) as e: Cls(empty_qs) @@ -360,7 +402,9 @@ def _default_expected_book_auto_filters(): 'id': {'orm_route': 'id', 'lookups': FL.numeric()}, 'title': {'orm_route': 'title', 'lookups': FL.string(), 'null_values': {RQL_NULL}}, 'current_price': { - 'orm_route': 'current_price', 'lookups': FL.numeric(), 'null_values': {RQL_NULL}, + 'orm_route': 'current_price', + 'lookups': FL.numeric(), + 'null_values': {RQL_NULL}, }, 'written': {'orm_route': 'written', 'lookups': FL.numeric()}, 'status': {'orm_route': 'status', 'lookups': FL.string(with_null=False)}, @@ -369,10 +413,12 @@ def _default_expected_book_auto_filters(): 'amazon_rating': {'orm_route': 'amazon_rating', 'lookups': FL.numeric()}, 'publishing_url': {'orm_route': 'publishing_url', 'lookups': FL.string()}, 'int_choice_field': { - 'orm_route': 'int_choice_field', 'lookups': FL.numeric(with_null=False), + 'orm_route': 'int_choice_field', + 'lookups': FL.numeric(with_null=False), }, 'str_choice_field': { - 'orm_route': 'str_choice_field', 'lookups': FL.string(with_null=False), + 'orm_route': 'str_choice_field', + 'lookups': FL.string(with_null=False), }, 'github_stars': {'orm_route': 'github_stars', 'lookups': FL.numeric()}, 'fsm_field': {'orm_route': 'fsm_field', 'lookups': FL.string()}, @@ -415,14 +461,16 @@ class Cls(AutoRQLFilterClass): expected_sub_dct = _default_expected_book_auto_filters() del expected_sub_dct['id'] del expected_sub_dct['fsm_field'] - expected_sub_dct.update({ - 'author.is_male': {'orm_route': 'author__is_male', 'lookups': FL.boolean()}, - 'author.email': {'orm_route': 'author__email', 'lookups': FL.string()}, - 'author.publisher.id': { - 'orm_route': 'author__publisher__id', - 'lookups': FL.numeric(), + expected_sub_dct.update( + { + 'author.is_male': {'orm_route': 'author__is_male', 'lookups': FL.boolean()}, + 'author.email': {'orm_route': 'author__email', 'lookups': FL.string()}, + 'author.publisher.id': { + 'orm_route': 'author__publisher__id', + 'lookups': FL.numeric(), + }, }, - }) + ) assert len(Cls(empty_qs).filters) == len(expected_sub_dct) assert_filter_cls( @@ -567,10 +615,12 @@ class Cls(NestedAutoRQLFilterClass): MODEL = AutoMain DEPTH = 3 EXCLUDE_FILTERS = ('common_int', 'parent.parent', 'related1') - FILTERS = ({ - 'filter': 'random', - 'source': 'id', - },) + FILTERS = ( + { + 'filter': 'random', + 'source': 'id', + }, + ) filter_set = set(Cls(AutoMain.objects.all()).filters.keys()) diff --git a/tests/test_filter_cls/test_select.py b/tests/test_filter_cls/test_select.py index c23238f..694e608 100644 --- a/tests/test_filter_cls/test_select.py +++ b/tests/test_filter_cls/test_select.py @@ -133,7 +133,8 @@ class Cls(SelectFilterCls): { 'filter': 'vi', 'source': 'id', - }, { + }, + { 'filter': 'nvi', 'source': 'id', 'hidden': True, @@ -722,7 +723,8 @@ class BookCls(SelectFilterCls): _, qs = BookCls(book_qs).apply_filters('') assert qs.query.select_related == {'author': {'publisher': {'fk1': {}, 'fk2': {}}, 'fk': {}}} assert qs._prefetch_related_lookups == ( - 'author__publisher__authors', 'author__books', + 'author__publisher__authors', + 'author__books', ) diff --git a/tests/test_qs.py b/tests/test_qs.py index 9692ac7..74af434 100644 --- a/tests/test_qs.py +++ b/tests/test_qs.py @@ -41,7 +41,8 @@ def test_extensions_no_extension(): def test_extensions_annotations(): assert DBOptimization('z', anno_1=1, anno_2=2).extensions == { - 'anno_1': 1, 'anno_2': 2, + 'anno_1': 1, + 'anno_2': 2, } diff --git a/tests/test_utils.py b/tests/test_utils.py index 4167150..acf2766 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -29,7 +29,7 @@ def test_no_key(): with pytest.raises(AssertionError) as e: assert_filter_cls(BooksFilterClass, mismatch, set(), set()) - assert "Filter `key` is not set" in str(e.value) + assert 'Filter `key` is not set' in str(e.value) def test_mismatch_for_list_data(): @@ -63,6 +63,5 @@ def test_mismatch_for_fields(): assert_filter_cls(BooksFilterClass, mismatch, set(), set()) assert str(e.value) == ( - "Wrong filter `id` configuration: assertion data " - "must contain `orm_route` and `lookups`." + 'Wrong filter `id` configuration: assertion data ' 'must contain `orm_route` and `lookups`.' )