Skip to content
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

Reimplement markdown quotes for object formatting. #267

Merged
1 commit merged into from
Jan 4, 2024
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
60 changes: 35 additions & 25 deletions pyglove/core/geno/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -1593,13 +1593,17 @@ def iter_dna(self):
self._ensure_dna_spec()
return self.spec.iter_dna(self)

def format(self,
compact: bool = False,
verbose: bool = True,
root_indent: int = 0,
list_wrap_threshold: int = 80,
as_dict: bool = False,
**kwargs):
def format(
self,
compact: bool = False,
verbose: bool = True,
root_indent: int = 0,
*,
markdown: bool = False,
list_wrap_threshold: int = 80,
as_dict: bool = False,
**kwargs,
):
"""Customize format method for DNA for more compact representation."""
if as_dict and self.spec:
details = object_utils.format(
Expand All @@ -1608,25 +1612,31 @@ def format(self,
verbose,
root_indent,
**kwargs)
return f'DNA({details})'

if 'list_wrap_threshold' not in kwargs:
kwargs['list_wrap_threshold'] = list_wrap_threshold

if not verbose:
return super().format(False, verbose, root_indent, **kwargs)

if self.is_leaf:
return f'DNA({self.value!r})'
s = f'DNA({details})'
compact = False
else:
if 'list_wrap_threshold' not in kwargs:
kwargs['list_wrap_threshold'] = list_wrap_threshold

rep = object_utils.format(
self.to_json(compact=True, type_info=False),
compact, verbose, root_indent, **kwargs)
if rep and rep[0] == '(':
# NOTE(daiyip): for conditional choice from the root,
# we don't want to keep duplicate round bracket.
return f'DNA{rep}'
return f'DNA({rep})'
if not verbose:
s = super().format(False, verbose, root_indent, **kwargs)
elif self.is_leaf:
s = f'DNA({self.value!r})'
else:
rep = object_utils.format(
self.to_json(compact=True, type_info=False),
compact,
verbose,
root_indent,
**kwargs,
)
if rep and rep[0] == '(':
# NOTE(daiyip): for conditional choice from the root,
# we don't want to keep duplicate round bracket.
s = f'DNA{rep}'
else:
s = f'DNA({rep})'
return object_utils.maybe_markdown_quote(s, markdown)

def parameters(
self, use_literal_values: bool = False) -> Dict[str, str]:
Expand Down
1 change: 1 addition & 0 deletions pyglove/core/object_utils/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@
from pyglove.core.object_utils.formatting import printv as print # pylint: disable=redefined-builtin
from pyglove.core.object_utils.formatting import kvlist_str
from pyglove.core.object_utils.formatting import quote_if_str
from pyglove.core.object_utils.formatting import maybe_markdown_quote
from pyglove.core.object_utils.formatting import comma_delimited_str
from pyglove.core.object_utils.formatting import auto_plural
from pyglove.core.object_utils.formatting import message_on_path
Expand Down
22 changes: 2 additions & 20 deletions pyglove/core/object_utils/common_traits.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,31 +76,13 @@ def __str__(self) -> str:
"""Returns the full (maybe multi-line) representation of this object."""
kwargs = dict(self.__str_format_kwargs__)
kwargs.update(thread_local.thread_local_kwargs(_TLS_STR_FORMAT_KWARGS))
return self._maybe_quote(self.format(**kwargs), **kwargs)
return self.format(**kwargs)

def __repr__(self) -> str:
"""Returns a single-line representation of this object."""
kwargs = dict(self.__repr_format_kwargs__)
kwargs.update(thread_local.thread_local_kwargs(_TLS_REPR_FORMAT_KWARGS))
return self._maybe_quote(self.format(**kwargs), **kwargs)

def _maybe_quote(
self,
s: str,
*,
compact: bool = False,
root_indent: int = 0,
markdown: bool = False,
**kwargs
) -> str:
"""Maybe quote the formatted string with markdown."""
del kwargs
if not markdown or root_indent > 0:
return s
if compact:
return f'`{s}`'
else:
return f'\n```\n{s}\n```\n'
return self.format(**kwargs)


class MaybePartial(metaclass=abc.ABCMeta):
Expand Down
7 changes: 0 additions & 7 deletions pyglove/core/object_utils/common_traits_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,13 +60,6 @@ def test_formattable_with_context_managers(self):
self.assertEqual(repr(foo), 'Foo(compact=False, verbose=True)')
self.assertEqual(str(foo), 'Foo(compact=False, verbose=False)')

bar = Bar(foo)
with common_traits.repr_format(markdown=True):
self.assertEqual(repr(bar), '`Bar(foo=Foo(compact=True, verbose=True))`')
with common_traits.str_format(markdown=True):
self.assertEqual(
str(bar), '\n```\nBar(foo=Foo(compact=False, verbose=True))\n```\n')


class ExplicitlyOverrideTest(unittest.TestCase):

Expand Down
42 changes: 28 additions & 14 deletions pyglove/core/object_utils/formatting.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,31 +114,35 @@ def bracket_chars(bracket_type: BracketType) -> Tuple[str, str]:
return _BRACKET_CHARS[int(bracket_type)]


def format(value: Any, # pylint: disable=redefined-builtin
compact: bool = False,
verbose: bool = True,
root_indent: int = 0,
list_wrap_threshold: int = 80,
strip_object_id: bool = False,
include_keys: Optional[Set[str]] = None,
exclude_keys: Optional[Set[str]] = None,
**kwargs) -> str:
def format( # pylint: disable=redefined-builtin
value: Any,
compact: bool = False,
verbose: bool = True,
root_indent: int = 0,
list_wrap_threshold: int = 80,
strip_object_id: bool = False,
include_keys: Optional[Set[str]] = None,
exclude_keys: Optional[Set[str]] = None,
markdown: bool = False,
**kwargs,
) -> str:
"""Formats a (maybe) hierarchical value with flags.

Args:
value: The value to format.
compact: If True, this object will be formatted into a single line.
verbose: If True, this object will be formatted with verbosity.
Subclasses should define `verbosity` on their own.
verbose: If True, this object will be formatted with verbosity. Subclasses
should define `verbosity` on their own.
root_indent: The start indent level for this object if the output is a
multi-line string.
list_wrap_threshold: A threshold in number of characters for wrapping a
list value in a single line.
list_wrap_threshold: A threshold in number of characters for wrapping a list
value in a single line.
strip_object_id: If True, format object as '<class-name>(...)' other than
'object at <address>'.
include_keys: A set of keys to include from the top-level dict or object.
exclude_keys: A set of keys to exclude from the top-level dict or object.
Applicable only when `include_keys` is set to None.
markdown: If True, use markdown notion to quote the formatted object.
**kwargs: Keyword arguments that will be passed through unto child
``Formattable`` objects.

Expand Down Expand Up @@ -211,7 +215,17 @@ def _format_child(v):
s = [repr(value) if compact else str(value)]
if strip_object_id and 'object at 0x' in s[-1]:
s = [f'{value.__class__.__name__}(...)']
return ''.join(s)
return maybe_markdown_quote(''.join(s), markdown)


def maybe_markdown_quote(s: str, markdown: bool = True) -> str:
"""Maybe quote the formatted string with markdown."""
if not markdown:
return s
if '\n' not in s:
return f'`{s}`'
else:
return f'```\n{s}\n```'


def printv(v: Any, **kwargs):
Expand Down
19 changes: 19 additions & 0 deletions pyglove/core/object_utils/formatting_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,25 @@ def _should_include(k):
}
}"""))

def test_markdown(self):
self.assertEqual(
formatting.format([1], compact=True, markdown=True), '`[1]`'
)
self.assertEqual(
formatting.format(
[1, 2, 3], list_wrap_threshold=5, compact=False, markdown=True
),
inspect.cleandoc("""
```
[
1,
2,
3
]
```
"""),
)


if __name__ == '__main__':
unittest.main()
25 changes: 15 additions & 10 deletions pyglove/core/symbolic/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,15 +61,18 @@ def __init__(self,
self.old_value = old_value
self.new_value = new_value

def format(self,
compact: bool = False,
verbose: bool = True,
root_indent: int = 0,
*,
python_format: bool = False,
hide_default_values: bool = False,
hide_missing_values: bool = False,
**kwargs) -> str:
def format(
self,
compact: bool = False,
verbose: bool = True,
root_indent: int = 0,
*,
python_format: bool = False,
markdown: bool = False,
hide_default_values: bool = False,
hide_missing_values: bool = False,
**kwargs,
) -> str:
"""Formats this object."""
kwargs.update({
'python_format': python_format,
Expand All @@ -87,7 +90,9 @@ def format(self,
self.new_value, compact, verbose, root_indent + 1, **kwargs),
object_utils.MISSING_VALUE),
])
return f'{self.__class__.__name__}({details})'
return object_utils.maybe_markdown_quote(
f'{self.__class__.__name__}({details})', markdown
)

def __eq__(self, other: Any) -> bool:
"""Operator ==."""
Expand Down
10 changes: 7 additions & 3 deletions pyglove/core/symbolic/dict.py
Original file line number Diff line number Diff line change
Expand Up @@ -879,6 +879,7 @@ def format(
root_indent: int = 0,
*,
python_format: bool = False,
markdown: bool = False,
hide_default_values: bool = False,
hide_missing_values: bool = False,
include_keys: Optional[Set[str]] = None,
Expand All @@ -888,7 +889,8 @@ def format(
bracket_type: object_utils.BracketType = object_utils.BracketType.CURLY,
key_as_attribute: bool = False,
extra_blankline_for_field_docstr: bool = False,
**kwargs) -> str:
**kwargs,
) -> str:
"""Formats this Dict."""
cls_name = cls_name or ''
exclude_keys = exclude_keys or set()
Expand Down Expand Up @@ -926,7 +928,9 @@ def _should_include_key(key):

open_bracket, close_bracket = object_utils.bracket_chars(bracket_type)
if not field_list:
return f'{cls_name}{open_bracket}{close_bracket}'
return object_utils.maybe_markdown_quote(
f'{cls_name}{open_bracket}{close_bracket}', markdown
)

if compact:
s = [f'{cls_name}{open_bracket}']
Expand Down Expand Up @@ -984,7 +988,7 @@ def _should_include_key(key):
s.append(_indent(f'\'{k}\': {v_str}', root_indent + 1))
s.append('\n')
s.append(_indent(close_bracket, root_indent))
return ''.join(s)
return object_utils.maybe_markdown_quote(''.join(s), markdown)

def __repr__(self) -> str:
"""Operator repr()."""
Expand Down
62 changes: 35 additions & 27 deletions pyglove/core/symbolic/dict_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -2046,39 +2046,47 @@ def test_compact_exclude_keys(self):

def test_compact_python_format(self):
self.assertEqual(
self._dict.format(compact=True, python_format=True),
'{\'a1\': 1, \'a2\': {\'b1\': {\'c1\': [{\'d1\': MISSING_VALUE, '
'\'d2\': True, \'d3\': A(x=2, y=MISSING_VALUE, z={\'p\': [None, True], '
'\'q\': \'foo\', \'t\': \'foo\'})}]}}}')
self._dict.format(compact=True, python_format=True, markdown=True),
"`{'a1': 1, 'a2': {'b1': {'c1': [{'d1': MISSING_VALUE, "
"'d2': True, 'd3': A(x=2, y=MISSING_VALUE, z={'p': [None, True], "
"'q': 'foo', 't': 'foo'})}]}}}`",
)

def test_noncompact_python_format(self):
self.assertEqual(
self._dict.format(compact=False, verbose=False, python_format=True),
inspect.cleandoc("""{
'a1': 1,
'a2': {
'b1': {
'c1': [
{
'd1': MISSING_VALUE(Str()),
'd2': True,
'd3': A(
x=2,
y=MISSING_VALUE(Str()),
z={
'p': [
None,
True
],
'q': 'foo',
't': 'foo'
self._dict.format(
compact=False, verbose=False, python_format=True, markdown=True
),
inspect.cleandoc("""
```
{
'a1': 1,
'a2': {
'b1': {
'c1': [
{
'd1': MISSING_VALUE(Str()),
'd2': True,
'd3': A(
x=2,
y=MISSING_VALUE(Str()),
z={
'p': [
None,
True
],
'q': 'foo',
't': 'foo'
}
)
}
)
]
}
]
}
}
}
}"""))
```
"""),
)

def test_noncompact_nonverbose(self):
self.assertEqual(
Expand Down
8 changes: 6 additions & 2 deletions pyglove/core/symbolic/diff.py
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,9 @@ def format(
verbose=verbose,
root_indent=root_indent,
cls_name='',
bracket_type=object_utils.BracketType.SQUARE)
bracket_type=object_utils.BracketType.SQUARE,
**kwargs,
)
if self.left is self.right:
cls_name = self.left.__name__
else:
Expand All @@ -138,7 +140,9 @@ def format(
verbose=verbose,
root_indent=root_indent,
cls_name=cls_name,
bracket_type=object_utils.BracketType.ROUND)
bracket_type=object_utils.BracketType.ROUND,
**kwargs,
)


# NOTE(daiyip): we add the symbolic attribute to Diff after its declaration
Expand Down
Loading
Loading