Metadata-Version: 2.4
Name: cs-ascii_art
Version: 20260912
Summary: Utilities to assist with ASCII art such as railroad diagrams; since these use Unicode box drawing characters and are better for diagrams such as railroad diagrams, this is neither ASCII nor art.
Keywords: python3
Author-email: Cameron Simpson <cs@cskk.id.au>
Description-Content-Type: text/markdown
Classifier: Programming Language :: Python
Classifier: Programming Language :: Python :: 3
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Operating System :: OS Independent
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
Requires-Dist: cs.context>=20250528
Requires-Dist: cs.deco>=20260912
Project-URL: MonoRepo Commits, https://bitbucket.org/cameron_simpson/css/commits/branch/main
Project-URL: Monorepo Git Mirror, https://github.com/cameron-simpson/css
Project-URL: Monorepo Hg/Mercurial Mirror, https://hg.sr.ht/~cameron-simpson/css
Project-URL: Source, https://github.com/cameron-simpson/css/blob/main/lib/python/cs/ascii_art.py

Utilities to assist with ASCII art such as railroad diagrams;
since these use Unicode box drawing characters and are better
for diagrams such as railroad diagrams, this is neither ASCII
nor art.

*Latest release 20260912*:
* _RailRoadMulti and subclasses: present the interior contents via __getitem__.
* Define the Morse Code diagram and add it to the demo.
* _RailRoadAround, _RailRoadMulti and subclasses: automatically promote self.content via RRBase.promote.
* Various small fixes.

This is still pretty alpha.

The current demo mode runs:

    rrprint(
        RR_START,
        RRRepeat("repeat me"),
        "2 lines\naaaa",
        (
            "one",
            "two",
            "three",
            "one\n"
            "two\n"
            "three\n"
            "four\n"
            "five",
            ["a", "sequence"],
        ),
        RR_END,
    )

which prints:

                                  ╭|one|──────────╮
                                  ├|two|──────────┤
                                  ├|three|────────┤
                       ╭───────╮  │╭─────╮        │
                       │2 lines│  ││one  │        │
    ├┼──┬|repeat me|┬──┤aaaa   ├──┤│two  │        ├──┼┤
        ╰─────←─────╯  ╰───────╯  ├┤three├────────┤
                                  ││four │        │
                                  ││five │        │
                                  │╰─────╯        │
                                  ╰|a|──|sequence|╯



Short summary:


* `box_char`: Return the Unicode entity for a box drawing glyph with the specified line weight and lines.


* `box_char_name`: Compute the Unicode entity name for a box drawing glyph with the specified line weight and lines.


* `Cell`: A representation of a character cell for a hypothetical future "drawing on a canvas" mode.


* `render`: A decorator to wrap rendering methods with the prevailing render context, as updated by any keyword arguments supplied to the method.


* `render_mode`: A context manager to temporarily apply `render_kw` to the rendering mode context `ctx` (default from `RRBase.render_context`). Yields the render context.


* `RR_END`: A bare text based symbol like `RR_START` or `RR_END`.


* `rr_morse`: RRChoice(content: list[cs.ascii_art.RRBase] = <factory>).


* `RR_START`: A bare text based symbol like `RR_START` or `RR_END`.


* `RRBase`: The abstract base class for various boxes.


* `RRChoice`: RRChoice(content: list[cs.ascii_art.RRBase] = <factory>).


* `RRMerge`: RRMerge multiple inputs into a single output.


* `RROptional`: RROptional(content: cs.ascii_art.RRBase, above: bool = False, middle: str = '→').


* `rrprint`: Promote the arguments to `RRBase` instances, make into an `RRSequence` and print it.


* `RRRepeat`: RRRepeat(content: cs.ascii_art.RRBase, above: bool = False, middle: str = '←').


* `RRSequence`: A railroad sequence.


* `RRSplit`: RRSplit a single input into multiple outputs.


* `RRStack`: An unadorned vertical stack of the content.


* `RRTextBox`: A text box with borders.


* `Symbol`: A bare text based symbol like `RR_START` or `RR_END`.


* `Terminal`: Like a symbol, but with a marker either side.


* `test_railroad`: Exercise various boxes.

## Symbols

```

```
A bare text based symbol like `RR_START` or `RR_END`.
```


RR_END = Symbol:'┼┤'

```
A bare text based symbol like `RR_START` or `RR_END`.
```


RR_START = Symbol:'├┼'

```
Return the Unicode entity for a box drawing glyph with
the specified line weight and lines.

Parameters:
* `arc`: return an arc character instead of a rectangluar box corner
* `ascii`: use ASCII characters rather than Unicode box drawing characters
* `heavy`: the line wieght: `HEAVY` for `True`, `LIGHT` for `False`
* `up`: with a line upward from the centre
* `down`: with a line downward from the centre
* `left`: with a line leftward from the centre
* `right`: with a line rightward from the centre
```


box_char = <functools._lru_cache_wrapper object at 0x109626fb0>

```
RRChoice(content: list[cs.ascii_art.RRBase] = <factory>)
```


rr_morse = RRChoice(content=[RRSequence(content=[Terminal(name='o E', refobj=None, height=1), RRSplit(content=[RRSequence(content=[Terminal(name='o I', refobj=None, height=1), RRSplit(content=[RRSequence(content=[Terminal(name='o S', refobj=None, height=1), RRSplit(content=[Terminal(name='o H', refobj=None, height=1), Terminal(name='- V', refobj=None, height=1)])]), RRSequence(content=[Terminal(name='- U', refobj=None, height=1), Terminal(name='o F', refobj=None, height=1)])])]), RRSequence(content=[Terminal(name='- A', refobj=None, height=1), RRSplit(content=[RRSequence(content=[Terminal(name='o R', refobj=None, height=1), Terminal(name='- L', refobj=None, height=1)]), RRSequence(content=[Terminal(name='- W', refobj=None, height=1), RRSplit(content=[Terminal(name='o P', refobj=None, height=1), Terminal(name='- J', refobj=None, height=1)])])])])])]), RRSequence(content=[Terminal(name='- T', refobj=None, height=1), RRSplit(content=[RRSequence(content=[Terminal(name='o N', refobj=None, height=1), RRSplit(content=[RRSequence(content=[Terminal(name='o D', refobj=None, height=1), RRSplit(content=[Terminal(name='o B', refobj=None, height=1), Terminal(name='- X', refobj=None, height=1)])]), RRSequence(content=[Terminal(name='- K', refobj=None, height=1), RRSplit(content=[Terminal(name='o C', refobj=None, height=1), Terminal(name='- Y', refobj=None, height=1)])])])]), RRSequence(content=[Terminal(name='- M', refobj=None, height=1), RRSplit(content=[RRSequence(content=[Terminal(name='o G', refobj=None, height=1), RRSplit(content=[Terminal(name='o Z', refobj=None, height=1), Terminal(name='- Q', refobj=None, height=1)])]), Terminal(name='- O', refobj=None, height=1)])])])])])

```

## Functions

### box_char_name(heavy=False, arc=False, up=False, down=False, left=False, right=False)

Compute the Unicode entity name for a box drawing glyph with
the specified line weight and lines.

See: https://www.unicode.org/charts/nameslist/n_2500.html

Parameters:
* `arc`: return an arc character instead of a rectangluar box corner
* `heavy`: the line wieght: `HEAVY` for `True`, `LIGHT` for `False`
* `up`: with a line upward from the centre
* `down`: with a line downward from the centre
* `left`: with a line leftward from the centre
* `right`: with a line rightward from the centre

### render(*da, **dkw)

A decorator to wrap rendering methods with the prevailing
render context, as updated by any keyword arguments supplied
to the method.

The decorator may be suplied with its own defaults; these
will be applied to the render context first, then whatever
render keyword arguments as supplied to the method.

The decorated method accepts an optional `ctx` parameter
to use as the render context; the default comes from
`self.render_context` which is normally an attribute of
`self.__class__`.

The wrapped method is called with a keyword argument for every
attribute of the updated render context.
The render context is returned to its previous state on return
from the method.

This means that the render methods have an unusual signature,
for example:

    @render(heavy=True)
    def renderlines(self, *, heavy, attach_w, attach_e, **_):

This:
- enumerates only the keyword parameters of interest to the method
- "default" values should not be supplied as `None` in the
  method definition
- has a `_` to collect any uninteresting render parameters

The interesting parameters should arrive prefilled and also
do not need to be passed into interior calls to render methods
because the render context has these values. Only values
different from the render context need provision.

### render_mode(ctx=None, **render_kw)

A context manager to temporarily apply `render_kw` to the
rendering mode context `ctx` (default from `RRBase.render_context`).
Yields the render context.

### rrprint(*seq, sep='')

Promote the arguments to `RRBase` instances, make into an
`RRSequence` and print it.

### test_railroad()

Exercise various boxes.

## Classes

### class Cell

A representation of a character cell for a hypothetical future
"drawing on a canvas" mode.

####`Cell.__annotations__`

dict() -> new empty dictionary
dict(mapping) -> new dictionary initialized from a mapping object's
    (key, value) pairs
dict(iterable) -> new dictionary initialized as if via:
    d = {}
    for k, v in iterable:
        d[k] = v
dict(**kwargs) -> new dictionary initialized with the name=value pairs
    in the keyword argument list.  For example:  dict(one=1, two=2)

####`Cell.__dataclass_fields__`

dict() -> new empty dictionary
dict(mapping) -> new dictionary initialized from a mapping object's
    (key, value) pairs
dict(iterable) -> new dictionary initialized as if via:
    d = {}
    for k, v in iterable:
        d[k] = v
dict(**kwargs) -> new dictionary initialized with the name=value pairs
    in the keyword argument list.  For example:  dict(one=1, two=2)

####`Cell.__firstlineno__`

int([x]) -> integer
int(x, base=10) -> integer

Convert a number or string to an integer, or return 0 if no arguments
are given.  If x is a number, return x.__int__().  For floating-point
numbers, this truncates towards zero.

If x is not a number or if base is given, then x must be a string,
bytes, or bytearray instance representing an integer literal in the
given base.  The literal can be preceded by '+' or '-' and be surrounded
by whitespace.  The base defaults to 10.  Valid bases are 0 and 2-36.
Base 0 means to interpret the base from the string as an integer literal.
>>> int('0b100', base=0)
4

####`Cell.__hash__`

The type of the None singleton.

####`Cell.__match_args__`

Built-in immutable sequence.

If no argument is given, the constructor returns an empty tuple.
If iterable is specified the tuple is initialized from iterable's items.

If the argument is a tuple, the return value is the same object.

####`Cell.__slots__`

Built-in immutable sequence.

If no argument is given, the constructor returns an empty tuple.
If iterable is specified the tuple is initialized from iterable's items.

If the argument is a tuple, the return value is the same object.

####`Cell.__static_attributes__`

Built-in immutable sequence.

If no argument is given, the constructor returns an empty tuple.
If iterable is specified the tuple is initialized from iterable's items.

If the argument is a tuple, the return value is the same object.

### class RRBase(cs.deco.Promotable)

The abstract base class for various boxes.

####`RRBase.__dataclass_fields__`

dict() -> new empty dictionary
dict(mapping) -> new dictionary initialized from a mapping object's
    (key, value) pairs
dict(iterable) -> new dictionary initialized as if via:
    d = {}
    for k, v in iterable:
        d[k] = v
dict(**kwargs) -> new dictionary initialized with the name=value pairs
    in the keyword argument list.  For example:  dict(one=1, two=2)

####`RRBase.__firstlineno__`

int([x]) -> integer
int(x, base=10) -> integer

Convert a number or string to an integer, or return 0 if no arguments
are given.  If x is a number, return x.__int__().  For floating-point
numbers, this truncates towards zero.

If x is not a number or if base is given, then x must be a string,
bytes, or bytearray instance representing an integer literal in the
given base.  The literal can be preceded by '+' or '-' and be surrounded
by whitespace.  The base defaults to 10.  Valid bases are 0 and 2-36.
Base 0 means to interpret the base from the string as an integer literal.
>>> int('0b100', base=0)
4

####`RRBase.__hash__`

The type of the None singleton.

####`RRBase.__match_args__`

Built-in immutable sequence.

If no argument is given, the constructor returns an empty tuple.
If iterable is specified the tuple is initialized from iterable's items.

If the argument is a tuple, the return value is the same object.

####`RRBase.__static_attributes__`

Built-in immutable sequence.

If no argument is given, the constructor returns an empty tuple.
If iterable is specified the tuple is initialized from iterable's items.

If the argument is a tuple, the return value is the same object.

#### `RRBase.__str__(self)`

Return the default rendering of the text box.

#### `RRBase.conn_char(index, lefts: Sequence[int], rights: Sequence[int], *, arc=True, heavy=False) -> str`

Compute the connective `box_char` for a column of connective characters.

#### `RRBase.from_str(s: str) -> Union[ForwardRef('Terminal'), ForwardRef('RRTextBox')]`

Promote a string to a `Terminal` or `RRTextBox`.
Nonempty strings with no newlines become `Terminal`s,
otherwise a `RRTextBox`.

####`RRBase.height`

The height of this box.

#### `RRBase.horiz(width: int, *, middle='', arc, heavy, ascii=False, left_up=False, left_down=False, right_up=False, right_down=False, **_)`

Compute a horizontal line with an optional symbol in the
middle and up or down connections.

#### `RRBase.pprint(self, **ppkw)`

Call `pprint.pprint` with this railroad node.

#### `RRBase.render(self, **render_kw)`

Render the text box as a single multiline string.

####`RRBase.render_context`

A simple attribute-based namespace.

#### `RRBase.render_lines(self, **render_kw) -> list[str]`

Render the box as a list of single line strings.

####`RRBase.width`

The width of this box.

### class RRChoice(RRStack)

RRChoice(content: list[cs.ascii_art.RRBase] = <factory>)

####`RRChoice.__dataclass_fields__`

dict() -> new empty dictionary
dict(mapping) -> new dictionary initialized from a mapping object's
    (key, value) pairs
dict(iterable) -> new dictionary initialized as if via:
    d = {}
    for k, v in iterable:
        d[k] = v
dict(**kwargs) -> new dictionary initialized with the name=value pairs
    in the keyword argument list.  For example:  dict(one=1, two=2)

####`RRChoice.__firstlineno__`

int([x]) -> integer
int(x, base=10) -> integer

Convert a number or string to an integer, or return 0 if no arguments
are given.  If x is a number, return x.__int__().  For floating-point
numbers, this truncates towards zero.

If x is not a number or if base is given, then x must be a string,
bytes, or bytearray instance representing an integer literal in the
given base.  The literal can be preceded by '+' or '-' and be surrounded
by whitespace.  The base defaults to 10.  Valid bases are 0 and 2-36.
Base 0 means to interpret the base from the string as an integer literal.
>>> int('0b100', base=0)
4

####`RRChoice.__match_args__`

Built-in immutable sequence.

If no argument is given, the constructor returns an empty tuple.
If iterable is specified the tuple is initialized from iterable's items.

If the argument is a tuple, the return value is the same object.

####`RRChoice.__static_attributes__`

Built-in immutable sequence.

If no argument is given, the constructor returns an empty tuple.
If iterable is specified the tuple is initialized from iterable's items.

If the argument is a tuple, the return value is the same object.

### class RRMerge(RRStack)

RRMerge multiple inputs into a single output.

####`RRMerge.__dataclass_fields__`

dict() -> new empty dictionary
dict(mapping) -> new dictionary initialized from a mapping object's
    (key, value) pairs
dict(iterable) -> new dictionary initialized as if via:
    d = {}
    for k, v in iterable:
        d[k] = v
dict(**kwargs) -> new dictionary initialized with the name=value pairs
    in the keyword argument list.  For example:  dict(one=1, two=2)

####`RRMerge.__firstlineno__`

int([x]) -> integer
int(x, base=10) -> integer

Convert a number or string to an integer, or return 0 if no arguments
are given.  If x is a number, return x.__int__().  For floating-point
numbers, this truncates towards zero.

If x is not a number or if base is given, then x must be a string,
bytes, or bytearray instance representing an integer literal in the
given base.  The literal can be preceded by '+' or '-' and be surrounded
by whitespace.  The base defaults to 10.  Valid bases are 0 and 2-36.
Base 0 means to interpret the base from the string as an integer literal.
>>> int('0b100', base=0)
4

####`RRMerge.__match_args__`

Built-in immutable sequence.

If no argument is given, the constructor returns an empty tuple.
If iterable is specified the tuple is initialized from iterable's items.

If the argument is a tuple, the return value is the same object.

####`RRMerge.__static_attributes__`

Built-in immutable sequence.

If no argument is given, the constructor returns an empty tuple.
If iterable is specified the tuple is initialized from iterable's items.

If the argument is a tuple, the return value is the same object.

####`RRMerge.e`

The east attachment points of the `RRMerge`, the midpoint of
the interior top and bottom attachment points.

####`RRMerge.es`

The east attachment points of the `RRMerge`, the midpoint of
the interior top and bottom attachment points.

#### `RRMerge.render_lines(self, *, arc, heavy, attach_e, **_) -> list[str]`

Render the `RRMerge` as a list of strings.

####`RRMerge.width`

The width of the `RRMerge`, the width of the `RRStack` plus 1
for the right attachments.

### class RROptional(_RailRoadAround)

RROptional(content: cs.ascii_art.RRBase, above: bool = False, middle: str = '→')

####`RROptional.__annotations__`

dict() -> new empty dictionary
dict(mapping) -> new dictionary initialized from a mapping object's
    (key, value) pairs
dict(iterable) -> new dictionary initialized as if via:
    d = {}
    for k, v in iterable:
        d[k] = v
dict(**kwargs) -> new dictionary initialized with the name=value pairs
    in the keyword argument list.  For example:  dict(one=1, two=2)

####`RROptional.__dataclass_fields__`

dict() -> new empty dictionary
dict(mapping) -> new dictionary initialized from a mapping object's
    (key, value) pairs
dict(iterable) -> new dictionary initialized as if via:
    d = {}
    for k, v in iterable:
        d[k] = v
dict(**kwargs) -> new dictionary initialized with the name=value pairs
    in the keyword argument list.  For example:  dict(one=1, two=2)

####`RROptional.__firstlineno__`

int([x]) -> integer
int(x, base=10) -> integer

Convert a number or string to an integer, or return 0 if no arguments
are given.  If x is a number, return x.__int__().  For floating-point
numbers, this truncates towards zero.

If x is not a number or if base is given, then x must be a string,
bytes, or bytearray instance representing an integer literal in the
given base.  The literal can be preceded by '+' or '-' and be surrounded
by whitespace.  The base defaults to 10.  Valid bases are 0 and 2-36.
Base 0 means to interpret the base from the string as an integer literal.
>>> int('0b100', base=0)
4

####`RROptional.__match_args__`

Built-in immutable sequence.

If no argument is given, the constructor returns an empty tuple.
If iterable is specified the tuple is initialized from iterable's items.

If the argument is a tuple, the return value is the same object.

####`RROptional.__static_attributes__`

Built-in immutable sequence.

If no argument is given, the constructor returns an empty tuple.
If iterable is specified the tuple is initialized from iterable's items.

If the argument is a tuple, the return value is the same object.

####`RROptional.middle`

str(object='') -> str
str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or
errors is specified, then the object must expose a data buffer
that will be decoded using the given encoding and error handler.
Otherwise, returns the result of object.__str__() (if defined)
or repr(object).
encoding defaults to 'utf-8'.
errors defaults to 'strict'.

### class RRRepeat(_RailRoadAround)

RRRepeat(content: cs.ascii_art.RRBase, above: bool = False, middle: str = '←')

####`RRRepeat.__annotations__`

dict() -> new empty dictionary
dict(mapping) -> new dictionary initialized from a mapping object's
    (key, value) pairs
dict(iterable) -> new dictionary initialized as if via:
    d = {}
    for k, v in iterable:
        d[k] = v
dict(**kwargs) -> new dictionary initialized with the name=value pairs
    in the keyword argument list.  For example:  dict(one=1, two=2)

####`RRRepeat.__dataclass_fields__`

dict() -> new empty dictionary
dict(mapping) -> new dictionary initialized from a mapping object's
    (key, value) pairs
dict(iterable) -> new dictionary initialized as if via:
    d = {}
    for k, v in iterable:
        d[k] = v
dict(**kwargs) -> new dictionary initialized with the name=value pairs
    in the keyword argument list.  For example:  dict(one=1, two=2)

####`RRRepeat.__firstlineno__`

int([x]) -> integer
int(x, base=10) -> integer

Convert a number or string to an integer, or return 0 if no arguments
are given.  If x is a number, return x.__int__().  For floating-point
numbers, this truncates towards zero.

If x is not a number or if base is given, then x must be a string,
bytes, or bytearray instance representing an integer literal in the
given base.  The literal can be preceded by '+' or '-' and be surrounded
by whitespace.  The base defaults to 10.  Valid bases are 0 and 2-36.
Base 0 means to interpret the base from the string as an integer literal.
>>> int('0b100', base=0)
4

####`RRRepeat.__match_args__`

Built-in immutable sequence.

If no argument is given, the constructor returns an empty tuple.
If iterable is specified the tuple is initialized from iterable's items.

If the argument is a tuple, the return value is the same object.

####`RRRepeat.__static_attributes__`

Built-in immutable sequence.

If no argument is given, the constructor returns an empty tuple.
If iterable is specified the tuple is initialized from iterable's items.

If the argument is a tuple, the return value is the same object.

####`RRRepeat.middle`

str(object='') -> str
str(bytes_or_buffer[, encoding[, errors]]) -> str

Create a new string object from the given object. If encoding or
errors is specified, then the object must expose a data buffer
that will be decoded using the given encoding and error handler.
Otherwise, returns the result of object.__str__() (if defined)
or repr(object).
encoding defaults to 'utf-8'.
errors defaults to 'strict'.

### class RRSequence(_RailRoadMulti)

A railroad sequence.

####`RRSequence.__dataclass_fields__`

dict() -> new empty dictionary
dict(mapping) -> new dictionary initialized from a mapping object's
    (key, value) pairs
dict(iterable) -> new dictionary initialized as if via:
    d = {}
    for k, v in iterable:
        d[k] = v
dict(**kwargs) -> new dictionary initialized with the name=value pairs
    in the keyword argument list.  For example:  dict(one=1, two=2)

####`RRSequence.__firstlineno__`

int([x]) -> integer
int(x, base=10) -> integer

Convert a number or string to an integer, or return 0 if no arguments
are given.  If x is a number, return x.__int__().  For floating-point
numbers, this truncates towards zero.

If x is not a number or if base is given, then x must be a string,
bytes, or bytearray instance representing an integer literal in the
given base.  The literal can be preceded by '+' or '-' and be surrounded
by whitespace.  The base defaults to 10.  Valid bases are 0 and 2-36.
Base 0 means to interpret the base from the string as an integer literal.
>>> int('0b100', base=0)
4

####`RRSequence.__match_args__`

Built-in immutable sequence.

If no argument is given, the constructor returns an empty tuple.
If iterable is specified the tuple is initialized from iterable's items.

If the argument is a tuple, the return value is the same object.

####`RRSequence.__static_attributes__`

Built-in immutable sequence.

If no argument is given, the constructor returns an empty tuple.
If iterable is specified the tuple is initialized from iterable's items.

If the argument is a tuple, the return value is the same object.

####`RRSequence.box_bottoms`

The downward extend of the boxes with respect to the left attachment point.

####`RRSequence.box_tops`

The upward extend of the boxes with respect to the left attachment point.

####`RRSequence.boxes_bottom`

The lowest extend of the boxes with respect to the left attachment point.

####`RRSequence.boxes_top`

The highest extend of the boxes with respect to the left attachment point.

####`RRSequence.height`

The render height of the `RRSequence` in lines.

#### `RRSequence.render_lines(self, *, attach_w, attach_e, sep_len, middle='', **_) -> list[str]`

Render the `RRSequence` as a list of one line strings.

####`RRSequence.width`

The render width of the `RRSequence` in characters..

### class RRSplit(RRStack)

RRSplit a single input into multiple outputs.

####`RRSplit.__dataclass_fields__`

dict() -> new empty dictionary
dict(mapping) -> new dictionary initialized from a mapping object's
    (key, value) pairs
dict(iterable) -> new dictionary initialized as if via:
    d = {}
    for k, v in iterable:
        d[k] = v
dict(**kwargs) -> new dictionary initialized with the name=value pairs
    in the keyword argument list.  For example:  dict(one=1, two=2)

####`RRSplit.__firstlineno__`

int([x]) -> integer
int(x, base=10) -> integer

Convert a number or string to an integer, or return 0 if no arguments
are given.  If x is a number, return x.__int__().  For floating-point
numbers, this truncates towards zero.

If x is not a number or if base is given, then x must be a string,
bytes, or bytearray instance representing an integer literal in the
given base.  The literal can be preceded by '+' or '-' and be surrounded
by whitespace.  The base defaults to 10.  Valid bases are 0 and 2-36.
Base 0 means to interpret the base from the string as an integer literal.
>>> int('0b100', base=0)
4

####`RRSplit.__match_args__`

Built-in immutable sequence.

If no argument is given, the constructor returns an empty tuple.
If iterable is specified the tuple is initialized from iterable's items.

If the argument is a tuple, the return value is the same object.

####`RRSplit.__static_attributes__`

Built-in immutable sequence.

If no argument is given, the constructor returns an empty tuple.
If iterable is specified the tuple is initialized from iterable's items.

If the argument is a tuple, the return value is the same object.

#### `RRSplit.render_lines(self, *, arc, heavy, attach_w, **_) -> list[str]`

Render the `RRSplit` as a list of strings.

####`RRSplit.w`

The west attachment point, the midpoint of the top and
bottom interior west attachment points.

####`RRSplit.width`

The width of the `RRSplit` in characters.

####`RRSplit.ws`

The west attachment points of the `RRSplit`.

### class RRStack(_RailRoadMulti)

An unadorned vertical stack of the content.

####`RRStack.__dataclass_fields__`

dict() -> new empty dictionary
dict(mapping) -> new dictionary initialized from a mapping object's
    (key, value) pairs
dict(iterable) -> new dictionary initialized as if via:
    d = {}
    for k, v in iterable:
        d[k] = v
dict(**kwargs) -> new dictionary initialized with the name=value pairs
    in the keyword argument list.  For example:  dict(one=1, two=2)

####`RRStack.__firstlineno__`

int([x]) -> integer
int(x, base=10) -> integer

Convert a number or string to an integer, or return 0 if no arguments
are given.  If x is a number, return x.__int__().  For floating-point
numbers, this truncates towards zero.

If x is not a number or if base is given, then x must be a string,
bytes, or bytearray instance representing an integer literal in the
given base.  The literal can be preceded by '+' or '-' and be surrounded
by whitespace.  The base defaults to 10.  Valid bases are 0 and 2-36.
Base 0 means to interpret the base from the string as an integer literal.
>>> int('0b100', base=0)
4

####`RRStack.__match_args__`

Built-in immutable sequence.

If no argument is given, the constructor returns an empty tuple.
If iterable is specified the tuple is initialized from iterable's items.

If the argument is a tuple, the return value is the same object.

####`RRStack.__static_attributes__`

Built-in immutable sequence.

If no argument is given, the constructor returns an empty tuple.
If iterable is specified the tuple is initialized from iterable's items.

If the argument is a tuple, the return value is the same object.

####`RRStack.es`

A cached `tuple` of the line offsets of each stacked box's `.e` position.

####`RRStack.height`

The overall height of the content.

####`RRStack.width`

The overall width of the content.

####`RRStack.ws`

A cached `tuple` of the line offsets of each stacked box's `.w` position.

### class RRTextBox(RRBase)

A text box with borders.

####`RRTextBox.__annotations__`

dict() -> new empty dictionary
dict(mapping) -> new dictionary initialized from a mapping object's
    (key, value) pairs
dict(iterable) -> new dictionary initialized as if via:
    d = {}
    for k, v in iterable:
        d[k] = v
dict(**kwargs) -> new dictionary initialized with the name=value pairs
    in the keyword argument list.  For example:  dict(one=1, two=2)

####`RRTextBox.__dataclass_fields__`

dict() -> new empty dictionary
dict(mapping) -> new dictionary initialized from a mapping object's
    (key, value) pairs
dict(iterable) -> new dictionary initialized as if via:
    d = {}
    for k, v in iterable:
        d[k] = v
dict(**kwargs) -> new dictionary initialized with the name=value pairs
    in the keyword argument list.  For example:  dict(one=1, two=2)

####`RRTextBox.__firstlineno__`

int([x]) -> integer
int(x, base=10) -> integer

Convert a number or string to an integer, or return 0 if no arguments
are given.  If x is a number, return x.__int__().  For floating-point
numbers, this truncates towards zero.

If x is not a number or if base is given, then x must be a string,
bytes, or bytearray instance representing an integer literal in the
given base.  The literal can be preceded by '+' or '-' and be surrounded
by whitespace.  The base defaults to 10.  Valid bases are 0 and 2-36.
Base 0 means to interpret the base from the string as an integer literal.
>>> int('0b100', base=0)
4

####`RRTextBox.__match_args__`

Built-in immutable sequence.

If no argument is given, the constructor returns an empty tuple.
If iterable is specified the tuple is initialized from iterable's items.

If the argument is a tuple, the return value is the same object.

####`RRTextBox.__static_attributes__`

Built-in immutable sequence.

If no argument is given, the constructor returns an empty tuple.
If iterable is specified the tuple is initialized from iterable's items.

If the argument is a tuple, the return value is the same object.

####`RRTextBox.arc`

Returns True when the argument is true, False otherwise.
The builtins True and False are the only two instances of the class bool.
The class bool is a subclass of the class int, and cannot be subclassed.

####`RRTextBox.e`

The vertical offset to the east connection point.

####`RRTextBox.heavy`

Returns True when the argument is true, False otherwise.
The builtins True and False are the only two instances of the class bool.
The class bool is a subclass of the class int, and cannot be subclassed.

####`RRTextBox.height`

The height including the borders

####`RRTextBox.lines`

The lines of text.

####`RRTextBox.max_text_length`

The length of the longest line of text.

####`RRTextBox.n`

The horizontal offset to the north connection point.

####`RRTextBox.nlines`

The number of lines of text.

####`RRTextBox.refobj`

The type of the None singleton.

#### `RRTextBox.render_lines(self, *, heavy, attach_w, attach_e, **_) -> list[str]`

Render the text box as a list of single line strings.

####`RRTextBox.s`

The horizontal offset to the south connection point.

####`RRTextBox.w`

The vertical offset to the west connection point.

####`RRTextBox.width`

The width including the borders

### class Symbol(RRBase)

A bare text based symbol like `RR_START` or `RR_END`.

####`Symbol.__annotations__`

dict() -> new empty dictionary
dict(mapping) -> new dictionary initialized from a mapping object's
    (key, value) pairs
dict(iterable) -> new dictionary initialized as if via:
    d = {}
    for k, v in iterable:
        d[k] = v
dict(**kwargs) -> new dictionary initialized with the name=value pairs
    in the keyword argument list.  For example:  dict(one=1, two=2)

####`Symbol.__dataclass_fields__`

dict() -> new empty dictionary
dict(mapping) -> new dictionary initialized from a mapping object's
    (key, value) pairs
dict(iterable) -> new dictionary initialized as if via:
    d = {}
    for k, v in iterable:
        d[k] = v
dict(**kwargs) -> new dictionary initialized with the name=value pairs
    in the keyword argument list.  For example:  dict(one=1, two=2)

####`Symbol.__firstlineno__`

int([x]) -> integer
int(x, base=10) -> integer

Convert a number or string to an integer, or return 0 if no arguments
are given.  If x is a number, return x.__int__().  For floating-point
numbers, this truncates towards zero.

If x is not a number or if base is given, then x must be a string,
bytes, or bytearray instance representing an integer literal in the
given base.  The literal can be preceded by '+' or '-' and be surrounded
by whitespace.  The base defaults to 10.  Valid bases are 0 and 2-36.
Base 0 means to interpret the base from the string as an integer literal.
>>> int('0b100', base=0)
4

####`Symbol.__match_args__`

Built-in immutable sequence.

If no argument is given, the constructor returns an empty tuple.
If iterable is specified the tuple is initialized from iterable's items.

If the argument is a tuple, the return value is the same object.

####`Symbol.__static_attributes__`

Built-in immutable sequence.

If no argument is given, the constructor returns an empty tuple.
If iterable is specified the tuple is initialized from iterable's items.

If the argument is a tuple, the return value is the same object.

####`Symbol.height`

int([x]) -> integer
int(x, base=10) -> integer

Convert a number or string to an integer, or return 0 if no arguments
are given.  If x is a number, return x.__int__().  For floating-point
numbers, this truncates towards zero.

If x is not a number or if base is given, then x must be a string,
bytes, or bytearray instance representing an integer literal in the
given base.  The literal can be preceded by '+' or '-' and be surrounded
by whitespace.  The base defaults to 10.  Valid bases are 0 and 2-36.
Base 0 means to interpret the base from the string as an integer literal.
>>> int('0b100', base=0)
4

####`Symbol.refobj`

The type of the None singleton.

### class Terminal(Symbol)

Like a symbol, but with a marker either side.

####`Terminal.__dataclass_fields__`

dict() -> new empty dictionary
dict(mapping) -> new dictionary initialized from a mapping object's
    (key, value) pairs
dict(iterable) -> new dictionary initialized as if via:
    d = {}
    for k, v in iterable:
        d[k] = v
dict(**kwargs) -> new dictionary initialized with the name=value pairs
    in the keyword argument list.  For example:  dict(one=1, two=2)

####`Terminal.__firstlineno__`

int([x]) -> integer
int(x, base=10) -> integer

Convert a number or string to an integer, or return 0 if no arguments
are given.  If x is a number, return x.__int__().  For floating-point
numbers, this truncates towards zero.

If x is not a number or if base is given, then x must be a string,
bytes, or bytearray instance representing an integer literal in the
given base.  The literal can be preceded by '+' or '-' and be surrounded
by whitespace.  The base defaults to 10.  Valid bases are 0 and 2-36.
Base 0 means to interpret the base from the string as an integer literal.
>>> int('0b100', base=0)
4

####`Terminal.__match_args__`

Built-in immutable sequence.

If no argument is given, the constructor returns an empty tuple.
If iterable is specified the tuple is initialized from iterable's items.

If the argument is a tuple, the return value is the same object.

####`Terminal.__static_attributes__`

Built-in immutable sequence.

If no argument is given, the constructor returns an empty tuple.
If iterable is specified the tuple is initialized from iterable's items.

If the argument is a tuple, the return value is the same object.

# Release Log



*Release 20260912*:
* _RailRoadMulti and subclasses: present the interior contents via __getitem__.
* Define the Morse Code diagram and add it to the demo.
* _RailRoadAround, _RailRoadMulti and subclasses: automatically promote self.content via RRBase.promote.
* Various small fixes.

*Release 20260403*:
Add LARGE_CIRCLE.

*Release 20260228*:
Initial release. Basic railroad diagrams and associated utilities.
