category module

Classes and functions for defining categories.

Category

class category.Category(cls, bases, classdict)[source]

Bases: enum.EnumMeta

MetaClass for Categorized (not for public use).

References

enum.EnumMeta

Categorized

class category.Categorized(value)[source]

Bases: enum.Enum

Derive from this class to define a new category. e.g.:

class _BoardOption(NamedTuple):
    STR: str
    AX: Callable[[matplotlib.figure.Figure, tuple],
        matplotlib.axes.Axes]
    VERSIONS: Iterable = ALL

class BoardOption(Categorized):
    HASH = _BoardOption(STR = _("a hash"), AX = hash_board)
    SQUARES = _BoardOption(
        STR = _("squares"),
        AX = squares_board,
        VERSIONS = from_version("1.5.0"),
    )
Raises

AttributeError – Upon attempt to add, delete or change member.

The above example assumes the existence of functions named hash_board and squares_board. It creates a Category named BoardOption with two members, BoardOption.HASH and BoardOption.SQUARES, each of which has three attributes: STR, AX and VERSIONS.

>>> isinstance(BoardOption, Category)
True
>>> isinstance(BoardOption.HASH, Categorized)
True

A dropdown is a classic example of a category because different values should be available in different versions and all values typically should display differently in different languages. A member with an attribute named “VERSIONS”, will appear only for those versions. If a member has an attribute named “STR”, then that’s how that member will print (use functions from babelwrap module). For example, the following would yield a dropdown that contains only the local language translation of “a hash” in version 1.0.0, but translations of both “a hash” and “squares” in version 1.5.0 and above:

ipywidgets.Dropdown(options=BoardOption)

This will work even if the dropdown is declared before calling setvers() and setlang(). A member evaluates to False if not in the set version:

>>> setvers("1.0.0")
(1,0,0)
>>> bool(BoardOption.HASH)
True
>>> bool(BoardOption.SQUARES)
False

If a member has an attribute named “CALL”, then the value of that attribute will be invoked when that member is called. If the CALL is a tuple-class (e.g. NamedTuple), then that member is a “factory member”, and calling it will return a new Categorized with the attributes of that tuple-class (initialized with the called parameters). For example:

class _Jump(NamedTuple):
    FROM: Tuple[int, ...]
    TO: Tuple[int, ...]
    VERSIONS: Iterable = ALL
    def __str__(self) -> str:
        return _("{origin} to {destination}").format(
            origin=self.FROM, destination=self.TO
        )

class _Move(NamedTuple):
        STR: str
        CALL: Optional[Callable] = None
        VERSIONS: Iterable = ALL

class Move(Categorized):
    PASS = _Move(STR=_("Pass"))
    JUMP = _Move(STR=_("Reposition"), CALL=_Jump)

jumps = (Move.JUMP(FROM=(1,2), TO=dest) for dest in ((3,1), (3,3), (2,4)))
CurrentLegal = ctg(*jumps, name="CurentLegal", uniquify=True) | Move.PASS

In this example, the Move Category has two members, Move.PASS and Move.JUMP, both of which have STR, CALL, and VERSIONS attributes.

>>> str(Move)
'Pass and Reposition'

Move.JUMP is a factory member used in the second-to-last line to create three new instances of Categorized. They do not become members of any category until the last line which creates the CurrentLegal category from them unioned with Move.PASS. Then the members of CurrentLegal are CurrentLegal.JUMP, CurrentLegal.JUMP1, CurrentLegal.JUMP2, and CurrentLegal.PASS (the names “JUMP1” and “JUMP2” are constructed by ctg() to avoid name-collision).

>>> str(CurrentLegal)
'(1,2) to (3,1), (1,2) to (3,3), (1,2) to (2,4) and Pass'

Each of the “JUMP” members of CurrentLegal has FROM, TO, and VERSIONS attributes, but CurrentLegal.PASS has the same attributes as Move.PASS. It is the same entity, so it evaluates as == and is in both categories:

>>> CurrentLegal.PASS == Move.PASS
True
>>> CurrentLegal.PASS in Move
True
>>> Move.PASS in CurrentLegal
True
>>> CurrentLegal.JUMP in Move
False

The only difference between the entities is context:

>>> str(type(Move.PASS))
'Pass and Reposition'
>>> str(type(CurrentLegal.PASS))
'(1,2) to (3,1), (1,2) to (3,3), (1,2) to (2,4) and Pass'

Categories support set operations, so you can get a new category containing all members that are in both categories (i.e. the set intersection):

>>> str(CurrentLegal & Move)
'Pass'

…set difference:

>>> str(CurrentLegal - Move)
'(1,2) to (3,1), (1,2) to (3,3) and (1,2) to (2,4)'

…set union:

>>> str(CurrentLegal | Move)
'(1,2) to (3,1), (1,2) to (3,3), (1,2) to (2,4), Pass and Reposition'

…and set symmetric difference:

>>> str(CurrentLegal ^ Move)
'(1,2) to (3,1), (1,2) to (3,3), (1,2) to (2,4) and Reposition'

You can also test for containment of entire categories:

>>> CurrentLegal >= (Move - Move.JUMP)
True

…and for proper superset (or subset):

>>> CurrentLegal > (Move - Move.JUMP)
True

Tip

The _() function should be used in categories to designate messages that need to be translated, and that function will be applied when categories are being initialized. To permit changing language after initialization, keep the initialized messages in the original language (the one for which you have translations) by setting this before setting the categories:

def _(message: str) -> str:
    return message

… then set _() to the actual translation function after setting the categories.

References

enum.Enum

ctg()

category.ctg(*members: Iterable, name: str = 'Categorized', uniquify: bool = False) type[source]

Generate a new Category from one or more Categorized. E.g.:

ctg(Color.BLACK, Marker.CIRCLE)
Parameters
  • *members – The members for the new Category.

  • name (str) – The name of the new Category. Defaults to “Categorized”

  • uniquify (bool) – If True, name collisions will be resolved by altering member names. Useful with factory members. Defaults to False.

Returns

The new Category.

Raises

TypeError – If attempt to combine non-equal members having the same name without setting uniquify to True.

setvers()

category.setvers(name: Optional[str] = None) Tuple[Union[int, str], ...][source]

Get or set the version. E.g.:

setvers()  # to get the currenty set version
setlang("1.1.0")  # to set a version (e.g. for testing)
setlang("")  # to restore the default from pyproject.toml
Parameters

name (str) – The name of the version to set. Default to None.

Returns

The currently set version as a tuple.

parse_version()

category.parse_version(name: Optional[str] = None, min_parts: int = 3) Tuple[Union[int, str], ...][source]

Yields sortable tuples for a version name. E.g.:

>>> parse_version("1.0.1-alpha")
(1, 0, 1, 'alpha')
Parameters
  • name (str) – dot/hyphen-delimited version name

  • min_parts (int) – The minimum parts in the tuple. Default is 3.

Returns

A tuple with one member per part of the name padded with as many zeros as necessary to achieve min_parts. The numeric parts are integers, so the tuples sort correctly.

To support semantic versioning, omits any leading “v”, and appends an extra “~” part to versions with no “-”. E.g.:

>>> parse_version("v1.0.0-alpha") < parse_version("1.0")
True

from_version()

category.from_version(start: str, to: Optional[str] = None) Iterable[source]

The simple interval starting with a certain version. E.g.:

>>> from_version("1.5.0")
[(1, 5, 0, '~'),+inf)
Parameters
  • start (str) – The starting version

  • to (str) – If set, the (excluded) last version. If None, there is no end version. Default to None.

Returns

The portion.interval.Interval

ALL

category.ALL = (-inf,+inf)

A shortcut for the portion.interval.Interval that contains all (e.g. versions)