From a6f8823027c13710af22fab6de48d39a4eff1801 Mon Sep 17 00:00:00 2001 From: NazarioJL Date: Fri, 9 Jul 2021 10:44:56 -0700 Subject: [PATCH] Format repository with `black` (#34) --- .gitignore | 1 + .pre-commit-config.yaml | 4 + pynamodb_attributes/__init__.py | 26 +++--- pynamodb_attributes/float.py | 1 + pynamodb_attributes/integer.py | 1 + pynamodb_attributes/integer_date.py | 1 + pynamodb_attributes/integer_enum.py | 11 ++- pynamodb_attributes/timedelta.py | 13 ++- pynamodb_attributes/timestamp.py | 13 ++- .../unicode_delimited_tuple.py | 27 ++++-- pynamodb_attributes/unicode_enum.py | 15 ++- pynamodb_attributes/uuid.py | 2 +- setup.py | 18 ++-- tests/conftest.py | 1 + tests/float_attribute_test.py | 6 +- tests/integer_attribute_test.py | 6 +- tests/integer_date_attribute_test.py | 6 +- tests/integer_enum_attribute_test.py | 34 ++++--- tests/meta.py | 4 +- tests/mypy_helpers.py | 30 +++--- tests/mypy_test.py | 91 +++++++++++-------- tests/timedelta_attribute_test.py | 21 +++-- tests/timestamp_attribute_test.py | 6 +- .../unicode_delimited_tuple_attribute_test.py | 40 ++++---- tests/unicode_enum_attribute_test.py | 46 ++++++---- 25 files changed, 255 insertions(+), 169 deletions(-) diff --git a/.gitignore b/.gitignore index 750b7ad..9ab19e1 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ *.egg-info .*.swp .DS_Store +.venv/ venv/ venv3/ .cache/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index dc772a6..8bdd606 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,4 +1,8 @@ repos: + - repo: https://github.com/psf/black + rev: stable + hooks: + - id: black - repo: https://github.com/asottile/reorder_python_imports rev: v2.3.6 hooks: diff --git a/pynamodb_attributes/__init__.py b/pynamodb_attributes/__init__.py index 013df9d..8d72c72 100644 --- a/pynamodb_attributes/__init__.py +++ b/pynamodb_attributes/__init__.py @@ -13,17 +13,17 @@ from .uuid import UUIDAttribute __all__ = [ - 'FloatAttribute', - 'IntegerAttribute', - 'IntegerDateAttribute', - 'IntegerEnumAttribute', - 'UnicodeDelimitedTupleAttribute', - 'UnicodeEnumAttribute', - 'TimedeltaAttribute', - 'TimedeltaMsAttribute', - 'TimedeltaUsAttribute', - 'TimestampAttribute', - 'TimestampMsAttribute', - 'TimestampUsAttribute', - 'UUIDAttribute', + "FloatAttribute", + "IntegerAttribute", + "IntegerDateAttribute", + "IntegerEnumAttribute", + "UnicodeDelimitedTupleAttribute", + "UnicodeEnumAttribute", + "TimedeltaAttribute", + "TimedeltaMsAttribute", + "TimedeltaUsAttribute", + "TimestampAttribute", + "TimestampMsAttribute", + "TimestampUsAttribute", + "UUIDAttribute", ] diff --git a/pynamodb_attributes/float.py b/pynamodb_attributes/float.py index 4696950..9d9dadb 100644 --- a/pynamodb_attributes/float.py +++ b/pynamodb_attributes/float.py @@ -6,6 +6,7 @@ class FloatAttribute(Attribute[float]): """ Unlike NumberAttribute, this attribute has its type hinted as 'float'. """ + attr_type = NumberAttribute.attr_type serialize = NumberAttribute.serialize # type: ignore deserialize = NumberAttribute.deserialize # type: ignore diff --git a/pynamodb_attributes/integer.py b/pynamodb_attributes/integer.py index c41038b..9104e76 100644 --- a/pynamodb_attributes/integer.py +++ b/pynamodb_attributes/integer.py @@ -6,6 +6,7 @@ class IntegerAttribute(Attribute[int]): """ Unlike NumberAttribute, this attribute has its type hinted as 'int'. """ + attr_type = NumberAttribute.attr_type serialize = NumberAttribute.serialize # type: ignore deserialize = NumberAttribute.deserialize # type: ignore diff --git a/pynamodb_attributes/integer_date.py b/pynamodb_attributes/integer_date.py index c656b62..3bcda8e 100644 --- a/pynamodb_attributes/integer_date.py +++ b/pynamodb_attributes/integer_date.py @@ -8,6 +8,7 @@ class IntegerDateAttribute(Attribute[date]): """Represents a date as an integer (e.g. 2015_12_31 for December 31st, 2015).""" + attr_type = IntegerAttribute.attr_type def serialize(self, value: date) -> str: diff --git a/pynamodb_attributes/integer_enum.py b/pynamodb_attributes/integer_enum.py index fb6e444..5b35846 100644 --- a/pynamodb_attributes/integer_enum.py +++ b/pynamodb_attributes/integer_enum.py @@ -7,7 +7,7 @@ import pynamodb.constants from pynamodb.attributes import Attribute -T = TypeVar('T', bound=Enum) +T = TypeVar("T", bound=Enum) _fail: Any = object() @@ -28,9 +28,12 @@ class IntegerEnumAttribute(Attribute[T]): >>> class Shake(Model): >>> flavor = IntegerEnumAttribute(ShakeFlavor) """ + attr_type = pynamodb.constants.NUMBER - def __init__(self, enum_type: Type[T], unknown_value: Optional[T] = _fail, **kwargs: Any) -> None: + def __init__( + self, enum_type: Type[T], unknown_value: Optional[T] = _fail, **kwargs: Any + ) -> None: """ :param enum_type: The type of the enum """ @@ -50,5 +53,7 @@ def deserialize(self, value: str) -> Optional[T]: def serialize(self, value: T) -> str: if not isinstance(value, self.enum_type): - raise TypeError(f"value has invalid type '{type(value)}'; expected '{self.enum_type}'") + raise TypeError( + f"value has invalid type '{type(value)}'; expected '{self.enum_type}'", + ) return str(value.value) diff --git a/pynamodb_attributes/timedelta.py b/pynamodb_attributes/timedelta.py index e189a81..1f9da56 100644 --- a/pynamodb_attributes/timedelta.py +++ b/pynamodb_attributes/timedelta.py @@ -7,13 +7,14 @@ class TimedeltaAttribute(Attribute[timedelta]): - """" + """ " Stores a timedelta as a number of seconds (truncated). >>> class MyModel(Model): >>> delta = TimedeltaAttribute(default=lambda: timedelta(seconds=5)) >>> delta_ms = TimedeltaMsAttribute(default=lambda: timedelta(milliseconds=500)) """ + attr_type = pynamodb.constants.NUMBER _multiplier = 1.0 @@ -25,19 +26,23 @@ def serialize(self, td: timedelta) -> int: def __set__(self, instance: Any, value: Optional[Any]) -> None: if value is not None and not isinstance(value, timedelta): - raise TypeError(f"value has invalid type '{type(value)}'; Optional[timedelta] expected") + raise TypeError( + f"value has invalid type '{type(value)}'; Optional[timedelta] expected", + ) return super().__set__(instance, value) class TimedeltaMsAttribute(TimedeltaAttribute): - """" + """ " Stores a timedelta as a number of milliseconds AKA ms (truncated). """ + _multiplier = 1000.0 class TimedeltaUsAttribute(TimedeltaAttribute): - """" + """ " Stores a timedelta as a number of microseconds AKA μs (truncated). """ + _multiplier = 1000000.0 diff --git a/pynamodb_attributes/timestamp.py b/pynamodb_attributes/timestamp.py index e9eeffd..0508625 100644 --- a/pynamodb_attributes/timestamp.py +++ b/pynamodb_attributes/timestamp.py @@ -8,13 +8,14 @@ class TimestampAttribute(Attribute[datetime]): - """" + """ " Stores time as a Unix epoch timestamp (in seconds) in a DynamoDB number. >>> class MyModel(Model): >>> created_at_seconds = TimestampAttribute(default=lambda: datetime.now(tz=timezone.utc)) >>> created_at_ms = TimestampMsAttribute(default=lambda: datetime.now(tz=timezone.utc)) """ + attr_type = pynamodb.constants.NUMBER _multiplier = 1.0 @@ -27,21 +28,25 @@ def serialize(self, value: datetime) -> str: def __set__(self, instance: Any, value: Optional[Any]) -> None: if value is not None: if not isinstance(value, datetime): - raise TypeError(f"value has invalid type '{type(value)}'; datetime expected") + raise TypeError( + f"value has invalid type '{type(value)}'; datetime expected", + ) if value.tzinfo is None or value.tzinfo.utcoffset(value) is None: raise TypeError("aware datetime expected") return super().__set__(instance, value) class TimestampMsAttribute(TimestampAttribute): - """" + """ " Stores time as a Unix epoch timestamp in milliseconds (ms) in a DynamoDB number. """ + _multiplier = 1000.0 class TimestampUsAttribute(TimestampAttribute): - """" + """ " Stores times as a Unix epoch timestamp in microseconds (μs) in a DynamoDB number. """ + _multiplier = 1000000.0 diff --git a/pynamodb_attributes/unicode_delimited_tuple.py b/pynamodb_attributes/unicode_delimited_tuple.py index 03e7605..479efe7 100644 --- a/pynamodb_attributes/unicode_delimited_tuple.py +++ b/pynamodb_attributes/unicode_delimited_tuple.py @@ -7,8 +7,8 @@ import pynamodb.constants from pynamodb.attributes import Attribute -T = TypeVar('T', bound=Tuple[Any, ...]) -_DEFAULT_FIELD_DELIMITER = '::' +T = TypeVar("T", bound=Tuple[Any, ...]) +_DEFAULT_FIELD_DELIMITER = "::" class UnicodeDelimitedTupleAttribute(Attribute[T]): @@ -29,7 +29,12 @@ class UnicodeDelimitedTupleAttribute(Attribute[T]): attr_type = pynamodb.constants.STRING - def __init__(self, tuple_type: Type[T], delimiter: str = _DEFAULT_FIELD_DELIMITER, **kwargs: Any) -> None: + def __init__( + self, + tuple_type: Type[T], + delimiter: str = _DEFAULT_FIELD_DELIMITER, + **kwargs: Any, + ) -> None: """ :param tuple_type: The type of the tuple -- may be a named or plain tuple :param delimiter: The delimiter to separate the tuple elements @@ -39,21 +44,27 @@ def __init__(self, tuple_type: Type[T], delimiter: str = _DEFAULT_FIELD_DELIMITE self.delimiter = delimiter def deserialize(self, value: str) -> T: - fields = getattr(self.tuple_type, '_fields', None) - field_types = getattr(self.tuple_type, '_field_types', None) + fields = getattr(self.tuple_type, "_fields", None) + field_types = getattr(self.tuple_type, "_field_types", None) if fields and field_types: values = value.split(self.delimiter, maxsplit=len(fields)) - return self.tuple_type(**{f: field_types[f](v) for f, v in zip(fields, values)}) + return self.tuple_type( + **{f: field_types[f](v) for f, v in zip(fields, values)} + ) else: return self.tuple_type(value.split(self.delimiter)) def serialize(self, value: T) -> str: if not isinstance(value, self.tuple_type): - raise TypeError(f"value has invalid type '{type(value)}'; expected '{self.tuple_type}'") + raise TypeError( + f"value has invalid type '{type(value)}'; expected '{self.tuple_type}'", + ) values: List[T] = list(value) while values and values[-1] is None: del values[-1] strings = [str(e) for e in values] if any(self.delimiter in s for s in strings): - raise ValueError(f"Tuple elements may not contain delimiter '{self.delimiter}'") + raise ValueError( + f"Tuple elements may not contain delimiter '{self.delimiter}'", + ) return self.delimiter.join(strings) diff --git a/pynamodb_attributes/unicode_enum.py b/pynamodb_attributes/unicode_enum.py index 5d2d6a7..ed1c198 100644 --- a/pynamodb_attributes/unicode_enum.py +++ b/pynamodb_attributes/unicode_enum.py @@ -7,7 +7,7 @@ import pynamodb.constants from pynamodb.attributes import Attribute -T = TypeVar('T', bound=Enum) +T = TypeVar("T", bound=Enum) _fail: Any = object() @@ -28,9 +28,12 @@ class UnicodeEnumAttribute(Attribute[T]): >>> class Shake(Model): >>> flavor = UnicodeEnumAttribute(ShakeFlavor) """ + attr_type = pynamodb.constants.STRING - def __init__(self, enum_type: Type[T], unknown_value: Optional[T] = _fail, **kwargs: Any) -> None: + def __init__( + self, enum_type: Type[T], unknown_value: Optional[T] = _fail, **kwargs: Any + ) -> None: """ :param enum_type: The type of the enum """ @@ -38,7 +41,9 @@ def __init__(self, enum_type: Type[T], unknown_value: Optional[T] = _fail, **kwa self.enum_type = enum_type self.unknown_value = unknown_value if not all(isinstance(e.value, str) for e in self.enum_type): - raise TypeError(f"Enumeration '{self.enum_type}' values must be all strings") + raise TypeError( + f"Enumeration '{self.enum_type}' values must be all strings", + ) def deserialize(self, value: str) -> Optional[T]: try: @@ -50,5 +55,7 @@ def deserialize(self, value: str) -> Optional[T]: def serialize(self, value: T) -> str: if not isinstance(value, self.enum_type): - raise TypeError(f"value has invalid type '{type(value)}'; expected '{self.enum_type}'") + raise TypeError( + f"value has invalid type '{type(value)}'; expected '{self.enum_type}'", + ) return value.value diff --git a/pynamodb_attributes/uuid.py b/pynamodb_attributes/uuid.py index 59e61f1..080ea77 100644 --- a/pynamodb_attributes/uuid.py +++ b/pynamodb_attributes/uuid.py @@ -26,7 +26,7 @@ def serialize(self, value: UUID) -> str: result = str(value) if self._remove_dashes: - result = result.replace('-', '') + result = result.replace("-", "") return result diff --git a/setup.py b/setup.py index 00a8a6e..83ab80f 100644 --- a/setup.py +++ b/setup.py @@ -2,17 +2,17 @@ from setuptools import setup setup( - name='pynamodb-attributes', - version='0.3.0', - description='Common attributes for PynamoDB', - url='https://www.github.com/lyft/pynamodb-attributes', - maintainer='Lyft', - maintainer_email='ikonstantinov@lyft.com', - packages=find_packages(exclude=['tests*']), + name="pynamodb-attributes", + version="0.3.0", + description="Common attributes for PynamoDB", + url="https://www.github.com/lyft/pynamodb-attributes", + maintainer="Lyft", + maintainer_email="ikonstantinov@lyft.com", + packages=find_packages(exclude=["tests*"]), dependency_links=[], install_requires=[ "pynamodb>=5.0.0", ], - python_requires='>=3', - package_data={'pynamodb_attributes': ['py.typed']}, + python_requires=">=3", + package_data={"pynamodb_attributes": ["py.typed"]}, ) diff --git a/tests/conftest.py b/tests/conftest.py index f484cae..ea8d469 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -4,4 +4,5 @@ @pytest.fixture def uuid_key(): from uuid import uuid4 + return str(uuid4()) diff --git a/tests/float_attribute_test.py b/tests/float_attribute_test.py index 594a3cc..74a2c71 100644 --- a/tests/float_attribute_test.py +++ b/tests/float_attribute_test.py @@ -14,7 +14,7 @@ class MyModel(Model): value = FloatAttribute(null=True) -@pytest.fixture(scope='module', autouse=True) +@pytest.fixture(scope="module", autouse=True) def create_table(): MyModel.create_table() @@ -27,7 +27,7 @@ def test_serialization_non_null(uuid_key): # verify underlying storage item = _connection(MyModel).get_item(uuid_key) - assert item['Item']['value'] == {'N': '45.6'} + assert item["Item"]["value"] == {"N": "45.6"} # verify deserialization model = MyModel.get(uuid_key) @@ -42,7 +42,7 @@ def test_serialization_null(uuid_key): # verify underlying storage item = _connection(MyModel).get_item(uuid_key) - assert 'value' not in item['Item'] + assert "value" not in item["Item"] # verify deserialization model = MyModel.get(uuid_key) diff --git a/tests/integer_attribute_test.py b/tests/integer_attribute_test.py index a0474b4..1dffc56 100644 --- a/tests/integer_attribute_test.py +++ b/tests/integer_attribute_test.py @@ -14,7 +14,7 @@ class MyModel(Model): value = IntegerAttribute(null=True) -@pytest.fixture(scope='module', autouse=True) +@pytest.fixture(scope="module", autouse=True) def create_table(): MyModel.create_table() @@ -27,7 +27,7 @@ def test_serialization_non_null(uuid_key): # verify underlying storage item = _connection(MyModel).get_item(uuid_key) - assert item['Item']['value'] == {'N': '456'} + assert item["Item"]["value"] == {"N": "456"} # verify deserialization model = MyModel.get(uuid_key) @@ -42,7 +42,7 @@ def test_serialization_null(uuid_key): # verify underlying storage item = _connection(MyModel).get_item(uuid_key) - assert 'value' not in item['Item'] + assert "value" not in item["Item"] # verify deserialization model = MyModel.get(uuid_key) diff --git a/tests/integer_date_attribute_test.py b/tests/integer_date_attribute_test.py index c2082c8..6e56ce0 100644 --- a/tests/integer_date_attribute_test.py +++ b/tests/integer_date_attribute_test.py @@ -17,7 +17,7 @@ class MyModel(Model): value = IntegerDateAttribute(null=True) -@pytest.fixture(scope='module', autouse=True) +@pytest.fixture(scope="module", autouse=True) def create_table(): MyModel.create_table() @@ -30,7 +30,7 @@ def test_serialization_non_null(uuid_key): # verify underlying storage item = _connection(MyModel).get_item(uuid_key) - assert item['Item'] == {'key': ANY, 'value': {'N': '20151231'}} + assert item["Item"] == {"key": ANY, "value": {"N": "20151231"}} # verify deserialization model = MyModel.get(uuid_key) @@ -47,7 +47,7 @@ def test_serialization_null(uuid_key): # verify underlying storage item = _connection(MyModel).get_item(uuid_key) - assert 'value' not in item['Item'] + assert "value" not in item["Item"] # verify deserialization model = MyModel.get(uuid_key) diff --git a/tests/integer_enum_attribute_test.py b/tests/integer_enum_attribute_test.py index c95027d..59a3f14 100644 --- a/tests/integer_enum_attribute_test.py +++ b/tests/integer_enum_attribute_test.py @@ -31,18 +31,22 @@ class MyModel(Model): key = UnicodeAttribute(hash_key=True) value = IntegerEnumAttribute(MyEnum, null=True) - value_with_unknown = IntegerEnumAttribute(MyEnum, unknown_value=MyEnum.unknown_key, null=True) + value_with_unknown = IntegerEnumAttribute( + MyEnum, + unknown_value=MyEnum.unknown_key, + null=True, + ) value_with_missing = IntegerEnumAttribute(MyEnumWithMissing, null=True) -@pytest.fixture(scope='module', autouse=True) +@pytest.fixture(scope="module", autouse=True) def create_table(): MyModel.create_table() def test_invalid_enum(): class StringEnum(Enum): - foo_key = 'foo_value' + foo_key = "foo_value" bar_key = 2 with pytest.raises(TypeError, match="values must be all ints"): @@ -60,8 +64,9 @@ def test_serialization_invalid_type(uuid_key): def test_serialization_unknown_value_fail(uuid_key): _connection(MyModel).put_item( - uuid_key, attributes={ - 'value': {'N': '9001'}, + uuid_key, + attributes={ + "value": {"N": "9001"}, }, ) with pytest.raises(ValueError, match="9001 is not a valid MyEnum"): @@ -70,8 +75,9 @@ def test_serialization_unknown_value_fail(uuid_key): def test_serialization_unknown_value_success(uuid_key): _connection(MyModel).put_item( - uuid_key, attributes={ - 'value_with_unknown': {'N': '9001'}, + uuid_key, + attributes={ + "value_with_unknown": {"N": "9001"}, }, ) model = MyModel.get(uuid_key) @@ -80,8 +86,9 @@ def test_serialization_unknown_value_success(uuid_key): def test_serialization_missing_value_success(uuid_key): _connection(MyModel).put_item( - uuid_key, attributes={ - 'value_with_missing': {'N': '9001'}, + uuid_key, + attributes={ + "value_with_missing": {"N": "9001"}, }, ) model = MyModel.get(uuid_key) @@ -89,10 +96,11 @@ def test_serialization_missing_value_success(uuid_key): @pytest.mark.parametrize( - ['value', 'expected_attributes'], [ + ["value", "expected_attributes"], + [ (None, {}), - (MyEnum.foo_key, {'value': {'N': '1'}}), - (MyEnum.bar_key, {'value': {'N': '2'}}), + (MyEnum.foo_key, {"value": {"N": "1"}}), + (MyEnum.bar_key, {"value": {"N": "2"}}), ], ) def test_serialization(value, expected_attributes, uuid_key): @@ -103,7 +111,7 @@ def test_serialization(value, expected_attributes, uuid_key): # verify underlying storage item = _connection(MyModel).get_item(uuid_key) - assert item['Item'] == {'key': ANY, **expected_attributes} + assert item["Item"] == {"key": ANY, **expected_attributes} # verify deserialization model = MyModel.get(uuid_key) diff --git a/tests/meta.py b/tests/meta.py index d390dd5..4488236 100644 --- a/tests/meta.py +++ b/tests/meta.py @@ -3,8 +3,8 @@ def dynamodb_table_meta(table_suffix): class Meta: - host = os.getenv('DYNAMODB_URL', 'http://localhost:8000') - table_name = f'pynamodb-attributes-{table_suffix}' + host = os.getenv("DYNAMODB_URL", "http://localhost:8000") + table_name = f"pynamodb-attributes-{table_suffix}" read_capacity_units = 10 write_capacity_units = 10 diff --git a/tests/mypy_helpers.py b/tests/mypy_helpers.py index 839e44a..7e36481 100644 --- a/tests/mypy_helpers.py +++ b/tests/mypy_helpers.py @@ -12,38 +12,42 @@ def _run_mypy(program: str) -> Iterable[str]: with TemporaryDirectory() as tempdirname: - with open(f'{tempdirname}/__main__.py', 'w') as f: + with open(f"{tempdirname}/__main__.py", "w") as f: f.write(program) - error_pattern = re.compile(fr'^{re.escape(f.name)}:(\d+): (?:error|note): (.*)$') - stdout, stderr, exit_status = mypy.api.run([ - f.name, - '--show-traceback', - ]) + error_pattern = re.compile( + fr"^{re.escape(f.name)}:(\d+): (?:error|note): (.*)$", + ) + stdout, stderr, exit_status = mypy.api.run( + [ + f.name, + "--show-traceback", + ], + ) assert exit_status in (0, 1), f"invalid exit status {exit_status}: {stdout}" if stderr: print(stderr, file=sys.stderr) # allow "printf debugging" of the plugin # Group errors by line errors_by_line: Dict[int, List[str]] = defaultdict(list) - for line in stdout.split('\n'): + for line in stdout.split("\n"): m = error_pattern.match(line) if m: errors_by_line[int(m.group(1))].append(m.group(2)) elif line: - print('x', repr(line)) # allow "printf debugging" of the plugin + print("x", repr(line)) # allow "printf debugging" of the plugin # Reconstruct the "actual" program with "error" comments - error_comment_pattern = re.compile(r'(\s+# E: .*)?$') - for line_no, line in enumerate(program.split('\n'), start=1): - line = error_comment_pattern.sub('', line) + error_comment_pattern = re.compile(r"(\s+# E: .*)?$") + for line_no, line in enumerate(program.split("\n"), start=1): + line = error_comment_pattern.sub("", line) errors = errors_by_line.get(line_no) if errors: - yield line + ''.join(f' # E: {error}' for error in errors) + yield line + "".join(f" # E: {error}" for error in errors) else: yield line def assert_mypy_output(program: str) -> None: program = dedent(program).strip() - actual = '\n'.join(_run_mypy(program)) + actual = "\n".join(_run_mypy(program)) assert actual == program diff --git a/tests/mypy_test.py b/tests/mypy_test.py index d91e1b6..12b7858 100644 --- a/tests/mypy_test.py +++ b/tests/mypy_test.py @@ -1,41 +1,47 @@ +# flake8: noqa E501 """ Note: The expected error strings may change in a future version of mypy. Please update as needed. """ import pytest -pytest.register_assert_rewrite('tests.mypy_helpers') +pytest.register_assert_rewrite("tests.mypy_helpers") from tests.mypy_helpers import assert_mypy_output # noqa def test_integer_attribute(): - assert_mypy_output(""" + assert_mypy_output( + """ from pynamodb.models import Model from pynamodb_attributes import IntegerAttribute class MyModel(Model): my_attr = IntegerAttribute() - reveal_type(MyModel.my_attr) # E: Revealed type is 'pynamodb_attributes.integer.IntegerAttribute' - reveal_type(MyModel().my_attr) # E: Revealed type is 'builtins.int*' - """) + reveal_type(MyModel.my_attr) # E: Revealed type is "pynamodb_attributes.integer.IntegerAttribute" + reveal_type(MyModel().my_attr) # E: Revealed type is "builtins.int*" + """, + ) def test_integer_date_attribute(): - assert_mypy_output(""" + assert_mypy_output( + """ from pynamodb.models import Model from pynamodb_attributes import IntegerDateAttribute class MyModel(Model): my_attr = IntegerDateAttribute() - reveal_type(MyModel.my_attr) # E: Revealed type is 'pynamodb_attributes.integer_date.IntegerDateAttribute' - reveal_type(MyModel().my_attr) # E: Revealed type is 'datetime.date*' - """) + reveal_type(MyModel.my_attr) # E: Revealed type is "pynamodb_attributes.integer_date.IntegerDateAttribute" + reveal_type(MyModel().my_attr) # E: Revealed type is "datetime.date*" + """, + ) def test_unicode_delimited_tuple_attribute(): - assert_mypy_output(""" + assert_mypy_output( + """ from typing import NamedTuple from pynamodb.models import Model from pynamodb_attributes import UnicodeDelimitedTupleAttribute @@ -47,35 +53,41 @@ class MyTuple(NamedTuple): class MyModel(Model): my_attr = UnicodeDelimitedTupleAttribute(MyTuple) - reveal_type(MyModel.my_attr) # E: Revealed type is 'pynamodb_attributes.unicode_delimited_tuple.UnicodeDelimitedTupleAttribute[Tuple[builtins.str, builtins.str, fallback=__main__.MyTuple]]' - reveal_type(MyModel().my_attr) # E: Revealed type is 'Tuple[builtins.str, builtins.str, fallback=__main__.MyTuple]' - reveal_type(MyModel().my_attr.foo) # E: Revealed type is 'builtins.str' - """) # noqa: E501 + reveal_type(MyModel.my_attr) # E: Revealed type is "pynamodb_attributes.unicode_delimited_tuple.UnicodeDelimitedTupleAttribute[Tuple[builtins.str, builtins.str, fallback=__main__.MyTuple]]" + reveal_type(MyModel().my_attr) # E: Revealed type is "Tuple[builtins.str, builtins.str, fallback=__main__.MyTuple]" + reveal_type(MyModel().my_attr.foo) # E: Revealed type is "builtins.str" + """, + ) def test_unicode_enum_attribute(): - assert_mypy_output(""" + assert_mypy_output( + """ from enum import Enum from pynamodb.models import Model from pynamodb_attributes import UnicodeEnumAttribute class MyEnum(Enum): - foo = 'foo' - bar = 'bar' + foo = "foo" + bar = "bar" class MyModel(Model): my_attr = UnicodeEnumAttribute(MyEnum) - reveal_type(MyModel.my_attr) # E: Revealed type is 'pynamodb_attributes.unicode_enum.UnicodeEnumAttribute[__main__.MyEnum]' - reveal_type(MyModel().my_attr) # E: Revealed type is '__main__.MyEnum*' - """) # noqa: E501 + reveal_type(MyModel.my_attr) # E: Revealed type is "pynamodb_attributes.unicode_enum.UnicodeEnumAttribute[__main__.MyEnum]" + reveal_type(MyModel().my_attr) # E: Revealed type is "__main__.MyEnum*" + """, + ) def test_timedelta_attribute(): - assert_mypy_output(""" + assert_mypy_output( + """ from datetime import timedelta from pynamodb.models import Model - from pynamodb_attributes import TimedeltaAttribute, TimedeltaMsAttribute, TimedeltaUsAttribute + from pynamodb_attributes import TimedeltaAttribute + from pynamodb_attributes import TimedeltaMsAttribute + from pynamodb_attributes import TimedeltaUsAttribute class MyModel(Model): td = TimedeltaAttribute() @@ -83,19 +95,23 @@ class MyModel(Model): td_us = TimedeltaUsAttribute() m = MyModel() - reveal_type(m.td) # E: Revealed type is 'datetime.timedelta*' - reveal_type(m.td_ms) # E: Revealed type is 'datetime.timedelta*' - reveal_type(m.td_us) # E: Revealed type is 'datetime.timedelta*' + reveal_type(m.td) # E: Revealed type is "datetime.timedelta*" + reveal_type(m.td_ms) # E: Revealed type is "datetime.timedelta*" + reveal_type(m.td_us) # E: Revealed type is "datetime.timedelta*" m.save(condition=MyModel.td == timedelta(seconds=5)) - """) + """, + ) def test_timestamp_attribute(): - assert_mypy_output(""" + assert_mypy_output( + """ from datetime import datetime from pynamodb.models import Model - from pynamodb_attributes import TimestampAttribute, TimestampMsAttribute, TimestampUsAttribute + from pynamodb_attributes import TimestampAttribute + from pynamodb_attributes import TimestampMsAttribute + from pynamodb_attributes import TimestampUsAttribute class MyModel(Model): ts = TimestampAttribute() @@ -103,22 +119,25 @@ class MyModel(Model): ts_us = TimestampUsAttribute() m = MyModel() - reveal_type(m.ts) # E: Revealed type is 'datetime.datetime*' - reveal_type(m.ts_ms) # E: Revealed type is 'datetime.datetime*' - reveal_type(m.ts_us) # E: Revealed type is 'datetime.datetime*' + reveal_type(m.ts) # E: Revealed type is "datetime.datetime*" + reveal_type(m.ts_ms) # E: Revealed type is "datetime.datetime*" + reveal_type(m.ts_us) # E: Revealed type is "datetime.datetime*" m.save(condition=MyModel.ts == datetime.now()) - """) + """, + ) def test_uuid_attribute(): - assert_mypy_output(""" + assert_mypy_output( + """ from pynamodb.models import Model from pynamodb_attributes import UUIDAttribute class MyModel(Model): my_attr = UUIDAttribute() - reveal_type(MyModel.my_attr) # E: Revealed type is 'pynamodb_attributes.uuid.UUIDAttribute' - reveal_type(MyModel().my_attr) # E: Revealed type is 'uuid.UUID*' - """) + reveal_type(MyModel.my_attr) # E: Revealed type is "pynamodb_attributes.uuid.UUIDAttribute" + reveal_type(MyModel().my_attr) # E: Revealed type is "uuid.UUID*" + """, + ) diff --git a/tests/timedelta_attribute_test.py b/tests/timedelta_attribute_test.py index 63be4f3..1129889 100644 --- a/tests/timedelta_attribute_test.py +++ b/tests/timedelta_attribute_test.py @@ -20,7 +20,7 @@ class MyModel(Model): value_us = TimedeltaUsAttribute(null=True) -@pytest.fixture(scope='module', autouse=True) +@pytest.fixture(scope="module", autouse=True) def create_table(): MyModel.create_table() @@ -28,14 +28,17 @@ def create_table(): def test_serialization_non_null(uuid_key): model = MyModel() model.key = uuid_key - model.value = model.value_ms = model.value_us = timedelta(seconds=456, microseconds=123456) + model.value = model.value_ms = model.value_us = timedelta( + seconds=456, + microseconds=123456, + ) model.save() # verify underlying storage item = _connection(MyModel).get_item(uuid_key) - assert item['Item']['value'] == {'N': '456'} - assert item['Item']['value_ms'] == {'N': '456123'} - assert item['Item']['value_us'] == {'N': '456123456'} + assert item["Item"]["value"] == {"N": "456"} + assert item["Item"]["value_ms"] == {"N": "456123"} + assert item["Item"]["value_us"] == {"N": "456123456"} # verify deserialization model = MyModel.get(uuid_key) @@ -54,9 +57,9 @@ def test_serialization_null(uuid_key): # verify underlying storage item = _connection(MyModel).get_item(uuid_key) - assert 'value' not in item['Item'] - assert 'value_ms' not in item['Item'] - assert 'value_us' not in item['Item'] + assert "value" not in item["Item"] + assert "value_ms" not in item["Item"] + assert "value_us" not in item["Item"] # verify deserialization model = MyModel.get(uuid_key) @@ -67,5 +70,5 @@ def test_serialization_null(uuid_key): def test_set_invalid_type(): model = MyModel() - with pytest.raises(TypeError, match='invalid type'): + with pytest.raises(TypeError, match="invalid type"): model.value = 42 diff --git a/tests/timestamp_attribute_test.py b/tests/timestamp_attribute_test.py index 229d034..364559c 100644 --- a/tests/timestamp_attribute_test.py +++ b/tests/timestamp_attribute_test.py @@ -22,7 +22,7 @@ class MyModel(Model): null_value = TimestampAttribute(null=True) -@pytest.fixture(scope='module', autouse=True) +@pytest.fixture(scope="module", autouse=True) def create_table(): MyModel.create_table() @@ -46,13 +46,13 @@ def test_serialization(uuid_key): def test_set_invalid_type(): model = MyModel() - with pytest.raises(TypeError, match='invalid type'): + with pytest.raises(TypeError, match="invalid type"): model.value = 42 def test_set_naive_datetime(): model = MyModel() - with pytest.raises(TypeError, match='aware datetime expected'): + with pytest.raises(TypeError, match="aware datetime expected"): model.value = datetime.utcnow() diff --git a/tests/unicode_delimited_tuple_attribute_test.py b/tests/unicode_delimited_tuple_attribute_test.py index 86dcad5..d911607 100644 --- a/tests/unicode_delimited_tuple_attribute_test.py +++ b/tests/unicode_delimited_tuple_attribute_test.py @@ -17,7 +17,7 @@ class MyTuple(NamedTuple): zip_code: int = None # type: ignore -@pytest.fixture(scope='module', autouse=True) +@pytest.fixture(scope="module", autouse=True) def create_table(): MyModel.create_table() @@ -27,7 +27,7 @@ class MyModel(Model): key = UnicodeAttribute(hash_key=True) default_delimiter = UnicodeDelimitedTupleAttribute(MyTuple, null=True) - custom_delimiter = UnicodeDelimitedTupleAttribute(MyTuple, delimiter='.', null=True) + custom_delimiter = UnicodeDelimitedTupleAttribute(MyTuple, delimiter=".", null=True) untyped = UnicodeDelimitedTupleAttribute(tuple, null=True) @@ -64,30 +64,31 @@ def test_serialization_invalid_type(uuid_key): def test_serialization_typing(uuid_key): model = MyModel() model.key = uuid_key - model.default_delimiter = MyTuple('US', 'San Francisco', 94107) + model.default_delimiter = MyTuple("US", "San Francisco", 94107) model.save() model = MyModel.get(uuid_key) - assert model.default_delimiter.country == 'US' - assert model.default_delimiter.city == 'San Francisco' + assert model.default_delimiter.country == "US" + assert model.default_delimiter.city == "San Francisco" assert model.default_delimiter.zip_code == 94107 # note the type @pytest.mark.parametrize( - ['value', 'expected_attributes'], [ + ["value", "expected_attributes"], + [ (None, {}), ( - MyTuple(country='US', city='San Francisco', zip_code=94107), + MyTuple(country="US", city="San Francisco", zip_code=94107), { - 'default_delimiter': {'S': 'US::San Francisco::94107'}, - 'custom_delimiter': {'S': 'US.San Francisco.94107'}, + "default_delimiter": {"S": "US::San Francisco::94107"}, + "custom_delimiter": {"S": "US.San Francisco.94107"}, }, ), ( - MyTuple(country='US', city='San Francisco'), + MyTuple(country="US", city="San Francisco"), { - 'default_delimiter': {'S': 'US::San Francisco'}, - 'custom_delimiter': {'S': 'US.San Francisco'}, + "default_delimiter": {"S": "US::San Francisco"}, + "custom_delimiter": {"S": "US.San Francisco"}, }, ), ], @@ -101,7 +102,7 @@ def test_serialization(expected_attributes, value, uuid_key): # verify underlying storage item = _connection(MyModel).get_item(uuid_key) - assert item['Item'] == {'key': ANY, **expected_attributes} + assert item["Item"] == {"key": ANY, **expected_attributes} # verify deserialization model = MyModel.get(uuid_key) @@ -110,18 +111,19 @@ def test_serialization(expected_attributes, value, uuid_key): @pytest.mark.parametrize( - ['value', 'expected_attributes'], [ + ["value", "expected_attributes"], + [ (None, {}), ( - ('US', 'San Francisco', '94107'), + ("US", "San Francisco", "94107"), { - 'untyped': {'S': 'US::San Francisco::94107'}, + "untyped": {"S": "US::San Francisco::94107"}, }, ), ( - ('US', 'San Francisco'), + ("US", "San Francisco"), { - 'untyped': {'S': 'US::San Francisco'}, + "untyped": {"S": "US::San Francisco"}, }, ), ], @@ -134,7 +136,7 @@ def test_serialization_untyped(expected_attributes, value, uuid_key): # verify underlying storage item = _connection(MyModel).get_item(uuid_key) - assert item['Item'] == {'key': ANY, **expected_attributes} + assert item["Item"] == {"key": ANY, **expected_attributes} # verify deserialization model = MyModel.get(uuid_key) diff --git a/tests/unicode_enum_attribute_test.py b/tests/unicode_enum_attribute_test.py index 43afc7d..ed00a72 100644 --- a/tests/unicode_enum_attribute_test.py +++ b/tests/unicode_enum_attribute_test.py @@ -11,15 +11,15 @@ class MyEnum(Enum): - foo_key = 'foo_value' - bar_key = 'bar_value' - unknown_key = 'unknown_value' + foo_key = "foo_value" + bar_key = "bar_value" + unknown_key = "unknown_value" class MyEnumWithMissing(Enum): - foo_key = 'foo_value' - bar_key = 'bar_value' - missing_key = 'missing_value' + foo_key = "foo_value" + bar_key = "bar_value" + missing_key = "missing_value" @classmethod def _missing_(cls, key): @@ -31,18 +31,22 @@ class MyModel(Model): key = UnicodeAttribute(hash_key=True) value = UnicodeEnumAttribute(MyEnum, null=True) - value_with_unknown = UnicodeEnumAttribute(MyEnum, unknown_value=MyEnum.unknown_key, null=True) + value_with_unknown = UnicodeEnumAttribute( + MyEnum, + unknown_value=MyEnum.unknown_key, + null=True, + ) value_with_missing = UnicodeEnumAttribute(MyEnumWithMissing, null=True) -@pytest.fixture(scope='module', autouse=True) +@pytest.fixture(scope="module", autouse=True) def create_table(): MyModel.create_table() def test_invalid_enum(): class IntEnum(Enum): - foo_key = 'foo_value' + foo_key = "foo_value" bar_key = 2 with pytest.raises(TypeError, match="values must be all strings"): @@ -60,8 +64,9 @@ def test_serialization_invalid_type(uuid_key): def test_serialization_unknown_value_fail(uuid_key): _connection(MyModel).put_item( - uuid_key, attributes={ - 'value': {'S': 'nonexistent_value'}, + uuid_key, + attributes={ + "value": {"S": "nonexistent_value"}, }, ) with pytest.raises(ValueError, match="'nonexistent_value' is not a valid MyEnum"): @@ -70,8 +75,9 @@ def test_serialization_unknown_value_fail(uuid_key): def test_serialization_unknown_value_success(uuid_key): _connection(MyModel).put_item( - uuid_key, attributes={ - 'value_with_unknown': {'S': 'nonexistent_value'}, + uuid_key, + attributes={ + "value_with_unknown": {"S": "nonexistent_value"}, }, ) model = MyModel.get(uuid_key) @@ -80,8 +86,9 @@ def test_serialization_unknown_value_success(uuid_key): def test_serialization_missing_value_success(uuid_key): _connection(MyModel).put_item( - uuid_key, attributes={ - 'value_with_missing': {'S': 'nonexistent_value'}, + uuid_key, + attributes={ + "value_with_missing": {"S": "nonexistent_value"}, }, ) model = MyModel.get(uuid_key) @@ -89,10 +96,11 @@ def test_serialization_missing_value_success(uuid_key): @pytest.mark.parametrize( - ['value', 'expected_attributes'], [ + ["value", "expected_attributes"], + [ (None, {}), - (MyEnum.foo_key, {'value': {'S': 'foo_value'}}), - (MyEnum.bar_key, {'value': {'S': 'bar_value'}}), + (MyEnum.foo_key, {"value": {"S": "foo_value"}}), + (MyEnum.bar_key, {"value": {"S": "bar_value"}}), ], ) def test_serialization(value, expected_attributes, uuid_key): @@ -103,7 +111,7 @@ def test_serialization(value, expected_attributes, uuid_key): # verify underlying storage item = _connection(MyModel).get_item(uuid_key) - assert item['Item'] == {'key': ANY, **expected_attributes} + assert item["Item"] == {"key": ANY, **expected_attributes} # verify deserialization model = MyModel.get(uuid_key)