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 +21 -0
- swizzle-2.0.0/PKG-INFO +127 -0
- swizzle-2.0.0/README.md +107 -0
- {swizzle-0.1.4 → swizzle-2.0.0}/setup.py +43 -43
- swizzle-2.0.0/swizzle/__init__.py +329 -0
- swizzle-2.0.0/swizzle.egg-info/PKG-INFO +127 -0
- {swizzle-0.1.4 → swizzle-2.0.0}/swizzle.egg-info/SOURCES.txt +1 -0
- swizzle-0.1.4/PKG-INFO +0 -126
- swizzle-0.1.4/README.md +0 -107
- swizzle-0.1.4/swizzle/__init__.py +0 -80
- swizzle-0.1.4/swizzle.egg-info/PKG-INFO +0 -126
- {swizzle-0.1.4 → swizzle-2.0.0}/setup.cfg +0 -0
- {swizzle-0.1.4 → swizzle-2.0.0}/swizzle.egg-info/dependency_links.txt +0 -0
- {swizzle-0.1.4 → swizzle-2.0.0}/swizzle.egg-info/top_level.txt +0 -0
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
|
+
[](https://badge.fury.io/py/swizzle)
|
23
|
+
[](https://pepy.tech/project/swizzle)
|
24
|
+
[](https://github.com/janthmueller/swizzle/blob/main/LICENSE)
|
25
|
+
[](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
|
+
|
swizzle-2.0.0/README.md
ADDED
@@ -0,0 +1,107 @@
|
|
1
|
+
# Swizzle
|
2
|
+
[](https://badge.fury.io/py/swizzle)
|
3
|
+
[](https://pepy.tech/project/swizzle)
|
4
|
+
[](https://github.com/janthmueller/swizzle/blob/main/LICENSE)
|
5
|
+
[](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)
|
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="
|
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
|
+
[](https://badge.fury.io/py/swizzle)
|
23
|
+
[](https://pepy.tech/project/swizzle)
|
24
|
+
[](https://github.com/janthmueller/swizzle/blob/main/LICENSE)
|
25
|
+
[](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
|
+
|
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
|
File without changes
|
File without changes
|