tesorotools-python 0.0.36__tar.gz → 0.0.37__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {tesorotools_python-0.0.36 → tesorotools_python-0.0.37}/PKG-INFO +1 -1
- {tesorotools_python-0.0.36 → tesorotools_python-0.0.37}/pyproject.toml +1 -1
- {tesorotools_python-0.0.36 → tesorotools_python-0.0.37}/src/tesorotools/__init__.py +27 -2
- tesorotools_python-0.0.37/src/tesorotools/_build_context.py +49 -0
- tesorotools_python-0.0.37/src/tesorotools/_registry.py +238 -0
- tesorotools_python-0.0.37/src/tesorotools/artists/__init__.py +47 -0
- tesorotools_python-0.0.37/src/tesorotools/artists/_common.py +493 -0
- tesorotools_python-0.0.37/src/tesorotools/artists/barh_plot.py +372 -0
- tesorotools_python-0.0.37/src/tesorotools/artists/line_plot.py +289 -0
- {tesorotools_python-0.0.36 → tesorotools_python-0.0.37}/src/tesorotools/artists/stacked.py +94 -21
- {tesorotools_python-0.0.36 → tesorotools_python-0.0.37}/src/tesorotools/artists/type_curve.py +48 -12
- tesorotools_python-0.0.37/src/tesorotools/database/push.py +28 -0
- {tesorotools_python-0.0.36 → tesorotools_python-0.0.37}/src/tesorotools/providers/__init__.py +7 -2
- {tesorotools_python-0.0.36 → tesorotools_python-0.0.37}/src/tesorotools/providers/base.py +42 -0
- {tesorotools_python-0.0.36 → tesorotools_python-0.0.37}/src/tesorotools/render/content/table.py +4 -5
- tesorotools_python-0.0.36/src/tesorotools/_registry.py +0 -117
- tesorotools_python-0.0.36/src/tesorotools/artists/__init__.py +0 -25
- tesorotools_python-0.0.36/src/tesorotools/artists/barh.md +0 -109
- tesorotools_python-0.0.36/src/tesorotools/artists/barh_plot.py +0 -510
- tesorotools_python-0.0.36/src/tesorotools/artists/line_plot.py +0 -621
- tesorotools_python-0.0.36/src/tesorotools/artists/table.py +0 -255
- tesorotools_python-0.0.36/src/tesorotools/convert.py +0 -123
- tesorotools_python-0.0.36/src/tesorotools/database/push.py +0 -58
- tesorotools_python-0.0.36/src/tesorotools/main.py +0 -37
- {tesorotools_python-0.0.36 → tesorotools_python-0.0.37}/.gitignore +0 -0
- {tesorotools_python-0.0.36 → tesorotools_python-0.0.37}/src/tesorotools/assets/README.md +0 -0
- {tesorotools_python-0.0.36 → tesorotools_python-0.0.37}/src/tesorotools/assets/fonts/CabinetGrotesk-Black.otf +0 -0
- {tesorotools_python-0.0.36 → tesorotools_python-0.0.37}/src/tesorotools/assets/fonts/CabinetGrotesk-Bold.otf +0 -0
- {tesorotools_python-0.0.36 → tesorotools_python-0.0.37}/src/tesorotools/assets/fonts/CabinetGrotesk-Extrabold.otf +0 -0
- {tesorotools_python-0.0.36 → tesorotools_python-0.0.37}/src/tesorotools/assets/fonts/CabinetGrotesk-Extralight.otf +0 -0
- {tesorotools_python-0.0.36 → tesorotools_python-0.0.37}/src/tesorotools/assets/fonts/CabinetGrotesk-Light.otf +0 -0
- {tesorotools_python-0.0.36 → tesorotools_python-0.0.37}/src/tesorotools/assets/fonts/CabinetGrotesk-Medium.otf +0 -0
- {tesorotools_python-0.0.36 → tesorotools_python-0.0.37}/src/tesorotools/assets/fonts/CabinetGrotesk-Regular.otf +0 -0
- {tesorotools_python-0.0.36 → tesorotools_python-0.0.37}/src/tesorotools/assets/fonts/CabinetGrotesk-Thin.otf +0 -0
- {tesorotools_python-0.0.36 → tesorotools_python-0.0.37}/src/tesorotools/assets/fonts/README.md +0 -0
- {tesorotools_python-0.0.36 → tesorotools_python-0.0.37}/src/tesorotools/assets/plots.yaml +0 -0
- {tesorotools_python-0.0.36 → tesorotools_python-0.0.37}/src/tesorotools/assets/tesoro.mplstyle +0 -0
- {tesorotools_python-0.0.36 → tesorotools_python-0.0.37}/src/tesorotools/data_sources/__init__.py +0 -0
- {tesorotools_python-0.0.36 → tesorotools_python-0.0.37}/src/tesorotools/data_sources/debug.py +0 -0
- {tesorotools_python-0.0.36 → tesorotools_python-0.0.37}/src/tesorotools/database/__init__.py +0 -0
- {tesorotools_python-0.0.36 → tesorotools_python-0.0.37}/src/tesorotools/database/local.py +0 -0
- {tesorotools_python-0.0.36 → tesorotools_python-0.0.37}/src/tesorotools/database/shared.py +0 -0
- {tesorotools_python-0.0.36 → tesorotools_python-0.0.37}/src/tesorotools/dependencies/__init__.py +0 -0
- {tesorotools_python-0.0.36 → tesorotools_python-0.0.37}/src/tesorotools/dependencies/node.py +0 -0
- {tesorotools_python-0.0.36 → tesorotools_python-0.0.37}/src/tesorotools/dependencies/resolution.py +0 -0
- {tesorotools_python-0.0.36 → tesorotools_python-0.0.37}/src/tesorotools/driver.py +0 -0
- {tesorotools_python-0.0.36 → tesorotools_python-0.0.37}/src/tesorotools/manifest.py +0 -0
- {tesorotools_python-0.0.36 → tesorotools_python-0.0.37}/src/tesorotools/offsets/__init__.py +0 -0
- {tesorotools_python-0.0.36 → tesorotools_python-0.0.37}/src/tesorotools/offsets/offsets.py +0 -0
- {tesorotools_python-0.0.36 → tesorotools_python-0.0.37}/src/tesorotools/offsets/outliers.py +0 -0
- {tesorotools_python-0.0.36 → tesorotools_python-0.0.37}/src/tesorotools/pipeline/__init__.py +0 -0
- {tesorotools_python-0.0.36 → tesorotools_python-0.0.37}/src/tesorotools/pipeline/diagnose.py +0 -0
- {tesorotools_python-0.0.36 → tesorotools_python-0.0.37}/src/tesorotools/pipeline/engine.py +0 -0
- {tesorotools_python-0.0.36 → tesorotools_python-0.0.37}/src/tesorotools/pipeline/rules.py +0 -0
- {tesorotools_python-0.0.36 → tesorotools_python-0.0.37}/src/tesorotools/providers/bde.py +0 -0
- {tesorotools_python-0.0.36 → tesorotools_python-0.0.37}/src/tesorotools/providers/ecb.py +0 -0
- {tesorotools_python-0.0.36 → tesorotools_python-0.0.37}/src/tesorotools/py.typed +0 -0
- {tesorotools_python-0.0.36 → tesorotools_python-0.0.37}/src/tesorotools/render/__init__.py +0 -0
- {tesorotools_python-0.0.36 → tesorotools_python-0.0.37}/src/tesorotools/render/content/__init__.py +0 -0
- {tesorotools_python-0.0.36 → tesorotools_python-0.0.37}/src/tesorotools/render/content/content.py +0 -0
- {tesorotools_python-0.0.36 → tesorotools_python-0.0.37}/src/tesorotools/render/content/images.py +0 -0
- {tesorotools_python-0.0.36 → tesorotools_python-0.0.37}/src/tesorotools/render/content/section.py +0 -0
- {tesorotools_python-0.0.36 → tesorotools_python-0.0.37}/src/tesorotools/render/content/subtitle.py +0 -0
- {tesorotools_python-0.0.36 → tesorotools_python-0.0.37}/src/tesorotools/render/content/text.py +0 -0
- {tesorotools_python-0.0.36 → tesorotools_python-0.0.37}/src/tesorotools/render/content/title.py +0 -0
- {tesorotools_python-0.0.36 → tesorotools_python-0.0.37}/src/tesorotools/render/report.py +0 -0
- {tesorotools_python-0.0.36 → tesorotools_python-0.0.37}/src/tesorotools/testing/__init__.py +0 -0
- {tesorotools_python-0.0.36 → tesorotools_python-0.0.37}/src/tesorotools/testing/compare.py +0 -0
- {tesorotools_python-0.0.36 → tesorotools_python-0.0.37}/src/tesorotools/utils/__init__.py +0 -0
- {tesorotools_python-0.0.36 → tesorotools_python-0.0.37}/src/tesorotools/utils/config.py +0 -0
- {tesorotools_python-0.0.36 → tesorotools_python-0.0.37}/src/tesorotools/utils/format.py +0 -0
- {tesorotools_python-0.0.36 → tesorotools_python-0.0.37}/src/tesorotools/utils/globals.py +0 -0
- {tesorotools_python-0.0.36 → tesorotools_python-0.0.37}/src/tesorotools/utils/matplotlib.py +0 -0
- {tesorotools_python-0.0.36 → tesorotools_python-0.0.37}/src/tesorotools/utils/series.py +0 -0
- {tesorotools_python-0.0.36 → tesorotools_python-0.0.37}/src/tesorotools/utils/shortcuts.py +0 -0
- {tesorotools_python-0.0.36 → tesorotools_python-0.0.37}/src/tesorotools/utils/template.py +0 -0
|
@@ -12,7 +12,9 @@ requires ``[ecb]``) are exposed lazily through
|
|
|
12
12
|
extras to be installed.
|
|
13
13
|
|
|
14
14
|
Third parties extend the package via ``register_artist``,
|
|
15
|
-
``register_tag``, and ``register_provider
|
|
15
|
+
``register_tag``, and ``register_provider`` (and their
|
|
16
|
+
plural variants). See ``docs/extending.md`` for the
|
|
17
|
+
recommended idioms.
|
|
16
18
|
"""
|
|
17
19
|
|
|
18
20
|
from typing import TYPE_CHECKING, Any
|
|
@@ -21,13 +23,24 @@ if TYPE_CHECKING:
|
|
|
21
23
|
from tesorotools.providers.bde import BdeProvider
|
|
22
24
|
from tesorotools.providers.ecb import EcbProvider
|
|
23
25
|
|
|
26
|
+
from tesorotools._build_context import BuildContext
|
|
24
27
|
from tesorotools._registry import (
|
|
25
28
|
Artist,
|
|
29
|
+
YamlConstructor,
|
|
30
|
+
all_artists,
|
|
31
|
+
all_providers,
|
|
32
|
+
all_tags,
|
|
26
33
|
get_artist,
|
|
27
34
|
get_provider,
|
|
35
|
+
iter_artists,
|
|
36
|
+
iter_providers,
|
|
37
|
+
iter_tags,
|
|
28
38
|
register_artist,
|
|
39
|
+
register_artists,
|
|
29
40
|
register_provider,
|
|
41
|
+
register_providers,
|
|
30
42
|
register_tag,
|
|
43
|
+
register_tags,
|
|
31
44
|
)
|
|
32
45
|
from tesorotools.artists import (
|
|
33
46
|
Format,
|
|
@@ -38,7 +51,7 @@ from tesorotools.artists import (
|
|
|
38
51
|
StackedBarPlot,
|
|
39
52
|
TypeCurve,
|
|
40
53
|
)
|
|
41
|
-
from tesorotools.providers.base import DataProvider
|
|
54
|
+
from tesorotools.providers.base import DataProvider, DataProviderProtocol
|
|
42
55
|
from tesorotools.render import (
|
|
43
56
|
Content,
|
|
44
57
|
Image,
|
|
@@ -77,8 +90,10 @@ _register_builtins()
|
|
|
77
90
|
__all__ = [
|
|
78
91
|
"Artist",
|
|
79
92
|
"BdeProvider",
|
|
93
|
+
"BuildContext",
|
|
80
94
|
"Content",
|
|
81
95
|
"DataProvider",
|
|
96
|
+
"DataProviderProtocol",
|
|
82
97
|
"EcbProvider",
|
|
83
98
|
"Format",
|
|
84
99
|
"HorizontalBarChart",
|
|
@@ -95,11 +110,21 @@ __all__ = [
|
|
|
95
110
|
"Text",
|
|
96
111
|
"Title",
|
|
97
112
|
"TypeCurve",
|
|
113
|
+
"YamlConstructor",
|
|
114
|
+
"all_artists",
|
|
115
|
+
"all_providers",
|
|
116
|
+
"all_tags",
|
|
98
117
|
"get_artist",
|
|
99
118
|
"get_provider",
|
|
119
|
+
"iter_artists",
|
|
120
|
+
"iter_providers",
|
|
121
|
+
"iter_tags",
|
|
100
122
|
"register_artist",
|
|
123
|
+
"register_artists",
|
|
101
124
|
"register_provider",
|
|
125
|
+
"register_providers",
|
|
102
126
|
"register_tag",
|
|
127
|
+
"register_tags",
|
|
103
128
|
]
|
|
104
129
|
|
|
105
130
|
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""Shared context object for ``build_for`` provider/artist factories.
|
|
2
|
+
|
|
3
|
+
Consumer projects increasingly converge on a pattern where
|
|
4
|
+
each provider/artist class has a ``build_for(cls, ctx) ->
|
|
5
|
+
dict[str, ...]`` classmethod that decides whether to
|
|
6
|
+
instantiate itself based on the catalog and runtime context.
|
|
7
|
+
|
|
8
|
+
``BuildContext`` is the minimal shape we expect callers to
|
|
9
|
+
share across that pattern. It deliberately stays small so
|
|
10
|
+
projects can subclass or compose freely:
|
|
11
|
+
|
|
12
|
+
.. code-block:: python
|
|
13
|
+
|
|
14
|
+
@dataclass(frozen=True)
|
|
15
|
+
class DiaryBuildContext(BuildContext):
|
|
16
|
+
lseg_fallback: Literal["mock", "raise"] = "mock"
|
|
17
|
+
|
|
18
|
+
The base fields capture the cross-project minimum:
|
|
19
|
+
|
|
20
|
+
* ``registry`` -- whatever catalog the orchestrator uses to
|
|
21
|
+
decide which series each provider must serve. Typed as
|
|
22
|
+
``Any`` because each project's catalog has a different
|
|
23
|
+
shape.
|
|
24
|
+
* ``consumer`` -- a free-form string identifying the calling
|
|
25
|
+
workflow (e.g. ``"diary"``, ``"weekly"``). ``build_for``
|
|
26
|
+
implementations dispatch on this.
|
|
27
|
+
* ``mock`` / ``mock_seed`` -- toggle for deterministic
|
|
28
|
+
fixtures during tests and demos.
|
|
29
|
+
* ``historic_file`` -- optional path to a historical dump
|
|
30
|
+
used by some providers as a fallback.
|
|
31
|
+
|
|
32
|
+
See ``docs/extending.md`` for the recommended ``build_for``
|
|
33
|
+
classmethod shape.
|
|
34
|
+
"""
|
|
35
|
+
|
|
36
|
+
from __future__ import annotations
|
|
37
|
+
|
|
38
|
+
from dataclasses import dataclass
|
|
39
|
+
from pathlib import Path
|
|
40
|
+
from typing import Any
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
@dataclass(frozen=True)
|
|
44
|
+
class BuildContext:
|
|
45
|
+
registry: Any
|
|
46
|
+
consumer: str
|
|
47
|
+
mock: bool = False
|
|
48
|
+
mock_seed: int | None = None
|
|
49
|
+
historic_file: Path | None = None
|
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
"""Registries for artists, providers, and YAML tags.
|
|
2
|
+
|
|
3
|
+
Single source of truth for "name -> class" lookups used both
|
|
4
|
+
by the YAML loader (``TemplateLoader``) and by code that
|
|
5
|
+
dispatches by string name.
|
|
6
|
+
|
|
7
|
+
Singular registration entry points:
|
|
8
|
+
|
|
9
|
+
``register_artist(name, cls, *, with_tag=True)``
|
|
10
|
+
Adds ``cls`` to the artist registry and, when
|
|
11
|
+
``with_tag`` is true (the default), binds the YAML tag
|
|
12
|
+
``!{name}`` to ``cls.from_yaml``. Pass
|
|
13
|
+
``with_tag=False`` to register an artist that is
|
|
14
|
+
dispatched only programmatically (no YAML tag, no
|
|
15
|
+
``from_yaml`` requirement).
|
|
16
|
+
|
|
17
|
+
``register_tag(name, constructor)``
|
|
18
|
+
Registers a YAML tag ``!{name}`` only. Use for things
|
|
19
|
+
that exist as YAML constructors but are not artists or
|
|
20
|
+
providers (Format, Legend, Title, Subtitle, Section,
|
|
21
|
+
Image, Text, Table, Report).
|
|
22
|
+
|
|
23
|
+
``register_provider(name, cls)``
|
|
24
|
+
Adds ``cls`` to the provider registry. Programmatic
|
|
25
|
+
only -- providers do not appear in YAML today.
|
|
26
|
+
|
|
27
|
+
Plural variants (``register_artists`` / ``register_tags`` /
|
|
28
|
+
``register_providers``) accept a ``Mapping[str, type]`` and
|
|
29
|
+
iterate, saving boilerplate when a project registers several
|
|
30
|
+
items at once.
|
|
31
|
+
|
|
32
|
+
Look up registered classes via ``get_artist`` /
|
|
33
|
+
``get_provider``; both raise ``KeyError`` listing the
|
|
34
|
+
available names. Iterate the live state via
|
|
35
|
+
``iter_artists`` / ``iter_providers`` / ``iter_tags`` (and
|
|
36
|
+
the eager ``all_*`` snapshots) so callers do not need to
|
|
37
|
+
import the private dictionaries.
|
|
38
|
+
"""
|
|
39
|
+
|
|
40
|
+
from __future__ import annotations
|
|
41
|
+
|
|
42
|
+
from collections.abc import Iterator, Mapping
|
|
43
|
+
from typing import Any, Callable, Literal, Protocol, Self, cast, overload
|
|
44
|
+
|
|
45
|
+
from yaml.nodes import MappingNode
|
|
46
|
+
|
|
47
|
+
from tesorotools.artists._common import Artist
|
|
48
|
+
from tesorotools.providers.base import DataProvider
|
|
49
|
+
from tesorotools.utils.template import TemplateLoader
|
|
50
|
+
|
|
51
|
+
# Re-export so ``from tesorotools._registry import Artist`` keeps working
|
|
52
|
+
# (and the runtime-checkable Protocol is the single source of truth in
|
|
53
|
+
# :mod:`tesorotools.artists._common`).
|
|
54
|
+
__all__ = ["Artist"]
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
class _YamlConstructable(Protocol):
|
|
58
|
+
"""Loose Protocol for any class plugged in as a YAML tag.
|
|
59
|
+
|
|
60
|
+
``register_tag`` and ``register_artist(with_tag=True)``
|
|
61
|
+
both end up calling ``cls.from_yaml`` on the registered
|
|
62
|
+
type; that is the only structural requirement. The
|
|
63
|
+
artist-grade Protocol (with ``plot()`` and ``out_path``)
|
|
64
|
+
lives in :mod:`tesorotools.artists._common`.
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
@classmethod
|
|
68
|
+
def from_yaml(cls, loader: Any, node: MappingNode) -> Self: ...
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
YamlConstructor = Callable[[Any, MappingNode], Any]
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
_ARTIST_REGISTRY: dict[str, type[Artist]] = {}
|
|
75
|
+
_PROVIDER_REGISTRY: dict[str, type[DataProvider]] = {}
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
# ----------------------------------------------------------------------
|
|
79
|
+
# Singular registration
|
|
80
|
+
# ----------------------------------------------------------------------
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
@overload
|
|
84
|
+
def register_artist(name: str, cls: type[Artist]) -> None: ...
|
|
85
|
+
@overload
|
|
86
|
+
def register_artist(
|
|
87
|
+
name: str, cls: type[Artist], *, with_tag: Literal[True]
|
|
88
|
+
) -> None: ...
|
|
89
|
+
@overload
|
|
90
|
+
def register_artist(
|
|
91
|
+
name: str, cls: type, *, with_tag: Literal[False]
|
|
92
|
+
) -> None: ...
|
|
93
|
+
def register_artist(name: str, cls: type, *, with_tag: bool = True) -> None:
|
|
94
|
+
"""Register ``cls`` as the artist for ``name``.
|
|
95
|
+
|
|
96
|
+
Adds ``cls`` to the artist registry. When ``with_tag``
|
|
97
|
+
is true (the default), also binds the YAML tag
|
|
98
|
+
``!{name}`` to ``cls.from_yaml``. Re-registering the
|
|
99
|
+
same name overrides both bindings.
|
|
100
|
+
|
|
101
|
+
Pass ``with_tag=False`` to skip the YAML side; in that
|
|
102
|
+
case ``cls`` is not required to expose ``from_yaml``.
|
|
103
|
+
"""
|
|
104
|
+
_ARTIST_REGISTRY[name] = cast(type[Artist], cls)
|
|
105
|
+
if with_tag:
|
|
106
|
+
TemplateLoader.add_constructor(
|
|
107
|
+
f"!{name}",
|
|
108
|
+
cast(YamlConstructor, cast(type[Artist], cls).from_yaml),
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def get_artist(name: str) -> type[Artist]:
|
|
113
|
+
try:
|
|
114
|
+
return _ARTIST_REGISTRY[name]
|
|
115
|
+
except KeyError:
|
|
116
|
+
available = sorted(_ARTIST_REGISTRY)
|
|
117
|
+
raise KeyError(
|
|
118
|
+
f"No artist registered as {name!r}. Available: {available}"
|
|
119
|
+
) from None
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
def register_provider(name: str, cls: type[DataProvider]) -> None:
|
|
123
|
+
"""Register ``cls`` as the provider for ``name``."""
|
|
124
|
+
_PROVIDER_REGISTRY[name] = cls
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
def get_provider(name: str) -> type[DataProvider]:
|
|
128
|
+
try:
|
|
129
|
+
return _PROVIDER_REGISTRY[name]
|
|
130
|
+
except KeyError:
|
|
131
|
+
available = sorted(_PROVIDER_REGISTRY)
|
|
132
|
+
raise KeyError(
|
|
133
|
+
f"No provider registered as {name!r}. Available: {available}"
|
|
134
|
+
) from None
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def register_tag(
|
|
138
|
+
name: str,
|
|
139
|
+
constructor: type[_YamlConstructable] | YamlConstructor,
|
|
140
|
+
) -> None:
|
|
141
|
+
"""Register a YAML tag ``!{name}``.
|
|
142
|
+
|
|
143
|
+
Accepts either a class with a ``from_yaml`` classmethod
|
|
144
|
+
or a bare callable matching the loader signature. Use
|
|
145
|
+
``register_artist`` / ``register_provider`` instead when
|
|
146
|
+
the registered name should also be looked up by code
|
|
147
|
+
via ``get_artist`` / ``get_provider``.
|
|
148
|
+
"""
|
|
149
|
+
if isinstance(constructor, type):
|
|
150
|
+
cls = cast(type[_YamlConstructable], constructor)
|
|
151
|
+
TemplateLoader.add_constructor(
|
|
152
|
+
f"!{name}", cast(YamlConstructor, cls.from_yaml)
|
|
153
|
+
)
|
|
154
|
+
else:
|
|
155
|
+
TemplateLoader.add_constructor(f"!{name}", constructor)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
# ----------------------------------------------------------------------
|
|
159
|
+
# Plural registration helpers
|
|
160
|
+
# ----------------------------------------------------------------------
|
|
161
|
+
|
|
162
|
+
|
|
163
|
+
@overload
|
|
164
|
+
def register_artists(items: Mapping[str, type[Artist]]) -> None: ...
|
|
165
|
+
@overload
|
|
166
|
+
def register_artists(
|
|
167
|
+
items: Mapping[str, type[Artist]], *, with_tag: Literal[True]
|
|
168
|
+
) -> None: ...
|
|
169
|
+
@overload
|
|
170
|
+
def register_artists(
|
|
171
|
+
items: Mapping[str, type], *, with_tag: Literal[False]
|
|
172
|
+
) -> None: ...
|
|
173
|
+
def register_artists(
|
|
174
|
+
items: Mapping[str, type], *, with_tag: bool = True
|
|
175
|
+
) -> None:
|
|
176
|
+
"""Register every (name, cls) entry as an artist.
|
|
177
|
+
|
|
178
|
+
Equivalent to looping over ``items.items()`` and calling
|
|
179
|
+
:func:`register_artist`. ``with_tag`` is forwarded to
|
|
180
|
+
every call; pass ``with_tag=False`` to register classes
|
|
181
|
+
that do not expose ``from_yaml``.
|
|
182
|
+
"""
|
|
183
|
+
for name, cls in items.items():
|
|
184
|
+
if with_tag:
|
|
185
|
+
register_artist(name, cast(type[Artist], cls))
|
|
186
|
+
else:
|
|
187
|
+
register_artist(name, cls, with_tag=False)
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
def register_providers(items: Mapping[str, type[DataProvider]]) -> None:
|
|
191
|
+
"""Register every (name, cls) entry as a provider."""
|
|
192
|
+
for name, cls in items.items():
|
|
193
|
+
register_provider(name, cls)
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def register_tags(
|
|
197
|
+
items: Mapping[str, type[Artist] | YamlConstructor],
|
|
198
|
+
) -> None:
|
|
199
|
+
"""Register every (name, constructor) entry as a YAML tag."""
|
|
200
|
+
for name, constructor in items.items():
|
|
201
|
+
register_tag(name, constructor)
|
|
202
|
+
|
|
203
|
+
|
|
204
|
+
# ----------------------------------------------------------------------
|
|
205
|
+
# Read-only views over the registries
|
|
206
|
+
# ----------------------------------------------------------------------
|
|
207
|
+
|
|
208
|
+
|
|
209
|
+
def iter_artists() -> Iterator[tuple[str, type[Artist]]]:
|
|
210
|
+
"""Yield (name, cls) pairs for every registered artist."""
|
|
211
|
+
return iter(_ARTIST_REGISTRY.items())
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def all_artists() -> dict[str, type[Artist]]:
|
|
215
|
+
"""Return a snapshot dict of every registered artist."""
|
|
216
|
+
return dict(_ARTIST_REGISTRY)
|
|
217
|
+
|
|
218
|
+
|
|
219
|
+
def iter_providers() -> Iterator[tuple[str, type[DataProvider]]]:
|
|
220
|
+
"""Yield (name, cls) pairs for every registered provider."""
|
|
221
|
+
return iter(_PROVIDER_REGISTRY.items())
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
def all_providers() -> dict[str, type[DataProvider]]:
|
|
225
|
+
"""Return a snapshot dict of every registered provider."""
|
|
226
|
+
return dict(_PROVIDER_REGISTRY)
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def iter_tags() -> Iterator[tuple[str, YamlConstructor]]:
|
|
230
|
+
"""Yield (tag, constructor) pairs for every ``!`` YAML tag."""
|
|
231
|
+
for tag, ctor in TemplateLoader.yaml_constructors.items():
|
|
232
|
+
if isinstance(tag, str) and tag.startswith("!"):
|
|
233
|
+
yield tag, cast(YamlConstructor, ctor)
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def all_tags() -> dict[str, YamlConstructor]:
|
|
237
|
+
"""Return a snapshot dict of every registered ``!`` YAML tag."""
|
|
238
|
+
return dict(iter_tags())
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
"""Image artists.
|
|
2
|
+
|
|
3
|
+
Five chart classes, one shared helpers module, one
|
|
4
|
+
matplotlib stylesheet side effect on import.
|
|
5
|
+
|
|
6
|
+
Each class follows the same shape:
|
|
7
|
+
|
|
8
|
+
* ``__init__(out_path, *, data | data_path, series, ...)``
|
|
9
|
+
-- kw-only configuration; ``out_path`` must be ``.png``.
|
|
10
|
+
Pass either ``data`` (DataFrame in memory) or
|
|
11
|
+
``data_path`` (``.feather`` on disk), never both.
|
|
12
|
+
* ``from_yaml(loader, node)`` -- builds the same instance
|
|
13
|
+
from a ``!{name}`` YAML tag.
|
|
14
|
+
* ``plot()`` -- renders the chart and writes the PNG; some
|
|
15
|
+
classes also expose ``build()`` / ``save()`` for
|
|
16
|
+
fine-grained control.
|
|
17
|
+
|
|
18
|
+
Shared layout, annotation and styling helpers (plus the
|
|
19
|
+
``Format`` and ``Legend`` config holders) live in
|
|
20
|
+
:mod:`tesorotools.artists._common`.
|
|
21
|
+
|
|
22
|
+
Document-element artists (Word tables, sections, titles)
|
|
23
|
+
live under :mod:`tesorotools.render`; this package is
|
|
24
|
+
strictly image output.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
import matplotlib.style
|
|
28
|
+
|
|
29
|
+
from tesorotools.artists._common import Artist, Format, Legend
|
|
30
|
+
from tesorotools.artists.barh_plot import HorizontalBarChart
|
|
31
|
+
from tesorotools.artists.line_plot import LinePlot
|
|
32
|
+
from tesorotools.artists.stacked import StackedAreaPlot, StackedBarPlot
|
|
33
|
+
from tesorotools.artists.type_curve import TypeCurve
|
|
34
|
+
from tesorotools.utils.globals import STYLE_SHEET
|
|
35
|
+
|
|
36
|
+
matplotlib.style.use(STYLE_SHEET)
|
|
37
|
+
|
|
38
|
+
__all__ = [
|
|
39
|
+
"Artist",
|
|
40
|
+
"Format",
|
|
41
|
+
"HorizontalBarChart",
|
|
42
|
+
"Legend",
|
|
43
|
+
"LinePlot",
|
|
44
|
+
"StackedAreaPlot",
|
|
45
|
+
"StackedBarPlot",
|
|
46
|
+
"TypeCurve",
|
|
47
|
+
]
|