plib3.stdlib 2.3.1__tar.gz → 2.4__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.
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/CHANGES.rst +17 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/PKG-INFO +2 -8
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/README.rst +0 -4
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/__init__.py +1 -1
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/_extras.py +9 -4
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/builtins.py +2 -1
- plib3_stdlib-2.4/plib/stdlib/enumtools.py +217 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/test/stdlib/__main__.py +1 -0
- plib3_stdlib-2.4/pyproject.toml +3 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/setup.cfg +1 -1
- plib3.stdlib-2.3.1/plib/stdlib/util/ModuleProxy.py +0 -183
- plib3.stdlib-2.3.1/plib/stdlib/util/__init__.py +0 -13
- plib3.stdlib-2.3.1/pyproject.toml +0 -3
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/LICENSE.txt +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/MANIFEST.in +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/TODO.rst +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/examples/pyget/pyget3.py +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/examples/pyidserver/pyidserver3.py +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/examples/scrips/scrips3.py +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/examples/tzname/tzname3.py +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/classtools.py +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/cmdline.py +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/coll/__init__.py +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/coll/_abc.py +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/coll/_bases.py +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/coll/_mixins.py +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/coll/_names.py +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/coll/_seq.py +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/coll/_tests.py +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/coll/_tuples.py +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/coll/_utils.py +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/copytools.py +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/csvtools.py +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/decotools/DelayedDecorator.py +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/decotools/IndexedGenerator.py +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/decotools/MemoizedGenerator.py +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/decotools/__init__.py +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/examples.py +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/fdtools.py +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/imp.py +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/ini/PIniFile.py +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/ini/PIniFileBase.py +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/ini/PIniFileOption.py +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/ini/PIniFileSection.py +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/ini/PIniInterface.py +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/ini/__init__.py +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/iters.py +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/jsontools.py +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/localize.py +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/mail.py +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/mathlib.py +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/net/__init__.py +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/net/client.py +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/net/transport.py +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/options.py +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/ostools.py +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/postinstall.py +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/proc.py +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/sigs.py +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/strings.py +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/systools.py +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/timer.py +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/tztools.py +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/test/__main__.py +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/test/stdlib/ModuleProxy_testmod.py +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/test/stdlib/__init__.py +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/test/stdlib/abstract_testlib.py +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/test/stdlib/doctests/test_stdlib.txt +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/test/stdlib/doctests/test_stdlib_Singleton.txt +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/test/stdlib/doctests/test_stdlib_SortMixin.txt +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/test/stdlib/doctests/test_stdlib_abc.txt +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/test/stdlib/doctests/test_stdlib_builtins.txt +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/test/stdlib/doctests/test_stdlib_cached_property.txt +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/test/stdlib/doctests/test_stdlib_coll.txt +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/test/stdlib/doctests/test_stdlib_newlines.txt +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/test/stdlib/doctests/test_stdlib_normalize_slice.txt +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/test/stdlib/mapping_testlib.py +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/test/stdlib/test_ModuleProxy.py +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/test/stdlib/test_abstractcontainer.py +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/test/stdlib/test_abstractdict.py +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/test/stdlib/test_abstractkeyed.py +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/test/stdlib/test_abstractlist.py +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/test/stdlib/test_abstractmapping.py +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/test/stdlib/test_abstractsequence.py +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/test/stdlib/test_basecontainer.py +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/test/stdlib/test_basedict.py +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/test/stdlib/test_basekeyed.py +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/test/stdlib/test_baselist.py +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/test/stdlib/test_basemapping.py +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/test/stdlib/test_basesequence.py +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/test/stdlib/test_ostools.py +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/test/stdlib/test_sigs.py +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/test/stdlib/test_stdlib.py +0 -0
- {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/test/support/__init__.py +0 -0
|
@@ -1,6 +1,23 @@
|
|
|
1
1
|
plib3.stdlib Change Log
|
|
2
2
|
=======================
|
|
3
3
|
|
|
4
|
+
Version 2.4
|
|
5
|
+
-----------
|
|
6
|
+
|
|
7
|
+
- Remove ``util`` sub-package with ``ModuleProxy`` class, no
|
|
8
|
+
longer needed in Python 3.8 or later since PEP 562 functionality
|
|
9
|
+
is available and fulfills the same purpose.
|
|
10
|
+
|
|
11
|
+
- Add requirement for Python 3.8 or later in ``setup.cfg``.
|
|
12
|
+
|
|
13
|
+
Version 2.3.2
|
|
14
|
+
-------------
|
|
15
|
+
|
|
16
|
+
- Use built-in ``prod`` function if available (in versions 3.8
|
|
17
|
+
and above) instead of adding the implementation from the
|
|
18
|
+
``_extras`` module to builtins when ``plib.stdlib.builtins``
|
|
19
|
+
is imported.
|
|
20
|
+
|
|
4
21
|
Version 2.3.1
|
|
5
22
|
-------------
|
|
6
23
|
|
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: plib3.stdlib
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.4
|
|
4
4
|
Summary: Useful packages and modules to extend the Python 3 standard library.
|
|
5
5
|
Home-page: http://pypi.org/project/plib3.stdlib
|
|
6
6
|
Author: Peter A. Donis
|
|
7
7
|
Author-email: peterdonis@alum.mit.edu
|
|
8
8
|
License: GPLv2
|
|
9
|
-
Platform: UNKNOWN
|
|
10
9
|
Classifier: Development Status :: 5 - Production/Stable
|
|
11
10
|
Classifier: Environment :: Console
|
|
12
11
|
Classifier: Environment :: MacOS X
|
|
@@ -19,6 +18,7 @@ Classifier: Operating System :: POSIX
|
|
|
19
18
|
Classifier: Operating System :: POSIX :: Linux
|
|
20
19
|
Classifier: Programming Language :: Python :: 3
|
|
21
20
|
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
21
|
+
Requires-Python: >=3.8
|
|
22
22
|
Description-Content-Type: text/x-rst
|
|
23
23
|
License-File: LICENSE.txt
|
|
24
24
|
|
|
@@ -141,10 +141,6 @@ The following modules or sub-packages are available in the
|
|
|
141
141
|
based on those in the Python docs for the ``datetime`` module,
|
|
142
142
|
and a function to return the local system timezone name.
|
|
143
143
|
|
|
144
|
-
- The ``util`` sub-package provides the ``ModuleProxy`` class, which
|
|
145
|
-
is used by a number of PLIB3 sub-packages. See the docstrings
|
|
146
|
-
for the class and the sub-packages using it for more information.
|
|
147
|
-
|
|
148
144
|
Installation
|
|
149
145
|
------------
|
|
150
146
|
|
|
@@ -201,5 +197,3 @@ GNU General Public License for more details.
|
|
|
201
197
|
You should have received a copy of the GNU General Public License
|
|
202
198
|
along with this program; if not, write to the Free Software
|
|
203
199
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
|
204
|
-
|
|
205
|
-
|
|
@@ -117,10 +117,6 @@ The following modules or sub-packages are available in the
|
|
|
117
117
|
based on those in the Python docs for the ``datetime`` module,
|
|
118
118
|
and a function to return the local system timezone name.
|
|
119
119
|
|
|
120
|
-
- The ``util`` sub-package provides the ``ModuleProxy`` class, which
|
|
121
|
-
is used by a number of PLIB3 sub-packages. See the docstrings
|
|
122
|
-
for the class and the sub-packages using it for more information.
|
|
123
|
-
|
|
124
120
|
Installation
|
|
125
121
|
------------
|
|
126
122
|
|
|
@@ -122,10 +122,15 @@ def last(iterable, default=None, deque=_deque):
|
|
|
122
122
|
return q[0] if q else default
|
|
123
123
|
|
|
124
124
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
125
|
+
try:
|
|
126
|
+
from math import prod
|
|
127
|
+
|
|
128
|
+
except ImportError:
|
|
129
|
+
|
|
130
|
+
def prod(iterable, mul=_mul):
|
|
131
|
+
"""Return the product of all items in iterable.
|
|
132
|
+
"""
|
|
133
|
+
return reduce(mul, iterable, 1)
|
|
129
134
|
|
|
130
135
|
|
|
131
136
|
def type_from_name(name):
|
|
@@ -61,7 +61,8 @@ def upgrade_builtins():
|
|
|
61
61
|
import builtins
|
|
62
62
|
from plib.stdlib import _extras
|
|
63
63
|
for extra_name in EXTRA_NAMES:
|
|
64
|
-
|
|
64
|
+
if not hasattr(builtins, extra_name):
|
|
65
|
+
setattr(builtins, extra_name, getattr(_extras, extra_name))
|
|
65
66
|
_upgraded = True
|
|
66
67
|
return "Upgraded built-ins installed!"
|
|
67
68
|
return "Upgrade already performed, nothing to do."
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Module ENUMTOOLS -- PLIB3 Enum Enhancements
|
|
4
|
+
Sub-Package STDLIB of Package PLIB3
|
|
5
|
+
Copyright (C) 2008-2022 by Peter A. Donis
|
|
6
|
+
|
|
7
|
+
Released under the GNU General Public License, Version 2
|
|
8
|
+
See the LICENSE and README files for more information
|
|
9
|
+
|
|
10
|
+
This module contains enhanced Enum support; the main purpose
|
|
11
|
+
is to enable user-defined additions to an existing Enum.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
import sys
|
|
15
|
+
from collections.abc import Mapping
|
|
16
|
+
import enum
|
|
17
|
+
|
|
18
|
+
from plib.stdlib.builtins import first
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _make_new_member(new_func, value_type, use_args):
|
|
22
|
+
# Code cribbed from EnumMeta.__new__, returns a function that
|
|
23
|
+
# constructs a new instance of the appropriate enum class
|
|
24
|
+
if use_args:
|
|
25
|
+
if issubclass(value_type, tuple):
|
|
26
|
+
def get_args(value):
|
|
27
|
+
return value
|
|
28
|
+
else:
|
|
29
|
+
def get_args(value):
|
|
30
|
+
return (value,)
|
|
31
|
+
def new_member(cls, value):
|
|
32
|
+
args = get_args(value)
|
|
33
|
+
member = new_func(cls, *args)
|
|
34
|
+
if not hasattr(member, '_value_'):
|
|
35
|
+
if value_type is object:
|
|
36
|
+
member._value_ = value
|
|
37
|
+
else:
|
|
38
|
+
member._value_ = value_type(*args)
|
|
39
|
+
return member
|
|
40
|
+
else:
|
|
41
|
+
def new_member(cls, value):
|
|
42
|
+
member = new_func(cls)
|
|
43
|
+
if not hasattr(member, '_value_'):
|
|
44
|
+
member._value_ = value
|
|
45
|
+
return member
|
|
46
|
+
return new_member
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class ExtendableEnumMeta(enum.EnumMeta):
|
|
50
|
+
"""Metaclass for extendable enum.
|
|
51
|
+
|
|
52
|
+
Overrides class construction and ``__call__`` methods to allow new
|
|
53
|
+
instances to be created while still disallowing subclassing once
|
|
54
|
+
the enum has members.
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
@classmethod
|
|
58
|
+
def __prepare__(meta, name, bases, **kwds):
|
|
59
|
+
# Note: this and what's in __new__ below seems like a lot of boilerplate to
|
|
60
|
+
# handle adding a keyword argument to a class definition (also it's not
|
|
61
|
+
# clear what exactly in EnumMeta.__prepare__ is checking for an unexpected
|
|
62
|
+
# keyword argument since **kwds is neither accessed nor passed in a super call)
|
|
63
|
+
disable_aliases = kwds.pop('disable_aliases', None)
|
|
64
|
+
classdict = enum.EnumMeta.__prepare__(name, bases, **kwds)
|
|
65
|
+
classdict._disable_aliases = disable_aliases
|
|
66
|
+
return classdict
|
|
67
|
+
|
|
68
|
+
def __new__(meta, name, bases, classdict, **kwds):
|
|
69
|
+
value_types = set(type(classdict[m]) for m in classdict._member_names)
|
|
70
|
+
if len(value_types) > 1:
|
|
71
|
+
raise ValueError("All members of an extendable enum must have the same value type.")
|
|
72
|
+
value_type = value_types.pop() if value_types else None
|
|
73
|
+
member_type, first_enum = meta._get_mixins_(name, bases)
|
|
74
|
+
new_func, _, use_args = meta._find_new_(
|
|
75
|
+
classdict, member_type, first_enum,
|
|
76
|
+
)
|
|
77
|
+
new_member = _make_new_member(new_func, value_type, use_args)
|
|
78
|
+
disable_aliases = classdict._disable_aliases
|
|
79
|
+
del classdict._disable_aliases
|
|
80
|
+
# Note: since we already popped it out of kwds in __prepare__ above,
|
|
81
|
+
# why is it still here?
|
|
82
|
+
assert kwds.pop('disable_aliases', None) is disable_aliases
|
|
83
|
+
enum_class = enum.EnumMeta.__new__(meta, name, bases, classdict, **kwds)
|
|
84
|
+
enum_class._value_type = value_type
|
|
85
|
+
enum_class._new_member = new_member
|
|
86
|
+
enum_class._disable_aliases = disable_aliases
|
|
87
|
+
return enum_class
|
|
88
|
+
|
|
89
|
+
def __call__(cls, value, names=None, *, module=None, qualname=None, type=None, start=1, disable_aliases=False):
|
|
90
|
+
enum_class = enum.EnumMeta.__call__(cls, value, names, module=module, qualname=qualname, type=type, start=start)
|
|
91
|
+
if names is not None:
|
|
92
|
+
# We created a new enum class
|
|
93
|
+
enum_class._disable_aliases = disable_aliases
|
|
94
|
+
return enum_class
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
class ExtendableEnum(enum.Enum, metaclass=ExtendableEnumMeta):
|
|
98
|
+
"""Extendable enum class.
|
|
99
|
+
|
|
100
|
+
Allows user code to add new members to an existing enum by calling the
|
|
101
|
+
``add_member`` class method. The ``extend`` class method is also provided
|
|
102
|
+
for convenience to allow adding multiple new members at once. Once an
|
|
103
|
+
extendable enum has members it still cannot be subclassed.
|
|
104
|
+
|
|
105
|
+
>>> class Color(ExtendableEnum):
|
|
106
|
+
... RED = 1
|
|
107
|
+
... GREEN = 2
|
|
108
|
+
...
|
|
109
|
+
>>> Color.add_member('BLUE', 3)
|
|
110
|
+
<Color.BLUE: 3>
|
|
111
|
+
>>> Color.add_member('GREEN', 4)
|
|
112
|
+
Traceback (most recent call last):
|
|
113
|
+
...
|
|
114
|
+
ValueError: Member name GREEN already exists in enum Color
|
|
115
|
+
>>> Color.add_member('YELLOW', 2)
|
|
116
|
+
<Color.YELLOW: 2>
|
|
117
|
+
>>> class UniqueColor(ExtendableEnum, disable_aliases=True):
|
|
118
|
+
... RED = 1
|
|
119
|
+
... GREEN = 2
|
|
120
|
+
...
|
|
121
|
+
>>> UniqueColor.add_member('BLUE', 3)
|
|
122
|
+
<UniqueColor.BLUE: 3>
|
|
123
|
+
>>> UniqueColor.add_member('GREEN', 4)
|
|
124
|
+
Traceback (most recent call last):
|
|
125
|
+
...
|
|
126
|
+
ValueError: Member name GREEN already exists in enum UniqueColor
|
|
127
|
+
>>> UniqueColor.add_member('YELLOW', 2)
|
|
128
|
+
Traceback (most recent call last):
|
|
129
|
+
...
|
|
130
|
+
ValueError: Aliases are disabled for enum UniqueColor
|
|
131
|
+
"""
|
|
132
|
+
|
|
133
|
+
@classmethod
|
|
134
|
+
def add_member(cls, name, value):
|
|
135
|
+
"""Add new member to an extendable enum.
|
|
136
|
+
|
|
137
|
+
Raises ``ValueError`` if a new member's value is not of the same type as
|
|
138
|
+
existing members' values, or if the member name already exists. If the
|
|
139
|
+
``disable_aliases`` keyword argument was used in constructing the extendable
|
|
140
|
+
enum class, existing values cannot be aliased, and ``ValueError`` is raised
|
|
141
|
+
if this is attempted.
|
|
142
|
+
"""
|
|
143
|
+
if not isinstance(value, cls._value_type):
|
|
144
|
+
raise ValueError("Invalid value type for extendable enum {}: {}".format(cls.__name__, repr(value)))
|
|
145
|
+
if name in cls.__members__:
|
|
146
|
+
raise ValueError("Member name {} already exists in enum {}".format(name, cls.__name__))
|
|
147
|
+
if cls._disable_aliases and (value in cls._value2member_map_):
|
|
148
|
+
raise ValueError("Aliases are disabled for enum {}".format(cls.__name__))
|
|
149
|
+
existing_member = first(m for m in cls.__members__.values() if m._value_ == value)
|
|
150
|
+
obj = cls._new_member(cls, value)
|
|
151
|
+
obj._name_ = name
|
|
152
|
+
obj._value_ = value
|
|
153
|
+
obj.__objclass__ = cls
|
|
154
|
+
cls._member_map_[name] = existing_member if existing_member is not None else obj
|
|
155
|
+
try:
|
|
156
|
+
# This will fail if value is not hashable, but enum supports that case so suppress exception
|
|
157
|
+
cls._value2member_map_[value] = existing_member if existing_member is not None else obj
|
|
158
|
+
except TypeError:
|
|
159
|
+
pass
|
|
160
|
+
if not existing_member:
|
|
161
|
+
# This list does not contain aliases
|
|
162
|
+
cls._member_names_.append(name)
|
|
163
|
+
return obj
|
|
164
|
+
|
|
165
|
+
@classmethod
|
|
166
|
+
def extend(cls, members):
|
|
167
|
+
"""Add multiple members at once to an extendable enum.
|
|
168
|
+
|
|
169
|
+
The ``members`` parameter must be an iterable of ``(name, value)`` tuples
|
|
170
|
+
or a mapping of names to values.
|
|
171
|
+
"""
|
|
172
|
+
for name, value in (members.items() if isinstance(members, Mapping) else members):
|
|
173
|
+
cls.add_member(name, value)
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def _get_transform(name, prefix_classname=False, classname_transform=None):
|
|
177
|
+
if prefix_classname:
|
|
178
|
+
prefix = name
|
|
179
|
+
if classname_transform:
|
|
180
|
+
prefix = classname_transform(prefix)
|
|
181
|
+
f = (lambda s: '{}_{}'.format(prefix, s))
|
|
182
|
+
else:
|
|
183
|
+
f = (lambda s: s)
|
|
184
|
+
return f
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
def _add_global(mod, s, m, f):
|
|
188
|
+
setattr(mod, f(s), m)
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def add_enum_member_global(modname, s, m, prefix_classname=False, classname_transform=None):
|
|
192
|
+
"""Add global to module modname for enum member ``m`` with name ``s``.
|
|
193
|
+
"""
|
|
194
|
+
f = _get_transform(type(m).__name__, prefix_classname, classname_transform)
|
|
195
|
+
_add_global(sys.modules[modname], s, m, f)
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def _add_globals(mod, name, obj, prefix_classname=False, classname_transform=None):
|
|
199
|
+
f = _get_transform(name, prefix_classname, classname_transform)
|
|
200
|
+
for s, m in obj.__members__.items():
|
|
201
|
+
_add_global(mod, s, m, f)
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
def add_enum_globals(modname, name, obj, prefix_classname=False, classname_transform=None):
|
|
205
|
+
"""Add globals to module ``modname`` for all members of enum with class name ``name``.
|
|
206
|
+
"""
|
|
207
|
+
return _add_globals(sys.modules[modname], name, obj, prefix_classname, classname_transform)
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
def add_all_enum_globals(modname, prefix_classname=False, classname_transform=None):
|
|
211
|
+
"""Add globals to module ``modname`` for all enums in module.
|
|
212
|
+
"""
|
|
213
|
+
mod = sys.modules[modname]
|
|
214
|
+
for name in dir(mod):
|
|
215
|
+
obj = getattr(mod, name)
|
|
216
|
+
if isinstance(obj, type) and issubclass(obj, enum.Enum):
|
|
217
|
+
_add_globals(mod, name, obj, prefix_classname, classname_transform)
|
|
@@ -23,6 +23,7 @@ classifiers =
|
|
|
23
23
|
Topic :: Software Development :: Libraries :: Python Modules
|
|
24
24
|
|
|
25
25
|
[options]
|
|
26
|
+
python_requires = >=3.8
|
|
26
27
|
packages =
|
|
27
28
|
plib
|
|
28
29
|
plib.stdlib
|
|
@@ -30,7 +31,6 @@ packages =
|
|
|
30
31
|
plib.stdlib.decotools
|
|
31
32
|
plib.stdlib.ini
|
|
32
33
|
plib.stdlib.net
|
|
33
|
-
plib.stdlib.util
|
|
34
34
|
plib.test
|
|
35
35
|
plib.test.stdlib
|
|
36
36
|
plib.test.support
|
|
@@ -1,183 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""
|
|
3
|
-
Module ModuleProxy
|
|
4
|
-
Sub-Package STDLIB.UTIL of Package PLIB3
|
|
5
|
-
Copyright (C) 2008-2022 by Peter A. Donis
|
|
6
|
-
|
|
7
|
-
Released under the GNU General Public License, Version 2
|
|
8
|
-
See the LICENSE and README files for more information
|
|
9
|
-
|
|
10
|
-
This module contains the ModuleProxy class. For examples
|
|
11
|
-
of intended use cases for this class, check out the
|
|
12
|
-
``main.py`` module in the PLIB3.GUI sub-package, or any
|
|
13
|
-
of a number of ``__init__.py`` modules in the PLIB3.IO
|
|
14
|
-
sub-packages. The docstrings and comments there give more
|
|
15
|
-
information.
|
|
16
|
-
|
|
17
|
-
One note about usage: whenever importing from a package
|
|
18
|
-
that uses this functionality, absolute imports should be
|
|
19
|
-
used. Using relative imports risks running afoul of the
|
|
20
|
-
name ambiguity between the object you want to import (a
|
|
21
|
-
class, usually) and the module it's defined in. Absolute
|
|
22
|
-
imports are "purer" anyway, so this is not viewed as a
|
|
23
|
-
bothersome restriction.
|
|
24
|
-
"""
|
|
25
|
-
|
|
26
|
-
import sys
|
|
27
|
-
import os
|
|
28
|
-
import glob
|
|
29
|
-
import types
|
|
30
|
-
|
|
31
|
-
from plib.stdlib.imp import import_from_module
|
|
32
|
-
|
|
33
|
-
__all__ = ('ModuleProxy',)
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
# Helper function for lazy loading of actual modules
|
|
37
|
-
|
|
38
|
-
def module_helper(pkgname, modname):
|
|
39
|
-
|
|
40
|
-
def f():
|
|
41
|
-
try:
|
|
42
|
-
return import_from_module(
|
|
43
|
-
'{}.{}'.format(pkgname, modname),
|
|
44
|
-
modname
|
|
45
|
-
)
|
|
46
|
-
except:
|
|
47
|
-
try:
|
|
48
|
-
# Try retrieving modname from pkgname as an attribute
|
|
49
|
-
# FIXME: why does this sometimes help? Nothing like
|
|
50
|
-
# this was needed in Python 2, and the advertised
|
|
51
|
-
# semantics of the above import_from_module call should
|
|
52
|
-
# mean that if the below works, the above should have!
|
|
53
|
-
return getattr(sys.modules.get(pkgname), modname)
|
|
54
|
-
except:
|
|
55
|
-
raise TypeError
|
|
56
|
-
|
|
57
|
-
f.__name__ = "{}_helper".format(modname)
|
|
58
|
-
return f
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
# Note that we subclass types.ModuleType in order to fool
|
|
62
|
-
# various introspection utilities (e.g., pydoc and the builtin
|
|
63
|
-
# 'help' command in the interpreter) into thinking ModuleProxy
|
|
64
|
-
# instances are actual modules. Among other things, this is
|
|
65
|
-
# necessary for the builtin 'help' command to properly display
|
|
66
|
-
# the info from the proxied module's docstring (setting the
|
|
67
|
-
# proxy's __doc__ to the actual module's __doc__ is *not*
|
|
68
|
-
# enough to do this by itself); note also that we don't
|
|
69
|
-
# override the constructor because various Python internals
|
|
70
|
-
# expect module objects to have a particular signature for
|
|
71
|
-
# the constructor; instead, we add the init_proxy method which
|
|
72
|
-
# must be called immediately on constructing the object to do
|
|
73
|
-
# the proxying magic
|
|
74
|
-
|
|
75
|
-
class ModuleProxy(types.ModuleType):
|
|
76
|
-
|
|
77
|
-
def init_proxy(self, name, path, globals_dict, locals_dict,
|
|
78
|
-
names=None, excludes=None, autodiscover=True):
|
|
79
|
-
|
|
80
|
-
self._excludes = excludes
|
|
81
|
-
|
|
82
|
-
names = dict(names or {})
|
|
83
|
-
excludes = set(excludes or [])
|
|
84
|
-
|
|
85
|
-
if autodiscover:
|
|
86
|
-
for pathname in path:
|
|
87
|
-
for filename in glob.glob(os.path.join(pathname, "*.py")):
|
|
88
|
-
modname = os.path.splitext(os.path.basename(filename))[0]
|
|
89
|
-
if not self._exclude(modname, excludes):
|
|
90
|
-
names[modname] = module_helper(name, modname)
|
|
91
|
-
|
|
92
|
-
self._names = names
|
|
93
|
-
self._realized_names = []
|
|
94
|
-
|
|
95
|
-
# Monkeypatch sys.modules with the proxy object (make
|
|
96
|
-
# sure we store a reference to the real module first!)
|
|
97
|
-
self._mod = sys.modules[name]
|
|
98
|
-
sys.modules[name] = self
|
|
99
|
-
|
|
100
|
-
# Pass-through module's docstring (this attribute name
|
|
101
|
-
# appears in both so __getattr__ below won't handle it);
|
|
102
|
-
# as noted above, this in itself isn't enough to make
|
|
103
|
-
# the 'help' builtin work right, but it is a necessary
|
|
104
|
-
# part of doing so
|
|
105
|
-
self.__doc__ = self._mod.__doc__
|
|
106
|
-
|
|
107
|
-
def __repr__(self):
|
|
108
|
-
return "<proxy of module '{}' from '{}'>".format(
|
|
109
|
-
self._mod.__name__, self._mod.__file__)
|
|
110
|
-
|
|
111
|
-
def _exclude(self, name, excludes):
|
|
112
|
-
# This allows customization of the exclusion algorithm; by
|
|
113
|
-
# default we exclude names in the excludes set and private
|
|
114
|
-
# names (ones that start with '_')
|
|
115
|
-
return (name in excludes) or name.startswith('_')
|
|
116
|
-
|
|
117
|
-
def _realize_name(self, name, value):
|
|
118
|
-
setattr(self, name, value) # so it gets retrieved without __getattr__
|
|
119
|
-
self._realized_names.append(name) # so __dir__ can see it
|
|
120
|
-
|
|
121
|
-
not_proxied = object()
|
|
122
|
-
|
|
123
|
-
def _get_name(self, name, module=None):
|
|
124
|
-
names = self.__dict__['_names']
|
|
125
|
-
result = names.get(name, self.not_proxied)
|
|
126
|
-
if result is not self.not_proxied:
|
|
127
|
-
|
|
128
|
-
if module is None:
|
|
129
|
-
# This hack allows features like 'lazy importing' to work;
|
|
130
|
-
# a callable names dict entry allows arbitrary (and possibly
|
|
131
|
-
# expensive) processing to take place only when the attribute
|
|
132
|
-
# is first accessed, instead of when the module is initially
|
|
133
|
-
# imported. Also, since we store the result in our __dict__,
|
|
134
|
-
# the expensive function won't get called again, so we are
|
|
135
|
-
# essentially memoizing the result.
|
|
136
|
-
try:
|
|
137
|
-
newresult = result()
|
|
138
|
-
except TypeError:
|
|
139
|
-
pass
|
|
140
|
-
else:
|
|
141
|
-
result = newresult
|
|
142
|
-
|
|
143
|
-
else:
|
|
144
|
-
# This can happen if the underlying sub-module was imported
|
|
145
|
-
# before we got a chance to proxy it
|
|
146
|
-
try:
|
|
147
|
-
newresult = getattr(module, name)
|
|
148
|
-
except AttributeError:
|
|
149
|
-
# It wasn't a proxied name after all (or else it isn't
|
|
150
|
-
# being proxied our way)
|
|
151
|
-
result = self.not_proxied
|
|
152
|
-
else:
|
|
153
|
-
result = newresult
|
|
154
|
-
|
|
155
|
-
if (result is not self.not_proxied) and (name in names):
|
|
156
|
-
assert name not in self._realized_names
|
|
157
|
-
self._realize_name(name, result)
|
|
158
|
-
del names[name] # no need to track it any more
|
|
159
|
-
|
|
160
|
-
return result
|
|
161
|
-
|
|
162
|
-
not_in_real_module = object()
|
|
163
|
-
|
|
164
|
-
def __getattr__(self, name):
|
|
165
|
-
# Remember we need to return the real module's attributes!
|
|
166
|
-
mod_result = getattr(self._mod, name, self.not_in_real_module)
|
|
167
|
-
if mod_result is self.not_in_real_module:
|
|
168
|
-
our_result = self._get_name(name)
|
|
169
|
-
if our_result is self.not_proxied:
|
|
170
|
-
raise AttributeError("{} object has no attribute {}".format(
|
|
171
|
-
self, name))
|
|
172
|
-
else:
|
|
173
|
-
return our_result
|
|
174
|
-
else:
|
|
175
|
-
if isinstance(mod_result, types.ModuleType):
|
|
176
|
-
# We might have gotten the underlying module for a proxied name
|
|
177
|
-
our_result = self._get_name(name, mod_result)
|
|
178
|
-
if (our_result is not self.not_proxied):
|
|
179
|
-
return our_result
|
|
180
|
-
return mod_result
|
|
181
|
-
|
|
182
|
-
def __dir__(self):
|
|
183
|
-
return sorted(dir(self._mod) + self._realized_names + list(self._names.keys()))
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env python3
|
|
2
|
-
"""
|
|
3
|
-
Sub-Package STDLIB.UTIL of Package PLIB3
|
|
4
|
-
Copyright (C) 2008-2022 by Peter A. Donis
|
|
5
|
-
|
|
6
|
-
Released under the GNU General Public License, Version 2
|
|
7
|
-
See the LICENSE and README files for more information
|
|
8
|
-
|
|
9
|
-
This sub-package exposes the ModuleProxy class by itself,
|
|
10
|
-
to ensure that importing it brings in no other objects.
|
|
11
|
-
"""
|
|
12
|
-
|
|
13
|
-
from .ModuleProxy import *
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/test/stdlib/doctests/test_stdlib_cached_property.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/test/stdlib/doctests/test_stdlib_normalize_slice.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|