Skip to content

Commit e1205bc

Browse files
committed
minor refactor
1 parent 6e6093c commit e1205bc

File tree

6 files changed

+59
-36
lines changed

6 files changed

+59
-36
lines changed

Makefile

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ help:
2929
init: ## install all dev dependencies for this project
3030
pip install -e .
3131
pip install -r requirements-dev.txt
32+
pip install -r docs/requirements.txt
3233

3334
clean: clean-build clean-pyc clean-test ## remove all build, test, coverage and Python artifacts
3435

README.rst

+10-9
Original file line numberDiff line numberDiff line change
@@ -127,16 +127,17 @@ on :ref:`invalid-characters` below.
127127
Issues with Invalid Characters
128128
******************************
129129

130-
A key name in the scope
131-
of this library must be a valid *identifier* in python, and
132-
also not a reserved *keyword* such as ``for`` or ``class``.
133-
In the case where your key name does not conform, the library
134-
will mutate your key to a safe, lower-cased format.
135-
136-
Spaces
137-
and invalid characters are replaced with ``_``. In the case
130+
A key name in the scope of the :class:`DotWizPlus` implementation must be
131+
a valid, lower-cased *identifier* in python, and also not a reserved
132+
*keyword* such as ``for`` or ``class``. In the case where your key name
133+
does not conform, the library will mutate your key to a safe,
134+
lower-cased format.
135+
136+
Spaces and invalid characters are replaced with ``_``. In the case
138137
of a key beginning with an *int*, a leading ``_`` is added.
139-
In the case of a *keyword*, a trailing ``_`` is added.
138+
In the case of a *keyword*, a trailing ``_`` is added. Keys that appear
139+
in different cases, such as ``myKey`` or ``My-Key``, will all be converted
140+
to a *snake case* variant, ``my_key`` in this example.
140141

141142
Features
142143
--------

benchmarks/test_getattr.py

+8
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,14 @@ def test_dotwiz(benchmark, my_data):
6767
assert result == 77
6868

6969

70+
def test_dotwiz_plus(benchmark, my_data):
71+
o = dotwiz.DotWizPlus(my_data)
72+
# print(o)
73+
74+
result = benchmark(lambda: o.c.bb[0].x)
75+
assert result == 77
76+
77+
7078
def test_dotmap(benchmark, my_data):
7179
o = dotmap.DotMap(my_data)
7280
# print(o)

dotwiz/plus.py

+30-19
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,9 @@ def make_dot_wiz_plus(*args, **kwargs):
3232
return DotWizPlus(kwargs)
3333

3434

35-
def __store_in_dot_wiz__(self, key: str, value,
36-
__dict,
37-
__set=dict.__setitem__,
38-
__is_keyword=keyword.iskeyword):
35+
def __store_in_object__(self, __self_dict, key, value,
36+
__set=dict.__setitem__,
37+
__is_keyword=keyword.iskeyword):
3938

4039
orig_key = key
4140
lower_key = key.lower()
@@ -57,9 +56,10 @@ def __store_in_dot_wiz__(self, key: str, value,
5756
# transform key to `snake case` and cache the result.
5857
key = snake(key)
5958

60-
# I've noticed for keys like 'a.b.c' or a'b'c, the result isn't
59+
# I've noticed for keys like `a.b.c` or `a'b'c`, the result isn't
6160
# `a_b_c` as we'd want it to be. So for now, do the conversion
6261
# ourselves.
62+
# See also: https://github.com/kevinheavey/pyheck/issues/10
6363
for ch in ('.', '\''):
6464
if ch in key:
6565
key = key.replace(ch, '_').replace('__', '_')
@@ -76,14 +76,11 @@ def __store_in_dot_wiz__(self, key: str, value,
7676

7777
# note: this logic is the same as `DotWizPlus.__setitem__()`
7878
__set(self, orig_key, value)
79-
__dict[key] = value
79+
__self_dict[key] = value
8080

8181

8282
# noinspection PyDefaultArgument
83-
def __upsert_into_dot_wiz_plus__(self, input_dict={},
84-
__set=dict.__setitem__,
85-
__is_keyword=keyword.iskeyword,
86-
**kwargs):
83+
def __upsert_into_dot_wiz_plus__(self, input_dict={}, **kwargs):
8784
"""
8885
Helper method to generate / update a :class:`DotWizPlus` (dot-access dict)
8986
from a Python ``dict`` object, and optional *keyword arguments*.
@@ -112,28 +109,27 @@ def __upsert_into_dot_wiz_plus__(self, input_dict={},
112109
elif t is list:
113110
value = [__resolve_value__(e, DotWizPlus) for e in value]
114111

115-
__store_in_dot_wiz__(self, key, value, __dict)
112+
__store_in_object__(self, __dict, key, value)
116113

117114

118-
def __setitem_impl__(self, key, value,
119-
__set=dict.__setitem__,
120-
__is_keyword=keyword.iskeyword):
115+
def __setitem_impl__(self, key, value):
121116
"""Implementation of `DotWizPlus.__setitem__` to preserve dot access"""
122117
value = __resolve_value__(value, DotWizPlus)
123-
__store_in_dot_wiz__(self, key, value, self.__dict__)
118+
__store_in_object__(self, self.__dict__, key, value)
124119

125120

126121
class DotWizPlus(dict, metaclass=__add_repr__, char='✪', use_attr_dict=True):
122+
# noinspection PyProtectedMember
127123
"""
128124
:class:`DotWizPlus` - a blazing *fast* ``dict`` subclass that also
129-
supports *dot access* notation.exit
130-
131-
Usage::
125+
supports *dot access* notation. This implementation enables you to
126+
turn special-cased keys into valid *snake_case* words in Python,
127+
as shown below.
132128
133129
>>> from dotwiz import DotWizPlus
134130
>>> dw = DotWizPlus({'Key 1': [{'3D': {'with': 2}}], 'keyTwo': '5', '[email protected]?': 3.21})
135131
>>> dw
136-
DotWizPlus(key_1=[DotWizPlus(_3d=DotWizPlus(with_=2))], key_two='5', r_2_d_2=3.21)
132+
(key_1=[(_3d=(with_=2))], key_two='5', r_2_d_2=3.21)
137133
>>> assert dw.key_1[0]._3d.with_ == 2
138134
>>> assert dw.key_two == '5'
139135
>>> assert dw.r_2_d_2 == 3.21
@@ -142,6 +138,21 @@ class DotWizPlus(dict, metaclass=__add_repr__, char='✪', use_attr_dict=True):
142138
>>> dw.to_attr_dict()
143139
{'key_1': [{'_3d': {'with_': 2}}], 'key_two': '5', 'r_2_d_2': 3.21}
144140
141+
Issues with Invalid Characters
142+
******************************
143+
144+
A key name in the scope of the :class:`DotWizPlus` implementation must be
145+
a valid, lower-cased *identifier* in python, and also not a reserved
146+
*keyword* such as ``for`` or ``class``. In the case where your key name
147+
does not conform, the library will mutate your key to a safe,
148+
lower-cased format.
149+
150+
Spaces and invalid characters are replaced with ``_``. In the case
151+
of a key beginning with an *int*, a leading ``_`` is added.
152+
In the case of a *keyword*, a trailing ``_`` is added. Keys that appear
153+
in different cases, such as ``myKey`` or ``My-Key``, will all be converted
154+
to a *snake case* variant, ``my_key`` in this example.
155+
145156
"""
146157
__slots__ = ('__dict__', )
147158

dotwiz/plus.pyi

+8-6
Original file line numberDiff line numberDiff line change
@@ -22,18 +22,21 @@ def to_snake_case(string: str) -> str: ...
2222
def make_dot_wiz_plus(*args: Iterable[_KT, _VT],
2323
**kwargs: _T) -> DotWizPlus: ...
2424

25+
def __store_in_object__(self: DotWizPlus,
26+
__self_dict: MutableMapping[_KT, _VT],
27+
key: _KT,
28+
value: _VT,
29+
*, __set: _SetItem = dict.__setitem__,
30+
__is_keyword=keyword.iskeyword): ...
31+
2532
# noinspection PyDefaultArgument
2633
def __upsert_into_dot_wiz_plus__(self: DotWizPlus,
2734
input_dict: MutableMapping[_KT, _VT] = {},
28-
*, __set: _SetItem =dict.__setitem__,
29-
__is_keyword=keyword.iskeyword,
3035
**kwargs: _T) -> None: ...
3136

3237
def __setitem_impl__(self: DotWizPlus,
3338
key: _KT,
34-
value: _VT,
35-
*, __set: _SetItem = dict.__setitem__,
36-
__is_keyword=keyword.iskeyword): ...
39+
value: _VT): ...
3740

3841

3942
class DotWizPlus(dict):
@@ -68,7 +71,6 @@ class DotWizPlus(dict):
6871
# noinspection PyDefaultArgument
6972
def update(self,
7073
__m: MutableMapping[_KT, _VT] = {},
71-
*, __set: _SetItem = dict.__setitem__,
7274
**kwargs: _T) -> None: ...
7375

7476
def __repr__(self) -> str: ...

tests/unit/test_dotwiz.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ def test_make_dot_wiz():
2121
dd = make_dot_wiz([(1, 'test'), ('two', [{'hello': 'world'}])],
2222
a=1, b='two', c={'d': [123]})
2323

24-
assert repr(dd) == "DotWiz(a=1, b='two', c=DotWiz(d=[123]), 1='test', two=[DotWiz(hello='world')])"
24+
assert repr(dd) == "(a=1, b='two', c=(d=[123]), 1='test', two=[(hello='world')])"
2525
assert dd.a == 1
2626
assert dd.b == 'two'
2727
assert dd[1] == 'test'
@@ -42,7 +42,7 @@ def test_dotwiz_init():
4242
'c': {'d': [123]}
4343
})
4444

45-
assert repr(dd) == "DotWiz(1='test', two=[DotWiz(hello='world')], a=1, b='two', c=DotWiz(d=[123]))"
45+
assert repr(dd) == "(1='test', two=[(hello='world')], a=1, b='two', c=(d=[123]))"
4646
assert dd.a == 1
4747
assert dd.b == 'two'
4848
assert dd[1] == 'test'

0 commit comments

Comments
 (0)