swizzle 0.1.4__tar.gz → 2.0.0__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.
swizzle-2.0.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Jan T. Mueller
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
swizzle-2.0.0/PKG-INFO ADDED
@@ -0,0 +1,127 @@
1
+ Metadata-Version: 2.1
2
+ Name: swizzle
3
+ Version: 2.0.0
4
+ Summary: Swizzle enables the retrieval of multiple attributes, similar to swizzling in computer graphics.
5
+ Home-page: https://github.com/janthmueller/swizzle
6
+ Author: Jan T. Müller
7
+ Author-email: mail@jantmueller.com
8
+ License: MIT
9
+ Project-URL: Documentation, https://github.com/janthmueller/swizzle/blob/main/README.md
10
+ Project-URL: Source, https://github.com/janthmueller/swizzle
11
+ Project-URL: Tracker, https://github.com/janthmueller/swizzle/issues
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3 :: Only
17
+ Requires-Python: >=3.6
18
+ Description-Content-Type: text/markdown
19
+ License-File: LICENSE
20
+
21
+ # Swizzle
22
+ [![PyPI version](https://badge.fury.io/py/swizzle.svg)](https://badge.fury.io/py/swizzle)
23
+ [![Downloads](https://pepy.tech/badge/swizzle)](https://pepy.tech/project/swizzle)
24
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/janthmueller/swizzle/blob/main/LICENSE)
25
+ [![GitHub Stars](https://img.shields.io/github/stars/janthmueller/swizzle.svg)](https://github.com/janthmueller/swizzle/stargazers)
26
+
27
+ **Swizzle** for Python enhances attribute lookup methods to facilitate dynamic and flexible retrieval of multiple attributes based on specified arrangements of their names.
28
+ > **Update v2:**
29
+ > Introducing `swizzlednamedtuple` , a new function that allows you to create **swizzled named tuples**. This feature is inspired by the `namedtuple` function from the [collections module](https://docs.python.org/3/library/collections.html#collections.namedtuple) and provides a concise way to define swizzled tuples.
30
+ > ```python
31
+ > from swizzle import swizzlednamedtuple
32
+ > Vector = swizzlednamedtuple('Vector', 'x y z', arrange_names = "y z x x")
33
+ >
34
+ > # Test the swizzle
35
+ > v = Vector(1, 2, 3)
36
+ > print(v) # Output: Vector(y=2, z=3, x=1, x=1)
37
+ > print(v.yzx) # Output: Vector(y = 2, z = 3, x = 1)
38
+ > print(v.yzx.xxzyzz) # Output: Vector(x=1, x=1, z=3, y=2, z=3, z=3)
39
+ >```
40
+
41
+ ### Swizzle Decorator:
42
+
43
+ ```python
44
+ import swizzle
45
+
46
+ @swizzle
47
+ class Vector:
48
+ def __init__(self, x, y, z):
49
+ self.x = x
50
+ self.y = y
51
+ self.z = z
52
+
53
+ # Test the swizzle
54
+ print(Vector(1, 2, 3).yzx) # Output: Vector(y = 2, z = 3, x = 1)
55
+ ```
56
+
57
+
58
+ ## Installation
59
+ ### From PyPI
60
+ ```bash
61
+ pip install swizzle
62
+ ```
63
+ ### From GitHub
64
+ ```bash
65
+ pip install git+https://github.com/janthmueller/swizzle.git
66
+ ```
67
+
68
+ ## Further Examples
69
+
70
+ ### Using `swizzle` with `dataclass`
71
+
72
+ ```python
73
+ import swizzle
74
+ from dataclasses import dataclass
75
+
76
+ @swizzle
77
+ @dataclass
78
+ class Vector:
79
+ x: int
80
+ y: int
81
+ z: int
82
+
83
+ # Test the swizzle
84
+ print(Vector(1, 2, 3).yzx) # Output: Vector(y = 2, z = 3, x = 1)
85
+ ```
86
+
87
+ ### Using `swizzle` with `IntEnum`
88
+
89
+ ```python
90
+ import swizzle
91
+ from enum import IntEnum
92
+
93
+ @swizzle(meta=True)
94
+ class Vector(IntEnum):
95
+ X = 1
96
+ Y = 2
97
+ Z = 3
98
+
99
+ # Test the swizzle
100
+ print(Vector.YXZ) # Output: Vector(Y=<Vector.Y: 2>, X=<Vector.X: 1>, Z=<Vector.Z: 3>)
101
+ ```
102
+ Setting the `meta` argument to `True` in the swizzle decorator extends the `getattr` behavior of the metaclass, enabling attribute swizzling directly on the class itself.
103
+
104
+
105
+ ### Sequential matching
106
+ Attributes are matched from left to right, starting with the longest substring match. This behavior can be controlled by the `seperator` argument in the swizzle decorator.
107
+ ```python
108
+ import swizzle
109
+
110
+ @swizzle(meta=True)
111
+ class Vector:
112
+ x = 1
113
+ y = 2
114
+ z = 3
115
+ xy = 4
116
+ yz = 5
117
+ xz = 6
118
+ xyz = 7
119
+
120
+ # Test the swizzle
121
+ print(Vector.xz) # Output: 6
122
+ print(Vector.yz) # Output: 5
123
+ print(Vector.xyyz) # Output: Vector(xy=4, yz=5)
124
+ print(Vector.xyzx) # Output: Vector(xyz=7, x=1)
125
+ ```
126
+
127
+
@@ -0,0 +1,107 @@
1
+ # Swizzle
2
+ [![PyPI version](https://badge.fury.io/py/swizzle.svg)](https://badge.fury.io/py/swizzle)
3
+ [![Downloads](https://pepy.tech/badge/swizzle)](https://pepy.tech/project/swizzle)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/janthmueller/swizzle/blob/main/LICENSE)
5
+ [![GitHub Stars](https://img.shields.io/github/stars/janthmueller/swizzle.svg)](https://github.com/janthmueller/swizzle/stargazers)
6
+
7
+ **Swizzle** for Python enhances attribute lookup methods to facilitate dynamic and flexible retrieval of multiple attributes based on specified arrangements of their names.
8
+ > **Update v2:**
9
+ > Introducing `swizzlednamedtuple` , a new function that allows you to create **swizzled named tuples**. This feature is inspired by the `namedtuple` function from the [collections module](https://docs.python.org/3/library/collections.html#collections.namedtuple) and provides a concise way to define swizzled tuples.
10
+ > ```python
11
+ > from swizzle import swizzlednamedtuple
12
+ > Vector = swizzlednamedtuple('Vector', 'x y z', arrange_names = "y z x x")
13
+ >
14
+ > # Test the swizzle
15
+ > v = Vector(1, 2, 3)
16
+ > print(v) # Output: Vector(y=2, z=3, x=1, x=1)
17
+ > print(v.yzx) # Output: Vector(y = 2, z = 3, x = 1)
18
+ > print(v.yzx.xxzyzz) # Output: Vector(x=1, x=1, z=3, y=2, z=3, z=3)
19
+ >```
20
+
21
+ ### Swizzle Decorator:
22
+
23
+ ```python
24
+ import swizzle
25
+
26
+ @swizzle
27
+ class Vector:
28
+ def __init__(self, x, y, z):
29
+ self.x = x
30
+ self.y = y
31
+ self.z = z
32
+
33
+ # Test the swizzle
34
+ print(Vector(1, 2, 3).yzx) # Output: Vector(y = 2, z = 3, x = 1)
35
+ ```
36
+
37
+
38
+ ## Installation
39
+ ### From PyPI
40
+ ```bash
41
+ pip install swizzle
42
+ ```
43
+ ### From GitHub
44
+ ```bash
45
+ pip install git+https://github.com/janthmueller/swizzle.git
46
+ ```
47
+
48
+ ## Further Examples
49
+
50
+ ### Using `swizzle` with `dataclass`
51
+
52
+ ```python
53
+ import swizzle
54
+ from dataclasses import dataclass
55
+
56
+ @swizzle
57
+ @dataclass
58
+ class Vector:
59
+ x: int
60
+ y: int
61
+ z: int
62
+
63
+ # Test the swizzle
64
+ print(Vector(1, 2, 3).yzx) # Output: Vector(y = 2, z = 3, x = 1)
65
+ ```
66
+
67
+ ### Using `swizzle` with `IntEnum`
68
+
69
+ ```python
70
+ import swizzle
71
+ from enum import IntEnum
72
+
73
+ @swizzle(meta=True)
74
+ class Vector(IntEnum):
75
+ X = 1
76
+ Y = 2
77
+ Z = 3
78
+
79
+ # Test the swizzle
80
+ print(Vector.YXZ) # Output: Vector(Y=<Vector.Y: 2>, X=<Vector.X: 1>, Z=<Vector.Z: 3>)
81
+ ```
82
+ Setting the `meta` argument to `True` in the swizzle decorator extends the `getattr` behavior of the metaclass, enabling attribute swizzling directly on the class itself.
83
+
84
+
85
+ ### Sequential matching
86
+ Attributes are matched from left to right, starting with the longest substring match. This behavior can be controlled by the `seperator` argument in the swizzle decorator.
87
+ ```python
88
+ import swizzle
89
+
90
+ @swizzle(meta=True)
91
+ class Vector:
92
+ x = 1
93
+ y = 2
94
+ z = 3
95
+ xy = 4
96
+ yz = 5
97
+ xz = 6
98
+ xyz = 7
99
+
100
+ # Test the swizzle
101
+ print(Vector.xz) # Output: 6
102
+ print(Vector.yz) # Output: 5
103
+ print(Vector.xyyz) # Output: Vector(xy=4, yz=5)
104
+ print(Vector.xyzx) # Output: Vector(xyz=7, x=1)
105
+ ```
106
+
107
+
@@ -1,43 +1,43 @@
1
- # Copyright (c) 2023 Jan T. Müller <mail@jantmueller.com>
2
-
3
- import sys
4
- import os
5
- from setuptools import setup, find_packages
6
- import swizzle
7
-
8
- if sys.version_info < (3, 6):
9
- sys.exit("ERROR: swizzle requires Python 3.6+")
10
-
11
- with open("README.md", "r", encoding="utf-8") as fh:
12
- long_description = fh.read()
13
-
14
- setup(
15
- name="swizzle",
16
- version=swizzle.__version__,
17
- packages=find_packages(exclude=["tests"]),
18
- author="Jan T. Müller",
19
- author_email="mail@jantmueller.com",
20
- description="The Swizzle Decorator enables the retrieval of multiple attributes, similar to swizzling in computer graphics.",
21
- long_description=long_description,
22
- long_description_content_type="text/markdown",
23
- url="https://github.com/janthmueller/swizzle",
24
- project_urls={
25
- "Documentation": "https://github.com/janthmueller/swizzle/blob/main/README.md",
26
- "Source": "https://github.com/janthmueller/swizzle",
27
- "Tracker": "https://github.com/janthmueller/swizzle/issues",
28
- },
29
- license="MIT",
30
- classifiers=[
31
- "Intended Audience :: Developers",
32
- "Topic :: Software Development :: Libraries :: Python Modules",
33
- "License :: OSI Approved :: MIT License",
34
- "Operating System :: OS Independent",
35
- "Programming Language :: Python :: 3 :: Only",
36
- ],
37
- python_requires=">=3.6",
38
- )
39
-
40
-
41
- # python setup.py sdist bdist_wheel
42
- # twine check dist/*
43
- # twine upload dist/* -> insert token
1
+ # Copyright (c) 2024 Jan T. Müller <mail@jantmueller.com>
2
+
3
+ import sys
4
+ import os
5
+ from setuptools import setup, find_packages
6
+ import swizzle
7
+
8
+ if sys.version_info < (3, 6):
9
+ sys.exit("ERROR: swizzle requires Python 3.6+")
10
+
11
+ with open("README.md", "r", encoding="utf-8") as fh:
12
+ long_description = fh.read()
13
+
14
+ setup(
15
+ name="swizzle",
16
+ version=swizzle.__version__,
17
+ packages=find_packages(exclude=["tests"]),
18
+ author="Jan T. Müller",
19
+ author_email="mail@jantmueller.com",
20
+ description="Swizzle enables the retrieval of multiple attributes, similar to swizzling in computer graphics.",
21
+ long_description=long_description,
22
+ long_description_content_type="text/markdown",
23
+ url="https://github.com/janthmueller/swizzle",
24
+ project_urls={
25
+ "Documentation": "https://github.com/janthmueller/swizzle/blob/main/README.md",
26
+ "Source": "https://github.com/janthmueller/swizzle",
27
+ "Tracker": "https://github.com/janthmueller/swizzle/issues",
28
+ },
29
+ license="MIT",
30
+ classifiers=[
31
+ "Intended Audience :: Developers",
32
+ "Topic :: Software Development :: Libraries :: Python Modules",
33
+ "License :: OSI Approved :: MIT License",
34
+ "Operating System :: OS Independent",
35
+ "Programming Language :: Python :: 3 :: Only",
36
+ ],
37
+ python_requires=">=3.6",
38
+ )
39
+
40
+
41
+ # python setup.py sdist bdist_wheel
42
+ # twine check dist/*
43
+ # twine upload dist/* -> insert token
@@ -0,0 +1,329 @@
1
+ # Copyright (c) 2024 Jan T. Müller <mail@jantmueller.com>
2
+
3
+ from functools import wraps
4
+ import types
5
+ import sys as _sys
6
+
7
+ from keyword import iskeyword as _iskeyword
8
+ from operator import itemgetter as _itemgetter
9
+ try:
10
+ from _collections import _tuplegetter
11
+ except ImportError:
12
+ _tuplegetter = lambda index, doc: property(_itemgetter(index), doc=doc)
13
+
14
+
15
+ __version__ = "2.0.0"
16
+
17
+ MISSING = object()
18
+
19
+ def swizzlednamedtuple(typename, field_names, *, rename=False, defaults=None, module=None, arrange_names = None, seperator = None):
20
+ # Validate the field names. Skip duplicate and underscore checks.
21
+ if isinstance(field_names, str):
22
+ field_names = field_names.replace(',', ' ').split()
23
+ field_names = list(map(str, field_names))
24
+ if arrange_names is not None:
25
+ if isinstance(arrange_names, str):
26
+ arrange_names = arrange_names.replace(',', ' ').split()
27
+ arrange_names = list(map(str, arrange_names))
28
+ assert set(arrange_names) == set(field_names), 'Arrangement must contain all field names'
29
+ else:
30
+ arrange_names = field_names.copy()
31
+
32
+
33
+ typename = _sys.intern(str(typename))
34
+
35
+ _dir = dir(tuple) + ['__match_args__', '__module__', '__slots__', '_asdict', '_field_defaults', '_fields', '_make', '_replace',]
36
+ if rename:
37
+ seen = set()
38
+ name_newname = {}
39
+ for index, name in enumerate(field_names):
40
+ if (not name.isidentifier()
41
+ or _iskeyword(name)
42
+ or name in _dir
43
+ or name in seen):
44
+ field_names[index] = f'_{index}'
45
+ name_newname[name] = field_names[index]
46
+ seen.add(name)
47
+ for index, name in enumerate(arrange_names):
48
+ arrange_names[index] = name_newname[name]
49
+
50
+ for name in [typename] + field_names:
51
+ if type(name) is not str:
52
+ raise TypeError('Type names and field names must be strings')
53
+ if not name.isidentifier():
54
+ raise ValueError('Type names and field names must be valid '
55
+ f'identifiers: {name!r}')
56
+ if _iskeyword(name):
57
+ raise ValueError('Type names and field names cannot be a '
58
+ f'keyword: {name!r}')
59
+ seen = set()
60
+ for name in field_names:
61
+ if name in _dir and not rename:
62
+ raise ValueError('Field names cannot be an attribute name which would shadow the namedtuple methods or attributes'
63
+ f'{name!r}')
64
+ if name in seen:
65
+ raise ValueError(f'Encountered duplicate field name: {name!r}')
66
+ seen.add(name)
67
+
68
+ arrange_indices = [field_names.index(name) for name in arrange_names]
69
+
70
+ def tuple_new(cls, iterable):
71
+ new = []
72
+ _iterable = list(iterable)
73
+ for index in arrange_indices:
74
+ new.append(_iterable[index])
75
+ return tuple.__new__(cls, new)
76
+
77
+ field_defaults = {}
78
+ if defaults is not None:
79
+ defaults = tuple(defaults)
80
+ if len(defaults) > len(field_names):
81
+ raise TypeError('Got more default values than field names')
82
+ field_defaults = dict(reversed(list(zip(reversed(field_names),
83
+ reversed(defaults)))))
84
+
85
+ field_names = tuple(map(_sys.intern, field_names))
86
+ arrange_names = tuple(map(_sys.intern, arrange_names))
87
+ num_fields = len(field_names)
88
+ num_arrange_fields = len(arrange_names)
89
+ arg_list = ', '.join(field_names)
90
+ if num_fields == 1:
91
+ arg_list += ','
92
+ repr_fmt = '(' + ', '.join(f'{name}=%r' for name in arrange_names) + ')'
93
+ _dict, _tuple, _len, _map, _zip = dict, tuple, len, map, zip
94
+
95
+ namespace = {
96
+ '_tuple_new': tuple_new,
97
+ '__builtins__': {},
98
+ '__name__': f'namedtuple_{typename}',
99
+ }
100
+ code = f'lambda _cls, {arg_list}: _tuple_new(_cls, ({arg_list}))'
101
+ __new__ = eval(code, namespace)
102
+ __new__.__name__ = '__new__'
103
+ __new__.__doc__ = f'Create new instance of {typename}({arg_list})'
104
+ if defaults is not None:
105
+ __new__.__defaults__ = defaults
106
+
107
+ @classmethod
108
+ def _make(cls, iterable):
109
+ result = tuple_new(cls, iterable)
110
+ if _len(result) != num_arrange_fields:
111
+ raise TypeError(f'Expected {num_arrange_fields} arguments, got {len(result)}')
112
+ return result
113
+
114
+ _make.__func__.__doc__ = (f'Make a new {typename} object from a sequence '
115
+ 'or iterable')
116
+
117
+ def _replace(self, /, **kwds):
118
+ def generator():
119
+ for name in field_names:
120
+ if name in kwds:
121
+ yield kwds.pop(name)
122
+ else:
123
+ yield getattr(self, name)
124
+
125
+ result = self._make(iter(generator()))
126
+ if kwds:
127
+ raise ValueError(f'Got unexpected field names: {list(kwds)!r}')
128
+ return result
129
+
130
+ _replace.__doc__ = (f'Return a new {typename} object replacing specified '
131
+ 'fields with new values')
132
+
133
+ def __repr__(self):
134
+ 'Return a nicely formatted representation string'
135
+ return self.__class__.__name__ + repr_fmt % self
136
+
137
+ def _asdict(self):
138
+ 'Return a new dict which maps field names to their values.'
139
+ return _dict(_zip(arrange_names, self))
140
+
141
+ def __getnewargs__(self):
142
+ 'Return self as a plain tuple. Used by copy and pickle.'
143
+ return _tuple(self)
144
+
145
+ @swizzle_attributes_retriever(separator=seperator, type = swizzlednamedtuple)
146
+ def __getattribute__(self, attr_name):
147
+ return super(_tuple, self).__getattribute__(attr_name)
148
+
149
+
150
+
151
+ for method in (
152
+ __new__,
153
+ _make.__func__,
154
+ _replace,
155
+ __repr__,
156
+ _asdict,
157
+ __getnewargs__,
158
+ __getattribute__,
159
+ ):
160
+ method.__qualname__ = f'{typename}.{method.__name__}'
161
+
162
+ class_namespace = {
163
+ '__doc__': f'{typename}({arg_list})',
164
+ '__slots__': (),
165
+ '_fields': field_names,
166
+ '_field_defaults': field_defaults,
167
+ '__new__': __new__,
168
+ '_make': _make,
169
+ '_replace': _replace,
170
+ '__repr__': __repr__,
171
+ '_asdict': _asdict,
172
+ '__getnewargs__': __getnewargs__,
173
+ '__getattribute__': __getattribute__
174
+ }
175
+ seen = set()
176
+ for index, name in enumerate(arrange_names):
177
+ if name in seen:
178
+ continue
179
+ doc = _sys.intern(f'Alias for field number {index}')
180
+ class_namespace[name] = _tuplegetter(index, doc)
181
+ seen.add(name)
182
+
183
+ result = type(typename, (tuple,), class_namespace)
184
+
185
+ if module is None:
186
+ try:
187
+ module = _sys._getframemodulename(1) or '__main__'
188
+ except AttributeError:
189
+ try:
190
+ module = _sys._getframe(1).f_globals.get('__name__', '__main__')
191
+ except (AttributeError, ValueError):
192
+ pass
193
+ if module is not None:
194
+ result.__module__ = module
195
+
196
+ return result
197
+
198
+
199
+ # Helper function to split a string based on a separator
200
+ def split_string(string, separator):
201
+ if separator == '':
202
+ return list(string)
203
+ else:
204
+ return string.split(separator)
205
+
206
+ # Helper function to collect attribute retrieval functions from a class or meta-class
207
+ def collect_attribute_functions(cls):
208
+ funcs = []
209
+ if hasattr(cls, '__getattribute__'):
210
+ funcs.append(cls.__getattribute__)
211
+ if hasattr(cls, '__getattr__'):
212
+ funcs.append(cls.__getattr__)
213
+ if not funcs:
214
+ raise AttributeError("No __getattr__ or __getattribute__ found on the class or meta-class")
215
+ return funcs
216
+
217
+ # Function to combine multiple attribute retrieval functions
218
+
219
+ def swizzle_attributes_retriever(attribute_funcs=None, separator=None, type = tuple):
220
+ def _swizzle_attributes_retriever(attribute_funcs):
221
+ if not isinstance(attribute_funcs, list):
222
+ attribute_funcs = [attribute_funcs]
223
+
224
+ def retrieve_attribute(obj, attr_name):
225
+ for func in attribute_funcs:
226
+ try:
227
+ return func(obj, attr_name)
228
+ except AttributeError:
229
+ continue
230
+ return MISSING
231
+
232
+ @wraps(attribute_funcs[-1])
233
+ def retrieve_swizzled_attributes(obj, attr_name):
234
+ # Attempt to find an exact attribute match
235
+ attribute = retrieve_attribute(obj, attr_name)
236
+ if attribute is not MISSING:
237
+ return attribute
238
+
239
+ matched_attributes = []
240
+ arranged_names = []
241
+ # If a separator is provided, split the name accordingly
242
+ if separator is not None:
243
+ attr_parts = split_string(attr_name, separator)
244
+ arranged_names = attr_parts
245
+ for part in attr_parts:
246
+ attribute = retrieve_attribute(obj, part)
247
+ if attribute is not MISSING:
248
+ matched_attributes.append(attribute)
249
+ else:
250
+ # No separator provided, attempt to match substrings
251
+ i = 0
252
+ while i < len(attr_name):
253
+ match_found = False
254
+ for j in range(len(attr_name), i, -1):
255
+ substring = attr_name[i:j]
256
+ attribute = retrieve_attribute(obj, substring)
257
+ if attribute is not MISSING:
258
+ matched_attributes.append(attribute)
259
+ arranged_names.append(substring)
260
+ i = j # Move index to end of the matched substring
261
+ match_found = True
262
+ break
263
+ if not match_found:
264
+ raise AttributeError(f"No matching attribute found for substring: {attr_name[i:]}")
265
+
266
+ if type == swizzlednamedtuple:
267
+ field_names = set(arranged_names)
268
+ field_values = [retrieve_attribute(obj, name) for name in field_names]
269
+ name = "swizzlednamedtuple"
270
+ if hasattr(obj, "__name__"):
271
+ name = obj.__name__
272
+ elif hasattr(obj, "__class__"):
273
+ if hasattr(obj.__class__, "__name__"):
274
+ name = obj.__class__.__name__
275
+ result = type(name, field_names, arrange_names=arranged_names)
276
+ result = result(*field_values)
277
+ return result
278
+
279
+
280
+ return type(matched_attributes)
281
+
282
+ return retrieve_swizzled_attributes
283
+
284
+ if attribute_funcs is not None:
285
+ return _swizzle_attributes_retriever(attribute_funcs)
286
+ else:
287
+ return _swizzle_attributes_retriever
288
+
289
+ # Decorator function to enable swizzling for a class
290
+ def swizzle(cls=None, use_meta=False, separator=None, _type = tuple):
291
+ def class_decorator(cls):
292
+ # Collect attribute retrieval functions from the class
293
+ attribute_funcs = collect_attribute_functions(cls)
294
+
295
+ # Apply the swizzling to the class's attribute retrieval
296
+ setattr(cls, attribute_funcs[-1].__name__, swizzle_attributes_retriever(attribute_funcs, separator, _type))
297
+
298
+ # Handle meta-class swizzling if requested
299
+ if use_meta:
300
+ print(cls)
301
+ meta_cls = type(cls)
302
+ if meta_cls == type:
303
+ class SwizzledMetaType(meta_cls):
304
+ pass
305
+ meta_cls = SwizzledMetaType
306
+ cls = meta_cls(cls.__name__, cls.__bases__, dict(cls.__dict__))
307
+ meta_cls = SwizzledMetaType
308
+ cls = meta_cls(cls.__name__, cls.__bases__, dict(cls.__dict__))
309
+
310
+ meta_funcs = collect_attribute_functions(meta_cls)
311
+ setattr(meta_cls, meta_funcs[-1].__name__, swizzle_attributes_retriever(meta_funcs, separator, _type))
312
+
313
+ return cls
314
+
315
+ if cls is None:
316
+ return class_decorator
317
+ else:
318
+ return class_decorator(cls)
319
+
320
+
321
+ class Swizzle(types.ModuleType):
322
+ def __init__(self):
323
+ types.ModuleType.__init__(self, __name__)
324
+ self.__dict__.update(_sys.modules[__name__].__dict__)
325
+
326
+ def __call__(self, cls=None, meta=False, sep = None, type = swizzlednamedtuple):
327
+ return swizzle(cls, meta, sep, type)
328
+
329
+ _sys.modules[__name__] = Swizzle()
@@ -0,0 +1,127 @@
1
+ Metadata-Version: 2.1
2
+ Name: swizzle
3
+ Version: 2.0.0
4
+ Summary: Swizzle enables the retrieval of multiple attributes, similar to swizzling in computer graphics.
5
+ Home-page: https://github.com/janthmueller/swizzle
6
+ Author: Jan T. Müller
7
+ Author-email: mail@jantmueller.com
8
+ License: MIT
9
+ Project-URL: Documentation, https://github.com/janthmueller/swizzle/blob/main/README.md
10
+ Project-URL: Source, https://github.com/janthmueller/swizzle
11
+ Project-URL: Tracker, https://github.com/janthmueller/swizzle/issues
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
14
+ Classifier: License :: OSI Approved :: MIT License
15
+ Classifier: Operating System :: OS Independent
16
+ Classifier: Programming Language :: Python :: 3 :: Only
17
+ Requires-Python: >=3.6
18
+ Description-Content-Type: text/markdown
19
+ License-File: LICENSE
20
+
21
+ # Swizzle
22
+ [![PyPI version](https://badge.fury.io/py/swizzle.svg)](https://badge.fury.io/py/swizzle)
23
+ [![Downloads](https://pepy.tech/badge/swizzle)](https://pepy.tech/project/swizzle)
24
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://github.com/janthmueller/swizzle/blob/main/LICENSE)
25
+ [![GitHub Stars](https://img.shields.io/github/stars/janthmueller/swizzle.svg)](https://github.com/janthmueller/swizzle/stargazers)
26
+
27
+ **Swizzle** for Python enhances attribute lookup methods to facilitate dynamic and flexible retrieval of multiple attributes based on specified arrangements of their names.
28
+ > **Update v2:**
29
+ > Introducing `swizzlednamedtuple` , a new function that allows you to create **swizzled named tuples**. This feature is inspired by the `namedtuple` function from the [collections module](https://docs.python.org/3/library/collections.html#collections.namedtuple) and provides a concise way to define swizzled tuples.
30
+ > ```python
31
+ > from swizzle import swizzlednamedtuple
32
+ > Vector = swizzlednamedtuple('Vector', 'x y z', arrange_names = "y z x x")
33
+ >
34
+ > # Test the swizzle
35
+ > v = Vector(1, 2, 3)
36
+ > print(v) # Output: Vector(y=2, z=3, x=1, x=1)
37
+ > print(v.yzx) # Output: Vector(y = 2, z = 3, x = 1)
38
+ > print(v.yzx.xxzyzz) # Output: Vector(x=1, x=1, z=3, y=2, z=3, z=3)
39
+ >```
40
+
41
+ ### Swizzle Decorator:
42
+
43
+ ```python
44
+ import swizzle
45
+
46
+ @swizzle
47
+ class Vector:
48
+ def __init__(self, x, y, z):
49
+ self.x = x
50
+ self.y = y
51
+ self.z = z
52
+
53
+ # Test the swizzle
54
+ print(Vector(1, 2, 3).yzx) # Output: Vector(y = 2, z = 3, x = 1)
55
+ ```
56
+
57
+
58
+ ## Installation
59
+ ### From PyPI
60
+ ```bash
61
+ pip install swizzle
62
+ ```
63
+ ### From GitHub
64
+ ```bash
65
+ pip install git+https://github.com/janthmueller/swizzle.git
66
+ ```
67
+
68
+ ## Further Examples
69
+
70
+ ### Using `swizzle` with `dataclass`
71
+
72
+ ```python
73
+ import swizzle
74
+ from dataclasses import dataclass
75
+
76
+ @swizzle
77
+ @dataclass
78
+ class Vector:
79
+ x: int
80
+ y: int
81
+ z: int
82
+
83
+ # Test the swizzle
84
+ print(Vector(1, 2, 3).yzx) # Output: Vector(y = 2, z = 3, x = 1)
85
+ ```
86
+
87
+ ### Using `swizzle` with `IntEnum`
88
+
89
+ ```python
90
+ import swizzle
91
+ from enum import IntEnum
92
+
93
+ @swizzle(meta=True)
94
+ class Vector(IntEnum):
95
+ X = 1
96
+ Y = 2
97
+ Z = 3
98
+
99
+ # Test the swizzle
100
+ print(Vector.YXZ) # Output: Vector(Y=<Vector.Y: 2>, X=<Vector.X: 1>, Z=<Vector.Z: 3>)
101
+ ```
102
+ Setting the `meta` argument to `True` in the swizzle decorator extends the `getattr` behavior of the metaclass, enabling attribute swizzling directly on the class itself.
103
+
104
+
105
+ ### Sequential matching
106
+ Attributes are matched from left to right, starting with the longest substring match. This behavior can be controlled by the `seperator` argument in the swizzle decorator.
107
+ ```python
108
+ import swizzle
109
+
110
+ @swizzle(meta=True)
111
+ class Vector:
112
+ x = 1
113
+ y = 2
114
+ z = 3
115
+ xy = 4
116
+ yz = 5
117
+ xz = 6
118
+ xyz = 7
119
+
120
+ # Test the swizzle
121
+ print(Vector.xz) # Output: 6
122
+ print(Vector.yz) # Output: 5
123
+ print(Vector.xyyz) # Output: Vector(xy=4, yz=5)
124
+ print(Vector.xyzx) # Output: Vector(xyz=7, x=1)
125
+ ```
126
+
127
+
@@ -1,3 +1,4 @@
1
+ LICENSE
1
2
  README.md
2
3
  setup.py
3
4
  swizzle/__init__.py
swizzle-0.1.4/PKG-INFO DELETED
@@ -1,126 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: swizzle
3
- Version: 0.1.4
4
- Summary: The Swizzle Decorator enables the retrieval of multiple attributes, similar to swizzling in computer graphics.
5
- Home-page: https://github.com/janthmueller/swizzle
6
- Author: Jan T. Müller
7
- Author-email: mail@jantmueller.com
8
- License: MIT
9
- Project-URL: Documentation, https://github.com/janthmueller/swizzle/blob/main/README.md
10
- Project-URL: Source, https://github.com/janthmueller/swizzle
11
- Project-URL: Tracker, https://github.com/janthmueller/swizzle/issues
12
- Classifier: Intended Audience :: Developers
13
- Classifier: Topic :: Software Development :: Libraries :: Python Modules
14
- Classifier: License :: OSI Approved :: MIT License
15
- Classifier: Operating System :: OS Independent
16
- Classifier: Programming Language :: Python :: 3 :: Only
17
- Requires-Python: >=3.6
18
- Description-Content-Type: text/markdown
19
-
20
- # Swizzle Decorator
21
-
22
- The **Swizzle Decorator** for Python enhances attribute lookup methods (`__getattr__` or `__getattribute__`) to facilitate dynamic and flexible retrieval of multiple attributes based on specified arrangements of their names. This concept is reminiscent of swizzling in computer graphics, where it allows efficient access to components of vectors or coordinates in various orders:
23
-
24
- ```python
25
- import swizzle
26
-
27
- @swizzle
28
- class Vector:
29
- def __init__(self, x, y, z):
30
- self.x = x
31
- self.y = y
32
- self.z = z
33
-
34
- print(Vector(1, 2, 3).yzx) # Output: (2, 3, 1)
35
- ```
36
-
37
- ## Installation
38
- ### From PyPI
39
- ```bash
40
- pip install swizzle
41
- ```
42
- ### From GitHub
43
- ```bash
44
- pip install git+https://github.com/janthmueller/swizzle.git
45
- ```
46
-
47
- ## Further Examples
48
-
49
- ### Using `swizzle` with `dataclass`
50
-
51
- ```python
52
- import swizzle
53
- from dataclasses import dataclass
54
-
55
- @swizzle
56
- @dataclass
57
- class XYZ:
58
- x: int
59
- y: int
60
- z: int
61
-
62
- # Test the swizzle
63
- xyz = XYZ(1, 2, 3)
64
- print(xyz.yzx) # Output: (2, 3, 1)
65
- ```
66
-
67
- ### Using `swizzle` with `IntEnum`
68
-
69
- ```python
70
- import swizzle
71
- from enum import IntEnum
72
-
73
- @swizzle(meta=True)
74
- class XYZ(IntEnum):
75
- X = 1
76
- Y = 2
77
- Z = 3
78
-
79
- # Test the swizzle
80
- print(XYZ.YXZ) # Output: (<XYZ.Y: 2>, <XYZ.X: 1>, <XYZ.Z: 3>)
81
- ```
82
- Setting the `meta` argument to `True` in the swizzle decorator extends the `getattr` behavior of the metaclass, enabling attribute swizzling directly on the class itself.
83
-
84
- ### Using `swizzle` with `NamedTuple`
85
-
86
- ```python
87
- import swizzle
88
- from typing import NamedTuple
89
-
90
- @swizzle
91
- class XYZ(NamedTuple):
92
- x: int
93
- y: int
94
- z: int
95
-
96
- # Test the swizzle
97
- xyz = XYZ(1, 2, 3)
98
- print(xyz.yzx) # Output: (2, 3, 1)
99
- ```
100
-
101
-
102
- ### Sequential matching
103
- Attributes are matched from left to right, starting with the longest substring match.
104
- ```python
105
- import swizzle
106
-
107
- @swizzle(meta=True)
108
- class Test:
109
- x = 1
110
- y = 2
111
- z = 3
112
- xy = 4
113
- yz = 5
114
- xz = 6
115
- xyz = 7
116
-
117
- # Test the swizzle
118
- print(Test.xz) # Output: 6
119
- print(Test.yz) # Output: 5
120
- print(Test.xyyz) # Output: (4, 5)
121
- print(Test.xyzx) # Output: (7, 1)
122
- ```
123
-
124
- ## To Do
125
- - [ ] Add support for module-level swizzling
126
- - [ ] Swizzle for method args (swizzle+partial)
swizzle-0.1.4/README.md DELETED
@@ -1,107 +0,0 @@
1
- # Swizzle Decorator
2
-
3
- The **Swizzle Decorator** for Python enhances attribute lookup methods (`__getattr__` or `__getattribute__`) to facilitate dynamic and flexible retrieval of multiple attributes based on specified arrangements of their names. This concept is reminiscent of swizzling in computer graphics, where it allows efficient access to components of vectors or coordinates in various orders:
4
-
5
- ```python
6
- import swizzle
7
-
8
- @swizzle
9
- class Vector:
10
- def __init__(self, x, y, z):
11
- self.x = x
12
- self.y = y
13
- self.z = z
14
-
15
- print(Vector(1, 2, 3).yzx) # Output: (2, 3, 1)
16
- ```
17
-
18
- ## Installation
19
- ### From PyPI
20
- ```bash
21
- pip install swizzle
22
- ```
23
- ### From GitHub
24
- ```bash
25
- pip install git+https://github.com/janthmueller/swizzle.git
26
- ```
27
-
28
- ## Further Examples
29
-
30
- ### Using `swizzle` with `dataclass`
31
-
32
- ```python
33
- import swizzle
34
- from dataclasses import dataclass
35
-
36
- @swizzle
37
- @dataclass
38
- class XYZ:
39
- x: int
40
- y: int
41
- z: int
42
-
43
- # Test the swizzle
44
- xyz = XYZ(1, 2, 3)
45
- print(xyz.yzx) # Output: (2, 3, 1)
46
- ```
47
-
48
- ### Using `swizzle` with `IntEnum`
49
-
50
- ```python
51
- import swizzle
52
- from enum import IntEnum
53
-
54
- @swizzle(meta=True)
55
- class XYZ(IntEnum):
56
- X = 1
57
- Y = 2
58
- Z = 3
59
-
60
- # Test the swizzle
61
- print(XYZ.YXZ) # Output: (<XYZ.Y: 2>, <XYZ.X: 1>, <XYZ.Z: 3>)
62
- ```
63
- Setting the `meta` argument to `True` in the swizzle decorator extends the `getattr` behavior of the metaclass, enabling attribute swizzling directly on the class itself.
64
-
65
- ### Using `swizzle` with `NamedTuple`
66
-
67
- ```python
68
- import swizzle
69
- from typing import NamedTuple
70
-
71
- @swizzle
72
- class XYZ(NamedTuple):
73
- x: int
74
- y: int
75
- z: int
76
-
77
- # Test the swizzle
78
- xyz = XYZ(1, 2, 3)
79
- print(xyz.yzx) # Output: (2, 3, 1)
80
- ```
81
-
82
-
83
- ### Sequential matching
84
- Attributes are matched from left to right, starting with the longest substring match.
85
- ```python
86
- import swizzle
87
-
88
- @swizzle(meta=True)
89
- class Test:
90
- x = 1
91
- y = 2
92
- z = 3
93
- xy = 4
94
- yz = 5
95
- xz = 6
96
- xyz = 7
97
-
98
- # Test the swizzle
99
- print(Test.xz) # Output: 6
100
- print(Test.yz) # Output: 5
101
- print(Test.xyyz) # Output: (4, 5)
102
- print(Test.xyzx) # Output: (7, 1)
103
- ```
104
-
105
- ## To Do
106
- - [ ] Add support for module-level swizzling
107
- - [ ] Swizzle for method args (swizzle+partial)
@@ -1,80 +0,0 @@
1
- from functools import wraps
2
- import sys
3
- import types
4
-
5
- __version__ = "0.1.4"
6
-
7
- def _split(s, sep):
8
- if sep == '':
9
- return list(s)
10
- else:
11
- return s.split(sep)
12
-
13
- def _swizzle(func, sep = None):
14
- def _getattr(obj, name, default=object()):
15
- try:
16
- return func(obj, name)
17
- except AttributeError:
18
- return default
19
-
20
- @wraps(func)
21
- def _swizzle_attributes(obj, name):
22
- """Find attributes of an object that match substrings of a given name."""
23
- try:
24
- return func(obj, name)
25
- except AttributeError:
26
- pass
27
- if sep is not None:
28
- names = _split(name, sep)
29
- found_attributes = [func(obj, n) for n in names]
30
- else:
31
- found_attributes = []
32
- sentinel = object()
33
- i = 0
34
- while i < len(name):
35
- match_found = False
36
- for j in range(len(name), i, -1):
37
- substr = name[i:j]
38
- attr = _getattr(obj, substr, sentinel)
39
- if attr is not sentinel:
40
- found_attributes.append(attr)
41
- i = j # Move index to end of the matched substring
42
- match_found = True
43
- break
44
- if not match_found:
45
- raise AttributeError(f"No matching attribute found for substring: {name[i:]}")
46
- return tuple(found_attributes)
47
- return _swizzle_attributes
48
-
49
- def swizzle(cls=None, meta = False, sep = None):
50
- def decorator(cls):
51
- # Decorate the class's __getattr__ or __getattribute__
52
- cls_fn = cls.__getattr__ if hasattr(cls, '__getattr__') else cls.__getattribute__
53
- setattr(cls, cls_fn.__name__, _swizzle(cls_fn, sep))
54
-
55
- # Handle the meta class
56
- if meta:
57
- meta_cls = type(cls)
58
- if meta_cls == type:
59
- class SwizzleType(meta_cls): pass
60
- meta_cls = SwizzleType
61
- cls = meta_cls(cls.__name__, cls.__bases__, dict(cls.__dict__))
62
- meta_fn = meta_cls.__getattr__ if hasattr(meta_cls, '__getattr__') else meta_cls.__getattribute__
63
- setattr(meta_cls, meta_fn.__name__, _swizzle(meta_fn, sep))
64
- return cls
65
-
66
- if cls is None:
67
- return decorator
68
- else:
69
- return decorator(cls)
70
-
71
- class Swizzle(types.ModuleType):
72
- def __init__(self):
73
- types.ModuleType.__init__(self, __name__)
74
- self.__dict__.update(sys.modules[__name__].__dict__)
75
-
76
- def __call__(self, cls=None, meta=False, sep = None):
77
- return swizzle(cls, meta, sep)
78
-
79
- sys.modules[__name__] = Swizzle()
80
-
@@ -1,126 +0,0 @@
1
- Metadata-Version: 2.1
2
- Name: swizzle
3
- Version: 0.1.4
4
- Summary: The Swizzle Decorator enables the retrieval of multiple attributes, similar to swizzling in computer graphics.
5
- Home-page: https://github.com/janthmueller/swizzle
6
- Author: Jan T. Müller
7
- Author-email: mail@jantmueller.com
8
- License: MIT
9
- Project-URL: Documentation, https://github.com/janthmueller/swizzle/blob/main/README.md
10
- Project-URL: Source, https://github.com/janthmueller/swizzle
11
- Project-URL: Tracker, https://github.com/janthmueller/swizzle/issues
12
- Classifier: Intended Audience :: Developers
13
- Classifier: Topic :: Software Development :: Libraries :: Python Modules
14
- Classifier: License :: OSI Approved :: MIT License
15
- Classifier: Operating System :: OS Independent
16
- Classifier: Programming Language :: Python :: 3 :: Only
17
- Requires-Python: >=3.6
18
- Description-Content-Type: text/markdown
19
-
20
- # Swizzle Decorator
21
-
22
- The **Swizzle Decorator** for Python enhances attribute lookup methods (`__getattr__` or `__getattribute__`) to facilitate dynamic and flexible retrieval of multiple attributes based on specified arrangements of their names. This concept is reminiscent of swizzling in computer graphics, where it allows efficient access to components of vectors or coordinates in various orders:
23
-
24
- ```python
25
- import swizzle
26
-
27
- @swizzle
28
- class Vector:
29
- def __init__(self, x, y, z):
30
- self.x = x
31
- self.y = y
32
- self.z = z
33
-
34
- print(Vector(1, 2, 3).yzx) # Output: (2, 3, 1)
35
- ```
36
-
37
- ## Installation
38
- ### From PyPI
39
- ```bash
40
- pip install swizzle
41
- ```
42
- ### From GitHub
43
- ```bash
44
- pip install git+https://github.com/janthmueller/swizzle.git
45
- ```
46
-
47
- ## Further Examples
48
-
49
- ### Using `swizzle` with `dataclass`
50
-
51
- ```python
52
- import swizzle
53
- from dataclasses import dataclass
54
-
55
- @swizzle
56
- @dataclass
57
- class XYZ:
58
- x: int
59
- y: int
60
- z: int
61
-
62
- # Test the swizzle
63
- xyz = XYZ(1, 2, 3)
64
- print(xyz.yzx) # Output: (2, 3, 1)
65
- ```
66
-
67
- ### Using `swizzle` with `IntEnum`
68
-
69
- ```python
70
- import swizzle
71
- from enum import IntEnum
72
-
73
- @swizzle(meta=True)
74
- class XYZ(IntEnum):
75
- X = 1
76
- Y = 2
77
- Z = 3
78
-
79
- # Test the swizzle
80
- print(XYZ.YXZ) # Output: (<XYZ.Y: 2>, <XYZ.X: 1>, <XYZ.Z: 3>)
81
- ```
82
- Setting the `meta` argument to `True` in the swizzle decorator extends the `getattr` behavior of the metaclass, enabling attribute swizzling directly on the class itself.
83
-
84
- ### Using `swizzle` with `NamedTuple`
85
-
86
- ```python
87
- import swizzle
88
- from typing import NamedTuple
89
-
90
- @swizzle
91
- class XYZ(NamedTuple):
92
- x: int
93
- y: int
94
- z: int
95
-
96
- # Test the swizzle
97
- xyz = XYZ(1, 2, 3)
98
- print(xyz.yzx) # Output: (2, 3, 1)
99
- ```
100
-
101
-
102
- ### Sequential matching
103
- Attributes are matched from left to right, starting with the longest substring match.
104
- ```python
105
- import swizzle
106
-
107
- @swizzle(meta=True)
108
- class Test:
109
- x = 1
110
- y = 2
111
- z = 3
112
- xy = 4
113
- yz = 5
114
- xz = 6
115
- xyz = 7
116
-
117
- # Test the swizzle
118
- print(Test.xz) # Output: 6
119
- print(Test.yz) # Output: 5
120
- print(Test.xyyz) # Output: (4, 5)
121
- print(Test.xyzx) # Output: (7, 1)
122
- ```
123
-
124
- ## To Do
125
- - [ ] Add support for module-level swizzling
126
- - [ ] Swizzle for method args (swizzle+partial)
File without changes