listclasses 1.0.0__tar.gz → 1.0.2__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.4
2
2
  Name: listclasses
3
- Version: 1.0.0
3
+ Version: 1.0.2
4
4
  Summary: A simple decorator to make custom sequence types
5
5
  Description-Content-Type: text/markdown
6
6
  Dynamic: description
@@ -13,13 +13,17 @@ type-safe, and highly customizable custom sequence types
13
13
 
14
14
  # Features:
15
15
 
16
- · Type Safety: Enforce strict data types for sequence items
17
- · Length Constraints: Define fixed-size or dynamic sequences
18
- · Immutability: Easily create read-only (frozen) collections
19
- · Rich API: Seamlessly integrates with built-in functions (len(), repr(), iteration, slicing)
20
- · Copy Support: Native support for shallow and deep copying
21
- · Pretty Printing: Built-in methods for clean text formatting out of the box
22
- · Installation: Add the listclass decorator code directly to your project
16
+ - Type Safety: Enforce strict data types for sequence items
17
+ - Length Constraints: Define fixed-size or dynamic sequences
18
+ - Immutability: Easily create read-only (frozen) collections
19
+ - Rich API: Seamlessly integrates with built-in functions (len(), repr(), iteration, slicing)
20
+ - Copy Support: Native support for shallow and deep copying
21
+ - Pretty Printing: Built-in methods for clean text formatting out of the box
22
+ - Installation: Add the listclass decorator code directly to your project
23
+
24
+ ```commandline
25
+ pip install listclasses
26
+ ```
23
27
 
24
28
  ```python
25
29
  from listclasses import listclass
@@ -46,7 +50,7 @@ print(len(items)) # Output: 3
46
50
 
47
51
  The @listclass decorator accepts configuration parameters to restrict container behavior
48
52
 
49
- ## Enforcing Strict Types
53
+ ### Enforcing Strict Types
50
54
 
51
55
  ```python
52
56
  @listclass(type=int)
@@ -60,21 +64,22 @@ numbers = IntegerList(1, 2, 3)
60
64
  numbers = IntegerList(1, "two", 3)
61
65
  ```
62
66
 
63
- ## Enforcing more than one type
67
+ ### Multi-Type Union Support
64
68
 
65
69
  ```python
66
- @listclass(type=int | None)
67
- class List:
70
+ @listclass(type=int | str)
71
+ class MixedList:
68
72
  pass
69
73
 
70
- # Works perfectly
71
- numbers_and_None = List(1, 2, 3, None)
74
+ # Both integers and strings are valid
75
+ mixed = MixedList(1, "hello", 42, "world")
72
76
 
73
- # Raises TypeError: Incorrect type: expected: int | None got: str
74
- numbers_and_None = List(1, "two", 3)
77
+ # Appending a float raises an error
78
+ # Raises TypeError: Incorrect type: expected: int | str got: float
79
+ mixed.append(3.14)
75
80
  ```
76
81
 
77
- ## Setting Fixed Lengths
82
+ ### Setting Fixed Lengths
78
83
 
79
84
  ```python
80
85
  @listclass(len=2)
@@ -88,7 +93,7 @@ point = Coordinates(10, 20)
88
93
  point = Coordinates(10, 20, 30)
89
94
  ```
90
95
 
91
- ## Creating Frozen (Immutable) Lists
96
+ ### Creating Frozen (Immutable) Lists
92
97
 
93
98
  ```python
94
99
  @listclass(frozen=True)
@@ -101,7 +106,7 @@ data = ReadOnlyData("A", "B")
101
106
  data[0] = "Z"
102
107
  ```
103
108
 
104
- ## Built-in Formatting
109
+ ### Built-in Formatting
105
110
  listclasses comes with utility methods to print your items cleanly.
106
111
 
107
112
  ```python
@@ -114,9 +119,9 @@ todos = TodoList("Buy milk", "Clean room", "Code Python")
114
119
  # Dotted list format
115
120
  todos.print_dotted()
116
121
  # Output:
117
- # · Buy milk
118
- # · Clean room
119
- # · Code Python
122
+ # - Buy milk
123
+ # - Clean room
124
+ # - Code Python
120
125
 
121
126
  # Numbered list format
122
127
  todos.print_numbered()
@@ -139,7 +144,7 @@ todos.print_numbered()
139
144
 
140
145
  Depending on configuration (frozen and len), instances support standard list operations:
141
146
 
142
- · Access: __getitem__, __iter__, __contains__, count(), index()
143
- · Mutation (Dynamic only): append(), pop(), clear(), insert(), __delitem__
144
- · Ordering (Mutable only): sort(), reverse()
145
- · Math: __add__ (+), __iadd__ (+=)
147
+ - Access: `__getitem__`, `__iter__`, `__contains__`, `count()`, `index()`
148
+ - Mutation (Dynamic only): `append()`, `pop()`, `clear()`, `insert()`, `__delitem__`
149
+ - Ordering (Mutable only): `sort()`, `reverse()`
150
+ - Math: `__add__`, `__iadd__`
@@ -4,13 +4,17 @@ type-safe, and highly customizable custom sequence types
4
4
 
5
5
  # Features:
6
6
 
7
- · Type Safety: Enforce strict data types for sequence items
8
- · Length Constraints: Define fixed-size or dynamic sequences
9
- · Immutability: Easily create read-only (frozen) collections
10
- · Rich API: Seamlessly integrates with built-in functions (len(), repr(), iteration, slicing)
11
- · Copy Support: Native support for shallow and deep copying
12
- · Pretty Printing: Built-in methods for clean text formatting out of the box
13
- · Installation: Add the listclass decorator code directly to your project
7
+ - Type Safety: Enforce strict data types for sequence items
8
+ - Length Constraints: Define fixed-size or dynamic sequences
9
+ - Immutability: Easily create read-only (frozen) collections
10
+ - Rich API: Seamlessly integrates with built-in functions (len(), repr(), iteration, slicing)
11
+ - Copy Support: Native support for shallow and deep copying
12
+ - Pretty Printing: Built-in methods for clean text formatting out of the box
13
+ - Installation: Add the listclass decorator code directly to your project
14
+
15
+ ```commandline
16
+ pip install listclasses
17
+ ```
14
18
 
15
19
  ```python
16
20
  from listclasses import listclass
@@ -37,7 +41,7 @@ print(len(items)) # Output: 3
37
41
 
38
42
  The @listclass decorator accepts configuration parameters to restrict container behavior
39
43
 
40
- ## Enforcing Strict Types
44
+ ### Enforcing Strict Types
41
45
 
42
46
  ```python
43
47
  @listclass(type=int)
@@ -51,21 +55,22 @@ numbers = IntegerList(1, 2, 3)
51
55
  numbers = IntegerList(1, "two", 3)
52
56
  ```
53
57
 
54
- ## Enforcing more than one type
58
+ ### Multi-Type Union Support
55
59
 
56
60
  ```python
57
- @listclass(type=int | None)
58
- class List:
61
+ @listclass(type=int | str)
62
+ class MixedList:
59
63
  pass
60
64
 
61
- # Works perfectly
62
- numbers_and_None = List(1, 2, 3, None)
65
+ # Both integers and strings are valid
66
+ mixed = MixedList(1, "hello", 42, "world")
63
67
 
64
- # Raises TypeError: Incorrect type: expected: int | None got: str
65
- numbers_and_None = List(1, "two", 3)
68
+ # Appending a float raises an error
69
+ # Raises TypeError: Incorrect type: expected: int | str got: float
70
+ mixed.append(3.14)
66
71
  ```
67
72
 
68
- ## Setting Fixed Lengths
73
+ ### Setting Fixed Lengths
69
74
 
70
75
  ```python
71
76
  @listclass(len=2)
@@ -79,7 +84,7 @@ point = Coordinates(10, 20)
79
84
  point = Coordinates(10, 20, 30)
80
85
  ```
81
86
 
82
- ## Creating Frozen (Immutable) Lists
87
+ ### Creating Frozen (Immutable) Lists
83
88
 
84
89
  ```python
85
90
  @listclass(frozen=True)
@@ -92,7 +97,7 @@ data = ReadOnlyData("A", "B")
92
97
  data[0] = "Z"
93
98
  ```
94
99
 
95
- ## Built-in Formatting
100
+ ### Built-in Formatting
96
101
  listclasses comes with utility methods to print your items cleanly.
97
102
 
98
103
  ```python
@@ -105,9 +110,9 @@ todos = TodoList("Buy milk", "Clean room", "Code Python")
105
110
  # Dotted list format
106
111
  todos.print_dotted()
107
112
  # Output:
108
- # · Buy milk
109
- # · Clean room
110
- # · Code Python
113
+ # - Buy milk
114
+ # - Clean room
115
+ # - Code Python
111
116
 
112
117
  # Numbered list format
113
118
  todos.print_numbered()
@@ -130,7 +135,7 @@ todos.print_numbered()
130
135
 
131
136
  Depending on configuration (frozen and len), instances support standard list operations:
132
137
 
133
- · Access: __getitem__, __iter__, __contains__, count(), index()
134
- · Mutation (Dynamic only): append(), pop(), clear(), insert(), __delitem__
135
- · Ordering (Mutable only): sort(), reverse()
136
- · Math: __add__ (+), __iadd__ (+=)
138
+ - Access: `__getitem__`, `__iter__`, `__contains__`, `count()`, `index()`
139
+ - Mutation (Dynamic only): `append()`, `pop()`, `clear()`, `insert()`, `__delitem__`
140
+ - Ordering (Mutable only): `sort()`, `reverse()`
141
+ - Math: `__add__`, `__iadd__`
@@ -23,26 +23,30 @@ def listclass(cls = None, *, len: int = 0, frozen: bool = False, type: type = an
23
23
 
24
24
 
25
25
  def wraps(cls):
26
+ # --- Type Validation Helper ---
27
+ def validate_items(items):
28
+ if type == any:
29
+ return
30
+
31
+ is_union = isinstance(type, types.UnionType) or get_origin(type) is Union
32
+ allowed = get_args(type) if is_union else type
33
+
34
+ for item in items:
35
+ if not isinstance(item, allowed):
36
+ if is_union:
37
+ type_names = " | ".join(t.__name__ for t in allowed)
38
+ else:
39
+ type_names = type.__name__ if hasattr(type, '__name__') else str(type)
40
+
41
+ got_name = _type(item).__name__
42
+ raise TypeError(f'Incorrect type: expected: {type_names} got: {got_name}')
43
+
26
44
  original_init = getattr(cls, '__init__', None)
45
+
27
46
  def new_init(self, *args, **kwargs):
28
47
  if args.__len__() == len or len == 0:
29
- if args.__len__() == len or len == 0:
30
- for i in range(args.__len__()):
31
- # Check if the allowed type is a modern Union (int | str) or legacy typing.Union
32
- is_union = isinstance(type, types.UnionType) or get_origin(type) is Union
33
-
34
- if is_union:
35
- allowed_types = get_args(type)
36
- if not isinstance(args[i], allowed_types):
37
- type_names = " | ".join(t.__name__ for t in allowed_types)
38
- got_name = _type(args[i]).__name__
39
- raise TypeError(f'Incorrect type: expected: {type_names} got: {got_name}')
40
- else:
41
- if (not isinstance(args[i], type)) and (type != any):
42
- expected_name = type.__name__ if hasattr(type, '__name__') else str(type)
43
- got_name = _type(args[i]).__name__
44
- raise TypeError(f'Incorrect type: expected: {expected_name} got: {got_name}')
45
- self._items = [*args]
48
+ validate_items(args)
49
+ self._items = [*args]
46
50
  elif args.__len__() > len:
47
51
  raise ValueError(f'Too many items: expected: {len} got: {args.__len__()}')
48
52
  elif args.__len__() < len:
@@ -56,20 +60,7 @@ def listclass(cls = None, *, len: int = 0, frozen: bool = False, type: type = an
56
60
  def new_setitem(self, key, value):
57
61
  if frozen:
58
62
  raise TypeError(f"'{cls.__name__}' object does not support item assignment")
59
- # Check if the allowed type is a modern Union (int | str) or legacy typing.Union
60
- is_union = isinstance(type, types.UnionType) or get_origin(type) is Union
61
-
62
- if is_union:
63
- allowed_types = get_args(type)
64
- if not isinstance(args[i], allowed_types):
65
- type_names = " | ".join(t.__name__ for t in allowed_types)
66
- got_name = _type(args[i]).__name__
67
- raise TypeError(f'Incorrect type: expected: {type_names} got: {got_name}')
68
- else:
69
- if (not isinstance(args[i], type)) and (type != any):
70
- expected_name = type.__name__ if hasattr(type, '__name__') else str(type)
71
- got_name = _type(args[i]).__name__
72
- raise TypeError(f'Incorrect type: expected: {expected_name} got: {got_name}')
63
+ validate_items([value])
73
64
  self._items[key] = value
74
65
 
75
66
  def new_copy(self):
@@ -85,7 +76,7 @@ def listclass(cls = None, *, len: int = 0, frozen: bool = False, type: type = an
85
76
  new_instance.__dict__[k] = deepcopy(v, memo)
86
77
  return new_instance
87
78
 
88
- def print_dotted(self) -> None:
79
+ def print_dotted(self, dot: str='·') -> None:
89
80
  """
90
81
  Prints a list in a format:
91
82
  · list[0]\n
@@ -93,7 +84,7 @@ def listclass(cls = None, *, len: int = 0, frozen: bool = False, type: type = an
93
84
  · list[2]
94
85
  """
95
86
  for item in self._items:
96
- print('·', item)
87
+ print(dot, item)
97
88
 
98
89
  def print_numbered(self) -> None:
99
90
  """
@@ -131,24 +122,14 @@ def listclass(cls = None, *, len: int = 0, frozen: bool = False, type: type = an
131
122
 
132
123
  def new_iadd(self, other):
133
124
  if isinstance(other, cls):
134
- self._items.extend(other._items)
125
+ items_to_add = other._items
135
126
  elif hasattr(other, '__iter__'):
136
- self._items.extend(other)
127
+ items_to_add = list(other)
137
128
  else:
138
129
  return NotImplemented
139
- # Check if the allowed type is a modern Union (int | str) or legacy typing.Union
140
- is_union = isinstance(type, types.UnionType) or get_origin(type) is Union
141
- if is_union:
142
- allowed_types = get_args(type)
143
- if not isinstance(args[i], allowed_types):
144
- type_names = " | ".join(t.__name__ for t in allowed_types)
145
- got_name = _type(args[i]).__name__
146
- raise TypeError(f'Incorrect type: expected: {type_names} got: {got_name}')
147
- else:
148
- if (not isinstance(args[i], type)) and (type != any):
149
- expected_name = type.__name__ if hasattr(type, '__name__') else str(type)
150
- got_name = _type(args[i]).__name__
151
- raise TypeError(f'Incorrect type: expected: {expected_name} got: {got_name}')
130
+ # Type check elements being added via '+='
131
+ validate_items(items_to_add)
132
+ self._items.extend(items_to_add)
152
133
  return self
153
134
 
154
135
  def new_class_getitem(cls, params):
@@ -198,4 +179,14 @@ def listclass(cls = None, *, len: int = 0, frozen: bool = False, type: type = an
198
179
  # We're called with parens.
199
180
  return wraps
200
181
  # We're called as @listclass without parens.
201
- return wraps(cls)
182
+ return wraps(cls)
183
+
184
+ # tests
185
+ if __name__ == '__main__':
186
+ @listclass
187
+ class List:
188
+ ...
189
+ l = List(0, 10)
190
+ l[0] = 10
191
+ l += List(100, 20)
192
+ print(l)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: listclasses
3
- Version: 1.0.0
3
+ Version: 1.0.2
4
4
  Summary: A simple decorator to make custom sequence types
5
5
  Description-Content-Type: text/markdown
6
6
  Dynamic: description
@@ -13,13 +13,17 @@ type-safe, and highly customizable custom sequence types
13
13
 
14
14
  # Features:
15
15
 
16
- · Type Safety: Enforce strict data types for sequence items
17
- · Length Constraints: Define fixed-size or dynamic sequences
18
- · Immutability: Easily create read-only (frozen) collections
19
- · Rich API: Seamlessly integrates with built-in functions (len(), repr(), iteration, slicing)
20
- · Copy Support: Native support for shallow and deep copying
21
- · Pretty Printing: Built-in methods for clean text formatting out of the box
22
- · Installation: Add the listclass decorator code directly to your project
16
+ - Type Safety: Enforce strict data types for sequence items
17
+ - Length Constraints: Define fixed-size or dynamic sequences
18
+ - Immutability: Easily create read-only (frozen) collections
19
+ - Rich API: Seamlessly integrates with built-in functions (len(), repr(), iteration, slicing)
20
+ - Copy Support: Native support for shallow and deep copying
21
+ - Pretty Printing: Built-in methods for clean text formatting out of the box
22
+ - Installation: Add the listclass decorator code directly to your project
23
+
24
+ ```commandline
25
+ pip install listclasses
26
+ ```
23
27
 
24
28
  ```python
25
29
  from listclasses import listclass
@@ -46,7 +50,7 @@ print(len(items)) # Output: 3
46
50
 
47
51
  The @listclass decorator accepts configuration parameters to restrict container behavior
48
52
 
49
- ## Enforcing Strict Types
53
+ ### Enforcing Strict Types
50
54
 
51
55
  ```python
52
56
  @listclass(type=int)
@@ -60,21 +64,22 @@ numbers = IntegerList(1, 2, 3)
60
64
  numbers = IntegerList(1, "two", 3)
61
65
  ```
62
66
 
63
- ## Enforcing more than one type
67
+ ### Multi-Type Union Support
64
68
 
65
69
  ```python
66
- @listclass(type=int | None)
67
- class List:
70
+ @listclass(type=int | str)
71
+ class MixedList:
68
72
  pass
69
73
 
70
- # Works perfectly
71
- numbers_and_None = List(1, 2, 3, None)
74
+ # Both integers and strings are valid
75
+ mixed = MixedList(1, "hello", 42, "world")
72
76
 
73
- # Raises TypeError: Incorrect type: expected: int | None got: str
74
- numbers_and_None = List(1, "two", 3)
77
+ # Appending a float raises an error
78
+ # Raises TypeError: Incorrect type: expected: int | str got: float
79
+ mixed.append(3.14)
75
80
  ```
76
81
 
77
- ## Setting Fixed Lengths
82
+ ### Setting Fixed Lengths
78
83
 
79
84
  ```python
80
85
  @listclass(len=2)
@@ -88,7 +93,7 @@ point = Coordinates(10, 20)
88
93
  point = Coordinates(10, 20, 30)
89
94
  ```
90
95
 
91
- ## Creating Frozen (Immutable) Lists
96
+ ### Creating Frozen (Immutable) Lists
92
97
 
93
98
  ```python
94
99
  @listclass(frozen=True)
@@ -101,7 +106,7 @@ data = ReadOnlyData("A", "B")
101
106
  data[0] = "Z"
102
107
  ```
103
108
 
104
- ## Built-in Formatting
109
+ ### Built-in Formatting
105
110
  listclasses comes with utility methods to print your items cleanly.
106
111
 
107
112
  ```python
@@ -114,9 +119,9 @@ todos = TodoList("Buy milk", "Clean room", "Code Python")
114
119
  # Dotted list format
115
120
  todos.print_dotted()
116
121
  # Output:
117
- # · Buy milk
118
- # · Clean room
119
- # · Code Python
122
+ # - Buy milk
123
+ # - Clean room
124
+ # - Code Python
120
125
 
121
126
  # Numbered list format
122
127
  todos.print_numbered()
@@ -139,7 +144,7 @@ todos.print_numbered()
139
144
 
140
145
  Depending on configuration (frozen and len), instances support standard list operations:
141
146
 
142
- · Access: __getitem__, __iter__, __contains__, count(), index()
143
- · Mutation (Dynamic only): append(), pop(), clear(), insert(), __delitem__
144
- · Ordering (Mutable only): sort(), reverse()
145
- · Math: __add__ (+), __iadd__ (+=)
147
+ - Access: `__getitem__`, `__iter__`, `__contains__`, `count()`, `index()`
148
+ - Mutation (Dynamic only): `append()`, `pop()`, `clear()`, `insert()`, `__delitem__`
149
+ - Ordering (Mutable only): `sort()`, `reverse()`
150
+ - Math: `__add__`, `__iadd__`
@@ -5,7 +5,7 @@ with open('README.md', 'r') as file:
5
5
 
6
6
  setup(
7
7
  name='listclasses',
8
- version='1.0.0',
8
+ version='1.0.2',
9
9
  packages=find_packages(),
10
10
  install_requires=[
11
11
 
@@ -13,4 +13,5 @@ setup(
13
13
  description='A simple decorator to make custom sequence types',
14
14
  long_description=description,
15
15
  long_description_content_type='text/markdown'
16
- )
16
+ )
17
+
File without changes