Source code for ftrack_api.formatter
# :coding: utf-8
# :copyright: Copyright (c) 2014 ftrack
import os
import sys
from builtins import str
import ftrack_api.entity.base
import ftrack_api.collection
import ftrack_api.symbol
import ftrack_api.inspection
#: Useful filters to pass to :func:`format`.`
FILTER = {
"ignore_unset": (lambda entity, name, value: value is not ftrack_api.symbol.NOT_SET)
}
def _can_do_colors():
"""check if we are ( likely ) to be able to handle colors."""
if "ANSI_COLORS_DISABLED" in os.environ:
return False
if "NO_COLOR" in os.environ:
return False
if "FORCE_COLOR" in os.environ:
return True
return (
hasattr(sys.stdout, "isatty")
and sys.stdout.isatty()
and os.environ.get("TERM") != "dumb"
)
[docs]def format(
entity,
formatters=None,
attribute_filter=None,
recursive=False,
indent=0,
indent_first_line=True,
_seen=None,
):
"""Return formatted string representing *entity*.
*formatters* can be used to customise formatting of elements. It should be a
mapping with one or more of the following keys:
* header - Used to format entity type.
* label - Used to format attribute names.
Specify an *attribute_filter* to control which attributes to include. By
default all attributes are included. The *attribute_filter* should be a
callable that accepts `(entity, attribute_name, attribute_value)` and
returns True if the attribute should be included in the output. For example,
to filter out all unset values::
attribute_filter=ftrack_api.formatter.FILTER['ignore_unset']
If *recursive* is True then recurse into Collections and format each entity
present.
*indent* specifies the overall indentation in spaces of the formatted text,
whilst *indent_first_line* determines whether to apply that indent to the
first generated line.
.. warning::
Iterates over all *entity* attributes which may cause multiple queries
to the server. Turn off auto populating in the session to prevent this.
"""
# Initialise default formatters.
if formatters is None:
formatters = dict()
formatters.setdefault(
"header",
lambda text: (
"\x1b[1m\x1b[44m\x1b[97m{}\x1b[0m\033[0m".format(text)
if _can_do_colors()
else text
),
)
formatters.setdefault(
"label",
lambda text: (
"\x1b[1m\x1b[34m{}\x1b[0m\033[0m".format(text) if _can_do_colors() else text
),
)
# Determine indents.
spacer = " " * indent
if indent_first_line:
first_line_spacer = spacer
else:
first_line_spacer = ""
# Avoid infinite recursion on circular references.
if _seen is None:
_seen = set()
identifier = str(ftrack_api.inspection.identity(entity))
if identifier in _seen:
return first_line_spacer + formatters["header"](entity.entity_type) + "{...}"
_seen.add(identifier)
information = list()
information.append(first_line_spacer + formatters["header"](entity.entity_type))
for key, value in sorted(entity.items()):
if attribute_filter is not None:
if not attribute_filter(entity, key, value):
continue
child_indent = indent + len(key) + 3
if isinstance(value, ftrack_api.entity.base.Entity):
value = format(
value,
formatters=formatters,
attribute_filter=attribute_filter,
recursive=recursive,
indent=child_indent,
indent_first_line=False,
_seen=_seen.copy(),
)
if isinstance(value, ftrack_api.collection.Collection):
if recursive:
child_values = []
for index, child in enumerate(value):
child_value = format(
child,
formatters=formatters,
attribute_filter=attribute_filter,
recursive=recursive,
indent=child_indent,
indent_first_line=index != 0,
_seen=_seen.copy(),
)
child_values.append(child_value)
value = "\n".join(child_values)
information.append(spacer + " {0}: {1}".format(formatters["label"](key), value))
return "\n".join(information)