Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
2 changes: 2 additions & 0 deletions docs/code_examples/outputs/docs_ex08_converters.output
Original file line number Diff line number Diff line change
@@ -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
Expand Down
2 changes: 2 additions & 0 deletions docs/code_examples/outputs/docs_ex09_annotated.output
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
28 changes: 23 additions & 5 deletions src/ducktools/classbuilder/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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
Expand Down Expand Up @@ -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}

Expand Down
30 changes: 20 additions & 10 deletions src/ducktools/classbuilder/prefab.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down