From 47b4b3ec5d165a4d20446d8679be44b6436783f3 Mon Sep 17 00:00:00 2001 From: Adam Hillier <7688302+AdamHillier@users.noreply.github.com> Date: Mon, 30 Nov 2020 14:04:51 +0000 Subject: [PATCH] Shorten the component `str` output by supressing inherited values. (#206) This should make the string output cleaner, especially when there is a deep nested tree of lots of inherited components. --- zookeeper/core/component.py | 77 +++++++++++++++----------------- zookeeper/core/component_test.py | 28 +++++++++--- 2 files changed, 56 insertions(+), 49 deletions(-) diff --git a/zookeeper/core/component.py b/zookeeper/core/component.py index d5f0f7a..b3c5ce8 100644 --- a/zookeeper/core/component.py +++ b/zookeeper/core/component.py @@ -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 @@ -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) @@ -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 = "" - 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"" - - 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) @@ -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 = "" + elif single_line: + value = repr(value) + else: + value = f"\n{INDENT}".join(str(value).split("\n")) + elif is_inherited: + value = "" + elif callable(value): + value = "" + 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"" + 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"" + 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)" diff --git a/zookeeper/core/component_test.py b/zookeeper/core/component_test.py index 412986e..b9ba3a1 100644 --- a/zookeeper/core/component_test.py +++ b/zookeeper/core/component_test.py @@ -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=))""" + == """Parent(b="foo", child_1=Child1(a=5), child_2=Child2(a=10, b=, c=[1.5, -1.2], d=, child_1=))""" ) assert ( click.unstyle(str(p)) == """Parent( b="foo", - child=Child( + child_1=Child1( + a=5 + ), + child_2=Child2( a=10, - b="foo", + b=, c=[1.5, -1.2], - d= + d=, + child_1= ) )""" )