Skip to content

Latest commit

 

History

History
498 lines (410 loc) · 11.7 KB

README.md

File metadata and controls

498 lines (410 loc) · 11.7 KB

monotable

ASCII table with per column format specs, multi-line content, formatting directives, column width control.

Dataclass to ASCII table printer.

default branch status

CI Test readthedocs codecov

Docs | Repos | Codecov | License

Sample usage

from monotable import mono

headings = ["purchased\nparrot\nheart rate", "life\nstate"]

# > is needed to right align None cell since it auto-aligns to left.
# monotable uses empty string to format the second column.
formats = [">(none=rest).0f"]
cells = [
    [0, "demised"],
    [0.0, "passed on"],
    [None, "is no more"],
    [-1],
    [0, "ceased to be"],
]

print(
    mono(
        headings,
        formats,
        cells,
        title="Complaint\n(registered)",
        # top guideline is equals, heading is period, bottom is omitted.
        guideline_chars="=. ",
    )
)

sample output:

       Complaint
      (registered)
========================
 purchased
    parrot  life
heart rate  state
........................
         0  demised
         0  passed on
      rest  is no more
        -1
         0  ceased to be

Dataclass to ASCII Table printer

from dataclasses import dataclass, field
from enum import auto, Enum
from monotable import dataclass_print
from monotable import dataclass_format
from monotable import stow

Print a dataclass instance

Print a dataclass as an ASCII table. The field names are left justified in the left column. The values are right justified in the right column.

@dataclass
class CurrentConditions:
    temperature: float
    humidity: float
    heat_index: int

weather_data = CurrentConditions(80.0, 0.71, 83)
dataclass_print(weather_data)
CurrentConditions
-----------------
temperature  80.0
humidity     0.71
heat_index     83
-----------------

Title

The table title defaults to the class name. The string passed to the "title" keyword is prepended to the class name.

dataclass_print(weather_data, title="Airport")
Airport : CurrentConditions
-----------------
temperature  80.0
humidity     0.71
heat_index     83
-----------------

Format and print later

Call dataclass_format() to print or log later.

text = dataclass_format(weather_data, title="Airport")
print(text)
Airport : CurrentConditions
-----------------
temperature  80.0
humidity     0.71
heat_index     83
-----------------

Add a format spec to a dataclass field

Specify formatting for a data class field as shown for the field() call in place of the default value for the humidity field below.

The function stow() assigns the dict {"spec": ".0%"} to the field's metadata dict as the value for the key "monotable". The code internally applies this f-string: f"{value:{spec}}" to format the value.

@dataclass
class SpecCurrentConditions:
    temperature: float
    humidity: float = field(metadata=stow(spec=".0%"))
    heat_index: int

weather_data = SpecCurrentConditions(80.0, 0.71, 83)
dataclass_print(weather_data)
SpecCurrentConditions
-----------------
temperature  80.0
humidity      71%
heat_index     83
-----------------

Add a format function to a dataclass field

Specify a format function to do the formatting for a field.

Set the 'spec' key to a callable. The function takes the field value as the parameter and returns a string. The string is printed in the table. Note that just the enumeration name "E" is printed instead of "Direction.E".

class Direction(Enum):
    N = auto()
    E = auto()
    S = auto()
    W = auto()


@dataclass
class Wind:
    speed: int
    direction: Direction = field(metadata=stow(spec=lambda x: x.name))

wind_data = Wind(speed=11,direction=Direction.E)
dataclass_print(wind_data)
     Wind
-------------
speed      11
direction   E
-------------

Add text to embellish a field name

Set the 'help' key to add text immediately after the field name. This is printed in the table left column:

  • dataclass field name
  • 2 spaces
  • 'help' key value.
@dataclass
class MoreConditions:
    visibility: float = field(metadata=stow(help="(mi)",spec=".2f"))
    dewpoint: int = field(metadata=stow(help="(degF)"))

more_data = MoreConditions(visibility=10.00,dewpoint=71)
dataclass_print(more_data)
     MoreConditions
-----------------------
visibility  (mi)  10.00
dewpoint  (degF)     71
-----------------------

When a dataclass field value is also dataclass

An additional ASCII table is printed for each nested dataclass. The table is below and indented two spaces for each level of nesting.

@dataclass
class MoreCurrentConditions:
    temperature: float
    humidity: float
    heat_index: int
    wind: Wind = field(metadata=stow(help="(2pm)"))

more_weather_data = MoreCurrentConditions(
    80.0, 0.71, 83, Wind(11, Direction.E)
    )
dataclass_print(more_weather_data)

The class name is printed in place of the value. The value of the wind field is printed in a second table below the first and indented two spaces.

MoreCurrentConditions
-----------------
temperature  80.0
humidity     0.71
heat_index     83
wind  (2pm)  Wind
-----------------

  MoreCurrentConditions.wind  (2pm) : Wind
  -------------
  speed      11
  direction   E
  -------------

Omit printing a nested dataclass

To prevent levels of nested dataclasses from printing pass keyword parameter max_depth. 1 means just print the top level of dataclass. Note that only the classname of the wind field value is printed.

dataclass_print(more_weather_data, max_depth=1)
MoreCurrentConditions
-----------------
temperature  80.0
humidity     0.71
heat_index     83
wind  (2pm)  Wind
-----------------

Print a bordered ASCII table

dataclass_print() passes extra keyword arguments to monotable.mono(). See monotable.mono()'s documentation. Some examples are below.

dataclass_print(more_weather_data, max_depth=1, bordered=True)
MoreCurrentConditions
+-------------+------+
| temperature | 80.0 |
+-------------+------+
| humidity    | 0.71 |
+-------------+------+
| heat_index  |   83 |
+-------------+------+
| wind  (2pm) | Wind |
+-------------+------+

Print ASCII table with indent

dataclass_print(more_weather_data, max_depth=1, indent="....")
....MoreCurrentConditions
....-----------------
....temperature  80.0
....humidity     0.71
....heat_index     83
....wind  (2pm)  Wind
....-----------------

Change the column alignment

dataclass_print(more_weather_data, max_depth=1, formats=(">", "<"))
MoreCurrentConditions
-----------------
temperature  80.0
   humidity  0.71
 heat_index  83
wind  (2pm)  Wind
-----------------

Print a nested dataclass that has a callable spec

For a dataclass field value, set the monotable field metadata "spec" key to a function so that the value is printed in the top level table rather than below as a separate table.

Note- This example is coded in Python REPL style so it can be tested by the PYPI project phmutest using --replmode.

>>> from dataclasses import dataclass, field
>>> from enum import auto, Enum
>>>
>>> from monotable import dataclass_print
>>> from monotable import stow
>>>
>>> class Direction(Enum):
...     N = auto()
...     E = auto()
...     S = auto()
...     W = auto()
>>>
>>> @dataclass
... class Wind:
...     speed: int
...     direction: Direction = field(metadata=stow(spec=lambda x: x.name))
>>>
>>> @dataclass
... class WindInline:
...     temperature: float
...     humidity: float
...     heat_index: int
...     wind: Wind = field(metadata=stow(spec=str))

>>> wind = Wind(11, Direction.E)
>>> wind_inline = WindInline(80.0, 0.71, 83, wind)
>>> dataclass_print(wind_inline)
                       WindInline
-------------------------------------------------------
temperature                                        80.0
humidity                                           0.71
heat_index                                           83
wind         Wind(speed=11, direction=<Direction.E: 2>)
-------------------------------------------------------

Left align the title

Note "<" at the start of title= specifies left alignment. monotable detects alignment from the first character of the title.

>>> dataclass_print(wind_inline, title="<Left Aligned Title")
Left Aligned Title : WindInline
-------------------------------------------------------
temperature                                        80.0
humidity                                           0.71
heat_index                                           83
wind         Wind(speed=11, direction=<Direction.E: 2>)
-------------------------------------------------------

Recipe to do dataclass_print as a mixin class.

from typing import Any, Tuple

class DCPrint:
    """Mixin class for dataclass to add member function dcprint()."""

    # This should be the same signature as dataclass_print()
    # where dataclass_instance is replaced by self.
    def dcprint(
        self,
        *,
        # note- These 2 keyword args are monotable positional args.
        formats: Tuple[str, str] = ("", ">"),
        title: str = "",  # monotable title prefix
        **monotable_kwargs: Any,  # keyword args passed to monotable.mono().
    ) -> None:

        dataclass_print(
            self,
            formats=formats,
            title=title,
            **monotable_kwargs,
        )

Add DCPrint as a base class to the dataclass definition.

@dataclass
class Temperatures(DCPrint):
    high: int
    low: int

temps = Temperatures(high=77, low=60)
temps.dcprint(title="High/Low Temperature")
High/Low Temperature : Temperatures
--------
high  77
low   60
--------

Copy of 2 earlier examples in REPL for testing on Python 3.7

>>> @dataclass
... class MoreConditions:
...     visibility: float = field(metadata=stow(help="(mi)",spec=".2f"))
...     dewpoint: int = field(metadata=stow(help="(degF)"))
>>>
>>> more_data = MoreConditions(visibility=10.00,dewpoint=71)
>>> dataclass_print(more_data)
     MoreConditions
-----------------------
visibility  (mi)  10.00
dewpoint  (degF)     71
-----------------------
>>>
>>> @dataclass
... class MoreCurrentConditions:
...     temperature: float
...     humidity: float
...     heat_index: int
...     wind: Wind = field(metadata=stow(help="(2pm)"))
>>>
>>> more_weather_data = MoreCurrentConditions(
...     80.0, 0.71, 83, Wind(11, Direction.E)
...     )
>>> dataclass_print(more_weather_data)
MoreCurrentConditions
-----------------
temperature  80.0
humidity     0.71
heat_index     83
wind  (2pm)  Wind
-----------------
<BLANKLINE>
  MoreCurrentConditions.wind  (2pm) : Wind
  -------------
  speed      11
  direction   E
  -------------