8000 bpo-41905: added abc.update_abstractmethods by bentheiii · Pull Request #22485 · python/cpython · GitHub
[go: up one dir, main page]

Skip to content

bpo-41905: added abc.update_abstractmethods #22485

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 21 commits into from
Oct 6, 2020
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
total_ordering now only overrides abtract methods defined in supercla…
…sses
  • Loading branch information
ben avrahami committed Oct 3, 2020
commit 74473c83f6619b3da7fc984b63d04d0a07020c09
20 changes: 13 additions & 7 deletions Lib/functools.py
Original file line number Diff line number Diff line change
Expand Up @@ -187,20 +187,26 @@ def _lt_from_ge(self, other, NotImplemented=NotImplemented):

def total_ordering(cls):
"""Class decorator that fills in missing ordering methods"""
# Find user-defined comparisons (not those inherited from object or abstract).
roots = {op for op in _convert
if (root := getattr(cls, op, None)) is not getattr(object, op, None)
and not getattr(root, '__isabstractmethod__', False)}
# Find user-defined comparisons (not those inherited from object or
# abstract).
def is_root(op):
root = getattr(cls, op, None)
if root is getattr(object, op, None):
return False
if getattr(root, '__isabstractmethod__', False):
# We only accept a root if it defined in the class itself.
return op in cls.__dict__
return True

roots = {op for op in _convert if is_root(op)}
if not roots:
raise ValueError('must define at least one ordering operation: < > <= >=')
root = max(roots) # prefer __lt__ to __le__ to __gt__ to __ge__
for opname, opfunc in _convert[root]:
if opname not in roots:
opfunc.__name__ = opname
setattr(cls, opname, opfunc)
# update the abstract methods of the class, if it is abstract
if isinstance(cls, ABCMeta):
update_abstractmethods(cls)
update_abstractmethods(cls)
return cls


Expand Down
24 changes: 23 additions & 1 deletion Lib/test/test_functools.py
Original file line number Diff line number Diff line change
Expand Up @@ -1158,16 +1158,38 @@ def test_pickle(self):
method_copy = pickle.loads(pickle.dumps(method, proto))
self.assertIs(method_copy, method)

def test_abc_forward(self):
@functools.total_ordering
class A(abc.ABC):
@abc.abstractmethod
def __lt__(self, other):
pass

self.assertLess({'__lt__', '__le__', '__gt__', '__ge__'}, A.__dict__.keys())
self.assertTrue(inspect.isabstract(A))
self.assertEqual(A.__abstractmethods__, {'__lt__'})

class Inf(A):
def __lt__(self, other):
return False

self.assertFalse(inspect.isabstract(Inf))
self.assertTrue(Inf().__gt__(None))

def test_abc_impl(self):
class A(abc.ABC):
@abc.abstractmethod
def __gt__(self, other):
pass

@abc.abstractmethod
def __lt__(self, other):
pass

@functools.total_ordering
class B(A):
def __lt__(self, other):
return True
B = functools.total_ordering(B)
B()
self.assertFalse(inspect.isabstract(B))

Expand Down
0