plotstyle 0.1.0a1__py3-none-any.whl
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.
- plotstyle/__init__.py +121 -0
- plotstyle/_utils/__init__.py +0 -0
- plotstyle/_utils/io.py +113 -0
- plotstyle/_utils/warnings.py +86 -0
- plotstyle/_version.py +24 -0
- plotstyle/cli/__init__.py +0 -0
- plotstyle/cli/main.py +553 -0
- plotstyle/color/__init__.py +42 -0
- plotstyle/color/_rendering.py +86 -0
- plotstyle/color/accessibility.py +286 -0
- plotstyle/color/data/okabe_ito.json +5 -0
- plotstyle/color/data/safe_grayscale.json +7 -0
- plotstyle/color/data/tol_bright.json +5 -0
- plotstyle/color/data/tol_muted.json +5 -0
- plotstyle/color/data/tol_vibrant.json +5 -0
- plotstyle/color/grayscale.py +284 -0
- plotstyle/color/palettes.py +259 -0
- plotstyle/core/__init__.py +0 -0
- plotstyle/core/export.py +418 -0
- plotstyle/core/figure.py +394 -0
- plotstyle/core/migrate.py +579 -0
- plotstyle/core/style.py +394 -0
- plotstyle/engine/__init__.py +0 -0
- plotstyle/engine/fonts.py +309 -0
- plotstyle/engine/latex.py +287 -0
- plotstyle/engine/rcparams.py +352 -0
- plotstyle/integrations/__init__.py +0 -0
- plotstyle/integrations/seaborn.py +305 -0
- plotstyle/preview/__init__.py +50 -0
- plotstyle/preview/gallery.py +337 -0
- plotstyle/preview/print_size.py +304 -0
- plotstyle/py.typed +0 -0
- plotstyle/specs/__init__.py +304 -0
- plotstyle/specs/_templates.toml +48 -0
- plotstyle/specs/acs.toml +36 -0
- plotstyle/specs/cell.toml +35 -0
- plotstyle/specs/elsevier.toml +35 -0
- plotstyle/specs/ieee.toml +35 -0
- plotstyle/specs/nature.toml +35 -0
- plotstyle/specs/plos.toml +35 -0
- plotstyle/specs/prl.toml +35 -0
- plotstyle/specs/schema.py +1095 -0
- plotstyle/specs/science.toml +35 -0
- plotstyle/specs/springer.toml +35 -0
- plotstyle/specs/units.py +761 -0
- plotstyle/specs/wiley.toml +35 -0
- plotstyle/validation/__init__.py +94 -0
- plotstyle/validation/checks/__init__.py +95 -0
- plotstyle/validation/checks/_base.py +149 -0
- plotstyle/validation/checks/colors.py +394 -0
- plotstyle/validation/checks/dimensions.py +166 -0
- plotstyle/validation/checks/export.py +205 -0
- plotstyle/validation/checks/lines.py +147 -0
- plotstyle/validation/checks/typography.py +200 -0
- plotstyle/validation/report.py +293 -0
- plotstyle-0.1.0a1.dist-info/METADATA +271 -0
- plotstyle-0.1.0a1.dist-info/RECORD +60 -0
- plotstyle-0.1.0a1.dist-info/WHEEL +4 -0
- plotstyle-0.1.0a1.dist-info/entry_points.txt +2 -0
- plotstyle-0.1.0a1.dist-info/licenses/LICENSE +21 -0
plotstyle/__init__.py
ADDED
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
"""PlotStyle — scientific journal figure style presets for Matplotlib.
|
|
2
|
+
|
|
3
|
+
PlotStyle makes it trivial to produce publication-ready figures that conform
|
|
4
|
+
to the typographic and dimensional requirements of major academic journals.
|
|
5
|
+
A single :func:`use` call reconfigures Matplotlib's ``rcParams``; helper
|
|
6
|
+
functions handle figure sizing, colour palettes, accessibility previews,
|
|
7
|
+
validation, and submission-ready export.
|
|
8
|
+
|
|
9
|
+
Quick start
|
|
10
|
+
-----------
|
|
11
|
+
>>> import matplotlib.pyplot as plt
|
|
12
|
+
>>> import plotstyle
|
|
13
|
+
>>>
|
|
14
|
+
>>> plotstyle.use("nature")
|
|
15
|
+
>>>
|
|
16
|
+
>>> fig, ax = plotstyle.subplots(columns=1)
|
|
17
|
+
>>> ax.plot([0, 1, 2], [0.2, 0.8, 0.4], color=plotstyle.palette("nature")[0])
|
|
18
|
+
>>> ax.set_xlabel("Time (s)")
|
|
19
|
+
>>> ax.set_ylabel("Signal (a.u.)")
|
|
20
|
+
>>>
|
|
21
|
+
>>> report = plotstyle.validate(fig, journal="nature")
|
|
22
|
+
>>> print(report)
|
|
23
|
+
>>>
|
|
24
|
+
>>> plotstyle.savefig(fig, "figure1.pdf")
|
|
25
|
+
|
|
26
|
+
Package layout
|
|
27
|
+
--------------
|
|
28
|
+
The public API is re-exported here from the following sub-packages:
|
|
29
|
+
|
|
30
|
+
- :mod:`plotstyle.core.style` — :func:`use`, :class:`JournalStyle`
|
|
31
|
+
- :mod:`plotstyle.core.figure` — :func:`figure`, :func:`subplots`
|
|
32
|
+
- :mod:`plotstyle.core.export` — :func:`savefig`, :func:`export_submission`
|
|
33
|
+
- :mod:`plotstyle.core.migrate` — :func:`diff`, :func:`migrate`, :class:`SpecDiff`
|
|
34
|
+
- :mod:`plotstyle.color.palettes` — :func:`palette`
|
|
35
|
+
- :mod:`plotstyle.color.accessibility` — :func:`preview_colorblind`
|
|
36
|
+
- :mod:`plotstyle.color.grayscale` — :func:`preview_grayscale`
|
|
37
|
+
- :mod:`plotstyle.validation` — :func:`validate`
|
|
38
|
+
- :mod:`plotstyle.specs` — :data:`registry`
|
|
39
|
+
- :mod:`plotstyle.preview.gallery` — :func:`gallery`
|
|
40
|
+
- :mod:`plotstyle.preview.print_size` — :func:`preview_print_size`
|
|
41
|
+
|
|
42
|
+
Version
|
|
43
|
+
-------
|
|
44
|
+
The installed version is accessible as :data:`plotstyle.__version__`.
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
from __future__ import annotations
|
|
48
|
+
|
|
49
|
+
# ---------------------------------------------------------------------------
|
|
50
|
+
# Package metadata
|
|
51
|
+
# ---------------------------------------------------------------------------
|
|
52
|
+
from importlib.metadata import PackageNotFoundError
|
|
53
|
+
from importlib.metadata import version as _pkg_version
|
|
54
|
+
|
|
55
|
+
# ---------------------------------------------------------------------------
|
|
56
|
+
# Colour utilities
|
|
57
|
+
# ---------------------------------------------------------------------------
|
|
58
|
+
from plotstyle.color.accessibility import preview_colorblind
|
|
59
|
+
from plotstyle.color.grayscale import preview_grayscale
|
|
60
|
+
from plotstyle.color.palettes import palette
|
|
61
|
+
|
|
62
|
+
# ---------------------------------------------------------------------------
|
|
63
|
+
# Core — style application, figure construction, export, spec migration
|
|
64
|
+
# ---------------------------------------------------------------------------
|
|
65
|
+
from plotstyle.core.export import export_submission, savefig
|
|
66
|
+
from plotstyle.core.figure import figure, subplots
|
|
67
|
+
from plotstyle.core.migrate import SpecDiff, diff, migrate
|
|
68
|
+
from plotstyle.core.style import JournalStyle, use
|
|
69
|
+
|
|
70
|
+
# ---------------------------------------------------------------------------
|
|
71
|
+
# Seaborn integration
|
|
72
|
+
# ---------------------------------------------------------------------------
|
|
73
|
+
from plotstyle.integrations.seaborn import patch_seaborn, plotstyle_theme, unpatch_seaborn
|
|
74
|
+
|
|
75
|
+
# ---------------------------------------------------------------------------
|
|
76
|
+
# Preview helpers
|
|
77
|
+
# ---------------------------------------------------------------------------
|
|
78
|
+
from plotstyle.preview.gallery import gallery
|
|
79
|
+
from plotstyle.preview.print_size import preview_print_size
|
|
80
|
+
|
|
81
|
+
# ---------------------------------------------------------------------------
|
|
82
|
+
# Journal spec registry
|
|
83
|
+
# ---------------------------------------------------------------------------
|
|
84
|
+
from plotstyle.specs import registry
|
|
85
|
+
|
|
86
|
+
# ---------------------------------------------------------------------------
|
|
87
|
+
# Validation
|
|
88
|
+
# ---------------------------------------------------------------------------
|
|
89
|
+
from plotstyle.validation import validate
|
|
90
|
+
|
|
91
|
+
try:
|
|
92
|
+
__version__: str = _pkg_version("plotstyle")
|
|
93
|
+
except PackageNotFoundError:
|
|
94
|
+
__version__ = "0.0.0+unknown"
|
|
95
|
+
|
|
96
|
+
# ---------------------------------------------------------------------------
|
|
97
|
+
# Public API surface
|
|
98
|
+
# ---------------------------------------------------------------------------
|
|
99
|
+
|
|
100
|
+
__all__: list[str] = [
|
|
101
|
+
"JournalStyle",
|
|
102
|
+
"SpecDiff",
|
|
103
|
+
"__version__",
|
|
104
|
+
"diff",
|
|
105
|
+
"export_submission",
|
|
106
|
+
"figure",
|
|
107
|
+
"gallery",
|
|
108
|
+
"migrate",
|
|
109
|
+
"palette",
|
|
110
|
+
"patch_seaborn",
|
|
111
|
+
"plotstyle_theme",
|
|
112
|
+
"preview_colorblind",
|
|
113
|
+
"preview_grayscale",
|
|
114
|
+
"preview_print_size",
|
|
115
|
+
"registry",
|
|
116
|
+
"savefig",
|
|
117
|
+
"subplots",
|
|
118
|
+
"unpatch_seaborn",
|
|
119
|
+
"use",
|
|
120
|
+
"validate",
|
|
121
|
+
]
|
|
File without changes
|
plotstyle/_utils/io.py
ADDED
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
"""File I/O helpers for PlotStyle.
|
|
2
|
+
|
|
3
|
+
This module provides thin wrappers around common file operations used
|
|
4
|
+
throughout PlotStyle:
|
|
5
|
+
|
|
6
|
+
- **TOML loading** — :func:`load_toml` reads journal specification and
|
|
7
|
+
configuration files. On Python 3.11+ the standard library
|
|
8
|
+
:mod:`tomllib` module is used; on older Python versions the third-party
|
|
9
|
+
:mod:`tomli` back-port is required.
|
|
10
|
+
|
|
11
|
+
Python version handling
|
|
12
|
+
-----------------------
|
|
13
|
+
TOML support was added to the Python standard library in 3.11 (PEP 680).
|
|
14
|
+
For Python 3.10 and earlier, PlotStyle requires the ``tomli`` package, which
|
|
15
|
+
is API-compatible with ``tomllib`` and is a zero-dependency pure-Python
|
|
16
|
+
library. The import shim at module level ensures the rest of the codebase
|
|
17
|
+
imports only from this module, insulating it from the stdlib/backport split.
|
|
18
|
+
|
|
19
|
+
Example
|
|
20
|
+
-------
|
|
21
|
+
>>> from pathlib import Path
|
|
22
|
+
>>> from plotstyle._utils.io import load_toml
|
|
23
|
+
>>> config = load_toml(Path("pyproject.toml"))
|
|
24
|
+
>>> config["project"]["name"]
|
|
25
|
+
'plotstyle'
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
from __future__ import annotations
|
|
29
|
+
|
|
30
|
+
import sys
|
|
31
|
+
from typing import TYPE_CHECKING, Any
|
|
32
|
+
|
|
33
|
+
if TYPE_CHECKING:
|
|
34
|
+
from pathlib import Path
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
# ---------------------------------------------------------------------------
|
|
38
|
+
# TOML back-compat shim
|
|
39
|
+
# ---------------------------------------------------------------------------
|
|
40
|
+
|
|
41
|
+
# tomllib became part of the standard library in Python 3.11 (PEP 680).
|
|
42
|
+
# For earlier versions we fall back to the 'tomli' third-party package, which
|
|
43
|
+
# is API-identical and is a declared dependency of PlotStyle for Python < 3.11.
|
|
44
|
+
if sys.version_info >= (3, 11):
|
|
45
|
+
import tomllib
|
|
46
|
+
else:
|
|
47
|
+
try:
|
|
48
|
+
import tomli as tomllib # type: ignore[no-redef]
|
|
49
|
+
except ImportError as exc: # pragma: no cover
|
|
50
|
+
raise ImportError(
|
|
51
|
+
"PlotStyle requires the 'tomli' package on Python < 3.11. "
|
|
52
|
+
"Install it with: pip install tomli"
|
|
53
|
+
) from exc
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
# ---------------------------------------------------------------------------
|
|
57
|
+
# Public helpers
|
|
58
|
+
# ---------------------------------------------------------------------------
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def load_toml(path: Path) -> dict[str, Any]:
|
|
62
|
+
"""Load and parse a TOML file into a Python dictionary.
|
|
63
|
+
|
|
64
|
+
Opens *path* in binary mode (as required by both :mod:`tomllib` and
|
|
65
|
+
:mod:`tomli`) and returns the fully parsed document. All standard TOML
|
|
66
|
+
types are mapped to their Python equivalents: strings, integers, floats,
|
|
67
|
+
booleans, datetimes, arrays, and inline tables.
|
|
68
|
+
|
|
69
|
+
Args:
|
|
70
|
+
path: Filesystem path to the ``.toml`` file. Must be an existing,
|
|
71
|
+
readable regular file. :class:`~pathlib.Path` is the canonical
|
|
72
|
+
type, but any object implementing the :meth:`open` protocol with
|
|
73
|
+
binary-read support is accepted.
|
|
74
|
+
|
|
75
|
+
Returns
|
|
76
|
+
-------
|
|
77
|
+
A :class:`dict` containing the top-level keys and values parsed from
|
|
78
|
+
the TOML document. Nested tables become nested dicts; TOML arrays
|
|
79
|
+
become Python lists.
|
|
80
|
+
|
|
81
|
+
Raises
|
|
82
|
+
------
|
|
83
|
+
FileNotFoundError: If *path* does not exist on the filesystem.
|
|
84
|
+
IsADirectoryError: If *path* points to a directory rather than a file.
|
|
85
|
+
PermissionError: If the process does not have read permission for
|
|
86
|
+
*path*.
|
|
87
|
+
OSError: For other low-level I/O failures (e.g., broken filesystem,
|
|
88
|
+
NFS timeout).
|
|
89
|
+
tomllib.TOMLDecodeError: If the file content is not valid TOML. The
|
|
90
|
+
exception message includes the line and column of the syntax error.
|
|
91
|
+
|
|
92
|
+
Example:
|
|
93
|
+
>>> from pathlib import Path
|
|
94
|
+
>>> from plotstyle._utils.io import load_toml
|
|
95
|
+
>>> data = load_toml(Path("journals/nature.toml"))
|
|
96
|
+
>>> data["journal"]["name"]
|
|
97
|
+
'Nature'
|
|
98
|
+
|
|
99
|
+
Notes
|
|
100
|
+
-----
|
|
101
|
+
- Both :mod:`tomllib` (stdlib, Python ≥ 3.11) and :mod:`tomli`
|
|
102
|
+
(backport, Python < 3.11) require the file to be opened in *binary*
|
|
103
|
+
mode (``"rb"``). This module handles that detail internally so
|
|
104
|
+
callers can treat :func:`load_toml` as a plain text-file loader.
|
|
105
|
+
- No caching is performed; each call re-reads the file from disk.
|
|
106
|
+
For files that are loaded repeatedly (e.g., palette data), callers
|
|
107
|
+
should cache the result at the call site.
|
|
108
|
+
- The function does not validate the contents of the parsed dictionary
|
|
109
|
+
beyond what the TOML spec requires. Schema validation is the
|
|
110
|
+
responsibility of the caller.
|
|
111
|
+
"""
|
|
112
|
+
with path.open("rb") as fh:
|
|
113
|
+
return tomllib.load(fh)
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
"""Warning hierarchy for PlotStyle.
|
|
2
|
+
|
|
3
|
+
All warnings emitted by PlotStyle derive from :class:`PlotStyleWarning`, which
|
|
4
|
+
itself subclasses the built-in :class:`UserWarning`. This two-level hierarchy
|
|
5
|
+
lets users selectively filter or silence PlotStyle-specific warnings without
|
|
6
|
+
affecting unrelated user warnings from other packages:
|
|
7
|
+
|
|
8
|
+
.. code-block:: python
|
|
9
|
+
|
|
10
|
+
import warnings
|
|
11
|
+
from plotstyle._utils.warnings import PlotStyleWarning
|
|
12
|
+
|
|
13
|
+
# Silence all PlotStyle warnings at once.
|
|
14
|
+
warnings.filterwarnings("ignore", category=PlotStyleWarning)
|
|
15
|
+
|
|
16
|
+
# Or silence only a specific sub-class.
|
|
17
|
+
from plotstyle._utils.warnings import FontFallbackWarning
|
|
18
|
+
|
|
19
|
+
warnings.filterwarnings("ignore", category=FontFallbackWarning)
|
|
20
|
+
|
|
21
|
+
Warning classes
|
|
22
|
+
---------------
|
|
23
|
+
.. autosummary::
|
|
24
|
+
|
|
25
|
+
PlotStyleWarning
|
|
26
|
+
FontFallbackWarning
|
|
27
|
+
|
|
28
|
+
Design notes
|
|
29
|
+
------------
|
|
30
|
+
- All warning classes are intentionally kept as thin stubs. They carry no
|
|
31
|
+
data attributes beyond the message string inherited from :class:`Warning`.
|
|
32
|
+
If structured warning data is ever needed, add it as constructor arguments
|
|
33
|
+
on the specific subclass rather than on the base class.
|
|
34
|
+
- ``pass`` is omitted from class bodies by convention (a docstring is
|
|
35
|
+
sufficient to make the body non-empty), improving readability in IDEs and
|
|
36
|
+
documentation renderers.
|
|
37
|
+
- Inheriting from :class:`UserWarning` (via :class:`PlotStyleWarning`) ensures
|
|
38
|
+
that ``warnings.warn(..., stacklevel=2)`` displays the caller's frame, not
|
|
39
|
+
the internals of PlotStyle, in the warning message.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
from __future__ import annotations
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class PlotStyleWarning(UserWarning):
|
|
46
|
+
"""Base class for all PlotStyle warnings.
|
|
47
|
+
|
|
48
|
+
Subclass :class:`UserWarning` so that PlotStyle warnings are visible by
|
|
49
|
+
default (Python does not suppress ``UserWarning`` the way it suppresses
|
|
50
|
+
:class:`DeprecationWarning`), while still being filterable as a group.
|
|
51
|
+
|
|
52
|
+
All other PlotStyle warning classes inherit from this class. To silence
|
|
53
|
+
every warning from PlotStyle in one call, filter on this base class:
|
|
54
|
+
|
|
55
|
+
.. code-block:: python
|
|
56
|
+
|
|
57
|
+
import warnings
|
|
58
|
+
|
|
59
|
+
warnings.filterwarnings("ignore", category=PlotStyleWarning)
|
|
60
|
+
|
|
61
|
+
Example:
|
|
62
|
+
>>> import warnings
|
|
63
|
+
>>> from plotstyle._utils.warnings import PlotStyleWarning
|
|
64
|
+
>>> warnings.warn("example", PlotStyleWarning, stacklevel=2)
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
class FontFallbackWarning(PlotStyleWarning):
|
|
69
|
+
"""Raised when a preferred font is unavailable and a fallback is used.
|
|
70
|
+
|
|
71
|
+
PlotStyle attempts to activate journal-specific fonts (e.g., Helvetica
|
|
72
|
+
Neue for Nature, Times New Roman for IEEE). When the requested font is
|
|
73
|
+
not installed on the system, Matplotlib falls back to its default font
|
|
74
|
+
family. This warning is emitted in that situation so that authors are
|
|
75
|
+
aware the rendered figure may not match the journal's typographic
|
|
76
|
+
requirements.
|
|
77
|
+
|
|
78
|
+
Example:
|
|
79
|
+
>>> import warnings
|
|
80
|
+
>>> from plotstyle._utils.warnings import FontFallbackWarning
|
|
81
|
+
>>> warnings.warn(
|
|
82
|
+
... "Font 'Helvetica Neue' not found; falling back to 'DejaVu Sans'.",
|
|
83
|
+
... FontFallbackWarning,
|
|
84
|
+
... stacklevel=2,
|
|
85
|
+
... )
|
|
86
|
+
"""
|
plotstyle/_version.py
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
# file generated by vcs-versioning
|
|
2
|
+
# don't change, don't track in version control
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
__all__ = [
|
|
6
|
+
"__version__",
|
|
7
|
+
"__version_tuple__",
|
|
8
|
+
"version",
|
|
9
|
+
"version_tuple",
|
|
10
|
+
"__commit_id__",
|
|
11
|
+
"commit_id",
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
version: str
|
|
15
|
+
__version__: str
|
|
16
|
+
__version_tuple__: tuple[int | str, ...]
|
|
17
|
+
version_tuple: tuple[int | str, ...]
|
|
18
|
+
commit_id: str | None
|
|
19
|
+
__commit_id__: str | None
|
|
20
|
+
|
|
21
|
+
__version__ = version = '0.1.0a1'
|
|
22
|
+
__version_tuple__ = version_tuple = (0, 1, 0, 'a1')
|
|
23
|
+
|
|
24
|
+
__commit_id__ = commit_id = None
|
|
File without changes
|