Updated enum + file_cmp library + tests - v3.13.10 (#6606)

* Updated enum library

* Updated filecmp + file_cmp test

* Removed unneeded @unittest.expectedFailures

* Updated unexpected success in enum test

* Fixed unexpected success in test_filecmp
This commit is contained in:
Terry Tianlin Luan authored and GitHub committed 2026-01-03 08:27:18 +09:00
1 parent 0eddee500a
commit 52dd8292c4
7 files changed
+974 -344

No files matched your search

Vendored
+289 -188
View File
@@ -1,12 +1,11 @@
import sys
import builtins as bltns
from functools import partial
from types import MappingProxyType, DynamicClassAttribute
from operator import or_ as _or_
from functools import reduce
__all__ = [
'EnumType', 'EnumMeta',
'EnumType', 'EnumMeta', 'EnumDict',
'Enum', 'IntEnum', 'StrEnum', 'Flag', 'IntFlag', 'ReprEnum',
'auto', 'unique', 'property', 'verify', 'member', 'nonmember',
'FlagBoundary', 'STRICT', 'CONFORM', 'EJECT', 'KEEP',
@@ -39,7 +38,7 @@ def _is_descriptor(obj):
"""
Returns True if obj is a descriptor, False otherwise.
"""
return (
return not isinstance(obj, partial) and (
hasattr(obj, '__get__') or
hasattr(obj, '__set__') or
hasattr(obj, '__delete__')
@@ -63,8 +62,8 @@ def _is_sunder(name):
return (
len(name) > 2 and
name[0] == name[-1] == '_' and
name[1:2] != '_' and
name[-2:-1] != '_'
name[1] != '_' and
name[-2] != '_'
)
def _is_internal_class(cls_name, obj):
@@ -83,7 +82,6 @@ def _is_private(cls_name, name):
if (
len(name) > pat_len
and name.startswith(pattern)
and name[pat_len:pat_len+1] != ['_']
and (name[-1] != '_' or name[-2] != '_')
):
return True
@@ -158,7 +156,6 @@ def _dedent(text):
Like textwrap.dedent. Rewritten because we cannot import textwrap.
"""
lines = text.split('\n')
blanks = 0
for i, ch in enumerate(lines[0]):
if ch != ' ':
break
@@ -166,6 +163,11 @@ def _dedent(text):
lines[j] = l[i:]
return '\n'.join(lines)
class _not_given:
def __repr__(self):
return('<not given>')
_not_given = _not_given()
class _auto_null:
def __repr__(self):
return '_auto_null'
@@ -283,9 +285,10 @@ class _proto_member:
enum_member._sort_order_ = len(enum_class._member_names_)
if Flag is not None and issubclass(enum_class, Flag):
enum_class._flag_mask_ |= value
if _is_single_bit(value):
enum_class._singles_mask_ |= value
if isinstance(value, int):
enum_class._flag_mask_ |= value
if _is_single_bit(value):
enum_class._singles_mask_ |= value
enum_class._all_bits_ = 2 ** ((enum_class._flag_mask_).bit_length()) - 1
# If another member with the same value was already defined, the
@@ -313,72 +316,40 @@ class _proto_member:
elif (
Flag is not None
and issubclass(enum_class, Flag)
and isinstance(value, int)
and _is_single_bit(value)
):
# no other instances found, record this member in _member_names_
enum_class._member_names_.append(member_name)
# if necessary, get redirect in place and then add it to _member_map_
found_descriptor = None
descriptor_type = None
class_type = None
for base in enum_class.__mro__[1:]:
attr = base.__dict__.get(member_name)
if attr is not None:
if isinstance(attr, (property, DynamicClassAttribute)):
found_descriptor = attr
class_type = base
descriptor_type = 'enum'
break
elif _is_descriptor(attr):
found_descriptor = attr
descriptor_type = descriptor_type or 'desc'
class_type = class_type or base
continue
else:
descriptor_type = 'attr'
class_type = base
if found_descriptor:
redirect = property()
redirect.member = enum_member
redirect.__set_name__(enum_class, member_name)
if descriptor_type in ('enum','desc'):
# earlier descriptor found; copy fget, fset, fdel to this one.
redirect.fget = getattr(found_descriptor, 'fget', None)
redirect._get = getattr(found_descriptor, '__get__', None)
redirect.fset = getattr(found_descriptor, 'fset', None)
redirect._set = getattr(found_descriptor, '__set__', None)
redirect.fdel = getattr(found_descriptor, 'fdel', None)
redirect._del = getattr(found_descriptor, '__delete__', None)
redirect._attr_type = descriptor_type
redirect._cls_type = class_type
setattr(enum_class, member_name, redirect)
else:
setattr(enum_class, member_name, enum_member)
# now add to _member_map_ (even aliases)
enum_class._member_map_[member_name] = enum_member
enum_class._add_member_(member_name, enum_member)
try:
# This may fail if value is not hashable. We can't add the value
# to the map, and by-value lookups for this value will be
# linear.
enum_class._value2member_map_.setdefault(value, enum_member)
if value not in enum_class._hashable_values_:
enum_class._hashable_values_.append(value)
except TypeError:
# keep track of the value in a list so containment checks are quick
enum_class._unhashable_values_.append(value)
enum_class._unhashable_values_map_.setdefault(member_name, []).append(value)
class _EnumDict(dict):
class EnumDict(dict):
"""
Track enum member order and ensure member names are not reused.
EnumType will use the names found in self._member_names as the
enumeration member names.
"""
def __init__(self):
def __init__(self, cls_name=None):
super().__init__()
self._member_names = {} # use a dict to keep insertion order
self._member_names = {} # use a dict -- faster look-up than a list, and keeps insertion order since 3.7
self._last_values = []
self._ignore = []
self._auto_called = False
self._cls_name = cls_name
def __setitem__(self, key, value):
"""
@@ -389,23 +360,19 @@ class _EnumDict(dict):
Single underscore (sunder) names are reserved.
"""
if _is_internal_class(self._cls_name, value):
import warnings
warnings.warn(
"In 3.13 classes created inside an enum will not become a member. "
"Use the `member` decorator to keep the current behavior.",
DeprecationWarning,
stacklevel=2,
)
if _is_private(self._cls_name, key):
# also do nothing, name will be a normal attribute
if self._cls_name is not None and _is_private(self._cls_name, key):
# do nothing, name will be a normal attribute
pass
elif _is_sunder(key):
if key not in (
'_order_',
'_generate_next_value_', '_numeric_repr_', '_missing_', '_ignore_',
'_iter_member_', '_iter_member_by_value_', '_iter_member_by_def_',
):
'_add_alias_', '_add_value_alias_',
# While not in use internally, those are common for pretty
# printing and thus excluded from Enum's reservation of
# _sunder_ names
) and not key.startswith('_repr_'):
raise ValueError(
'_sunder_ names, such as %r, are reserved for future Enum use'
% (key, )
@@ -439,12 +406,17 @@ class _EnumDict(dict):
elif isinstance(value, nonmember):
# unwrap value here; it won't be processed by the below `else`
value = value.value
elif isinstance(value, partial):
import warnings
warnings.warn('functools.partial will be a method descriptor '
'in future Python versions; wrap it in '
'enum.member() if you want to preserve the '
'old behavior', FutureWarning, stacklevel=2)
elif _is_descriptor(value):
pass
# TODO: uncomment next three lines in 3.13
# elif _is_internal_class(self._cls_name, value):
# # do nothing, name will be a normal attribute
# pass
elif self._cls_name is not None and _is_internal_class(self._cls_name, value):
# do nothing, name will be a normal attribute
pass
else:
if key in self:
# enum overwriting a descriptor?
@@ -457,10 +429,11 @@ class _EnumDict(dict):
if isinstance(value, auto):
single = True
value = (value, )
if type(value) is tuple and any(isinstance(v, auto) for v in value):
if isinstance(value, tuple) and any(isinstance(v, auto) for v in value):
# insist on an actual tuple, no subclasses, in keeping with only supporting
# top-level auto() usage (not contained in any other data structure)
auto_valued = []
t = type(value)
for v in value:
if isinstance(v, auto):
non_auto_store = False
@@ -475,12 +448,21 @@ class _EnumDict(dict):
if single:
value = auto_valued[0]
else:
value = tuple(auto_valued)
try:
# accepts iterable as multiple arguments?
value = t(auto_valued)
except TypeError:
# then pass them in singly
value = t(*auto_valued)
self._member_names[key] = None
if non_auto_store:
self._last_values.append(value)
super().__setitem__(key, value)
@property
def member_names(self):
return list(self._member_names)
def update(self, members, **more_members):
try:
for name in members.keys():
@@ -491,6 +473,8 @@ class _EnumDict(dict):
for name, value in more_members.items():
self[name] = value
_EnumDict = EnumDict # keep private name for backwards compatibility
class EnumType(type):
"""
@@ -502,8 +486,7 @@ class EnumType(type):
# check that previous enum members do not exist
metacls._check_for_existing_members_(cls, bases)
# create the namespace dict
enum_dict = _EnumDict()
enum_dict._cls_name = cls
enum_dict = EnumDict(cls)
# inherit previous flags and _generate_next_value_ function
member_type, first_enum = metacls._get_mixins_(cls, bases)
if first_enum is not None:
@@ -564,7 +547,9 @@ class EnumType(type):
classdict['_member_names_'] = []
classdict['_member_map_'] = {}
classdict['_value2member_map_'] = {}
classdict['_unhashable_values_'] = []
classdict['_hashable_values_'] = [] # for comparing with non-hashable types
classdict['_unhashable_values_'] = [] # e.g. frozenset() with set()
classdict['_unhashable_values_map_'] = {}
classdict['_member_type_'] = member_type
# now set the __repr__ for the value
classdict['_value_repr_'] = metacls._find_data_repr_(cls, bases)
@@ -579,15 +564,16 @@ class EnumType(type):
classdict['_all_bits_'] = 0
classdict['_inverted_'] = None
try:
exc = None
classdict['_%s__in_progress' % cls] = True
enum_class = super().__new__(metacls, cls, bases, classdict, **kwds)
except RuntimeError as e:
# any exceptions raised by member.__new__ will get converted to a
# RuntimeError, so get that original exception back and raise it instead
exc = e.__cause__ or e
if exc is not None:
raise exc
#
classdict['_%s__in_progress' % cls] = False
delattr(enum_class, '_%s__in_progress' % cls)
except Exception as e:
# since 3.12 the note "Error calling __set_name__ on '_proto_member' instance ..."
# is tacked on to the error instead of raising a RuntimeError, so discard it
if hasattr(e, '__notes__'):
del e.__notes__
raise
# update classdict with any changes made by __init_subclass__
classdict.update(enum_class.__dict__)
#
@@ -706,7 +692,7 @@ class EnumType(type):
"""
return True
def __call__(cls, value, names=None, *values, module=None, qualname=None, type=None, start=1, boundary=None):
def __call__(cls, value, names=_not_given, *values, module=None, qualname=None, type=None, start=1, boundary=None):
"""
Either returns an existing member, or creates a new enum class.
@@ -735,18 +721,18 @@ class EnumType(type):
"""
if cls._member_map_:
# simple value lookup if members exist
if names:
if names is not _not_given:
value = (value, names) + values
return cls.__new__(cls, value)
# otherwise, functional API: we're creating a new Enum type
if names is None and type is None:
if names is _not_given and type is None:
# no body? no data-type? possibly wrong usage
raise TypeError(
f"{cls} has no members; specify `names=()` if you meant to create a new, empty, enum"
)
return cls._create_(
class_name=value,
names=names,
names=None if names is _not_given else names,
module=module,
qualname=qualname,
type=type,
@@ -760,10 +746,20 @@ class EnumType(type):
`value` is in `cls` if:
1) `value` is a member of `cls`, or
2) `value` is the value of one of the `cls`'s members.
3) `value` is a pseudo-member (flags)
"""
if isinstance(value, cls):
return True
return value in cls._value2member_map_ or value in cls._unhashable_values_
if issubclass(cls, Flag):
try:
result = cls._missing_(value)
return isinstance(result, cls)
except ValueError:
pass
return (
value in cls._unhashable_values_ # both structures are lists
or value in cls._hashable_values_
)
def __delattr__(cls, attr):
# nicer error message when someone tries to delete an attribute
@@ -1059,7 +1055,55 @@ class EnumType(type):
else:
use_args = True
return __new__, save_new, use_args
EnumMeta = EnumType
def _add_member_(cls, name, member):
# _value_ structures are not updated
if name in cls._member_map_:
if cls._member_map_[name] is not member:
raise NameError('%r is already bound: %r' % (name, cls._member_map_[name]))
return
#
# if necessary, get redirect in place and then add it to _member_map_
found_descriptor = None
descriptor_type = None
class_type = None
for base in cls.__mro__[1:]:
attr = base.__dict__.get(name)
if attr is not None:
if isinstance(attr, (property, DynamicClassAttribute)):
found_descriptor = attr
class_type = base
descriptor_type = 'enum'
break
elif _is_descriptor(attr):
found_descriptor = attr
descriptor_type = descriptor_type or 'desc'
class_type = class_type or base
continue
else:
descriptor_type = 'attr'
class_type = base
if found_descriptor:
redirect = property()
redirect.member = member
redirect.__set_name__(cls, name)
if descriptor_type in ('enum', 'desc'):
# earlier descriptor found; copy fget, fset, fdel to this one.
redirect.fget = getattr(found_descriptor, 'fget', None)
redirect._get = getattr(found_descriptor, '__get__', None)
redirect.fset = getattr(found_descriptor, 'fset', None)
redirect._set = getattr(found_descriptor, '__set__', None)
redirect.fdel = getattr(found_descriptor, 'fdel', None)
redirect._del = getattr(found_descriptor, '__delete__', None)
redirect._attr_type = descriptor_type
redirect._cls_type = class_type
setattr(cls, name, redirect)
else:
setattr(cls, name, member)
# now add to _member_map_ (even aliases)
cls._member_map_[name] = member
EnumMeta = EnumType # keep EnumMeta name for backwards compatibility
class Enum(metaclass=EnumType):
@@ -1125,12 +1169,17 @@ class Enum(metaclass=EnumType):
pass
except TypeError:
# not there, now do long search -- O(n) behavior
for member in cls._member_map_.values():
if member._value_ == value:
return member
for name, unhashable_values in cls._unhashable_values_map_.items():
if value in unhashable_values:
return cls[name]
for name, member in cls._member_map_.items():
if value == member._value_:
return cls[name]
# still not found -- verify that members exist, in-case somebody got here mistakenly
# (such as via super when trying to override __new__)
if not cls._member_map_:
if getattr(cls, '_%s__in_progress' % cls.__name__, False):
raise TypeError('do not use `super().__new__; call the appropriate __new__ directly') from None
raise TypeError("%r has no members defined" % cls)
#
# still not found -- try _missing_ hook
@@ -1168,6 +1217,34 @@ class Enum(metaclass=EnumType):
def __init__(self, *args, **kwds):
pass
def _add_alias_(self, name):
self.__class__._add_member_(name, self)
def _add_value_alias_(self, value):
cls = self.__class__
try:
if value in cls._value2member_map_:
if cls._value2member_map_[value] is not self:
raise ValueError('%r is already bound: %r' % (value, cls._value2member_map_[value]))
return
except TypeError:
# unhashable value, do long search
for m in cls._member_map_.values():
if m._value_ == value:
if m is not self:
raise ValueError('%r is already bound: %r' % (value, cls._value2member_map_[value]))
return
try:
# This may fail if value is not hashable. We can't add the value
# to the map, and by-value lookups for this value will be
# linear.
cls._value2member_map_.setdefault(value, self)
cls._hashable_values_.append(value)
except TypeError:
# keep track of the value in a list so containment checks are quick
cls._unhashable_values_.append(value)
cls._unhashable_values_map_.setdefault(self.name, []).append(value)
@staticmethod
def _generate_next_value_(name, start, count, last_values):
"""
@@ -1181,28 +1258,13 @@ class Enum(metaclass=EnumType):
if not last_values:
return start
try:
last = last_values[-1]
last_values.sort()
if last == last_values[-1]:
# no difference between old and new methods
return last + 1
else:
# trigger old method (with warning)
raise TypeError
last_value = sorted(last_values).pop()
except TypeError:
import warnings
warnings.warn(
"In 3.13 the default `auto()`/`_generate_next_value_` will require all values to be sortable and support adding +1\n"
"and the value returned will be the largest value in the enum incremented by 1",
DeprecationWarning,
stacklevel=3,
)
for v in reversed(last_values):
try:
return v + 1
except TypeError:
pass
return start
raise TypeError('unable to sort non-numeric values') from None
try:
return last_value + 1
except TypeError:
raise TypeError('unable to increment %r' % (last_value, )) from None
@classmethod
def _missing_(cls, value):
@@ -1217,14 +1279,13 @@ class Enum(metaclass=EnumType):
def __dir__(self):
"""
Returns all members and all public methods
Returns public methods and other interesting attributes.
"""
if self.__class__._member_type_ is object:
interesting = set(['__class__', '__doc__', '__eq__', '__hash__', '__module__', 'name', 'value'])
else:
interesting = set()
if self.__class__._member_type_ is not object:
interesting = set(object.__dir__(self))
for name in getattr(self, '__dict__', []):
if name[0] != '_':
if name[0] != '_' and name not in self._member_map_:
interesting.add(name)
for cls in self.__class__.mro():
for name, obj in cls.__dict__.items():
@@ -1237,7 +1298,7 @@ class Enum(metaclass=EnumType):
else:
# in case it was added by `dir(self)`
interesting.discard(name)
else:
elif name not in self._member_map_:
interesting.add(name)
names = sorted(
set(['__class__', '__doc__', '__eq__', '__hash__', '__module__'])
@@ -1525,37 +1586,50 @@ class Flag(Enum, boundary=STRICT):
def __bool__(self):
return bool(self._value_)
def _get_value(self, flag):
if isinstance(flag, self.__class__):
return flag._value_
elif self._member_type_ is not object and isinstance(flag, self._member_type_):
return flag
return NotImplemented
def __or__(self, other):
if isinstance(other, self.__class__):
other = other._value_
elif self._member_type_ is not object and isinstance(other, self._member_type_):
other = other
else:
other_value = self._get_value(other)
if other_value is NotImplemented:
return NotImplemented
for flag in self, other:
if self._get_value(flag) is None:
raise TypeError(f"'{flag}' cannot be combined with other flags with |")
value = self._value_
return self.__class__(value | other)
return self.__class__(value | other_value)
def __and__(self, other):
if isinstance(other, self.__class__):
other = other._value_
elif self._member_type_ is not object and isinstance(other, self._member_type_):
other = other
else:
other_value = self._get_value(other)
if other_value is NotImplemented:
return NotImplemented
for flag in self, other:
if self._get_value(flag) is None:
raise TypeError(f"'{flag}' cannot be combined with other flags with &")
value = self._value_
return self.__class__(value & other)
return self.__class__(value & other_value)
def __xor__(self, other):
if isinstance(other, self.__class__):
other = other._value_
elif self._member_type_ is not object and isinstance(other, self._member_type_):
other = other
else:
other_value = self._get_value(other)
if other_value is NotImplemented:
return NotImplemented
for flag in self, other:
if self._get_value(flag) is None:
raise TypeError(f"'{flag}' cannot be combined with other flags with ^")
value = self._value_
return self.__class__(value ^ other)
return self.__class__(value ^ other_value)
def __invert__(self):
if self._get_value(self) is None:
raise TypeError(f"'{self}' cannot be inverted")
if self._inverted_ is None:
if self._boundary_ in (EJECT, KEEP):
self._inverted_ = self.__class__(~self._value_)
@@ -1622,7 +1696,7 @@ def global_flag_repr(self):
cls_name = self.__class__.__name__
if self._name_ is None:
return "%s.%s(%r)" % (module, cls_name, self._value_)
if _is_single_bit(self):
if _is_single_bit(self._value_):
return '%s.%s' % (module, self._name_)
if self._boundary_ is not FlagBoundary.KEEP:
return '|'.join(['%s.%s' % (module, name) for name in self.name.split('|')])
@@ -1665,7 +1739,7 @@ def _simple_enum(etype=Enum, *, boundary=None, use_args=None):
Class decorator that converts a normal class into an :class:`Enum`. No
safety checks are done, and some advanced behavior (such as
:func:`__init_subclass__`) is not available. Enum creation can be faster
using :func:`simple_enum`.
using :func:`_simple_enum`.
>>> from enum import Enum, _simple_enum
>>> @_simple_enum(Enum)
@@ -1696,7 +1770,9 @@ def _simple_enum(etype=Enum, *, boundary=None, use_args=None):
body['_member_names_'] = member_names = []
body['_member_map_'] = member_map = {}
body['_value2member_map_'] = value2member_map = {}
body['_unhashable_values_'] = []
body['_hashable_values_'] = hashable_values = []
body['_unhashable_values_'] = unhashable_values = []
body['_unhashable_values_map_'] = {}
body['_member_type_'] = member_type = etype._member_type_
body['_value_repr_'] = etype._value_repr_
if issubclass(etype, Flag):
@@ -1743,35 +1819,42 @@ def _simple_enum(etype=Enum, *, boundary=None, use_args=None):
for name, value in attrs.items():
if isinstance(value, auto) and auto.value is _auto_null:
value = gnv(name, 1, len(member_names), gnv_last_values)
if value in value2member_map:
# an alias to an existing member
member = value2member_map[value]
redirect = property()
redirect.member = member
redirect.__set_name__(enum_class, name)
setattr(enum_class, name, redirect)
member_map[name] = member
# create basic member (possibly isolate value for alias check)
if use_args:
if not isinstance(value, tuple):
value = (value, )
member = new_member(enum_class, *value)
value = value[0]
else:
# create the member
if use_args:
if not isinstance(value, tuple):
value = (value, )
member = new_member(enum_class, *value)
value = value[0]
else:
member = new_member(enum_class)
if __new__ is None:
member._value_ = value
member = new_member(enum_class)
if __new__ is None:
member._value_ = value
# now check if alias
try:
contained = value2member_map.get(member._value_)
except TypeError:
contained = None
if member._value_ in unhashable_values or member.value in hashable_values:
for m in enum_class:
if m._value_ == member._value_:
contained = m
break
if contained is not None:
# an alias to an existing member
contained._add_alias_(name)
else:
# finish creating member
member._name_ = name
member.__objclass__ = enum_class
member.__init__(value)
redirect = property()
redirect.member = member
redirect.__set_name__(enum_class, name)
setattr(enum_class, name, redirect)
member_map[name] = member
member._sort_order_ = len(member_names)
if name not in ('name', 'value'):
setattr(enum_class, name, member)
member_map[name] = member
else:
enum_class._add_member_(name, member)
value2member_map[value] = member
hashable_values.append(value)
if _is_single_bit(value):
# not a multi-bit alias, record in _member_names_ and _flag_mask_
member_names.append(name)
@@ -1793,37 +1876,53 @@ def _simple_enum(etype=Enum, *, boundary=None, use_args=None):
if value.value is _auto_null:
value.value = gnv(name, 1, len(member_names), gnv_last_values)
value = value.value
if value in value2member_map:
# an alias to an existing member
member = value2member_map[value]
redirect = property()
redirect.member = member
redirect.__set_name__(enum_class, name)
setattr(enum_class, name, redirect)
member_map[name] = member
# create basic member (possibly isolate value for alias check)
if use_args:
if not isinstance(value, tuple):
value = (value, )
member = new_member(enum_class, *value)
value = value[0]
else:
# create the member
if use_args:
if not isinstance(value, tuple):
value = (value, )
member = new_member(enum_class, *value)
value = value[0]
else:
member = new_member(enum_class)
if __new__ is None:
member._value_ = value
member = new_member(enum_class)
if __new__ is None:
member._value_ = value
# now check if alias
try:
contained = value2member_map.get(member._value_)
except TypeError:
contained = None
if member._value_ in unhashable_values or member._value_ in hashable_values:
for m in enum_class:
if m._value_ == member._value_:
contained = m
break
if contained is not None:
# an alias to an existing member
contained._add_alias_(name)
else:
# finish creating member
member._name_ = name
member.__objclass__ = enum_class
member.__init__(value)
member._sort_order_ = len(member_names)
redirect = property()
redirect.member = member
redirect.__set_name__(enum_class, name)
setattr(enum_class, name, redirect)
member_map[name] = member
value2member_map[value] = member
if name not in ('name', 'value'):
setattr(enum_class, name, member)
member_map[name] = member
else:
enum_class._add_member_(name, member)
member_names.append(name)
gnv_last_values.append(value)
try:
# This may fail if value is not hashable. We can't add the value
# to the map, and by-value lookups for this value will be
# linear.
enum_class._value2member_map_.setdefault(value, member)
if value not in hashable_values:
hashable_values.append(value)
except TypeError:
# keep track of the value in a list so containment checks are quick
enum_class._unhashable_values_.append(value)
enum_class._unhashable_values_map_.setdefault(name, []).append(value)
if '__new__' in body:
enum_class.__new_member__ = enum_class.__new__
enum_class.__new__ = Enum.__new__
@@ -1880,7 +1979,7 @@ class verify:
if 2**i not in values:
missing.append(2**i)
elif enum_type == 'enum':
# check for powers of one
# check for missing consecutive integers
for i in range(low+1, high):
if i not in values:
missing.append(i)
@@ -1908,7 +2007,8 @@ class verify:
missed = [v for v in values if v not in member_values]
if missed:
missing_names.append(name)
missing_value |= reduce(_or_, missed)
for val in missed:
missing_value |= val
if missing_names:
if len(missing_names) == 1:
alias = 'alias %s is missing' % missing_names[0]
@@ -1957,7 +2057,8 @@ def _test_simple_enum(checked_enum, simple_enum):
+ list(simple_enum._member_map_.keys())
)
for key in set(checked_keys + simple_keys):
if key in ('__module__', '_member_map_', '_value2member_map_', '__doc__'):
if key in ('__module__', '_member_map_', '_value2member_map_', '__doc__',
'__static_attributes__', '__firstlineno__'):
# keys known to be different, or very long
continue
elif key in member_names:
+15 -8
View File
@@ -88,12 +88,15 @@ def _do_cmp(f1, f2):
class dircmp:
"""A class that manages the comparison of 2 directories.
dircmp(a, b, ignore=None, hide=None)
dircmp(a, b, ignore=None, hide=None, *, shallow=True)
A and B are directories.
IGNORE is a list of names to ignore,
defaults to DEFAULT_IGNORES.
HIDE is a list of names to hide,
defaults to [os.curdir, os.pardir].
SHALLOW specifies whether to just check the stat signature (do not read
the files).
defaults to True.
High level usage:
x = dircmp(dir1, dir2)
@@ -121,7 +124,7 @@ class dircmp:
in common_dirs.
"""
def __init__(self, a, b, ignore=None, hide=None): # Initialize
def __init__(self, a, b, ignore=None, hide=None, *, shallow=True): # Initialize
self.left = a
self.right = b
if hide is None:
@@ -132,6 +135,7 @@ class dircmp:
self.ignore = DEFAULT_IGNORES
else:
self.ignore = ignore
self.shallow = shallow
def phase0(self): # Compare everything except common subdirectories
self.left_list = _filter(os.listdir(self.left),
@@ -160,12 +164,14 @@ class dircmp:
ok = True
try:
a_stat = os.stat(a_path)
except OSError:
except (OSError, ValueError):
# See https://github.com/python/cpython/issues/122400
# for the rationale for protecting against ValueError.
# print('Can\'t stat', a_path, ':', why.args[1])
ok = False
try:
b_stat = os.stat(b_path)
except OSError:
except (OSError, ValueError):
# print('Can\'t stat', b_path, ':', why.args[1])
ok = False
@@ -184,7 +190,7 @@ class dircmp:
self.common_funny.append(x)
def phase3(self): # Find out differences between common files
xx = cmpfiles(self.left, self.right, self.common_files)
xx = cmpfiles(self.left, self.right, self.common_files, self.shallow)
self.same_files, self.diff_files, self.funny_files = xx
def phase4(self): # Find out differences between common subdirectories
@@ -196,7 +202,8 @@ class dircmp:
for x in self.common_dirs:
a_x = os.path.join(self.left, x)
b_x = os.path.join(self.right, x)
self.subdirs[x] = self.__class__(a_x, b_x, self.ignore, self.hide)
self.subdirs[x] = self.__class__(a_x, b_x, self.ignore, self.hide,
shallow=self.shallow)
def phase4_closure(self): # Recursively call phase4() on subdirectories
self.phase4()
@@ -280,12 +287,12 @@ def cmpfiles(a, b, common, shallow=True):
# Return:
# 0 for equal
# 1 for different
# 2 for funny cases (can't stat, etc.)
# 2 for funny cases (can't stat, NUL bytes, etc.)
#
def _cmp(a, b, sh, abs=abs, cmp=cmp):
try:
return not abs(cmp(a, b, sh))
except OSError:
except (OSError, ValueError):
return 2
-1
View File
@@ -995,7 +995,6 @@ class AST_Tests(unittest.TestCase):
f"identifier field can't represent '{constant[0]}' constant"):
ast.parse(constant[1], mode="eval")
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_precedence_enum(self):
class _Precedence(enum.IntEnum):
"""Precedence table that originated from python grammar."""
+524 -117
View File
File diff suppressed because it is too large. Load diff
+146 -25
View File
@@ -1,5 +1,6 @@
import filecmp
import os
import re
import shutil
import tempfile
import unittest
@@ -8,11 +9,24 @@ from test import support
from test.support import os_helper
def _create_file_shallow_equal(template_path, new_path):
"""create a file with the same size and mtime but different content."""
shutil.copy2(template_path, new_path)
with open(new_path, 'r+b') as f:
next_char = bytearray(f.read(1))
next_char[0] = (next_char[0] + 1) % 256
f.seek(0)
f.write(next_char)
shutil.copystat(template_path, new_path)
assert os.stat(new_path).st_size == os.stat(template_path).st_size
assert os.stat(new_path).st_mtime == os.stat(template_path).st_mtime
class FileCompareTestCase(unittest.TestCase):
def setUp(self):
self.name = os_helper.TESTFN
self.name_same = os_helper.TESTFN + '-same'
self.name_diff = os_helper.TESTFN + '-diff'
self.name_same_shallow = os_helper.TESTFN + '-same-shallow'
data = 'Contents of file go here.\n'
for name in [self.name, self.name_same, self.name_diff]:
with open(name, 'w', encoding="utf-8") as output:
@@ -20,12 +34,19 @@ class FileCompareTestCase(unittest.TestCase):
with open(self.name_diff, 'a+', encoding="utf-8") as output:
output.write('An extra line.\n')
for name in [self.name_same, self.name_diff]:
shutil.copystat(self.name, name)
_create_file_shallow_equal(self.name, self.name_same_shallow)
self.dir = tempfile.gettempdir()
def tearDown(self):
os.unlink(self.name)
os.unlink(self.name_same)
os.unlink(self.name_diff)
os.unlink(self.name_same_shallow)
def test_matching(self):
self.assertTrue(filecmp.cmp(self.name, self.name),
@@ -36,12 +57,17 @@ class FileCompareTestCase(unittest.TestCase):
"Comparing file to identical file fails")
self.assertTrue(filecmp.cmp(self.name, self.name_same, shallow=False),
"Comparing file to identical file fails")
self.assertTrue(filecmp.cmp(self.name, self.name_same_shallow),
"Shallow identical files should be considered equal")
def test_different(self):
self.assertFalse(filecmp.cmp(self.name, self.name_diff),
"Mismatched files compare as equal")
self.assertFalse(filecmp.cmp(self.name, self.dir),
"File and directory compare as equal")
self.assertFalse(filecmp.cmp(self.name, self.name_same_shallow,
shallow=False),
"Mismatched file to shallow identical file compares as equal")
def test_cache_clear(self):
first_compare = filecmp.cmp(self.name, self.name_same, shallow=False)
@@ -56,6 +82,8 @@ class DirCompareTestCase(unittest.TestCase):
self.dir = os.path.join(tmpdir, 'dir')
self.dir_same = os.path.join(tmpdir, 'dir-same')
self.dir_diff = os.path.join(tmpdir, 'dir-diff')
self.dir_diff_file = os.path.join(tmpdir, 'dir-diff-file')
self.dir_same_shallow = os.path.join(tmpdir, 'dir-same-shallow')
# Another dir is created under dir_same, but it has a name from the
# ignored list so it should not affect testing results.
@@ -63,7 +91,17 @@ class DirCompareTestCase(unittest.TestCase):
self.caseinsensitive = os.path.normcase('A') == os.path.normcase('a')
data = 'Contents of file go here.\n'
for dir in (self.dir, self.dir_same, self.dir_diff, self.dir_ignored):
shutil.rmtree(self.dir, True)
os.mkdir(self.dir)
subdir_path = os.path.join(self.dir, 'subdir')
os.mkdir(subdir_path)
dir_file_path = os.path.join(self.dir, "file")
with open(dir_file_path, 'w', encoding="utf-8") as output:
output.write(data)
for dir in (self.dir_same, self.dir_same_shallow,
self.dir_diff, self.dir_diff_file):
shutil.rmtree(dir, True)
os.mkdir(dir)
subdir_path = os.path.join(dir, 'subdir')
@@ -72,14 +110,25 @@ class DirCompareTestCase(unittest.TestCase):
fn = 'FiLe' # Verify case-insensitive comparison
else:
fn = 'file'
with open(os.path.join(dir, fn), 'w', encoding="utf-8") as output:
output.write(data)
file_path = os.path.join(dir, fn)
if dir is self.dir_same_shallow:
_create_file_shallow_equal(dir_file_path, file_path)
else:
shutil.copy2(dir_file_path, file_path)
with open(os.path.join(self.dir_diff, 'file2'), 'w', encoding="utf-8") as output:
output.write('An extra file.\n')
# Add different file2 with respect to dir_diff
with open(os.path.join(self.dir_diff_file, 'file2'), 'w', encoding="utf-8") as output:
output.write('Different contents.\n')
def tearDown(self):
for dir in (self.dir, self.dir_same, self.dir_diff):
for dir in (self.dir, self.dir_same, self.dir_diff,
self.dir_same_shallow, self.dir_diff_file):
shutil.rmtree(dir)
def test_default_ignores(self):
@@ -102,25 +151,65 @@ class DirCompareTestCase(unittest.TestCase):
shallow=False),
"Comparing directory to same fails")
# Add different file2
with open(os.path.join(self.dir, 'file2'), 'w', encoding="utf-8") as output:
output.write('Different contents.\n')
self.assertFalse(filecmp.cmpfiles(self.dir, self.dir_same,
self.assertFalse(filecmp.cmpfiles(self.dir, self.dir_diff_file,
['file', 'file2']) ==
(['file'], ['file2'], []),
"Comparing mismatched directories fails")
def test_cmpfiles_invalid_names(self):
# See https://github.com/python/cpython/issues/122400.
for file, desc in [
('\x00', 'NUL bytes filename'),
(__file__ + '\x00', 'filename with embedded NUL bytes'),
("\uD834\uDD1E.py", 'surrogate codes (MUSICAL SYMBOL G CLEF)'),
('a' * 1_000_000, 'very long filename'),
]:
for other_dir in [self.dir, self.dir_same, self.dir_diff]:
with self.subTest(f'cmpfiles: {desc}', other_dir=other_dir):
res = filecmp.cmpfiles(self.dir, other_dir, [file])
self.assertTupleEqual(res, ([], [], [file]))
def test_dircmp_invalid_names(self):
for bad_dir, desc in [
('\x00', 'NUL bytes dirname'),
(f'Top{os.sep}Mid\x00', 'dirname with embedded NUL bytes'),
("\uD834\uDD1E", 'surrogate codes (MUSICAL SYMBOL G CLEF)'),
('a' * 1_000_000, 'very long dirname'),
]:
d1 = filecmp.dircmp(self.dir, bad_dir)
d2 = filecmp.dircmp(bad_dir, self.dir)
for target in [
# attributes where os.listdir() raises OSError or ValueError
'left_list', 'right_list',
'left_only', 'right_only', 'common',
]:
with self.subTest(f'dircmp(ok, bad): {desc}', target=target):
with self.assertRaises((OSError, ValueError)):
getattr(d1, target)
with self.subTest(f'dircmp(bad, ok): {desc}', target=target):
with self.assertRaises((OSError, ValueError)):
getattr(d2, target)
def _assert_lists(self, actual, expected):
"""Assert that two lists are equal, up to ordering."""
self.assertEqual(sorted(actual), sorted(expected))
def test_dircmp_identical_directories(self):
self._assert_dircmp_identical_directories()
self._assert_dircmp_identical_directories(shallow=False)
def test_dircmp(self):
def test_dircmp_different_file(self):
self._assert_dircmp_different_file()
self._assert_dircmp_different_file(shallow=False)
def test_dircmp_different_directories(self):
self._assert_dircmp_different_directories()
self._assert_dircmp_different_directories(shallow=False)
def _assert_dircmp_identical_directories(self, **options):
# Check attributes for comparison of two identical directories
left_dir, right_dir = self.dir, self.dir_same
d = filecmp.dircmp(left_dir, right_dir)
d = filecmp.dircmp(left_dir, right_dir, **options)
self.assertEqual(d.left, left_dir)
self.assertEqual(d.right, right_dir)
if self.caseinsensitive:
@@ -142,9 +231,10 @@ class DirCompareTestCase(unittest.TestCase):
]
self._assert_report(d.report, expected_report)
def _assert_dircmp_different_directories(self, **options):
# Check attributes for comparison of two different directories (right)
left_dir, right_dir = self.dir, self.dir_diff
d = filecmp.dircmp(left_dir, right_dir)
d = filecmp.dircmp(left_dir, right_dir, **options)
self.assertEqual(d.left, left_dir)
self.assertEqual(d.right, right_dir)
self._assert_lists(d.left_list, ['file', 'subdir'])
@@ -164,12 +254,8 @@ class DirCompareTestCase(unittest.TestCase):
self._assert_report(d.report, expected_report)
# Check attributes for comparison of two different directories (left)
left_dir, right_dir = self.dir, self.dir_diff
shutil.move(
os.path.join(self.dir_diff, 'file2'),
os.path.join(self.dir, 'file2')
)
d = filecmp.dircmp(left_dir, right_dir)
left_dir, right_dir = self.dir_diff, self.dir
d = filecmp.dircmp(left_dir, right_dir, **options)
self.assertEqual(d.left, left_dir)
self.assertEqual(d.right, right_dir)
self._assert_lists(d.left_list, ['file', 'file2', 'subdir'])
@@ -180,27 +266,62 @@ class DirCompareTestCase(unittest.TestCase):
self.assertEqual(d.same_files, ['file'])
self.assertEqual(d.diff_files, [])
expected_report = [
"diff {} {}".format(self.dir, self.dir_diff),
"Only in {} : ['file2']".format(self.dir),
"diff {} {}".format(self.dir_diff, self.dir),
"Only in {} : ['file2']".format(self.dir_diff),
"Identical files : ['file']",
"Common subdirectories : ['subdir']",
]
self._assert_report(d.report, expected_report)
# Add different file2
with open(os.path.join(self.dir_diff, 'file2'), 'w', encoding="utf-8") as output:
output.write('Different contents.\n')
d = filecmp.dircmp(self.dir, self.dir_diff)
def _assert_dircmp_different_file(self, **options):
# A different file2
d = filecmp.dircmp(self.dir_diff, self.dir_diff_file, **options)
self.assertEqual(d.same_files, ['file'])
self.assertEqual(d.diff_files, ['file2'])
expected_report = [
"diff {} {}".format(self.dir, self.dir_diff),
"diff {} {}".format(self.dir_diff, self.dir_diff_file),
"Identical files : ['file']",
"Differing files : ['file2']",
"Common subdirectories : ['subdir']",
]
self._assert_report(d.report, expected_report)
def test_dircmp_no_shallow_different_file(self):
# A non shallow different file2
d = filecmp.dircmp(self.dir, self.dir_same_shallow, shallow=False)
self.assertEqual(d.same_files, [])
self.assertEqual(d.diff_files, ['file'])
expected_report = [
"diff {} {}".format(self.dir, self.dir_same_shallow),
"Differing files : ['file']",
"Common subdirectories : ['subdir']",
]
self._assert_report(d.report, expected_report)
def test_dircmp_shallow_same_file(self):
# A non shallow different file2
d = filecmp.dircmp(self.dir, self.dir_same_shallow)
self.assertEqual(d.same_files, ['file'])
self.assertEqual(d.diff_files, [])
expected_report = [
"diff {} {}".format(self.dir, self.dir_same_shallow),
"Identical files : ['file']",
"Common subdirectories : ['subdir']",
]
self._assert_report(d.report, expected_report)
def test_dircmp_shallow_is_keyword_only(self):
with self.assertRaisesRegex(
TypeError,
re.escape("dircmp.__init__() takes from 3 to 5 positional arguments but 6 were given"),
):
filecmp.dircmp(self.dir, self.dir_same, None, None, True)
self.assertIsInstance(
filecmp.dircmp(self.dir, self.dir_same, None, None, shallow=True),
filecmp.dircmp,
)
def test_dircmp_subdirs_type(self):
"""Check that dircmp.subdirs respects subclassing."""
class MyDirCmp(filecmp.dircmp):
-4
View File
@@ -5375,7 +5375,6 @@ class TestPreHandshakeClose(unittest.TestCase):
class TestEnumerations(unittest.TestCase):
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_tlsversion(self):
class CheckedTLSVersion(enum.IntEnum):
MINIMUM_SUPPORTED = _ssl.PROTO_MINIMUM_SUPPORTED
@@ -5387,7 +5386,6 @@ class TestEnumerations(unittest.TestCase):
MAXIMUM_SUPPORTED = _ssl.PROTO_MAXIMUM_SUPPORTED
enum._test_simple_enum(CheckedTLSVersion, TLSVersion)
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_tlscontenttype(self):
class Checked_TLSContentType(enum.IntEnum):
"""Content types (record layer)
@@ -5403,7 +5401,6 @@ class TestEnumerations(unittest.TestCase):
INNER_CONTENT_TYPE = 0x101
enum._test_simple_enum(Checked_TLSContentType, _TLSContentType)
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_tlsalerttype(self):
class Checked_TLSAlertType(enum.IntEnum):
"""Alert types for TLSContentType.ALERT messages
@@ -5446,7 +5443,6 @@ class TestEnumerations(unittest.TestCase):
NO_APPLICATION_PROTOCOL = 120
enum._test_simple_enum(Checked_TLSAlertType, _TLSAlertType)
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_tlsmessagetype(self):
class Checked_TLSMessageType(enum.IntEnum):
"""Message types (handshake protocol)
-1
View File
@@ -33,7 +33,6 @@ def mock_get_command_stdout(data):
class BaseTestUUID:
uuid = None
@unittest.expectedFailure # TODO: RUSTPYTHON
def test_safe_uuid_enum(self):
class CheckedSafeUUID(enum.Enum):
safe = 0