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

pretty print models #314

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
68 changes: 34 additions & 34 deletions linkml_runtime/linkml_model/meta.py
Original file line number Diff line number Diff line change
Expand Up @@ -144,7 +144,7 @@ class UniqueKeyUniqueKeyName(extended_str):

Anything = Any

@dataclass
@dataclass(repr=False)
class CommonMetadata(YAMLRoot):
"""
Generic metadata shared across definitions
Expand Down Expand Up @@ -307,7 +307,7 @@ def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
super().__post_init__(**kwargs)


@dataclass
@dataclass(repr=False)
class Element(YAMLRoot):
"""
A named element in the model
Expand Down Expand Up @@ -512,7 +512,7 @@ def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
super().__post_init__(**kwargs)


@dataclass
@dataclass(repr=False)
class SchemaDefinition(Element):
"""
A collection of definitions that make up a schema or a data model.
Expand Down Expand Up @@ -619,7 +619,7 @@ def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
super().__post_init__(**kwargs)


@dataclass
@dataclass(repr=False)
class AnonymousTypeExpression(YAMLRoot):
"""
A type expression that is not a top-level named type definition. Used for nesting.
Expand Down Expand Up @@ -687,7 +687,7 @@ def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
super().__post_init__(**kwargs)


@dataclass
@dataclass(repr=False)
class TypeDefinition(Element):
"""
an element that whose instances are atomic scalar values that can be mapped to primitive types
Expand Down Expand Up @@ -782,7 +782,7 @@ def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
super().__post_init__(**kwargs)


@dataclass
@dataclass(repr=False)
class SubsetDefinition(Element):
"""
an element that can be used to group other metamodel elements
Expand All @@ -805,7 +805,7 @@ def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
super().__post_init__(**kwargs)


@dataclass
@dataclass(repr=False)
class Definition(Element):
"""
abstract base class for core metaclasses
Expand Down Expand Up @@ -854,7 +854,7 @@ def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
super().__post_init__(**kwargs)


@dataclass
@dataclass(repr=False)
class AnonymousEnumExpression(YAMLRoot):
"""
An enum_expression that is not named
Expand Down Expand Up @@ -918,7 +918,7 @@ def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
super().__post_init__(**kwargs)


@dataclass
@dataclass(repr=False)
class EnumDefinition(Definition):
"""
an element whose instances must be drawn from a specified set of permissible values
Expand Down Expand Up @@ -992,7 +992,7 @@ def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
super().__post_init__(**kwargs)


@dataclass
@dataclass(repr=False)
class MatchQuery(YAMLRoot):
"""
A query that is used on an enum expression to dynamically obtain a set of permissivle values via a query that
Expand All @@ -1018,7 +1018,7 @@ def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
super().__post_init__(**kwargs)


@dataclass
@dataclass(repr=False)
class ReachabilityQuery(YAMLRoot):
"""
A query that is used on an enum expression to dynamically obtain a set of permissible values via walking from a
Expand Down Expand Up @@ -1062,7 +1062,7 @@ def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
super().__post_init__(**kwargs)


@dataclass
@dataclass(repr=False)
class StructuredAlias(YAMLRoot):
"""
object that contains meta data about a synonym or alias including where it came from (source) and its scope
Expand Down Expand Up @@ -1254,7 +1254,7 @@ class Expression(YAMLRoot):
class_model_uri: ClassVar[URIRef] = LINKML.Expression


@dataclass
@dataclass(repr=False)
class TypeExpression(Expression):
"""
An abstract class grouping named types and anonymous type expressions
Expand Down Expand Up @@ -1322,7 +1322,7 @@ def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
super().__post_init__(**kwargs)


@dataclass
@dataclass(repr=False)
class EnumExpression(Expression):
"""
An expression that constrains the range of a slot
Expand Down Expand Up @@ -1386,7 +1386,7 @@ def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
super().__post_init__(**kwargs)


@dataclass
@dataclass(repr=False)
class AnonymousExpression(YAMLRoot):
"""
An abstract parent class for any nested expression
Expand Down Expand Up @@ -1555,7 +1555,7 @@ def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
super().__post_init__(**kwargs)


@dataclass
@dataclass(repr=False)
class PathExpression(YAMLRoot):
"""
An expression that describes an abstract path from an object to another through a sequence of slot lookups
Expand Down Expand Up @@ -1760,7 +1760,7 @@ def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
super().__post_init__(**kwargs)


@dataclass
@dataclass(repr=False)
class SlotExpression(Expression):
"""
an expression that constrains the range of values a slot can take
Expand Down Expand Up @@ -1884,7 +1884,7 @@ def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
super().__post_init__(**kwargs)


@dataclass
@dataclass(repr=False)
class AnonymousSlotExpression(AnonymousExpression):
_inherited_slots: ClassVar[List[str]] = ["range", "required", "recommended", "inlined", "inlined_as_list", "minimum_value", "maximum_value", "pattern", "structured_pattern", "value_presence", "equals_string", "equals_string_in", "equals_number", "equals_expression", "exact_cardinality", "minimum_cardinality", "maximum_cardinality"]

Expand Down Expand Up @@ -2005,7 +2005,7 @@ def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
super().__post_init__(**kwargs)


@dataclass
@dataclass(repr=False)
class SlotDefinition(Definition):
"""
an element that describes how instances are related to other instances
Expand Down Expand Up @@ -2304,7 +2304,7 @@ def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
super().__post_init__(**kwargs)


@dataclass
@dataclass(repr=False)
class ClassExpression(YAMLRoot):
"""
A boolean expression that can be used to dynamically determine membership of a class
Expand Down Expand Up @@ -2344,7 +2344,7 @@ def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
super().__post_init__(**kwargs)


@dataclass
@dataclass(repr=False)
class AnonymousClassExpression(AnonymousExpression):
_inherited_slots: ClassVar[List[str]] = []

Expand Down Expand Up @@ -2385,7 +2385,7 @@ def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
super().__post_init__(**kwargs)


@dataclass
@dataclass(repr=False)
class ClassDefinition(Definition):
"""
an element whose instances are complex objects that may have slot-value assignments
Expand Down Expand Up @@ -2520,7 +2520,7 @@ class ClassLevelRule(YAMLRoot):
class_model_uri: ClassVar[URIRef] = LINKML.ClassLevelRule


@dataclass
@dataclass(repr=False)
class ClassRule(ClassLevelRule):
"""
A rule that applies to instances of a class
Expand Down Expand Up @@ -2713,7 +2713,7 @@ def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
super().__post_init__(**kwargs)


@dataclass
@dataclass(repr=False)
class ArrayExpression(YAMLRoot):
"""
defines the dimensions of an array
Expand Down Expand Up @@ -2900,7 +2900,7 @@ def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
super().__post_init__(**kwargs)


@dataclass
@dataclass(repr=False)
class DimensionExpression(YAMLRoot):
"""
defines one of the dimensions of an array
Expand Down Expand Up @@ -3085,7 +3085,7 @@ def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
super().__post_init__(**kwargs)


@dataclass
@dataclass(repr=False)
class PatternExpression(YAMLRoot):
"""
a regular expression pattern used to evaluate conformance of a string
Expand Down Expand Up @@ -3266,7 +3266,7 @@ def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
super().__post_init__(**kwargs)


@dataclass
@dataclass(repr=False)
class ImportExpression(YAMLRoot):
"""
an expression describing an import
Expand Down Expand Up @@ -3448,7 +3448,7 @@ def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
super().__post_init__(**kwargs)


@dataclass
@dataclass(repr=False)
class Setting(YAMLRoot):
"""
assignment of a key to a value
Expand Down Expand Up @@ -3477,7 +3477,7 @@ def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
super().__post_init__(**kwargs)


@dataclass
@dataclass(repr=False)
class Prefix(YAMLRoot):
"""
prefix URI tuple
Expand Down Expand Up @@ -3506,7 +3506,7 @@ def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
super().__post_init__(**kwargs)


@dataclass
@dataclass(repr=False)
class LocalName(YAMLRoot):
"""
an attributed label
Expand Down Expand Up @@ -3535,7 +3535,7 @@ def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
super().__post_init__(**kwargs)


@dataclass
@dataclass(repr=False)
class Example(YAMLRoot):
"""
usage example and description
Expand All @@ -3561,7 +3561,7 @@ def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
super().__post_init__(**kwargs)


@dataclass
@dataclass(repr=False)
class AltDescription(YAMLRoot):
"""
an attributed description
Expand Down Expand Up @@ -3590,7 +3590,7 @@ def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
super().__post_init__(**kwargs)


@dataclass
@dataclass(repr=False)
class PermissibleValue(YAMLRoot):
"""
a permissible value, accompanied by intended text and an optional mapping to a concept URI
Expand Down Expand Up @@ -3782,7 +3782,7 @@ def __post_init__(self, *_: List[str], **kwargs: Dict[str, Any]):
super().__post_init__(**kwargs)


@dataclass
@dataclass(repr=False)
class UniqueKey(YAMLRoot):
"""
a collection of slots whose values uniquely identify an instance of a class
Expand Down
2 changes: 1 addition & 1 deletion linkml_runtime/utils/enumerations.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,4 +102,4 @@ def __str__(self) -> str:

def __repr__(self) -> str:
rlist = [(f.name, getattr(self._code, f.name)) for f in fields(self._code)]
return '(' + ', '.join([f"{f[0]}={repr(f[1])}" for f in rlist if f[1]]) + ')'
return self.__class__.__name__ + '(' + ', '.join([f"{f[0]}={repr(f[1])}" for f in rlist if f[1]]) + ')'
2 changes: 1 addition & 1 deletion linkml_runtime/utils/schemaview.py
Original file line number Diff line number Diff line change
Expand Up @@ -802,7 +802,7 @@ def slot_children(self, slot_name: SLOT_NAME, imports=True, mixins=True, is_a=Tr
:param is_a: include is_a parents (default is True)
:return: all direct child slot names (is_a and mixins)
"""
elts = [self.get_slot(x) for x in self.all_slots(imports)]
elts = [self.get_slot(x) for x in self.all_slots(imports=imports)]
return [x.name for x in elts if (x.is_a == slot_name and is_a) or (mixins and slot_name in x.mixins)]

@lru_cache(None)
Expand Down
46 changes: 45 additions & 1 deletion linkml_runtime/utils/yamlutils.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
from copy import copy
from json import JSONDecoder
from typing import Union, Any, List, Optional, Type, Callable, Dict
from pprint import pformat
import textwrap
import re

import yaml
from deprecated.classic import deprecated
Expand All @@ -10,7 +13,7 @@
from yaml.constructor import ConstructorError

from linkml_runtime.utils.context_utils import CONTEXTS_PARAM_TYPE, merge_contexts
from linkml_runtime.utils.formatutils import is_empty
from linkml_runtime.utils.formatutils import is_empty, remove_empty_items, is_list, is_dict, items

YAMLObjTypes = Union[JsonObjTypes, "YAMLRoot"]

Expand Down Expand Up @@ -277,6 +280,47 @@ def MissingRequiredField(self, field_name: str) -> None:
""" Generic loader error handler """
raise ValueError(f"{field_name} must be supplied")

def __repr__(self):
return _pformat(items(self), self.__class__.__name__)

def __str__(self):
return repr(self)

def _pformat(fields:dict, cls_name:str, indent:str = ' ') -> str:
"""
pretty format the fields of the items of a ``YAMLRoot`` object without the wonky indentation of pformat.
see ``YAMLRoot.__repr__``.

formatting is similar to black - items at similar levels of nesting have similar levels of indentation,
rather than getting placed at essentially random levels of indentation depending on what came before them.
"""
res = []
total_len = 0
for key, val in fields:
if val == [] or val == {} or val is None:
continue
# pformat handles everything else that isn't a YAMLRoot object, but it sure does look ugly
# use it to split lines and as the thing of last resort, but otherwise indent = 0, we'll do that
val_str = pformat(val, indent=0, compact=True, sort_dicts=False)
# now we indent everything except the first line by indenting and then using regex to remove just the first indent
val_str = re.sub(rf'\A{re.escape(indent)}', '', textwrap.indent(val_str, indent))
# now recombine with the key in a format that can be re-eval'd into an object if indent is just whitespace
val_str = f"'{key}': " + val_str

# count the total length of this string so we know if we need to linebreak or not later
total_len += len(val_str)
res.append(val_str)

if total_len > 80:
inside = ',\n'.join(res)
# we indent twice - once for the inner contents of every inner object, and one to
# offset from the root element. that keeps us from needing to be recursive except for the
# single pformat call
inside = textwrap.indent(inside, indent)
return cls_name + '({\n' + inside + '\n})'
else:
return cls_name + '({' + ', '.join(res) + '})'


def root_representer(dumper: yaml.Dumper, data: YAMLRoot):
""" YAML callback -- used to filter out empty values (None, {}, [] and false)
Expand Down
Loading
Loading