Skip to content

Commit

Permalink
Shorten the component str output by supressing inherited values. (#206
Browse files Browse the repository at this point in the history
)

This should make the string output cleaner, especially when there
is a deep nested tree of lots of inherited components.
  • Loading branch information
AdamHillier authored Nov 30, 2020
1 parent 92df0c0 commit 47b4b3e
Show file tree
Hide file tree
Showing 2 changed files with 56 additions and 49 deletions.
77 changes: 35 additions & 42 deletions zookeeper/core/component.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ class C:

import functools
import inspect
from typing import Any, Dict, List, Optional, Type
from typing import Any, Dict, Iterator, List, Optional, Type

from zookeeper.core import utils
from zookeeper.core.factory_registry import FACTORY_REGISTRY
Expand Down Expand Up @@ -226,9 +226,9 @@ def wrapped_fn(instance, name):

# Syntatic sugar around `__base_getattribute__`, similar to `getattr` for
# calling `__getattribute__` in the standard library.
def base_getattr(instance, name):
if utils.is_component_instance:
return instance.__class__.__base_getattribute__(instance, name)
def base_getattr(instance, name: str):
if utils.is_component_instance(instance):
return instance.__class__.__base_getattribute__(instance, name) # type: ignore
return getattr(instance, name)


Expand Down Expand Up @@ -282,25 +282,7 @@ def wrapped_fn(instance) -> List[str]:
INDENT = " " * 4


def _field_key_val_str(key: str, value: Any, color: bool, single_line: bool) -> str:
if utils.is_component_instance(value):
if single_line:
value = repr(value)
else:
value = f"\n{INDENT}".join(str(value).split("\n"))
elif callable(value):
value = "<callable>"
elif isinstance(value, str):
value = f'"{value}"'

return f"{key}={value}" if color else f"{key}={value}"


def __component_repr__(instance):
if not instance.__component_configured__:
return f"<Unconfigured component '{instance.__component_name__}' instance>"

field_strings = []
def _list_field_strings(instance, color: bool, single_line: bool) -> Iterator[str]:
for field_name, field in instance.__component_fields__.items():
try:
value = base_getattr(instance, field_name)
Expand All @@ -309,34 +291,45 @@ def __component_repr__(instance):
value = utils.missing
else:
raise e from None
field_strings.append(
_field_key_val_str(field_name, value, color=False, single_line=True)

parent_instance = next(
utils.generate_component_ancestors_with_field(instance, field_name), None
)
if parent_instance is not None:
is_inherited = base_getattr(parent_instance, field_name) == value # type: ignore
else:
is_inherited = False

joined_str = ", ".join(field_strings)
if utils.is_component_instance(value):
if is_inherited:
value = "<inherited component instance>"
elif single_line:
value = repr(value)
else:
value = f"\n{INDENT}".join(str(value).split("\n"))
elif is_inherited:
value = "<inherited value>"
elif callable(value):
value = "<callable>"
elif isinstance(value, str):
value = f'"{value}"'

return f"{instance.__class__.__name__}({joined_str})"
yield f"{field_name}={value}" if color else f"{field_name}={value}"


def __component_str__(instance):
def __component_repr__(instance):
if not instance.__component_configured__:
return f"<Unconfigured component '{instance.__component_name__}' instance>"
joined_str = ", ".join(_list_field_strings(instance, color=False, single_line=True))
return f"{instance.__class__.__name__}({joined_str})"

field_strings = []
for field_name, field in instance.__component_fields__.items():
try:
value = base_getattr(instance, field_name)
except AttributeError as e:
if field.allow_missing:
value = utils.missing
else:
raise e from None
field_strings.append(
_field_key_val_str(field_name, value, color=True, single_line=False)
)

joined_str = f",\n{INDENT}".join(field_strings)

def __component_str__(instance):
if not instance.__component_configured__:
return f"<Unconfigured component '{instance.__component_name__}' instance>"
joined_str = f",\n{INDENT}".join(
_list_field_strings(instance, color=True, single_line=False)
)
return f"{instance.__class__.__name__}(\n{INDENT}{joined_str}\n)"


Expand Down
28 changes: 21 additions & 7 deletions zookeeper/core/component_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -284,34 +284,48 @@ def test_str_and_repr():
"""

@component
class Child:
class Child1:
a: int = Field()

@component
class Child2:
a: int = Field()
b: str = Field()
c: List[float] = Field()
d: int = Field(allow_missing=True)
child_1: Child1 = ComponentField()

@component
class Parent:
b: str = Field("bar")
child: Child = ComponentField(Child)
child_1: Child1 = ComponentField(Child1)
child_2: Child2 = ComponentField(Child2)

p = Parent()

configure(p, {"child.a": 10, "b": "foo", "child.c": [1.5, -1.2]}, name="parent")
configure(
p,
{"child_1.a": 5, "child_2.a": 10, "b": "foo", "child_2.c": [1.5, -1.2]},
name="parent",
)

assert (
click.unstyle(repr(p))
== """Parent(b="foo", child=Child(a=10, b="foo", c=[1.5, -1.2], d=<missing>))"""
== """Parent(b="foo", child_1=Child1(a=5), child_2=Child2(a=10, b=<inherited value>, c=[1.5, -1.2], d=<missing>, child_1=<inherited component instance>))"""
)
assert (
click.unstyle(str(p))
== """Parent(
b="foo",
child=Child(
child_1=Child1(
a=5
),
child_2=Child2(
a=10,
b="foo",
b=<inherited value>,
c=[1.5, -1.2],
d=<missing>
d=<missing>,
child_1=<inherited component instance>
)
)"""
)
Expand Down

0 comments on commit 47b4b3e

Please sign in to comment.