swizzle 0.1.0__py3-none-any.whl
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/__init__.py +70 -0
- swizzle-0.1.0.dist-info/METADATA +118 -0
- swizzle-0.1.0.dist-info/RECORD +5 -0
- swizzle-0.1.0.dist-info/WHEEL +5 -0
- swizzle-0.1.0.dist-info/top_level.txt +1 -0
swizzle/__init__.py
ADDED
@@ -0,0 +1,70 @@
|
|
1
|
+
from functools import wraps
|
2
|
+
import sys
|
3
|
+
import types
|
4
|
+
import inspect
|
5
|
+
|
6
|
+
__version__ = "0.1.0"
|
7
|
+
|
8
|
+
def _swizzle(func):
|
9
|
+
def _getattr(obj, name, default=object()):
|
10
|
+
try:
|
11
|
+
return func(obj, name)
|
12
|
+
except AttributeError:
|
13
|
+
return default
|
14
|
+
|
15
|
+
@wraps(func)
|
16
|
+
def _swizzle_attributes(obj, name):
|
17
|
+
"""Find attributes of an object that match substrings of a given name."""
|
18
|
+
found_attributes = []
|
19
|
+
sentinel = object()
|
20
|
+
i = 0
|
21
|
+
while i < len(name):
|
22
|
+
match_found = False
|
23
|
+
for j in range(len(name), i, -1):
|
24
|
+
substr = name[i:j]
|
25
|
+
attr = _getattr(obj, substr, sentinel)
|
26
|
+
if attr is not sentinel:
|
27
|
+
found_attributes.append(attr)
|
28
|
+
i = j # Move index to end of the matched substring
|
29
|
+
match_found = True
|
30
|
+
break
|
31
|
+
if not match_found:
|
32
|
+
raise AttributeError(f"No matching attribute found for substring: {name[i:]}")
|
33
|
+
return tuple(found_attributes)
|
34
|
+
return _swizzle_attributes
|
35
|
+
|
36
|
+
def swizzle(cls=None, meta = False):
|
37
|
+
def decorator(cls):
|
38
|
+
# Decorate the class's __getattr__ or __getattribute__
|
39
|
+
cls_fn = cls.__getattr__ if hasattr(cls, '__getattr__') else cls.__getattribute__
|
40
|
+
setattr(cls, cls_fn.__name__, _swizzle(cls_fn))
|
41
|
+
|
42
|
+
# Handle the meta class
|
43
|
+
if meta:
|
44
|
+
meta_cls = type(cls)
|
45
|
+
if meta_cls == type:
|
46
|
+
class SwizzleType(meta_cls): pass
|
47
|
+
meta_cls = SwizzleType
|
48
|
+
cls = meta_cls(cls.__name__, cls.__bases__, dict(cls.__dict__))
|
49
|
+
meta_fn = meta_cls.__getattr__ if hasattr(meta_cls, '__getattr__') else meta_cls.__getattribute__
|
50
|
+
setattr(meta_cls, meta_fn.__name__, _swizzle(meta_fn))
|
51
|
+
return cls
|
52
|
+
|
53
|
+
if cls is None:
|
54
|
+
return decorator
|
55
|
+
else:
|
56
|
+
return decorator(cls)
|
57
|
+
|
58
|
+
|
59
|
+
|
60
|
+
# make swizzle a callable module
|
61
|
+
class Swizzle(types.ModuleType):
|
62
|
+
def __init__(self):
|
63
|
+
types.ModuleType.__init__(self, __name__)
|
64
|
+
self.__dict__.update(sys.modules[__name__].__dict__)
|
65
|
+
|
66
|
+
def __call__(self, cls=None, meta=False):
|
67
|
+
return swizzle(cls, meta)
|
68
|
+
|
69
|
+
sys.modules[__name__] = Swizzle()
|
70
|
+
|
@@ -0,0 +1,118 @@
|
|
1
|
+
Metadata-Version: 2.1
|
2
|
+
Name: swizzle
|
3
|
+
Version: 0.1.0
|
4
|
+
Summary: Transforms a string representation of a Python literal into the corresponding Python object.
|
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
|
+
```bash
|
39
|
+
pip install git+https://github.com/janthmueller/swizzle.git
|
40
|
+
```
|
41
|
+
|
42
|
+
## Further Examples
|
43
|
+
|
44
|
+
### Using `swizzle` with `dataclass`
|
45
|
+
|
46
|
+
```python
|
47
|
+
import swizzle
|
48
|
+
from dataclasses import dataclass
|
49
|
+
|
50
|
+
@swizzle
|
51
|
+
@dataclass
|
52
|
+
class XYZ:
|
53
|
+
x: int
|
54
|
+
y: int
|
55
|
+
z: int
|
56
|
+
|
57
|
+
# Test the swizzle
|
58
|
+
xyz = XYZ(1, 2, 3)
|
59
|
+
print(xyz.yzx) # Output: (2, 3, 1)
|
60
|
+
```
|
61
|
+
|
62
|
+
### Using `swizzle` with `IntEnum`
|
63
|
+
|
64
|
+
```python
|
65
|
+
import swizzle
|
66
|
+
from enum import IntEnum
|
67
|
+
|
68
|
+
@swizzle(meta=True)
|
69
|
+
class XYZ(IntEnum):
|
70
|
+
X = 1
|
71
|
+
Y = 2
|
72
|
+
Z = 3
|
73
|
+
|
74
|
+
# Test the swizzle
|
75
|
+
print(XYZ.yxz) # Output: (2, 3, 1)
|
76
|
+
```
|
77
|
+
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.
|
78
|
+
|
79
|
+
### Using `swizzle` with `NamedTuple`
|
80
|
+
|
81
|
+
```python
|
82
|
+
import swizzle
|
83
|
+
from typing import NamedTuple
|
84
|
+
|
85
|
+
@swizzle
|
86
|
+
class XYZ(NamedTuple):
|
87
|
+
x: int
|
88
|
+
y: int
|
89
|
+
z: int
|
90
|
+
|
91
|
+
# Test the swizzle
|
92
|
+
xyz = XYZ(1, 2, 3)
|
93
|
+
print(xyz.yzx) # Output: (2, 3, 1)
|
94
|
+
```
|
95
|
+
|
96
|
+
|
97
|
+
### Sequential matching
|
98
|
+
Attributes are matched from left to right, starting with the longest substring match.
|
99
|
+
```python
|
100
|
+
import swizzle
|
101
|
+
|
102
|
+
@swizzle(meta=True)
|
103
|
+
class Test:
|
104
|
+
x = 1
|
105
|
+
y = 2
|
106
|
+
z = 3
|
107
|
+
xy = 4
|
108
|
+
yz = 5
|
109
|
+
xz = 6
|
110
|
+
xyz = 7
|
111
|
+
|
112
|
+
# Test the swizzle
|
113
|
+
print(Test.xz) # Output: (6,)
|
114
|
+
print(Test.yz) # Output: (5,)
|
115
|
+
print(Test.xyyz) # Output: (4, 5)
|
116
|
+
print(Test.xyzx) # Output: (7, 1)
|
117
|
+
```
|
118
|
+
|
@@ -0,0 +1,5 @@
|
|
1
|
+
swizzle/__init__.py,sha256=6xrT6tZERBQWGNPvWofOiSnVGzq-0-nRWbAjt9-czJY,2281
|
2
|
+
swizzle-0.1.0.dist-info/METADATA,sha256=f4MejVS5TgwsY0hroO6wR7gWIJjLl1Guzx4K_zhm8j4,2807
|
3
|
+
swizzle-0.1.0.dist-info/WHEEL,sha256=2wepM1nk4DS4eFpYrW1TTqPcoGNfHhhO_i5m4cOimbo,92
|
4
|
+
swizzle-0.1.0.dist-info/top_level.txt,sha256=XFSQti81x2zM0zAMCY1YD0lqB1eSg5my9BB03uFgCic,8
|
5
|
+
swizzle-0.1.0.dist-info/RECORD,,
|
@@ -0,0 +1 @@
|
|
1
|
+
swizzle
|