Skip to content

Commit

Permalink
Cleaned up top-level namespace 🧹
Browse files Browse the repository at this point in the history
  • Loading branch information
todofixthis committed Oct 19, 2024
1 parent 3f70ceb commit 696676b
Show file tree
Hide file tree
Showing 14 changed files with 166 additions and 76 deletions.
101 changes: 92 additions & 9 deletions src/filters/__init__.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,99 @@
# Make the base filters accessible from the top level of the package.
# Note that the order is important here, due to dependencies.
from .base import *
from .handlers import *
from .macros import *
from .number import *
from .simple import *
from .complex import *
from .string import *

# Additional filters are loaded into a separate namespace, so that IDEs
# don't go insane.
from filters.extensions import FilterExtensionRegistry
from .base import Type
from .complex import (
FilterMapper,
FilterRepeater,
FilterSwitch,
NamedTuple,
)
from .extensions import FilterExtensionRegistry
from .number import (
Decimal,
Int,
Max,
Min,
Round,
)
from .simple import (
Array,
ByteArray,
Call,
Date,
Datetime,
Empty,
Item,
Length,
MaxLength,
MinLength,
NoOp,
NotEmpty,
Omit,
Optional,
Pick,
Required,
)
from .string import (
Base64Decode,
ByteString,
CaseFold,
Choice,
IpAddress,
JsonDecode,
MaxBytes,
MaxChars,
Regex,
Split,
Strip,
Unicode,
Uuid,
)

__all__ = [
"Array",
"Base64Decode",
"ByteArray",
"ByteString",
"Call",
"CaseFold",
"Choice",
"Date",
"Datetime",
"Decimal",
"Empty",
"FilterMapper",
"FilterRepeater",
"FilterSwitch",
"Int",
"IpAddress",
"Item",
"JsonDecode",
"Length",
"Max",
"MaxBytes",
"MaxChars",
"MaxLength",
"Min",
"MinLength",
"NamedTuple",
"NoOp",
"NotEmpty",
"Omit",
"Optional",
"Pick",
"Regex",
"Required",
"Round",
"Split",
"Strip",
"Type",
"Unicode",
"Uuid",
"ext",
]

# Initialise extensions namespace.
ext = FilterExtensionRegistry()
del FilterExtensionRegistry
2 changes: 1 addition & 1 deletion src/filters/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
]

FilterCompatible = typing.Optional[
typing.Union["BaseFilter", "FilterMeta", typing.Callable[[], "BaseFilter"]]
typing.Union["BaseFilter", "FilterMeta", typing.Callable[..., "BaseFilter"]]
]
"""
Used in PEP-484 type hints to indicate a value that can be normalized
Expand Down
2 changes: 1 addition & 1 deletion src/filters/complex.py
Original file line number Diff line number Diff line change
Expand Up @@ -246,7 +246,7 @@ def __str__(self):
type=type(self).__name__,
filters=", ".join(
"{key}={filter}".format(key=key, filter=filter_chain)
for key, filter_chain in self._filters.items()
for key, filter_chain in self._filters.items()
),
)

Expand Down
7 changes: 4 additions & 3 deletions src/filters/macros.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from abc import ABCMeta
import typing
from abc import ABC
from functools import WRAPPER_ASSIGNMENTS, partial

from filters.base import BaseFilter, FilterMeta
Expand All @@ -9,7 +10,7 @@
]


class FilterMacroType(BaseFilter, metaclass=ABCMeta):
class FilterMacroType(BaseFilter, ABC):
"""
Base type for filter macros. Doesn't do anything on its own, but
it is useful for identifying filter macros when paired with an
Expand All @@ -33,7 +34,7 @@ def MyMacro():
pass


def filter_macro(func, *args, **kwargs):
def filter_macro(func, *args, **kwargs) -> typing.Type[FilterMacroType]:
"""
Promotes a function that returns a filter into its own filter type.
Expand Down
6 changes: 3 additions & 3 deletions src/filters/simple.py
Original file line number Diff line number Diff line change
Expand Up @@ -555,7 +555,7 @@ def __str__(self):
def _apply(self, value):
if len(value) > self.max_length:
if self.truncate:
return value[0 : self.max_length]
return value[0: self.max_length]

return self._invalid_value(
value=value,
Expand Down Expand Up @@ -773,7 +773,7 @@ def __init__(
self.default = (
staticmethod(default).__wrapped__
if self.call_default is True
or (self.call_default is None and callable(default))
or (self.call_default is None and callable(default))
else default
)

Expand Down Expand Up @@ -807,7 +807,7 @@ def _get_default(self):
return (
self.default()
if self.call_default is True
or (self.call_default is None and callable(self.default))
or (self.call_default is None and callable(self.default))
else self.default
)

Expand Down
6 changes: 3 additions & 3 deletions src/filters/string.py
Original file line number Diff line number Diff line change
Expand Up @@ -312,7 +312,7 @@ class MaxBytes(BaseFilter[bytes]):

templates = {
CODE_TOO_LONG: "Value is too long (must be < {max_bytes} "
"bytes when encoded using {encoding}).",
"bytes when encoded using {encoding}).",
}

def __init__(
Expand Down Expand Up @@ -505,7 +505,7 @@ def truncate_bytes(self, bytes_value: bytes) -> bytes:
# Progressively chop bytes off the end of the string until we
# have something that can be successfully decoded using the
# specified encoding.
truncated = bytes_value[0 : target_bytes - trim]
truncated = bytes_value[0: target_bytes - trim]

try:
truncated.decode(self.encoding)
Expand Down Expand Up @@ -603,7 +603,7 @@ def _apply(self, value):
# Edge case where ``self.max_chars`` isn't big enough to fit
# even ``self.suffix`` by itself.
if target_chars < 0:
return self.suffix[0 : self.max_chars]
return self.suffix[0: self.max_chars]

return (self.prefix + value)[0:target_chars] + self.suffix

Expand Down
4 changes: 2 additions & 2 deletions src/filters/test.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from traceback import format_exception
from unittest import TestCase

from filters.base import BaseFilter
from filters.base import FilterCompatible
from filters.handlers import FilterRunner

__all__ = [
Expand Down Expand Up @@ -40,7 +40,7 @@ class BaseFilterTestCase(TestCase):
``assertFilterErrors`` to check use cases.
"""

filter_type: typing.Callable[[...], BaseFilter] = None
filter_type: FilterCompatible = None

class unmodified(object):
"""
Expand Down
8 changes: 5 additions & 3 deletions test/__init__.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
import typing

import filters as f
from filters.base import BaseFilter
from filters.macros import filter_macro


class FilterAlpha(f.BaseFilter):
class FilterAlpha(BaseFilter):
"""
A filter that can be used for testing.
"""
Expand All @@ -12,7 +14,7 @@ def _apply(self, value):
return value


class FilterBravo(f.BaseFilter):
class FilterBravo(BaseFilter):
"""
A filter that will can be used for testing.
"""
Expand All @@ -26,7 +28,7 @@ def _apply(self, value):
return value


@f.filter_macro
@filter_macro
def FilterCharlie():
"""
A filter macro that can be used for testing.
Expand Down
21 changes: 11 additions & 10 deletions test/test_complex.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from collections import namedtuple

import filters as f
from filters.base import FilterChain, FilterError
from filters.test import BaseFilterTestCase


Expand All @@ -20,9 +21,9 @@ def test_implicit_chain_null(self):
unsurprisingly, the chain only contains the one filter.
"""
filter_chain = f.Int() | None
self.assertIsInstance(filter_chain, f.FilterChain)
self.assertIsInstance(filter_chain, FilterChain)

with self.assertRaises(f.FilterError):
with self.assertRaises(FilterError):
filter_chain.apply("not an int")

# noinspection SpellCheckingInspection
Expand Down Expand Up @@ -842,7 +843,7 @@ def test_mapperception(self):
"attachment": f.FilterMapper(
{
"type": f.Required
| f.Choice(choices={"image/jpeg", "image/png"}),
| f.Choice(choices={"image/jpeg", "image/png"}),
"data": f.Required | f.Base64Decode,
},
allow_extra_keys=False,
Expand All @@ -861,8 +862,8 @@ def test_mapperception(self):
"attachment": {
"type": "image/jpeg",
"data": b"R0lGODlhDwAPAKECAAAAzMzM/////wAAACwAAAAAD"
b"wAPAAACIISPeQHsrZ5ModrLlN48CXF8m2iQ3YmmKq"
b"VlRtW4MLwWACH+EVRIRSBDQUtFIElTIEEgTElFOw==",
b"wAPAAACIISPeQHsrZ5ModrLlN48CXF8m2iQ3YmmKq"
b"VlRtW4MLwWACH+EVRIRSBDQUtFIElTIEEgTElFOw==",
},
},
{
Expand All @@ -871,11 +872,11 @@ def test_mapperception(self):
"attachment": {
"type": "image/jpeg",
"data": b"GIF89a\x0f\x00\x0f\x00\xa1\x02\x00\x00\x00"
b"\xcc\xcc\xcc\xff\xff\xff\xff\x00\x00\x00,\x00"
b"\x00\x00\x00\x0f\x00\x0f\x00\x00\x02 \x84\x8f"
b"y\x01\xec\xad\x9eL\xa1\xda\xcb\x94\xde<\tq|"
b"\x9bh\x90\xdd\x89\xa6*\xa5eF\xd5\xb80\xbc\x16"
b"\x00!\xfe\x11THE CAKE IS A LIE;",
b"\xcc\xcc\xcc\xff\xff\xff\xff\x00\x00\x00,\x00"
b"\x00\x00\x00\x0f\x00\x0f\x00\x00\x02 \x84\x8f"
b"y\x01\xec\xad\x9eL\xa1\xda\xcb\x94\xde<\tq|"
b"\x9bh\x90\xdd\x89\xa6*\xa5eF\xd5\xb80\xbc\x16"
b"\x00!\xfe\x11THE CAKE IS A LIE;",
},
},
)
Expand Down
Loading

0 comments on commit 696676b

Please sign in to comment.