Skip to content

Commit

Permalink
Colored repr's.
Browse files Browse the repository at this point in the history
  • Loading branch information
riga committed Sep 30, 2023
1 parent c148081 commit 70863bc
Show file tree
Hide file tree
Showing 3 changed files with 135 additions and 6 deletions.
13 changes: 10 additions & 3 deletions order/models/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@

from order.types import Any, GeneratorType, Field, FieldInfo, Lazy
from order.adapters.base import AdapterModel, DataProvider
from order.util import no_value
from order.util import no_value, colored


class ModelMeta(type(BaseModel)):
Expand Down Expand Up @@ -160,11 +160,15 @@ class Model(BaseModel, metaclass=ModelMeta):
Base model for all order entities.
"""

def __repr_name__(self) -> str:
return colored(super().__repr_name__(), color="light_green")

def __repr_args__(self) -> GeneratorType:
"""
Yields all key-values pairs to be injected into the representation.
"""
yield from super().__repr_args__()
for attr, value in super().__repr_args__():
yield colored(attr, color="light_blue"), value

for attr, lazy_attr in self._lazy_attrs.items():
# skip when field was originally skipped
Expand All @@ -173,4 +177,7 @@ def __repr_args__(self) -> GeneratorType:
continue

value = getattr(self, lazy_attr)
yield attr, f"lazy({value.adapter})" if isinstance(value, AdapterModel) else value
yield (
colored(attr, color="light_blue"),
f"lazy({value.adapter})" if isinstance(value, AdapterModel) else value,
)
2 changes: 1 addition & 1 deletion order/types.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

import re
from collections.abc import KeysView, ValuesView # noqa
from typing import Any, Union, TypeVar, ClassVar, List, Tuple, Sequence, Set, Dict # noqa
from typing import Any, Union, TypeVar, ClassVar, List, Tuple, Sequence, Set, Dict, Callable # noqa
from types import GeneratorType # noqa

from typing_extensions import Annotated, _AnnotatedAlias as AnnotatedType # noqa
Expand Down
126 changes: 124 additions & 2 deletions order/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,139 @@
from __future__ import annotations


__all__ = ["no_value", "create_hash", "DotAccessProxy"]
__all__ = ["no_value", "colored", "uncolored", "create_hash", "DotAccessProxy"]


import os
import sys
import re
import random
import hashlib
from typing import Any, Callable

try:
import ipykernel
import ipykernel.iostream
except ImportError:
ipykernel = None

from order.types import Any, Callable


#: Unique object denoting *no value*.
no_value = object()


# terminal codes for colors
colors = {
"default": 39,
"black": 30,
"red": 31,
"green": 32,
"yellow": 33,
"blue": 34,
"magenta": 35,
"cyan": 36,
"light_gray": 37,
"dark_gray": 90,
"light_red": 91,
"light_green": 92,
"light_yellow": 93,
"light_blue": 94,
"light_magenta": 95,
"light_cyan": 96,
"white": 97,
}

# terminal codes for backgrounds
backgrounds = {
"default": 49,
"black": 40,
"red": 41,
"green": 42,
"yellow": 43,
"blue": 44,
"magenta": 45,
"cyan": 46,
"light_gray": 47,
"dark_gray": 100,
"light_red": 101,
"light_green": 102,
"light_yellow": 103,
"light_blue": 104,
"light_magenta": 105,
"light_cyan": 106,
"white": 107,
}

# terminal codes for styles
styles = {
"default": 0,
"bright": 1,
"dim": 2,
"underlined": 4,
"blink": 5,
"inverted": 7,
"hidden": 8,
}


def colored(msg, color=None, background=None, style=None, force=False):
"""
Return the colored version of a string *msg*. For *color*, *background* and *style* options, see
https://misc.flogisoft.com/bash/tip_colors_and_formatting. They can also be explicitely set to
``"random"`` to get a random value. Unless *force* is *True*, the *msg* string is returned
unchanged in case the output is neither a tty nor an IPython output stream.
"""
if not force:
tty = False
ipy = False

try:
tty = os.isatty(sys.stdout.fileno())
except:
pass

if not tty and ipykernel is not None:
ipy = isinstance(sys.stdout, ipykernel.iostream.OutStream)

if not tty and not ipy:
return msg

# get the color
if color == "random":
color = random.choice(list(colors.values()))
else:
color = colors.get(color, colors["default"])

# get the background
if background == "random":
background = random.choice(list(backgrounds.values()))
else:
background = backgrounds.get(background, backgrounds["default"])

# get multiple styles
if not isinstance(style, (tuple, list, set)):
style = (style,)
style_values = list(styles.values())
style = ";".join(
str(random.choice(style_values) if s == "random" else styles.get(s, styles["default"]))
for s in style
)

return f"\033[{style};{background};{color}m{msg}\033[0m"


# compiled regular expression for removing all terminal style codes
uncolor_cre = re.compile(r"(\x1B\[[0-?]*[ -/]*[@-~])")


def uncolored(s):
"""
Removes all terminal style codes from a string *s* and returns it.
"""
return uncolor_cre.sub("", s)


def create_hash(inp: Any, l: int = 10, algo: str = "sha256", to_int: bool = False) -> str | int:
"""
Takes an arbitrary input *inp* and creates a hexadecimal string hash based on an algorithm
Expand Down

0 comments on commit 70863bc

Please sign in to comment.