swizzle 0.1.3__tar.gz → 1.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: swizzle
3
- Version: 0.1.3
3
+ Version: 1.0.0
4
4
  Summary: The Swizzle Decorator enables the retrieval of multiple attributes, similar to swizzling in computer graphics.
5
5
  Home-page: https://github.com/janthmueller/swizzle
6
6
  Author: Jan T. Müller
@@ -77,7 +77,7 @@ class XYZ(IntEnum):
77
77
  Z = 3
78
78
 
79
79
  # Test the swizzle
80
- print(XYZ.yxz) # Output: (<XYZ.Y: 2>, <XYZ.X: 1>, <XYZ.Z: 3>)
80
+ print(XYZ.YXZ) # Output: (<XYZ.Y: 2>, <XYZ.X: 1>, <XYZ.Z: 3>)
81
81
  ```
82
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
83
 
@@ -99,8 +99,8 @@ print(xyz.yzx) # Output: (2, 3, 1)
99
99
  ```
100
100
 
101
101
 
102
- ### Sequential matching
103
- Attributes are matched from left to right, starting with the longest substring match.
102
+ ### Sequential matching
103
+ Attributes are matched from left to right, starting with the longest substring match.
104
104
  ```python
105
105
  import swizzle
106
106
 
@@ -121,3 +121,5 @@ print(Test.xyyz) # Output: (4, 5)
121
121
  print(Test.xyzx) # Output: (7, 1)
122
122
  ```
123
123
 
124
+ ## To Do
125
+ - [ ] Swizzle for method args (swizzle+partial)
@@ -1,104 +1,106 @@
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
-
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
+ - [ ] Swizzle for method args (swizzle+partial)
@@ -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) 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
@@ -0,0 +1,112 @@
1
+ from functools import wraps
2
+ import sys
3
+ import types
4
+
5
+ __version__ = "1.0.0"
6
+ MISSING = object()
7
+
8
+
9
+ # Helper function to split a string based on a separator
10
+ def split_string(string, separator):
11
+ if separator == '':
12
+ return list(string)
13
+ else:
14
+ return string.split(separator)
15
+
16
+ # Helper function to collect attribute retrieval functions from a class or meta-class
17
+ def collect_attribute_functions(cls):
18
+ funcs = []
19
+ if hasattr(cls, '__getattribute__'):
20
+ funcs.append(cls.__getattribute__)
21
+ if hasattr(cls, '__getattr__'):
22
+ funcs.append(cls.__getattr__)
23
+ if not funcs:
24
+ raise AttributeError("No __getattr__ or __getattribute__ found on the class or meta-class")
25
+ return funcs
26
+
27
+ # Function to combine multiple attribute retrieval functions
28
+ def swizzle_attributes_retriever(attribute_funcs, separator=None):
29
+ def retrieve_attribute(obj, attr_name):
30
+ for func in attribute_funcs:
31
+ try:
32
+ return func(obj, attr_name)
33
+ except AttributeError:
34
+ continue
35
+ return MISSING
36
+
37
+ @wraps(attribute_funcs[-1])
38
+ def retrieve_swizzled_attributes(obj, attr_name):
39
+ # Attempt to find an exact attribute match
40
+ attribute = retrieve_attribute(obj, attr_name)
41
+ if attribute is not MISSING:
42
+ return attribute
43
+
44
+ matched_attributes = []
45
+
46
+ # If a separator is provided, split the name accordingly
47
+ if separator is not None:
48
+ attr_parts = split_string(attr_name, separator)
49
+ for part in attr_parts:
50
+ attribute = retrieve_attribute(obj, part)
51
+ if attribute is not MISSING:
52
+ matched_attributes.append(attribute)
53
+ else:
54
+ # No separator provided, attempt to match substrings
55
+ i = 0
56
+ while i < len(attr_name):
57
+ match_found = False
58
+ for j in range(len(attr_name), i, -1):
59
+ substring = attr_name[i:j]
60
+ attribute = retrieve_attribute(obj, substring)
61
+ if attribute is not MISSING:
62
+ matched_attributes.append(attribute)
63
+ i = j # Move index to end of the matched substring
64
+ match_found = True
65
+ break
66
+ if not match_found:
67
+ raise AttributeError(f"No matching attribute found for substring: {attr_name[i:]}")
68
+
69
+ return tuple(matched_attributes)
70
+
71
+ return retrieve_swizzled_attributes
72
+
73
+ # Decorator function to enable swizzling for a class
74
+ def swizzle(cls=None, use_meta=False, separator=None):
75
+ def class_decorator(cls):
76
+ # Collect attribute retrieval functions from the class
77
+ attribute_funcs = collect_attribute_functions(cls)
78
+
79
+ # Apply the swizzling to the class's attribute retrieval
80
+ setattr(cls, attribute_funcs[-1].__name__, swizzle_attributes_retriever(attribute_funcs, separator))
81
+
82
+ # Handle meta-class swizzling if requested
83
+ if use_meta:
84
+ meta_cls = type(cls)
85
+ if meta_cls == type:
86
+ class SwizzledMetaType(meta_cls):
87
+ pass
88
+ meta_cls = SwizzledMetaType
89
+ cls = meta_cls(cls.__name__, cls.__bases__, dict(cls.__dict__))
90
+ meta_cls = SwizzledMetaType
91
+ cls = meta_cls(cls.__name__, cls.__bases__, dict(cls.__dict__))
92
+
93
+ meta_funcs = collect_attribute_functions(meta_cls)
94
+ setattr(meta_cls, meta_funcs[-1].__name__, swizzle_attributes_retriever(meta_funcs, separator))
95
+
96
+ return cls
97
+
98
+ if cls is None:
99
+ return class_decorator
100
+ else:
101
+ return class_decorator(cls)
102
+
103
+
104
+ class Swizzle(types.ModuleType):
105
+ def __init__(self):
106
+ types.ModuleType.__init__(self, __name__)
107
+ self.__dict__.update(sys.modules[__name__].__dict__)
108
+
109
+ def __call__(self, cls=None, meta=False, sep = None):
110
+ return swizzle(cls, meta, sep)
111
+
112
+ sys.modules[__name__] = Swizzle()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: swizzle
3
- Version: 0.1.3
3
+ Version: 1.0.0
4
4
  Summary: The Swizzle Decorator enables the retrieval of multiple attributes, similar to swizzling in computer graphics.
5
5
  Home-page: https://github.com/janthmueller/swizzle
6
6
  Author: Jan T. Müller
@@ -77,7 +77,7 @@ class XYZ(IntEnum):
77
77
  Z = 3
78
78
 
79
79
  # Test the swizzle
80
- print(XYZ.yxz) # Output: (<XYZ.Y: 2>, <XYZ.X: 1>, <XYZ.Z: 3>)
80
+ print(XYZ.YXZ) # Output: (<XYZ.Y: 2>, <XYZ.X: 1>, <XYZ.Z: 3>)
81
81
  ```
82
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
83
 
@@ -99,8 +99,8 @@ print(xyz.yzx) # Output: (2, 3, 1)
99
99
  ```
100
100
 
101
101
 
102
- ### Sequential matching
103
- Attributes are matched from left to right, starting with the longest substring match.
102
+ ### Sequential matching
103
+ Attributes are matched from left to right, starting with the longest substring match.
104
104
  ```python
105
105
  import swizzle
106
106
 
@@ -121,3 +121,5 @@ print(Test.xyyz) # Output: (4, 5)
121
121
  print(Test.xyzx) # Output: (7, 1)
122
122
  ```
123
123
 
124
+ ## To Do
125
+ - [ ] 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.3"
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
-
File without changes