diff --git a/docs/code_examples/outputs/docs_ex08_converters.output b/docs/code_examples/outputs/docs_ex08_converters.output index 1d0eebb..9507f86 100644 --- a/docs/code_examples/outputs/docs_ex08_converters.output +++ b/docs/code_examples/outputs/docs_ex08_converters.output @@ -1,6 +1,8 @@ ConverterEx(unconverted='42', converted=42) Source: def __eq__(self, other): + if self is other: + return True return ( self.unconverted == other.unconverted and self.converted == other.converted diff --git a/docs/code_examples/outputs/docs_ex09_annotated.output b/docs/code_examples/outputs/docs_ex09_annotated.output index 2e1db9b..cd74465 100644 --- a/docs/code_examples/outputs/docs_ex09_annotated.output +++ b/docs/code_examples/outputs/docs_ex09_annotated.output @@ -15,6 +15,8 @@ Slots: {'x': None, 'a': None, 'b': None, 'c': None, 'd': None, 'e': None, 'f': N Source: def __eq__(self, other): + if self is other: + return True return ( self.x == other.x and self.a == other.a diff --git a/src/ducktools/classbuilder/__init__.py b/src/ducktools/classbuilder/__init__.py index c01361d..9527ce8 100644 --- a/src/ducktools/classbuilder/__init__.py +++ b/src/ducktools/classbuilder/__init__.py @@ -486,6 +486,8 @@ def eq_generator(cls, funcname="__eq__"): # fmt: off code = ( f"def {funcname}(self, other):\n" + f" if self is other:\n" + f" return True\n" f" return (\n" f" {instance_comparison}\n" f" ) if {class_comparison} else NotImplemented\n" @@ -497,20 +499,36 @@ def eq_generator(cls, funcname="__eq__"): def get_order_generator(cls, funcname, *, operator): + class_comparison = "self.__class__ is other.__class__" field_names = [ name for name, attrib in get_fields(cls).items() if attrib.compare ] - self_tuple = ", ".join(f"self.{name}" for name in field_names) - other_tuple = self_tuple.replace("self.", "other.") + # Equal objects should be False for gt/lt comparisons + eq_return = "True" if "=" in operator else "False" + + instance_comparisons = [ + ( + f" if self.{name} != other.{name}:\n" + f" return self.{name} {operator} other.{name}\n" + ) + for name in field_names + ] + instance_comparisons.append( + f" return {eq_return}" + ) + + instance_comparison = "".join(instance_comparisons) # fmt: off code = ( f"def {funcname}(self, other):\n" - f" if self.__class__ is other.__class__:\n" - f" return ({self_tuple}) {operator} ({other_tuple})\n" + f" if self is other:\n" + f" return {eq_return}\n" + f" if {class_comparison}:\n" + f"{instance_comparison}\n" f" return NotImplemented\n" ) # fmt: on @@ -1057,7 +1075,7 @@ def __init__( def __init_subclass__(cls, frozen=False, ignore_annotations=False): # Subclasses of Field can be created as if they are dataclasses - field_methods = {_field_init_maker, repr_maker, eq_maker} + field_methods = {_field_init_maker, repr_maker, eq_maker, replace_maker} if frozen or _UNDER_TESTING: field_methods |= {frozen_setattr_maker, frozen_delattr_maker} diff --git a/src/ducktools/classbuilder/prefab.py b/src/ducktools/classbuilder/prefab.py index ed00418..9bd508a 100644 --- a/src/ducktools/classbuilder/prefab.py +++ b/src/ducktools/classbuilder/prefab.py @@ -36,23 +36,33 @@ except ImportError: from types import GenericAlias, NoneType +#fmt: off from . import ( + # Constants NOTHING, FIELD_NOTHING, + + # Classes Field, MethodMaker, GatheredFields, GeneratedCode, SlotMakerMeta, - builder, get_flags, get_fields, - make_unified_gatherer, + + # Builder + builder, + + # Internals Retrieval + build_completed, + get_flags, get_fields, get_methods, + + # Method Makers eq_maker, - lt_maker, - le_maker, - gt_maker, - ge_maker, - frozen_setattr_maker, - frozen_delattr_maker, + frozen_delattr_maker, frozen_setattr_maker, + get_repr_generator, + ge_maker, gt_maker, le_maker, lt_maker, hash_maker, replace_maker, - get_repr_generator, - build_completed, + + # Gatherer + make_unified_gatherer, ) +#fmt: on from .annotations import get_func_annotations, is_type, replace_generic_with_arg