Skip to content

Commit 281be1c

Browse files
committed
Fix issues with #244
2.22.1
1 parent 65dc6d2 commit 281be1c

File tree

8 files changed

+49
-18
lines changed

8 files changed

+49
-18
lines changed

docs/structures.rst

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -966,6 +966,10 @@ Typedpy exposes several global defaults. These can be views as the Typedpy confi
966966
8. TypedPyDefaults.block_unknown_consts - if set to True, will raise an exception if a Structure class definition contains
967967
attributes that are not fields, and not Typedpy attributes. It is meant to avoid silly mistakes, like
968968
setting "_ignore_nonnes".
969+
In case you still want to use your own custom (non-typedpy) attribute in a Structure while block_unknown_consts is on
970+
you can either use a method, a property or a class variable with a name that starts with "_custom_attribute_".
971+
972+
969973

970974

971975

setup.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,9 @@
4646
license="MIT",
4747
long_description=long_description,
4848
url="http://github.com/loyada/typedpy",
49-
download_url="https://github.com/loyada/typedpy/archive/v2.22.0.tar.gz",
49+
download_url="https://github.com/loyada/typedpy/archive/v2.22.1.tar.gz",
5050
keywords=["testing", "type-safe", "strict", "schema", "validation"],
51-
version="2.22.0",
51+
version="2.22.1",
5252
)
5353

5454
# coverage run --source=typedpy/ setup.py test

tests/conftest.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,8 +48,17 @@ def fixture_no_defensive_copy_on_get():
4848

4949
@pytest.fixture(name="block_unknown_consts")
5050
def fixture_block_unknown_consts():
51+
saved = TypedPyDefaults.block_unknown_consts
5152
TypedPyDefaults.block_unknown_consts = True
5253
yield
54+
TypedPyDefaults.block_unknown_consts = saved
55+
56+
57+
@pytest.fixture(name="unblock_unknown_consts")
58+
def fixture_unblock_unknown_consts():
59+
saved = TypedPyDefaults.block_unknown_consts
5360
TypedPyDefaults.block_unknown_consts = False
61+
yield
62+
TypedPyDefaults.block_unknown_consts = saved
5463

5564

tests/test_block_unknown_consts.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
from pytest import raises
33

44

5-
def test_dont_block_unknown_consts():
5+
def test_dont_block_unknown_consts(unblock_unknown_consts):
66
class Foo(Structure):
77
a: int
88

@@ -11,6 +11,19 @@ class Foo(Structure):
1111
# no exception thrown
1212

1313

14+
def test_block_unknown_consts_default_config():
15+
with raises(ValueError) as excinfo:
16+
17+
class Foo(Structure):
18+
a: int
19+
20+
_asdasd = True
21+
22+
assert "attribute _asdasd is not a valid TypedPy attribute." in str(
23+
excinfo.value
24+
)
25+
26+
1427
def test_block_unknown_consts(block_unknown_consts):
1528
with raises(ValueError) as excinfo:
1629

typedpy/serialization/serialization.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
wrap_val,
1414
)
1515
from typedpy.serialization.versioned_mapping import (
16-
VERSION_MAPPING,
16+
VERSIONS_MAPPING,
1717
Versioned,
1818
convert_dict,
1919
)
@@ -561,8 +561,8 @@ class reference field. Users are not supposed to use this argument.
561561
if issubclass(cls, Versioned):
562562
if not isinstance(the_dict, dict) or "version" not in the_dict:
563563
raise TypeError("Expected a dictionary with a 'version' value")
564-
if getattr(cls, VERSION_MAPPING):
565-
versions_mapping = getattr(cls, VERSION_MAPPING)
564+
if getattr(cls, VERSIONS_MAPPING):
565+
versions_mapping = getattr(cls, VERSIONS_MAPPING)
566566
input_dict = convert_dict(the_dict, versions_mapping)
567567

568568
if (

typedpy/serialization/versioned_mapping.py

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,11 @@
22

33
from typedpy.commons import Constant, deep_get
44
from typedpy.structures import Structure
5+
from typedpy.structures.consts import VERSIONS_MAPPING
56
from typedpy.fields import FunctionCall, PositiveInt
67
from .mappers import Deleted
78

89

9-
VERSION_MAPPING = "_versions_mapping"
10-
11-
1210
class Versioned(Structure):
1311
"""
1412
Marks a structure as can be deserialized from multiple versions.
@@ -26,7 +24,7 @@ class Versioned(Structure):
2624
version: PositiveInt
2725

2826
def __init__(self, *args, **kwargs):
29-
versions_mapping = getattr(self, VERSION_MAPPING, [])
27+
versions_mapping = getattr(self, VERSIONS_MAPPING, [])
3028
default_version = len(versions_mapping) + 1
3129
kwargs["version"] = default_version
3230
super().__init__(*args, **kwargs)

typedpy/structures/defaults.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,4 +6,4 @@ class TypedPyDefaults:
66
uniqueness_features_enabled = False
77
defensive_copy_on_get = True
88
allow_none_for_optionals = False
9-
block_unknown_consts = False
9+
block_unknown_consts = True

typedpy/structures/structures.py

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@
4747
from .defaults import TypedPyDefaults
4848
from .type_mapping import convert_basic_types
4949

50-
5150
T = typing.TypeVar("T")
5251

5352
_immutable_types = (int, float, str, tuple, bool, enum.Enum)
@@ -659,12 +658,21 @@ def _apply_default_and_update_required_not_to_include_fields_with_defaults(
659658

660659
def _block_invalid_consts(cls_dict):
661660
annotations = cls_dict.get("__annotations__", {})
661+
known_attributes = (
662+
annotations.keys()
663+
| SPECIAL_ATTRIBUTES
664+
| {"_fields", "_fail_fast", "_field_by_name", "_constants"}
665+
)
666+
""
662667
for k, v in cls_dict.items():
663-
if k in annotations or k in SPECIAL_ATTRIBUTES or _is_dunder(k) or k.startswith(CUSTOM_ATTRIBUTE_MARKER):
668+
if (
669+
k in known_attributes
670+
or _is_dunder(k)
671+
or k.startswith(CUSTOM_ATTRIBUTE_MARKER)
672+
):
664673
continue
665674
if isinstance(v, (bool, list, dict)):
666-
raise ValueError(f"attribute {k} is not a valid TypedPy attribute. "
667-
f"If it is not a typo, please add an annotation")
675+
raise ValueError(f"attribute {k} is not a valid TypedPy attribute.")
668676

669677

670678
class StructMeta(type):
@@ -678,7 +686,7 @@ def __prepare__(cls, name, bases):
678686

679687
def __new__(
680688
cls, name, bases, cls_dict
681-
): # pylint: disable=too-many-locals, too-many-branches
689+
): # pylint: disable=too-many-locals, too-many-branches
682690
bases_params, bases_required = get_base_info(bases)
683691
add_annotations_to_class_dict(cls_dict, previous_frame=currentframe().f_back)
684692
defaults = cls_dict[DEFAULTS]
@@ -1531,8 +1539,7 @@ def from_trusted_data(cls, source_object=None, *, ignore_props=None, **kw):
15311539
if is_mapping
15321540
else getattr(source_object, k, None)
15331541
for k in cls.get_all_fields_by_name()
1534-
if k not in ignore_props
1535-
and k not in getattr(cls, "_constants", {})
1542+
if k not in ignore_props and k not in getattr(cls, "_constants", {})
15361543
}
15371544
kwargs = {**args_from_model, **kw}
15381545
else:

0 commit comments

Comments
 (0)