more-abc 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.
@@ -0,0 +1,4 @@
1
+ .vscode/
2
+ .venv/
3
+ __pycache__/
4
+ dist/
more_abc-2.0.0/License ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (C) 2026 Evan Yang <quantbit@126.com>
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.
@@ -0,0 +1,312 @@
1
+ Metadata-Version: 2.4
2
+ Name: more-abc
3
+ Version: 2.0.0
4
+ Summary: An extension of the abc module with many similar features added.
5
+ Project-URL: Homepage, https://github.com/aiwonderland/more_abc
6
+ Project-URL: Repository, https://github.com/aiwonderland/more_abc
7
+ Author-email: Evan Yang <quantbit@126.com>
8
+ License: MIT
9
+ License-File: License
10
+ Keywords: abc,abstract,collections,dataclass,enum,logging,mixin
11
+ Classifier: Development Status :: 5 - Production/Stable
12
+ Classifier: Intended Audience :: Developers
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Programming Language :: Python :: 3
15
+ Classifier: Programming Language :: Python :: 3.9
16
+ Classifier: Programming Language :: Python :: 3.10
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Topic :: Software Development :: Libraries :: Python Modules
21
+ Classifier: Typing :: Typed
22
+ Requires-Python: >=3.9
23
+ Description-Content-Type: text/markdown
24
+
25
+ # more_abc
26
+
27
+ This module is an extension of the `abc` module,
28
+ with many similar features added.
29
+
30
+ ![License: MIT](https://img.shields.io/badge/License-MIT-%2396DA45?style=for-the-badge&logo=opensourceinitiative&logoColor=white)
31
+ ![](https://img.shields.io/badge/Python-3776AB?logo=python&logoColor=white)
32
+ ![](https://img.shields.io/badge/Status-Active-brightgreen)
33
+
34
+ ## Installation
35
+
36
+ ```bash
37
+ pip install more_abc
38
+ ```
39
+
40
+ ## Usage
41
+
42
+ ### ABCMixin
43
+
44
+ `ABCMixin` enforces implementation of `initialize`, `validate`, and `to_dict` on subclasses, and provides `is_valid()` and `get_info()` for free.
45
+
46
+ ```python
47
+ from more_abc import ABCMixin
48
+
49
+ class User(ABCMixin):
50
+ def __init__(self, name: str, age: int):
51
+ self.name = name
52
+ self.age = age
53
+ self.initialize()
54
+
55
+ def initialize(self):
56
+ self.active = True
57
+
58
+ def validate(self) -> bool:
59
+ return isinstance(self.name, str) and self.age >= 0
60
+
61
+ def to_dict(self) -> dict:
62
+ return {"name": self.name, "age": self.age, "active": self.active}
63
+
64
+ user = User("Alice", 30)
65
+ print(user.is_valid()) # True
66
+ print(user.get_info()) # {'data': {'name': 'Alice', 'age': 30, 'active': True}, 'is_valid': True, 'class_name': 'User'}
67
+ print(repr(user)) # User({'name': 'Alice', 'age': 30, 'active': True})
68
+ ```
69
+
70
+ ### ABCException
71
+
72
+ `ABCException` is an abstract base for custom exceptions. Subclasses must implement `_get_message()`.
73
+
74
+ ```python
75
+ from more_abc import ABCException
76
+
77
+ class NotFoundError(ABCException):
78
+ def _get_message(self) -> str:
79
+ return f"Class {self.cls!r} was not found."
80
+
81
+ raise NotFoundError(cls="MyClass")
82
+ # NotFoundError: Class 'MyClass' was not found.
83
+ ```
84
+
85
+ ### ABCWarning
86
+
87
+ `ABCWarning` works the same way as `ABCException` but for warnings.
88
+
89
+ ```python
90
+ import warnings
91
+ from more_abc import ABCWarning
92
+
93
+ class DeprecatedWarning(ABCWarning):
94
+ def _get_message(self) -> str:
95
+ return f"{self.cls!r} is deprecated and will be removed in a future version."
96
+
97
+ warnings.warn(DeprecatedWarning(cls="OldClass"))
98
+ # DeprecatedWarning: 'OldClass' is deprecated and will be removed in a future version.
99
+ ```
100
+
101
+ ### AbcEnum
102
+
103
+ `AbcEnum` is an `Enum` base class that supports abstract methods. Use it when you want to define an enum interface that concrete subclasses must implement.
104
+
105
+ ```python
106
+ from abc import abstractmethod
107
+ from more_abc import AbcEnum
108
+
109
+ class Direction(AbcEnum):
110
+ NORTH = "N"
111
+ SOUTH = "S"
112
+ EAST = "E"
113
+ WEST = "W"
114
+
115
+ @abstractmethod
116
+ def opposite(self) -> "Direction": ...
117
+
118
+ # Direction.NORTH → TypeError: Can't instantiate abstract class Direction …
119
+
120
+ class CardinalDirection(Direction):
121
+ NORTH = "N"
122
+ SOUTH = "S"
123
+ EAST = "E"
124
+ WEST = "W"
125
+
126
+ def opposite(self) -> "CardinalDirection":
127
+ _opp = {"N": "S", "S": "N", "E": "W", "W": "E"}
128
+ return CardinalDirection(_opp[self.value])
129
+
130
+ print(CardinalDirection.NORTH.opposite()) # CardinalDirection.SOUTH
131
+ ```
132
+
133
+ `ABCEnumMeta` is the underlying combined metaclass (`ABCMeta + EnumMeta`) and is available for advanced use cases.
134
+
135
+ ### abc_dataclass
136
+
137
+ `abc_dataclass` is a drop-in replacement for `@dataclass` that automatically gives the class `ABCMeta` as its metaclass, so you can use `@abstractmethod` without manually inheriting from `ABC`.
138
+
139
+ ```python
140
+ from abc import abstractmethod
141
+ from more_abc import abc_dataclass
142
+
143
+ @abc_dataclass
144
+ class Shape:
145
+ color: str
146
+
147
+ @abstractmethod
148
+ def area(self) -> float: ...
149
+
150
+ @abc_dataclass(frozen=True)
151
+ class Circle(Shape):
152
+ radius: float
153
+
154
+ def area(self) -> float:
155
+ return 3.14159 * self.radius ** 2
156
+
157
+ c = Circle(color="red", radius=5.0)
158
+ print(c.area()) # 78.53975
159
+ print(c) # Circle(color='red', radius=5.0)
160
+ ```
161
+
162
+ Attempting to instantiate an abstract class raises `TypeError` as expected:
163
+
164
+ ```python
165
+ Shape(color="blue") # TypeError: Can't instantiate abstract class Shape ...
166
+ ```
167
+
168
+ ### Type aliases
169
+
170
+ `ABCclassType` and `ABCMetaclassType` mirror the pattern from the `types` module.
171
+
172
+ ```python
173
+ from more_abc import ABCclassType, ABCMetaclassType
174
+ from abc import ABC, ABCMeta
175
+
176
+ assert ABCclassType is type(ABC) # True
177
+ assert ABCMetaclassType is type(ABCMeta) # True
178
+
179
+ def accepts_abc_class(cls: ABCclassType):
180
+ print(f"Got an ABC class: {cls}")
181
+
182
+ accepts_abc_class(ABC)
183
+ ```
184
+
185
+ ### AbstractLogHandler
186
+
187
+ `AbstractLogHandler` is an abstract base for `logging.Handler`. Subclasses must implement `configure()` and `emit()`. The concrete `close()` method handles thread-safe resource cleanup automatically.
188
+
189
+ ```python
190
+ import logging
191
+ from more_abc import AbstractLogHandler
192
+
193
+ class PrintHandler(AbstractLogHandler):
194
+ def configure(self, config: dict) -> None:
195
+ self._handler_config.update(config)
196
+
197
+ def emit(self, record: logging.LogRecord) -> None:
198
+ print(self.format(record))
199
+
200
+ handler = PrintHandler(level=logging.DEBUG)
201
+ handler.configure({"prefix": "[LOG]"})
202
+
203
+ logger = logging.getLogger("demo")
204
+ logger.addHandler(handler)
205
+ logger.warning("something happened")
206
+ ```
207
+
208
+ ### AbstractLogFormatter
209
+
210
+ `AbstractLogFormatter` is an abstract base for `logging.Formatter`. Subclasses must implement `format()`.
211
+
212
+ ```python
213
+ import logging
214
+ from more_abc import AbstractLogFormatter
215
+
216
+ class UpperFormatter(AbstractLogFormatter):
217
+ def format(self, record: logging.LogRecord) -> str:
218
+ return f"[{record.levelname}] {record.getMessage().upper()}"
219
+
220
+ handler = logging.StreamHandler()
221
+ handler.setFormatter(UpperFormatter())
222
+ ```
223
+
224
+ ### AbstractLogFilter
225
+
226
+ `AbstractLogFilter` is an abstract base for `logging.Filter`. Subclasses must implement `filter()`, returning `True` to allow a record through or `False` to discard it.
227
+
228
+ ```python
229
+ import logging
230
+ from more_abc import AbstractLogFilter
231
+
232
+ class ErrorOnlyFilter(AbstractLogFilter):
233
+ def filter(self, record: logging.LogRecord) -> bool:
234
+ return record.levelno >= logging.ERROR
235
+
236
+ handler = logging.StreamHandler()
237
+ handler.addFilter(ErrorOnlyFilter())
238
+ ```
239
+
240
+ ### abc re-exports
241
+
242
+ `more_abc` re-exports all public symbols from the standard `abc` module, so you can use it as a single import for everything ABC-related.
243
+
244
+ ```python
245
+ # Instead of:
246
+ from abc import ABC, ABCMeta, abstractmethod
247
+ from more_abc import ABCMixin, abc_dataclass
248
+
249
+ # You can do:
250
+ from more_abc import ABC, ABCMeta, abstractmethod, ABCMixin, abc_dataclass
251
+ ```
252
+
253
+ Available re-exports: `ABC`, `ABCMeta`, `abstractmethod`, `abstractproperty`, `get_cache_token`.
254
+
255
+ ### Sortable / Filterable / Transformable
256
+
257
+ `more_abc.collections_abc` provides three ABC families for custom collection types, each following the same `Base* / *Mixin / *` pattern.
258
+
259
+ **Sortable** — in-place sorting via `__sort__`:
260
+
261
+ ```python
262
+ from more_abc import Sortable
263
+
264
+ class NumberList(Sortable):
265
+ def __init__(self, data: list):
266
+ self._data = list(data)
267
+
268
+ def __sort__(self, reverse=False):
269
+ self._data.sort(reverse=reverse)
270
+
271
+ def __copy__(self):
272
+ return NumberList(self._data)
273
+
274
+ nl = NumberList([3, 1, 2])
275
+ nl.sort() # in-place: [1, 2, 3]
276
+ asc = nl.sorted(reverse=True) # new copy: [3, 2, 1]
277
+ ```
278
+
279
+ **Filterable** — predicate filtering via `__filter__`:
280
+
281
+ ```python
282
+ from more_abc import Filterable
283
+
284
+ class NumberList(Filterable):
285
+ def __init__(self, data: list):
286
+ self._data = list(data)
287
+
288
+ def __filter__(self, predicate):
289
+ return NumberList([x for x in self._data if predicate(x)])
290
+
291
+ nl = NumberList([1, 2, 3, 4, 5])
292
+ evens = nl.filter(lambda x: x % 2 == 0) # [2, 4]
293
+ odds = nl.reject(lambda x: x % 2 == 0) # [1, 3, 5]
294
+ ```
295
+
296
+ **Transformable** — element-wise mapping via `__transform__`:
297
+
298
+ ```python
299
+ from more_abc import Transformable
300
+
301
+ class NumberList(Transformable):
302
+ def __init__(self, data: list):
303
+ self._data = list(data)
304
+
305
+ def __transform__(self, func):
306
+ return NumberList([func(x) for x in self._data])
307
+
308
+ nl = NumberList([1, 2, 3])
309
+ doubled = nl.map(lambda x: x * 2) # [2, 4, 6]
310
+ ```
311
+
312
+ `BaseSortable`, `SortableMixin`, `BaseFilterable`, `FilterableMixin`, `BaseTransformable`, and `TransformableMixin` are also exported for advanced composition.
@@ -0,0 +1,288 @@
1
+ # more_abc
2
+
3
+ This module is an extension of the `abc` module,
4
+ with many similar features added.
5
+
6
+ ![License: MIT](https://img.shields.io/badge/License-MIT-%2396DA45?style=for-the-badge&logo=opensourceinitiative&logoColor=white)
7
+ ![](https://img.shields.io/badge/Python-3776AB?logo=python&logoColor=white)
8
+ ![](https://img.shields.io/badge/Status-Active-brightgreen)
9
+
10
+ ## Installation
11
+
12
+ ```bash
13
+ pip install more_abc
14
+ ```
15
+
16
+ ## Usage
17
+
18
+ ### ABCMixin
19
+
20
+ `ABCMixin` enforces implementation of `initialize`, `validate`, and `to_dict` on subclasses, and provides `is_valid()` and `get_info()` for free.
21
+
22
+ ```python
23
+ from more_abc import ABCMixin
24
+
25
+ class User(ABCMixin):
26
+ def __init__(self, name: str, age: int):
27
+ self.name = name
28
+ self.age = age
29
+ self.initialize()
30
+
31
+ def initialize(self):
32
+ self.active = True
33
+
34
+ def validate(self) -> bool:
35
+ return isinstance(self.name, str) and self.age >= 0
36
+
37
+ def to_dict(self) -> dict:
38
+ return {"name": self.name, "age": self.age, "active": self.active}
39
+
40
+ user = User("Alice", 30)
41
+ print(user.is_valid()) # True
42
+ print(user.get_info()) # {'data': {'name': 'Alice', 'age': 30, 'active': True}, 'is_valid': True, 'class_name': 'User'}
43
+ print(repr(user)) # User({'name': 'Alice', 'age': 30, 'active': True})
44
+ ```
45
+
46
+ ### ABCException
47
+
48
+ `ABCException` is an abstract base for custom exceptions. Subclasses must implement `_get_message()`.
49
+
50
+ ```python
51
+ from more_abc import ABCException
52
+
53
+ class NotFoundError(ABCException):
54
+ def _get_message(self) -> str:
55
+ return f"Class {self.cls!r} was not found."
56
+
57
+ raise NotFoundError(cls="MyClass")
58
+ # NotFoundError: Class 'MyClass' was not found.
59
+ ```
60
+
61
+ ### ABCWarning
62
+
63
+ `ABCWarning` works the same way as `ABCException` but for warnings.
64
+
65
+ ```python
66
+ import warnings
67
+ from more_abc import ABCWarning
68
+
69
+ class DeprecatedWarning(ABCWarning):
70
+ def _get_message(self) -> str:
71
+ return f"{self.cls!r} is deprecated and will be removed in a future version."
72
+
73
+ warnings.warn(DeprecatedWarning(cls="OldClass"))
74
+ # DeprecatedWarning: 'OldClass' is deprecated and will be removed in a future version.
75
+ ```
76
+
77
+ ### AbcEnum
78
+
79
+ `AbcEnum` is an `Enum` base class that supports abstract methods. Use it when you want to define an enum interface that concrete subclasses must implement.
80
+
81
+ ```python
82
+ from abc import abstractmethod
83
+ from more_abc import AbcEnum
84
+
85
+ class Direction(AbcEnum):
86
+ NORTH = "N"
87
+ SOUTH = "S"
88
+ EAST = "E"
89
+ WEST = "W"
90
+
91
+ @abstractmethod
92
+ def opposite(self) -> "Direction": ...
93
+
94
+ # Direction.NORTH → TypeError: Can't instantiate abstract class Direction …
95
+
96
+ class CardinalDirection(Direction):
97
+ NORTH = "N"
98
+ SOUTH = "S"
99
+ EAST = "E"
100
+ WEST = "W"
101
+
102
+ def opposite(self) -> "CardinalDirection":
103
+ _opp = {"N": "S", "S": "N", "E": "W", "W": "E"}
104
+ return CardinalDirection(_opp[self.value])
105
+
106
+ print(CardinalDirection.NORTH.opposite()) # CardinalDirection.SOUTH
107
+ ```
108
+
109
+ `ABCEnumMeta` is the underlying combined metaclass (`ABCMeta + EnumMeta`) and is available for advanced use cases.
110
+
111
+ ### abc_dataclass
112
+
113
+ `abc_dataclass` is a drop-in replacement for `@dataclass` that automatically gives the class `ABCMeta` as its metaclass, so you can use `@abstractmethod` without manually inheriting from `ABC`.
114
+
115
+ ```python
116
+ from abc import abstractmethod
117
+ from more_abc import abc_dataclass
118
+
119
+ @abc_dataclass
120
+ class Shape:
121
+ color: str
122
+
123
+ @abstractmethod
124
+ def area(self) -> float: ...
125
+
126
+ @abc_dataclass(frozen=True)
127
+ class Circle(Shape):
128
+ radius: float
129
+
130
+ def area(self) -> float:
131
+ return 3.14159 * self.radius ** 2
132
+
133
+ c = Circle(color="red", radius=5.0)
134
+ print(c.area()) # 78.53975
135
+ print(c) # Circle(color='red', radius=5.0)
136
+ ```
137
+
138
+ Attempting to instantiate an abstract class raises `TypeError` as expected:
139
+
140
+ ```python
141
+ Shape(color="blue") # TypeError: Can't instantiate abstract class Shape ...
142
+ ```
143
+
144
+ ### Type aliases
145
+
146
+ `ABCclassType` and `ABCMetaclassType` mirror the pattern from the `types` module.
147
+
148
+ ```python
149
+ from more_abc import ABCclassType, ABCMetaclassType
150
+ from abc import ABC, ABCMeta
151
+
152
+ assert ABCclassType is type(ABC) # True
153
+ assert ABCMetaclassType is type(ABCMeta) # True
154
+
155
+ def accepts_abc_class(cls: ABCclassType):
156
+ print(f"Got an ABC class: {cls}")
157
+
158
+ accepts_abc_class(ABC)
159
+ ```
160
+
161
+ ### AbstractLogHandler
162
+
163
+ `AbstractLogHandler` is an abstract base for `logging.Handler`. Subclasses must implement `configure()` and `emit()`. The concrete `close()` method handles thread-safe resource cleanup automatically.
164
+
165
+ ```python
166
+ import logging
167
+ from more_abc import AbstractLogHandler
168
+
169
+ class PrintHandler(AbstractLogHandler):
170
+ def configure(self, config: dict) -> None:
171
+ self._handler_config.update(config)
172
+
173
+ def emit(self, record: logging.LogRecord) -> None:
174
+ print(self.format(record))
175
+
176
+ handler = PrintHandler(level=logging.DEBUG)
177
+ handler.configure({"prefix": "[LOG]"})
178
+
179
+ logger = logging.getLogger("demo")
180
+ logger.addHandler(handler)
181
+ logger.warning("something happened")
182
+ ```
183
+
184
+ ### AbstractLogFormatter
185
+
186
+ `AbstractLogFormatter` is an abstract base for `logging.Formatter`. Subclasses must implement `format()`.
187
+
188
+ ```python
189
+ import logging
190
+ from more_abc import AbstractLogFormatter
191
+
192
+ class UpperFormatter(AbstractLogFormatter):
193
+ def format(self, record: logging.LogRecord) -> str:
194
+ return f"[{record.levelname}] {record.getMessage().upper()}"
195
+
196
+ handler = logging.StreamHandler()
197
+ handler.setFormatter(UpperFormatter())
198
+ ```
199
+
200
+ ### AbstractLogFilter
201
+
202
+ `AbstractLogFilter` is an abstract base for `logging.Filter`. Subclasses must implement `filter()`, returning `True` to allow a record through or `False` to discard it.
203
+
204
+ ```python
205
+ import logging
206
+ from more_abc import AbstractLogFilter
207
+
208
+ class ErrorOnlyFilter(AbstractLogFilter):
209
+ def filter(self, record: logging.LogRecord) -> bool:
210
+ return record.levelno >= logging.ERROR
211
+
212
+ handler = logging.StreamHandler()
213
+ handler.addFilter(ErrorOnlyFilter())
214
+ ```
215
+
216
+ ### abc re-exports
217
+
218
+ `more_abc` re-exports all public symbols from the standard `abc` module, so you can use it as a single import for everything ABC-related.
219
+
220
+ ```python
221
+ # Instead of:
222
+ from abc import ABC, ABCMeta, abstractmethod
223
+ from more_abc import ABCMixin, abc_dataclass
224
+
225
+ # You can do:
226
+ from more_abc import ABC, ABCMeta, abstractmethod, ABCMixin, abc_dataclass
227
+ ```
228
+
229
+ Available re-exports: `ABC`, `ABCMeta`, `abstractmethod`, `abstractproperty`, `get_cache_token`.
230
+
231
+ ### Sortable / Filterable / Transformable
232
+
233
+ `more_abc.collections_abc` provides three ABC families for custom collection types, each following the same `Base* / *Mixin / *` pattern.
234
+
235
+ **Sortable** — in-place sorting via `__sort__`:
236
+
237
+ ```python
238
+ from more_abc import Sortable
239
+
240
+ class NumberList(Sortable):
241
+ def __init__(self, data: list):
242
+ self._data = list(data)
243
+
244
+ def __sort__(self, reverse=False):
245
+ self._data.sort(reverse=reverse)
246
+
247
+ def __copy__(self):
248
+ return NumberList(self._data)
249
+
250
+ nl = NumberList([3, 1, 2])
251
+ nl.sort() # in-place: [1, 2, 3]
252
+ asc = nl.sorted(reverse=True) # new copy: [3, 2, 1]
253
+ ```
254
+
255
+ **Filterable** — predicate filtering via `__filter__`:
256
+
257
+ ```python
258
+ from more_abc import Filterable
259
+
260
+ class NumberList(Filterable):
261
+ def __init__(self, data: list):
262
+ self._data = list(data)
263
+
264
+ def __filter__(self, predicate):
265
+ return NumberList([x for x in self._data if predicate(x)])
266
+
267
+ nl = NumberList([1, 2, 3, 4, 5])
268
+ evens = nl.filter(lambda x: x % 2 == 0) # [2, 4]
269
+ odds = nl.reject(lambda x: x % 2 == 0) # [1, 3, 5]
270
+ ```
271
+
272
+ **Transformable** — element-wise mapping via `__transform__`:
273
+
274
+ ```python
275
+ from more_abc import Transformable
276
+
277
+ class NumberList(Transformable):
278
+ def __init__(self, data: list):
279
+ self._data = list(data)
280
+
281
+ def __transform__(self, func):
282
+ return NumberList([func(x) for x in self._data])
283
+
284
+ nl = NumberList([1, 2, 3])
285
+ doubled = nl.map(lambda x: x * 2) # [2, 4, 6]
286
+ ```
287
+
288
+ `BaseSortable`, `SortableMixin`, `BaseFilterable`, `FilterableMixin`, `BaseTransformable`, and `TransformableMixin` are also exported for advanced composition.