python-dictattr 0.0.4__tar.gz → 0.0.5__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {python_dictattr-0.0.4 → python_dictattr-0.0.5}/PKG-INFO +8 -6
- python_dictattr-0.0.5/dictattr/__init__.py +190 -0
- {python_dictattr-0.0.4 → python_dictattr-0.0.5}/pyproject.toml +4 -4
- python_dictattr-0.0.4/dictattr/__init__.py +0 -179
- {python_dictattr-0.0.4 → python_dictattr-0.0.5}/LICENSE +0 -0
- {python_dictattr-0.0.4 → python_dictattr-0.0.5}/dictattr/py.typed +0 -0
- {python_dictattr-0.0.4 → python_dictattr-0.0.5}/readme.md +0 -0
|
@@ -1,26 +1,28 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
2
|
Name: python-dictattr
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.5
|
|
4
4
|
Summary: Python dictattr.
|
|
5
|
-
Home-page: https://github.com/ChenyangGao/web-mount-packs/tree/main/python-module/python-dictattr
|
|
6
5
|
License: MIT
|
|
6
|
+
License-File: LICENSE
|
|
7
7
|
Keywords: dictattr,attrdict
|
|
8
8
|
Author: ChenyangGao
|
|
9
9
|
Author-email: wosiwujm@gmail.com
|
|
10
|
-
Requires-Python: >=3.
|
|
10
|
+
Requires-Python: >=3.12,<4.0
|
|
11
11
|
Classifier: Development Status :: 5 - Production/Stable
|
|
12
12
|
Classifier: Intended Audience :: Developers
|
|
13
13
|
Classifier: License :: OSI Approved :: MIT License
|
|
14
14
|
Classifier: Operating System :: OS Independent
|
|
15
15
|
Classifier: Programming Language :: Python
|
|
16
16
|
Classifier: Programming Language :: Python :: 3
|
|
17
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
18
17
|
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
19
20
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
20
21
|
Classifier: Topic :: Software Development
|
|
21
22
|
Classifier: Topic :: Software Development :: Libraries
|
|
22
23
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
23
|
-
Project-URL:
|
|
24
|
+
Project-URL: Homepage, https://github.com/ChenyangGao/python-modules/tree/main/python-dictattr
|
|
25
|
+
Project-URL: Repository, https://github.com/ChenyangGao/python-modules/tree/main/python-dictattr
|
|
24
26
|
Description-Content-Type: text/markdown
|
|
25
27
|
|
|
26
28
|
# Python dictattr.
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
# encoding: utf-8
|
|
3
|
+
|
|
4
|
+
from __future__ import annotations
|
|
5
|
+
|
|
6
|
+
__author__ = "ChenyangGao <https://chenyanggao.github.io>"
|
|
7
|
+
__version__ = (0, 0, 5)
|
|
8
|
+
__all__ = [
|
|
9
|
+
"AttrDict", "MapAttr", "MuMapAttr", "DictAttr", "UserDictAttr", "ChainUserDictAttr",
|
|
10
|
+
"IntMapAttr", "IntMuMapAttr", "StrMapAttr", "StrMuMapAttr",
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
from collections import UserDict
|
|
14
|
+
from collections.abc import Iterator, Mapping, MutableMapping
|
|
15
|
+
from typing import overload, Any, Self, Generic
|
|
16
|
+
from typing import _GenericAlias # type: ignore
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
class DictAttrMixin[K, V](MutableMapping[K, V]):
|
|
20
|
+
|
|
21
|
+
def __delattr__(self, name: K, /): # type: ignore
|
|
22
|
+
try:
|
|
23
|
+
del self[name]
|
|
24
|
+
except KeyError as e:
|
|
25
|
+
raise AttributeError(name) from e
|
|
26
|
+
|
|
27
|
+
def __getattr__(self, name: K, /) -> V: # type: ignore
|
|
28
|
+
try:
|
|
29
|
+
return self[name]
|
|
30
|
+
except KeyError as e:
|
|
31
|
+
raise AttributeError(name) from e
|
|
32
|
+
|
|
33
|
+
def __setattr__(self, name: K, value: V, /): # type: ignore
|
|
34
|
+
try:
|
|
35
|
+
self[name] = value
|
|
36
|
+
except KeyError as e:
|
|
37
|
+
raise AttributeError(name) from e
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
class MapAttrMixin[K, V](Mapping[K, V]):
|
|
41
|
+
__dict__: dict[K, V] # type: ignore
|
|
42
|
+
|
|
43
|
+
def __contains__(self, key, /) -> bool:
|
|
44
|
+
return key in self.__dict__
|
|
45
|
+
|
|
46
|
+
def __getitem__(self, key: K, /) -> V:
|
|
47
|
+
return self.__dict__[key]
|
|
48
|
+
|
|
49
|
+
def __iter__(self, /) -> Iterator[K]:
|
|
50
|
+
return iter(self.__dict__)
|
|
51
|
+
|
|
52
|
+
def __len__(self, /) -> int:
|
|
53
|
+
return len(self.__dict__)
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
class MuMapAttrMixin[K, V](MapAttrMixin[K, V], MutableMapping[K, V]):
|
|
57
|
+
|
|
58
|
+
def __delitem__(self, key: K, /):
|
|
59
|
+
del self.__dict__[key]
|
|
60
|
+
|
|
61
|
+
def __setitem__(self, key: K, val: V, /):
|
|
62
|
+
self.__dict__[key] = val
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class ValueMapAttrMixin[K, V](MapAttrMixin[K, V]):
|
|
66
|
+
|
|
67
|
+
def __new__(cls, value=None, /, *args, **kwds):
|
|
68
|
+
if isinstance(value, (tuple, list, Mapping)):
|
|
69
|
+
args = value, *args
|
|
70
|
+
value = None
|
|
71
|
+
for a in args:
|
|
72
|
+
kwds.update(a)
|
|
73
|
+
if value is None:
|
|
74
|
+
value = kwds.get("id")
|
|
75
|
+
base: Any
|
|
76
|
+
for base in reversed(cls.__bases__):
|
|
77
|
+
if not (base is Generic or isinstance(base, _GenericAlias)):
|
|
78
|
+
break
|
|
79
|
+
self = base.__new__(cls, value or 0)
|
|
80
|
+
if kwds:
|
|
81
|
+
self.__dict__.update(kwds)
|
|
82
|
+
return self
|
|
83
|
+
|
|
84
|
+
def __repr__(self, /) -> str:
|
|
85
|
+
cls = type(self)
|
|
86
|
+
return f"{cls.__module__}.{cls.__qualname__}({super().__repr__()}, {self.__dict__!r})"
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class ValueMuMapAttrMixin[K, V](ValueMapAttrMixin[K, V], MuMapAttrMixin[K, V]):
|
|
90
|
+
pass
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
class AttrDict[K, V](dict[K, V]):
|
|
94
|
+
|
|
95
|
+
def __init__(self, /, *args, **kwds):
|
|
96
|
+
super().__init__(*args, **kwds)
|
|
97
|
+
self.__dict__ = self # type: ignore
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class MapAttr[K, V](MapAttrMixin[K, V]):
|
|
101
|
+
|
|
102
|
+
def __init__(self, /, *args, **kwds):
|
|
103
|
+
self.__dict__: dict[K, V] # type: ignore
|
|
104
|
+
self.__dict__.update(*args, **kwds)
|
|
105
|
+
|
|
106
|
+
def __repr__(self, /) -> str:
|
|
107
|
+
cls = type(self)
|
|
108
|
+
return f"{cls.__module__}.{cls.__qualname__}({self.__dict__})"
|
|
109
|
+
|
|
110
|
+
@classmethod
|
|
111
|
+
def of(
|
|
112
|
+
cls,
|
|
113
|
+
d: None | dict[K, V] = None,
|
|
114
|
+
/,
|
|
115
|
+
) -> Self:
|
|
116
|
+
self = cls.__new__(cls)
|
|
117
|
+
if d is not None:
|
|
118
|
+
self.__dict__ = d
|
|
119
|
+
return self
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
class MuMapAttr[K, V](MapAttr[K, V], MuMapAttrMixin[K, V]):
|
|
123
|
+
pass
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
class DictAttr[K, V](DictAttrMixin[K, V], dict[K, V]):
|
|
127
|
+
pass
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
class UserDictAttr[K, V](DictAttrMixin[K, V], UserDict[K, V]):
|
|
131
|
+
|
|
132
|
+
def __getitem__(self, key, /):
|
|
133
|
+
d = super().__getitem__(key)
|
|
134
|
+
if isinstance(d, Mapping) and not isinstance(d, __class__): # type: ignore
|
|
135
|
+
return type(self)(d)
|
|
136
|
+
return d
|
|
137
|
+
|
|
138
|
+
def __repr__(self, /) -> str:
|
|
139
|
+
cls = type(self)
|
|
140
|
+
return f"{cls.__module__}.{cls.__qualname__}.of({self.data!r})"
|
|
141
|
+
|
|
142
|
+
@classmethod
|
|
143
|
+
def of(cls, m: Mapping, /) -> Self:
|
|
144
|
+
self = cls()
|
|
145
|
+
self.__dict__["data"] = m
|
|
146
|
+
return self
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
class ChainDictAttr[K, V](UserDictAttr[K, V | "ChainDictAttr"]):
|
|
150
|
+
|
|
151
|
+
def __getitem__(self, key, /) -> V | ChainDictAttr:
|
|
152
|
+
try:
|
|
153
|
+
return super().__getitem__(key)
|
|
154
|
+
except KeyError:
|
|
155
|
+
d = self.__dict__[key] = type(self)()
|
|
156
|
+
return d
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
class IntMapAttr[K, V](ValueMapAttrMixin[K, V], int):
|
|
160
|
+
pass
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
class IntMuMapAttr[K, V](ValueMuMapAttrMixin[K, V], int):
|
|
164
|
+
pass
|
|
165
|
+
|
|
166
|
+
|
|
167
|
+
class StrMapAttr[K, V](ValueMapAttrMixin[K, V], str):
|
|
168
|
+
|
|
169
|
+
@overload # type: ignore
|
|
170
|
+
def __getitem__(self, key: int | slice, /) -> str:
|
|
171
|
+
...
|
|
172
|
+
@overload
|
|
173
|
+
def __getitem__(self, key: K, /) -> V:
|
|
174
|
+
...
|
|
175
|
+
def __getitem__(self, key: int | slice | K, /) -> str | V:
|
|
176
|
+
if isinstance(key, (int, slice)):
|
|
177
|
+
return str.__getitem__(self, key)
|
|
178
|
+
else:
|
|
179
|
+
return self.__dict__[key]
|
|
180
|
+
|
|
181
|
+
def __iter__(self, /):
|
|
182
|
+
return str.__iter__(self)
|
|
183
|
+
|
|
184
|
+
def __len__(self, /):
|
|
185
|
+
return str.__len__(self)
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
class StrMuMapAttr[K, V](ValueMuMapAttrMixin[K, V], StrMapAttr[K, V], str):
|
|
189
|
+
pass
|
|
190
|
+
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
[tool.poetry]
|
|
2
2
|
name = "python-dictattr"
|
|
3
|
-
version = "0.0.
|
|
3
|
+
version = "0.0.5"
|
|
4
4
|
description = "Python dictattr."
|
|
5
5
|
authors = ["ChenyangGao <wosiwujm@gmail.com>"]
|
|
6
6
|
license = "MIT"
|
|
7
7
|
readme = "readme.md"
|
|
8
|
-
homepage = "https://github.com/ChenyangGao/
|
|
9
|
-
repository = "https://github.com/ChenyangGao/
|
|
8
|
+
homepage = "https://github.com/ChenyangGao/python-modules/tree/main/python-dictattr"
|
|
9
|
+
repository = "https://github.com/ChenyangGao/python-modules/tree/main/python-dictattr"
|
|
10
10
|
keywords = ["dictattr", "attrdict"]
|
|
11
11
|
classifiers = [
|
|
12
12
|
"License :: OSI Approved :: MIT License",
|
|
@@ -25,7 +25,7 @@ include = [
|
|
|
25
25
|
]
|
|
26
26
|
|
|
27
27
|
[tool.poetry.dependencies]
|
|
28
|
-
python = "^3.
|
|
28
|
+
python = "^3.12"
|
|
29
29
|
|
|
30
30
|
[build-system]
|
|
31
31
|
requires = ["poetry-core"]
|
|
@@ -1,179 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
# encoding: utf-8
|
|
3
|
-
|
|
4
|
-
from __future__ import annotations
|
|
5
|
-
|
|
6
|
-
__author__ = "ChenyangGao <https://chenyanggao.github.io>"
|
|
7
|
-
__version__ = (0, 0, 4)
|
|
8
|
-
__all__ = [
|
|
9
|
-
"odict", "AttrDict", "MapAttr", "MuMapAttr", "DictAttr",
|
|
10
|
-
"ChainDictAttr", "UserDictAttr", "PriorityUserDictAttr",
|
|
11
|
-
]
|
|
12
|
-
|
|
13
|
-
from collections import UserDict
|
|
14
|
-
from collections.abc import Iterator, Mapping, MutableMapping
|
|
15
|
-
from typing import Generic, Self, TypeVar
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
K = TypeVar("K")
|
|
19
|
-
V = TypeVar("V")
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
class odict(dict[K, V]):
|
|
23
|
-
|
|
24
|
-
__getattr__ = dict.__getitem__
|
|
25
|
-
__setattr__ = dict.__setitem__ # type: ignore
|
|
26
|
-
__delattr__ = dict.__delitem__ # type: ignore
|
|
27
|
-
|
|
28
|
-
def __hash__(self, /) -> int: # type: ignore
|
|
29
|
-
return id(self)
|
|
30
|
-
|
|
31
|
-
def __eq__(self, value, /) -> bool:
|
|
32
|
-
return self is value or super().__eq__(value)
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
class AttrDict(dict[K, V]):
|
|
36
|
-
|
|
37
|
-
def __init__(self, /, *args, **kwds):
|
|
38
|
-
super().__init__(*args, **kwds)
|
|
39
|
-
self.__dict__ = self # type: ignore
|
|
40
|
-
|
|
41
|
-
def __hash__(self, /) -> int: # type: ignore
|
|
42
|
-
return id(self)
|
|
43
|
-
|
|
44
|
-
def __eq__(self, value, /) -> bool:
|
|
45
|
-
return self is value or super().__eq__(value)
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
@Mapping.register
|
|
49
|
-
class MapAttr(Generic[K, V]):
|
|
50
|
-
|
|
51
|
-
def __init__(self, /, *args, **kwds):
|
|
52
|
-
self.__dict__: dict[K, V] # type: ignore
|
|
53
|
-
self.__dict__.update(*args, **kwds)
|
|
54
|
-
|
|
55
|
-
def __contains__(self, key, /) -> bool:
|
|
56
|
-
return key in self.__dict__
|
|
57
|
-
|
|
58
|
-
def __getitem__(self, key, /) -> V:
|
|
59
|
-
return self.__dict__[key]
|
|
60
|
-
|
|
61
|
-
def __iter__(self, /) -> Iterator[K]:
|
|
62
|
-
return iter(self.__dict__)
|
|
63
|
-
|
|
64
|
-
def __len__(self, /) -> int:
|
|
65
|
-
return len(self.__dict__)
|
|
66
|
-
|
|
67
|
-
def __repr__(self, /) -> str:
|
|
68
|
-
cls = type(self)
|
|
69
|
-
if (mod := cls.__module__) == "__main__":
|
|
70
|
-
return f"{cls.__qualname__}({self.__dict__})"
|
|
71
|
-
else:
|
|
72
|
-
return f"{mod}.{cls.__qualname__}({self.__dict__})"
|
|
73
|
-
|
|
74
|
-
@classmethod
|
|
75
|
-
def of(
|
|
76
|
-
cls,
|
|
77
|
-
d: None | dict[K, V] = None,
|
|
78
|
-
/,
|
|
79
|
-
) -> Self:
|
|
80
|
-
if d is None:
|
|
81
|
-
return cls()
|
|
82
|
-
self = __class__.__new__(cls) # type: ignore
|
|
83
|
-
self.__dict__ = d
|
|
84
|
-
return self
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
@MutableMapping.register
|
|
88
|
-
class MuMapAttr(MapAttr[K, V]):
|
|
89
|
-
|
|
90
|
-
def __delitem__(self, key, /):
|
|
91
|
-
del self.__dict__[key]
|
|
92
|
-
|
|
93
|
-
def __setitem__(self, key: K, val: V, /):
|
|
94
|
-
self.__dict__[key] = val
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
class DictAttr(MuMapAttr[K, V]):
|
|
98
|
-
|
|
99
|
-
def __getattr__(self, attr, /):
|
|
100
|
-
return getattr(self.__dict__, attr)
|
|
101
|
-
|
|
102
|
-
def __getattribute__(self, attr, /):
|
|
103
|
-
if attr == "__dict__":
|
|
104
|
-
return super().__getattribute__(attr)
|
|
105
|
-
try:
|
|
106
|
-
return self[attr]
|
|
107
|
-
except KeyError:
|
|
108
|
-
return super().__getattribute__(attr)
|
|
109
|
-
|
|
110
|
-
def __getitem__(self, key, /) -> V | Self: # type: ignore
|
|
111
|
-
d = self.__dict__[key]
|
|
112
|
-
if type(d) is dict:
|
|
113
|
-
return type(self)(d)
|
|
114
|
-
return d
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
class ChainDictAttr(DictAttr[K, V | "ChainDictAttr"]):
|
|
118
|
-
|
|
119
|
-
def __getitem__(self, key, /) -> V | ChainDictAttr:
|
|
120
|
-
try:
|
|
121
|
-
return super().__getitem__(key)
|
|
122
|
-
except KeyError:
|
|
123
|
-
d = self.__dict__[key] = type(self)()
|
|
124
|
-
return d
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
class UserDictAttr(UserDict):
|
|
128
|
-
|
|
129
|
-
def __getattr__(self, attr, /):
|
|
130
|
-
try:
|
|
131
|
-
return self[attr]
|
|
132
|
-
except KeyError as e:
|
|
133
|
-
raise AttributeError(attr) from e
|
|
134
|
-
|
|
135
|
-
def __getitem__(self, key, /):
|
|
136
|
-
d = super().__getitem__(key)
|
|
137
|
-
if isinstance(d, Mapping) and not isinstance(d, __class__): # type: ignore
|
|
138
|
-
return type(self)(d)
|
|
139
|
-
return d
|
|
140
|
-
|
|
141
|
-
def __repr__(self, /) -> str:
|
|
142
|
-
cls = type(self)
|
|
143
|
-
name = cls.__qualname__
|
|
144
|
-
if (module := cls.__module__) != "__main__":
|
|
145
|
-
name = f"{module}.{name}"
|
|
146
|
-
return f"{name}.of({self.data!r})"
|
|
147
|
-
|
|
148
|
-
@classmethod
|
|
149
|
-
def of(cls, m: Mapping, /) -> Self:
|
|
150
|
-
self = cls()
|
|
151
|
-
self.__dict__["data"] = m
|
|
152
|
-
return self
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
class PriorityUserDictAttr(UserDictAttr):
|
|
156
|
-
|
|
157
|
-
def __delattr__(self, attr, /):
|
|
158
|
-
try:
|
|
159
|
-
del self[attr]
|
|
160
|
-
except KeyError:
|
|
161
|
-
super().__delattr__(attr)
|
|
162
|
-
|
|
163
|
-
def __getattribute__(self, attr, /):
|
|
164
|
-
if attr in ("__dict__", "data") or attr == f"__{attr.strip('_')}__":
|
|
165
|
-
return super().__getattribute__(attr)
|
|
166
|
-
try:
|
|
167
|
-
return self[attr]
|
|
168
|
-
except KeyError:
|
|
169
|
-
return super().__getattribute__(attr)
|
|
170
|
-
|
|
171
|
-
def __getattr__(self, attr, /):
|
|
172
|
-
raise AttributeError(attr)
|
|
173
|
-
|
|
174
|
-
def __setattr__(self, attr, val, /):
|
|
175
|
-
if attr == "data" and "data" not in self.__dict__:
|
|
176
|
-
self.__dict__["data"] = val
|
|
177
|
-
else:
|
|
178
|
-
self[attr] = val
|
|
179
|
-
|
|
File without changes
|
|
File without changes
|
|
File without changes
|