mirror of
https://github.com/RustPython/RustPython.git
synced 2026-10-28 10:44:36 +08:00
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:
1 parent
0eddee500a
commit
52dd8292c4
7 files changed
+974
-344
No files matched your search
Vendored
+289
-188
@@ -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:
|
||||
|
||||
Vendored
+15
-8
@@ -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
|
||||
|
||||
|
||||
|
||||
Vendored
-1
@@ -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."""
|
||||
|
||||
Vendored
+524
-117
File diff suppressed because it is too large.
Load diff
Vendored
+146
-25
@@ -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):
|
||||
|
||||
Vendored
-4
@@ -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)
|
||||
|
||||
Vendored
-1
@@ -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
|
||||
|
||||
Reference in new issue
Block a user