diff --git a/README.rst b/README.rst index 41c73b9..8902e56 100644 --- a/README.rst +++ b/README.rst @@ -31,7 +31,7 @@ database. One may simply encode and decode Python objects to MongoDB BSON-friendly representations:: - class MyObject(object): + class MyObject: def __init__(self, val): self.val = val diff --git a/jaraco/modb/__init__.py b/jaraco/modb/__init__.py index c50c21c..7d2d822 100644 --- a/jaraco/modb/__init__.py +++ b/jaraco/modb/__init__.py @@ -1,4 +1,7 @@ +from __future__ import annotations + import datetime as dt +from typing import Any import jsonpickle.pickler import jsonpickle.unpickler @@ -6,8 +9,8 @@ # override the default pickler/unpickler to better handle some types -class Pickler(jsonpickle.pickler.Pickler): - def _flatten(self, obj): +class Pickler(jsonpickle.pickler.Pickler): # type: ignore[misc] # jsonpickle/jsonpickle#441 + def _flatten(self, obj: object) -> Any | None: if isinstance(obj, dt.datetime) and not obj.utcoffset(): # naive datetimes or UTC datetimes can be stored directly return obj @@ -16,13 +19,13 @@ def _flatten(self, obj): return super(Pickler, self)._flatten(obj) -class Unpickler(jsonpickle.unpickler.Unpickler): +class Unpickler(jsonpickle.unpickler.Unpickler): # type: ignore[misc] # jsonpickle/jsonpickle#441 pass -def encode(value): +def encode(value: object) -> Any | None: return Pickler().flatten(value) -def decode(value): +def decode(value: object) -> Any | None: return Unpickler().restore(value) diff --git a/jaraco/modb/py.typed b/jaraco/modb/py.typed new file mode 100644 index 0000000..e69de29 diff --git a/mypy.ini b/mypy.ini index efcb8cb..4703a84 100644 --- a/mypy.ini +++ b/mypy.ini @@ -1,6 +1,6 @@ [mypy] # Is the project well-typed? -strict = False +strict = True # Early opt-in even when strict = False warn_unused_ignores = True @@ -13,3 +13,7 @@ explicit_package_bases = True disable_error_code = # Disable due to many false positives overload-overlap, + +# jsonpickle/jsonpickle#441 +[mypy-jsonpickle.*] +ignore_missing_imports = True diff --git a/newsfragments/3.feature.rst b/newsfragments/3.feature.rst new file mode 100644 index 0000000..88e9b7c --- /dev/null +++ b/newsfragments/3.feature.rst @@ -0,0 +1 @@ +Complete annotations and add ``py.typed`` marker -- by :user:`Avasam` diff --git a/pyproject.toml b/pyproject.toml index 8def4f4..123e325 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -67,7 +67,3 @@ type = [ [tool.setuptools_scm] - - -[tool.pytest-enabler.mypy] -# Disabled due to jaraco/skeleton#143 diff --git a/tests/test_handlers.py b/tests/test_handlers.py index ed0470c..bee715a 100644 --- a/tests/test_handlers.py +++ b/tests/test_handlers.py @@ -1,38 +1,40 @@ -import importlib +from __future__ import annotations + import collections import decimal +import importlib import jsonpickle -def setup_module(): +def setup_module() -> None: importlib.import_module('jaraco.modb') -def roundtrip(ob): +def roundtrip(ob: object) -> None: encoded = jsonpickle.encode(ob) print('encoded is', encoded) decoded = jsonpickle.decode(encoded) assert decoded == ob -def test_OrderedDict(): +def test_OrderedDict() -> None: d = collections.OrderedDict(y=3) roundtrip(d) -def test_Decimal(): +def test_Decimal() -> None: roundtrip(decimal.Decimal(1.0)) roundtrip(decimal.Decimal(1000)) class MyText(str): - def __reduce__(self): + def __reduce__(self) -> tuple[type[MyText], tuple[str]]: return MyText, (str(self),) -class TestUnicodeSubclass(object): +class TestUnicodeSubclass: "This technique demonstrates how to handle a text-type subclass" - def test_UnicodeSubclass(self): + def test_UnicodeSubclass(self) -> None: roundtrip(MyText('foo')) diff --git a/tests/test_main.py b/tests/test_main.py index 4a8a43c..d21dd51 100644 --- a/tests/test_main.py +++ b/tests/test_main.py @@ -1,29 +1,33 @@ -import datetime import collections +import datetime +from typing import Dict, Generic, TypeVar import bson.tz_util import jaraco.modb +_T = TypeVar("_T") + -def test_to_bson(): +def test_to_bson() -> None: sample = dict( a='a string', b='another string'.encode('ascii'), c='some binary bytes'.encode('ascii') + b'\x00\xff', ) res = jaraco.modb.encode(sample) + assert res is not None assert res['a'] == sample['a'] assert res['b'] == sample['b'] assert jaraco.modb.decode(res) == sample -class ObjectUnderTest(object): - def __init__(self, val): +class ObjectUnderTest(Generic[_T]): + def __init__(self, val: _T) -> None: self.val = val -def test_object(): +def test_object() -> None: ob = ObjectUnderTest(1) serialized = jaraco.modb.encode(ob) ob_res = jaraco.modb.decode(serialized) @@ -32,7 +36,7 @@ def test_object(): assert ob.val == ob_res.val -def test_nested_object(): +def test_nested_object() -> None: ob_nested = ObjectUnderTest('child') ob_parent = ObjectUnderTest(ob_nested) serialized = jaraco.modb.encode(ob_parent) @@ -42,11 +46,11 @@ def test_nested_object(): assert restored.val.val == 'child' -class MyDict(dict): +class MyDict(Dict[str, _T]): pass -def test_encode_dict_subclass(): +def test_encode_dict_subclass() -> None: d = MyDict(a=3, b=4) encoded = jaraco.modb.encode(d) assert 'MyDict' in str(encoded) @@ -54,7 +58,7 @@ def test_encode_dict_subclass(): assert isinstance(decoded, MyDict) -def test_ordered_dict(): +def test_ordered_dict() -> None: items = ('a', 1), ('c', 3), ('b', 4) ob = collections.OrderedDict(items) serialized = jaraco.modb.encode(ob) @@ -64,7 +68,7 @@ def test_ordered_dict(): assert list(restored.values()) == [1, 3, 4] -def test_datetime_naive(): +def test_datetime_naive() -> None: now = datetime.datetime.now() serialized = jaraco.modb.encode(now) assert isinstance(serialized, datetime.datetime) @@ -72,7 +76,7 @@ def test_datetime_naive(): assert restored == now -def test_datetime_utc(): +def test_datetime_utc() -> None: now = datetime.datetime.now(bson.tz_util.utc) serialized = jaraco.modb.encode(now) assert isinstance(serialized, datetime.datetime) @@ -80,7 +84,7 @@ def test_datetime_utc(): assert restored == now -def test_datetime_local(): +def test_datetime_local() -> None: est = bson.tz_util.FixedOffset(-60 * 5, 'EST') now = datetime.datetime.now(est) serialized = jaraco.modb.encode(now)