Added checks for storage name clashes to dataclassio

This commit is contained in:
Eric Froemling 2021-05-26 11:37:52 -05:00
parent 7f1458304d
commit f2c0067ee1
No known key found for this signature in database
GPG Key ID: 89C93F0F8D6D5A98
6 changed files with 54 additions and 5 deletions

View File

@ -1649,6 +1649,7 @@
<w>positionadjusted</w>
<w>posixpath</w>
<w>posixsubprocess</w>
<w>posonlyargs</w>
<w>postinit</w>
<w>postinited</w>
<w>postrun</w>

View File

@ -738,6 +738,7 @@
<w>positivex</w>
<w>positivey</w>
<w>positivez</w>
<w>posonlyargs</w>
<w>postinit</w>
<w>postrun</w>
<w>powerup</w>

View File

@ -1,5 +1,5 @@
<!-- THIS FILE IS AUTO GENERATED; DO NOT EDIT BY HAND -->
<h4><em>last updated on 2021-05-25 for Ballistica version 1.6.4 build 20369</em></h4>
<h4><em>last updated on 2021-05-26 for Ballistica version 1.6.4 build 20369</em></h4>
<p>This page documents the Python classes and functions in the 'ba' module,
which are the ones most relevant to modding in Ballistica. If you come across something you feel should be included here or could be better explained, please <a href="mailto:support@froemling.net">let me know</a>. Happy modding!</p>
<hr>

View File

@ -623,3 +623,31 @@ def test_dict() -> None:
obj4.dval = {_GoodEnum.VAL1: 999} # type: ignore
with pytest.raises(TypeError):
dataclass_to_dict(obj4)
def test_name_clashes() -> None:
"""Make sure we catch name clashes since we can remap attr names."""
with pytest.raises(TypeError):
@ioprepped
@dataclass
class _TestClass:
ival: Annotated[int, IOAttrs('i')] = 4
ival2: Annotated[int, IOAttrs('i')] = 5
with pytest.raises(TypeError):
@ioprepped
@dataclass
class _TestClass2:
ival: int = 4
ival2: Annotated[int, IOAttrs('ival')] = 5
with pytest.raises(TypeError):
@ioprepped
@dataclass
class _TestClass3:
ival: Annotated[int, IOAttrs(store_default=False)] = 4
ival2: Annotated[int, IOAttrs('ival')] = 5

View File

@ -38,7 +38,7 @@ except ModuleNotFoundError:
_pytz_utc = None # pylint: disable=invalid-name
if TYPE_CHECKING:
from typing import Any, Dict, Type, Tuple, Optional, List
from typing import Any, Dict, Type, Tuple, Optional, List, Set
T = TypeVar('T')
@ -58,8 +58,14 @@ EXTRA_ATTRS_ATTR = '_DCIOEXATTRS'
class Codec(Enum):
"""Influences format used for input/output."""
"""Specifies expected data format exported to or imported from."""
# Use only types that will translate cleanly to/from json: lists,
# dicts with str keys, bools, ints, floats, and None.
JSON = 'json'
# Mostly like JSON but passes bytes and datetime objects through
# as-is instead of converting them to json-friendly types.
FIRESTORE = 'firestore'
@ -267,8 +273,8 @@ class PrepSession:
'efro.dataclassio: implicitly prepping dataclass: %s.'
' It is highly recommended to explicitly prep dataclasses'
' as soon as possible after definition (via'
' efro.dataclassio.dataclass_prep() or the'
' @efro.dataclassio.prepped decorator).', cls)
' efro.dataclassio.ioprep() or the'
' @efro.dataclassio.ioprepped decorator).', cls)
try:
# NOTE: perhaps we want to expose the globalns/localns args
@ -287,6 +293,7 @@ class PrepSession:
fields = dataclasses.fields(cls)
fields_by_name = {f.name: f for f in fields}
all_storage_names: Set[str] = set()
storage_names_to_attr_names: Dict[str, str] = {}
# Ok; we've resolved actual types for this dataclass.
@ -301,7 +308,18 @@ class PrepSession:
if ioattrs is not None:
ioattrs.validate_for_field(cls, fields_by_name[attrname])
if ioattrs.storagename is not None:
storagename = ioattrs.storagename
storage_names_to_attr_names[ioattrs.storagename] = attrname
else:
storagename = attrname
else:
storagename = attrname
# Make sure we don't have any clashes in our storage names.
if storagename in all_storage_names:
raise TypeError(f'Multiple attrs on {cls} are using'
f' storage-name \'{storagename}\'')
all_storage_names.add(storagename)
self.prep_type(cls,
attrname,

View File

@ -140,6 +140,7 @@ def func_annotations_filter(node: nc.NodeNG) -> nc.NodeNG:
node.args.varargannotation = None
node.args.kwargannotation = None
node.args.kwonlyargs_annotations = [None for _ in node.args.kwonlyargs]
node.args.posonlyargs_annotations = [None for _ in node.args.kwonlyargs]
# Wipe out return-value annotation.
if node.returns is not None: