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.
Files changed (94) hide show
  1. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/CHANGES.rst +17 -0
  2. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/PKG-INFO +2 -8
  3. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/README.rst +0 -4
  4. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/__init__.py +1 -1
  5. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/_extras.py +9 -4
  6. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/builtins.py +2 -1
  7. plib3_stdlib-2.4/plib/stdlib/enumtools.py +217 -0
  8. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/test/stdlib/__main__.py +1 -0
  9. plib3_stdlib-2.4/pyproject.toml +3 -0
  10. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/setup.cfg +1 -1
  11. plib3.stdlib-2.3.1/plib/stdlib/util/ModuleProxy.py +0 -183
  12. plib3.stdlib-2.3.1/plib/stdlib/util/__init__.py +0 -13
  13. plib3.stdlib-2.3.1/pyproject.toml +0 -3
  14. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/LICENSE.txt +0 -0
  15. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/MANIFEST.in +0 -0
  16. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/TODO.rst +0 -0
  17. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/examples/pyget/pyget3.py +0 -0
  18. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/examples/pyidserver/pyidserver3.py +0 -0
  19. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/examples/scrips/scrips3.py +0 -0
  20. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/examples/tzname/tzname3.py +0 -0
  21. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/classtools.py +0 -0
  22. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/cmdline.py +0 -0
  23. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/coll/__init__.py +0 -0
  24. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/coll/_abc.py +0 -0
  25. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/coll/_bases.py +0 -0
  26. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/coll/_mixins.py +0 -0
  27. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/coll/_names.py +0 -0
  28. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/coll/_seq.py +0 -0
  29. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/coll/_tests.py +0 -0
  30. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/coll/_tuples.py +0 -0
  31. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/coll/_utils.py +0 -0
  32. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/copytools.py +0 -0
  33. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/csvtools.py +0 -0
  34. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/decotools/DelayedDecorator.py +0 -0
  35. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/decotools/IndexedGenerator.py +0 -0
  36. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/decotools/MemoizedGenerator.py +0 -0
  37. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/decotools/__init__.py +0 -0
  38. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/examples.py +0 -0
  39. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/fdtools.py +0 -0
  40. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/imp.py +0 -0
  41. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/ini/PIniFile.py +0 -0
  42. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/ini/PIniFileBase.py +0 -0
  43. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/ini/PIniFileOption.py +0 -0
  44. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/ini/PIniFileSection.py +0 -0
  45. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/ini/PIniInterface.py +0 -0
  46. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/ini/__init__.py +0 -0
  47. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/iters.py +0 -0
  48. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/jsontools.py +0 -0
  49. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/localize.py +0 -0
  50. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/mail.py +0 -0
  51. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/mathlib.py +0 -0
  52. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/net/__init__.py +0 -0
  53. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/net/client.py +0 -0
  54. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/net/transport.py +0 -0
  55. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/options.py +0 -0
  56. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/ostools.py +0 -0
  57. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/postinstall.py +0 -0
  58. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/proc.py +0 -0
  59. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/sigs.py +0 -0
  60. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/strings.py +0 -0
  61. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/systools.py +0 -0
  62. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/timer.py +0 -0
  63. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/stdlib/tztools.py +0 -0
  64. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/test/__main__.py +0 -0
  65. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/test/stdlib/ModuleProxy_testmod.py +0 -0
  66. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/test/stdlib/__init__.py +0 -0
  67. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/test/stdlib/abstract_testlib.py +0 -0
  68. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/test/stdlib/doctests/test_stdlib.txt +0 -0
  69. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/test/stdlib/doctests/test_stdlib_Singleton.txt +0 -0
  70. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/test/stdlib/doctests/test_stdlib_SortMixin.txt +0 -0
  71. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/test/stdlib/doctests/test_stdlib_abc.txt +0 -0
  72. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/test/stdlib/doctests/test_stdlib_builtins.txt +0 -0
  73. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/test/stdlib/doctests/test_stdlib_cached_property.txt +0 -0
  74. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/test/stdlib/doctests/test_stdlib_coll.txt +0 -0
  75. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/test/stdlib/doctests/test_stdlib_newlines.txt +0 -0
  76. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/test/stdlib/doctests/test_stdlib_normalize_slice.txt +0 -0
  77. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/test/stdlib/mapping_testlib.py +0 -0
  78. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/test/stdlib/test_ModuleProxy.py +0 -0
  79. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/test/stdlib/test_abstractcontainer.py +0 -0
  80. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/test/stdlib/test_abstractdict.py +0 -0
  81. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/test/stdlib/test_abstractkeyed.py +0 -0
  82. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/test/stdlib/test_abstractlist.py +0 -0
  83. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/test/stdlib/test_abstractmapping.py +0 -0
  84. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/test/stdlib/test_abstractsequence.py +0 -0
  85. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/test/stdlib/test_basecontainer.py +0 -0
  86. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/test/stdlib/test_basedict.py +0 -0
  87. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/test/stdlib/test_basekeyed.py +0 -0
  88. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/test/stdlib/test_baselist.py +0 -0
  89. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/test/stdlib/test_basemapping.py +0 -0
  90. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/test/stdlib/test_basesequence.py +0 -0
  91. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/test/stdlib/test_ostools.py +0 -0
  92. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/test/stdlib/test_sigs.py +0 -0
  93. {plib3.stdlib-2.3.1 → plib3_stdlib-2.4}/plib/test/stdlib/test_stdlib.py +0 -0
  94. {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.1
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
 
@@ -11,4 +11,4 @@ modify the Python standard library, broken out into modules and
11
11
  further sub-packages just as the standard library itself is.
12
12
  """
13
13
 
14
- __version__ = "2.3.1"
14
+ __version__ = "2.4"
@@ -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
- def prod(iterable, mul=_mul):
126
- """Return the product of all items in iterable.
127
- """
128
- return reduce(mul, iterable, 1)
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
- setattr(builtins, extra_name, getattr(_extras, extra_name))
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)
@@ -17,6 +17,7 @@ modules_with_doctests = [
17
17
  'plib.stdlib.coll',
18
18
  'plib.stdlib.coll._tuples',
19
19
  'plib.stdlib.decotools',
20
+ 'plib.stdlib.enumtools',
20
21
  'plib.stdlib.iters',
21
22
  'plib.stdlib.localize'
22
23
  ]
@@ -0,0 +1,3 @@
1
+ [build-system]
2
+ requires = ["setuputils3 >= 2.2", "setuptools >= 40.8.0", "wheel"]
3
+ build-backend = "setuputils_build"
@@ -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 *
@@ -1,3 +0,0 @@
1
- [build-system]
2
- requires = ["setuputils3 >= 2.1", "setuptools >= 40.8.0", "wheel"]
3
- build-backend = "setuputils_build"
File without changes
File without changes
File without changes