• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

domdfcoding / enum_tools / 5278844857

pending completion
5278844857

push

github

domdfcoding
Bump version v0.9.0.post1 -> v0.10.0

1 of 1 new or added line in 1 file covered. (100.0%)

445 of 508 relevant lines covered (87.6%)

1.75 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

83.58
/enum_tools/custom_enums.py
1
#!/usr/bin/env python3
2
#
3
#  custom_enums.py
4
"""
2✔
5
Custom subclasses of :class:`enum.Enum` and :class:`enum.Flag`.
6
"""
7
#
8
#  Copyright (c) 2020-2021 Dominic Davis-Foster <dominic@davis-foster.co.uk>
9
#
10
#  This program is free software; you can redistribute it and/or modify
11
#  it under the terms of the GNU General Public License as published by
12
#  the Free Software Foundation; either version 3 of the License, or
13
#  (at your option) any later version.
14
#
15
#  This program is distributed in the hope that it will be useful,
16
#  but WITHOUT ANY WARRANTY; without even the implied warranty of
17
#  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18
#  GNU General Public License for more details.
19
#
20
#  You should have received a copy of the GNU General Public License
21
#  along with this program; if not, write to the Free Software
22
#  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
23
#  MA 02110-1301, USA.
24
#
25
#  Parts based on https://docs.python.org/3/library/enum.html
26
#  and https://github.com/python/cpython/pull/22221
27
#  and https://github.com/python/cpython/pull/22337
28
#  PSF License 2.0
29
#
30

31
# stdlib
32
import sys
2✔
33
from enum import Enum, Flag, IntFlag
2✔
34
from typing import Any, Iterator
2✔
35

36
__all__ = [
2✔
37
                "MemberDirEnum",
38
                "IntEnum",
39
                "StrEnum",
40
                "AutoNumberEnum",
41
                "OrderedEnum",
42
                "DuplicateFreeEnum",
43
                "IterableFlag",
44
                "IterableIntFlag",
45
                ]
46

47
if sys.version_info >= (3, 11):  # pragma: no cover
48

49
        # stdlib
50
        from enum import _high_bit
51

52
        def _power_of_two(value):
53
                # From CPython. Removed in https://github.com/python/cpython/commit/8cef9c0f92720f6810be1c74e00f611acb4b8b1e
54
                if value < 1:
55
                        return False
56
                return value == 2**_high_bit(value)
57

58
        def _decompose(flag, value):
59
                """
60
                Extract all members from the value.
61
                """
62

63
                # From CPython. Removed in https://github.com/python/cpython/pull/24215
64

65
                # _decompose is only called if the value is not named
66
                not_covered = value
67

68
                # issue29167: wrap accesses to _value2member_map_ in a list to avoid race
69
                #             conditions between iterating over it and having more pseudo-
70
                #             members added to it
71

72
                flags_to_check = []
73

74
                if value < 0:
75
                        # only check for named flags
76
                        for v, m in list(flag._value2member_map_.items()):
77
                                if m.name is not None:
78
                                        flags_to_check.append((m, v))
79
                else:
80
                        # check for named flags and powers-of-two flags
81
                        for v, m in list(flag._value2member_map_.items()):
82
                                if m.name is not None or _power_of_two(v):
83
                                        flags_to_check.append((m, v))
84

85
                members = []
86

87
                for member, member_value in flags_to_check:
88
                        if member_value and member_value & value == member_value:
89
                                members.append(member)
90
                                not_covered &= ~member_value
91

92
                if not members and value in flag._value2member_map_:
93
                        members.append(flag._value2member_map_[value])
94

95
                members.sort(key=lambda m: m._value_, reverse=True)
96

97
                if len(members) > 1 and members[0].value == value:
98
                        # we have the breakdown, don't need the value member itself
99
                        members.pop(0)
100

101
                return members, not_covered
102

103
else:  # pragma: no cover (py310+)
104

105
        # stdlib
106
        from enum import _decompose  # type: ignore
2✔
107

108

109
class MemberDirEnum(Enum):
2✔
110
        """
111
        :class:`~enum.Enum` which includes attributes as well as methods.
112

113
        This will be part of the :mod:`enum` module starting with Python 3.10.
114

115
        .. seealso:: Pull request :pull:`19219 <python/cpython>` by Angelin BOOZ, which added this to CPython.
116

117
        .. versionadded:: 0.6.0
118
        """
119

120
        def __dir__(self):
2✔
121
                return super().__dir__() + [m for m in self.__dict__ if m[0] != '_']
×
122

123

124
class IntEnum(int, Enum):
2✔
125
        """
126
        :class:`~enum.Enum` where members are also (and must be) ints.
127
        """
128

129

130
#         def __int__(self):
131
#                 return self.value
132

133
#         def __eq__(self, other):
134
#                 if int(self) == other:
135
#                         return True
136
#                 else:
137
#                         return super().__eq__(other)
138

139

140
class StrEnum(str, Enum):
2✔
141
        """
142
        :class:`~enum.Enum` where members are also (and must be) strings.
143
        """
144

145
        def __str__(self) -> str:
2✔
146
                return self.value
2✔
147

148
        def __new__(cls, *values):  # noqa: D102
2✔
149
                if len(values) > 3:
2✔
150
                        raise TypeError(f'too many arguments for str(): {values!r}')
×
151
                if len(values) == 1:
2✔
152
                        # it must be a string
153
                        if not isinstance(values[0], str):
2✔
154
                                raise TypeError(f'{values[0]!r} is not a string')
2✔
155
                if len(values) > 1:
2✔
156
                        # check that encoding argument is a string
157
                        if not isinstance(values[1], str):
2✔
158
                                raise TypeError(f'encoding must be a string, not {values[1]!r}')
2✔
159
                        if len(values) > 2:
2✔
160
                                # check that errors argument is a string
161
                                if not isinstance(values[2], str):
2✔
162
                                        raise TypeError(f'errors must be a string, not {values[2]!r}')
2✔
163
                value = str(*values)
2✔
164
                member = str.__new__(cls, value)
2✔
165
                member._value_ = value
2✔
166
                return member
2✔
167

168
        # def __repr__(self):
169
        #         return self.value
170

171
        # def __eq__(self, other):
172
        #         if str(self) == other:
173
        #                 return True
174
        #         else:
175
        #                 return super().__eq__(other)
176

177

178
class AutoNumberEnum(Enum):
2✔
179
        """
180
        :class:`~enum.Enum` that automatically assigns increasing values to members.
181
        """
182

183
        def __new__(cls, *args, **kwds) -> Any:  # noqa: D102
2✔
184
                value = len(cls.__members__) + 1
2✔
185
                obj = object.__new__(cls)
2✔
186
                obj._value_ = value
2✔
187
                return obj
2✔
188

189

190
class OrderedEnum(Enum):
2✔
191
        """
192
        :class:`~enum.Enum` that adds ordering based on the values of its members.
193
        """
194

195
        def __ge__(self, other) -> bool:
2✔
196
                if self.__class__ is other.__class__:
2✔
197
                        return self._value_ >= other._value_
2✔
198
                return NotImplemented
×
199

200
        def __gt__(self, other) -> bool:
2✔
201
                if self.__class__ is other.__class__:
2✔
202
                        return self._value_ > other._value_
2✔
203
                return NotImplemented
×
204

205
        def __le__(self, other) -> bool:
2✔
206
                if self.__class__ is other.__class__:
2✔
207
                        return self._value_ <= other._value_
2✔
208
                return NotImplemented
×
209

210
        def __lt__(self, other) -> bool:
2✔
211
                if self.__class__ is other.__class__:
2✔
212
                        return self._value_ < other._value_
2✔
213
                return NotImplemented
×
214

215

216
class DuplicateFreeEnum(Enum):
2✔
217
        """
218
        :class:`~enum.Enum` that disallows duplicated member names.
219
        """
220

221
        def __init__(self, *args) -> None:
2✔
222
                cls = self.__class__
×
223
                if any(self.value == e.value for e in cls):
×
224
                        a = self.name
×
225
                        e = cls(self.value).name
×
226
                        raise ValueError(f"aliases are not allowed in DuplicateFreeEnum:  {a!r} --> {e!r}")
×
227

228

229
class IterableFlag(Flag):
2✔
230
        """
231
        :class:`~enum.Flag` with support for iterating over members and member combinations.
232

233
        This functionality was added to Python 3.10's :mod:`enum` module in :pull:`22221 <python/cpython>`.
234

235
        .. versionadded:: 0.5.0
236
        """
237

238
        def __iter__(self) -> Iterator[Flag]:
2✔
239
                """
240
                Returns members in definition order.
241

242
                :rtype:
243

244
                .. latex:clearpage::
245
                """
246

247
                members, extra_flags = _decompose(self.__class__, self.value)
2✔
248
                return (m for m in members if m._value_ != 0)
2✔
249

250

251
class IterableIntFlag(IntFlag):
2✔
252
        """
253
        :class:`~enum.IntFlag` with support for iterating over members and member combinations.
254

255
        This functionality was added to Python 3.10's :mod:`enum` module in :pull:`22221 <python/cpython>`.
256

257
        .. versionadded:: 0.5.0
258
        """
259

260
        def __iter__(self) -> Iterator[IntFlag]:
2✔
261
                """
262
                Returns members in definition order.
263
                """
264

265
                members, extra_flags = _decompose(self.__class__, self.value)
2✔
266
                return (m for m in members if m._value_ != 0)
2✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc