Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 72 additions & 7 deletions .github/workflows/static_analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ on:
- devel

jobs:
static-analysis:
cloc:
runs-on: ubuntu-latest
steps:
- name: Checkout the code
Expand All @@ -24,38 +24,103 @@ jobs:
./cloc --version
./cloc $(git ls-files)

black:
runs-on: ubuntu-latest
steps:
- name: Checkout the code
uses: actions/checkout@v4

- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: "3.11"

- name: Code formatting with black
run: |
pip install black
pip install "black[jupyter]"
black --check src/
black --check tutorials/

isort:
runs-on: ubuntu-latest
steps:
- name: Checkout the code
uses: actions/checkout@v4

- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: "3.11"

- name: Code formatting with isort
run: |
pip install isort
isort --check src/
isort --check tutorials/

- name: Code formatting with prospector
mypy:
runs-on: ubuntu-latest
steps:
- name: Checkout the code
uses: actions/checkout@v4

- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: "3.11"

- name: Type check with mypy
continue-on-error: true
run: |
pip install mypy
mypy src/

- name: Code formatting with prospector
prospector:
runs-on: ubuntu-latest
steps:
- name: Checkout the code
uses: actions/checkout@v4

- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: "3.11"

- name: Static analysis with prospector
continue-on-error: true
run: |
pip install prospector
prospector src/

- name: Code formatting with ruff

ruff:
runs-on: ubuntu-latest
steps:
- name: Checkout the code
uses: actions/checkout@v4

- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: "3.11"

- name: Lint with ruff
continue-on-error: true
run: |
pip install ruff
ruff check src/

- name: Code formatting with pylint
pylint:
runs-on: ubuntu-latest
steps:
- name: Checkout the code
uses: actions/checkout@v4

- name: Setup Python
uses: actions/setup-python@v5
with:
python-version: "3.11"

- name: Lint with pylint
continue-on-error: true
run: |
pip install pylint
Expand Down
55 changes: 48 additions & 7 deletions src/maxplotlib/canvas/canvas.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,13 @@ def to_gridspec_kw(self) -> dict[str, float]:
return {"wspace": self.wspace, "hspace": self.hspace}


def _parse_bool_env_var(name: str, default: bool = False) -> bool:
value = os.getenv(name)
if value is None:
return default
return value.strip().lower() in {"1", "true", "yes", "on"}


def plot_matplotlib(tikzfigure: TikzFigure, ax, layers=None):
"""
Plot all nodes and paths on the provided axis using Matplotlib.
Expand Down Expand Up @@ -179,6 +186,7 @@ def __init__(
dpi: int = 300,
width: str = "5cm",
ratio: str = "golden", # TODO Add literal
usetex: bool | None = None,
subplot_spacing: SubplotSpacing | None = None,
gridspec_kw: Mapping[str, float] | None = None,
):
Expand All @@ -196,6 +204,8 @@ def __init__(
dpi (int): DPI for the figure. Default is 300.
width (str): Width of the figure. Default is "17cm".
ratio (str): Aspect ratio. Default is "golden".
usetex (bool | None): Default text.usetex behavior for this canvas.
If None, read from MAXPLOTLIB_USETEX environment variable.
subplot_spacing (SubplotSpacing): Typed subplot spacing.
Default is SubplotSpacing(wspace=0.08, hspace=0.1).
gridspec_kw (Mapping[str, float]): Optional matplotlib gridspec kwargs.
Expand All @@ -212,6 +222,11 @@ def __init__(
self._dpi = dpi
self._width = width
self._ratio = ratio
self._usetex = (
_parse_bool_env_var("MAXPLOTLIB_USETEX", default=False)
if usetex is None
else usetex
)
if subplot_spacing is not None and gridspec_kw is not None:
raise ValueError("Pass either subplot_spacing or gridspec_kw, not both.")
if subplot_spacing is None and gridspec_kw is None:
Expand Down Expand Up @@ -764,34 +779,43 @@ def plot(
backend: Backends = "matplotlib",
savefig=False,
layers=None,
usetex: bool | None = None,
verbose: bool = False,
):
resolved_usetex = self._usetex if usetex is None else usetex

if verbose:
print(f"Plotting figure using backend: {backend}")

if backend == "matplotlib":
return self.plot_matplotlib(
savefig=savefig,
layers=layers,
usetex=resolved_usetex,
verbose=verbose,
)
elif backend == "plotly":
return self.plot_plotly(savefig=savefig)
return self.plot_plotly(
savefig=savefig,
usetex=resolved_usetex,
verbose=verbose,
)
elif backend == "plotext":
return self.plot_plotext(
savefig=savefig,
layers=layers,
verbose=verbose,
)
elif backend == "tikzfigure":
return self.plot_tikzfigure(savefig=savefig)
return self.plot_tikzfigure(savefig=savefig, verbose=verbose)
else:
raise ValueError(f"Invalid backend: {backend}")

def show(
self,
backend: Backends = "matplotlib",
layers: list | None = None,
usetex: bool | None = None,
verbose: bool = False,
):
if verbose:
Expand All @@ -802,11 +826,13 @@ def show(
backend="matplotlib",
savefig=False,
layers=layers,
usetex=usetex,
verbose=verbose,
)
# self._matplotlib_fig.show()
elif backend == "plotly":
self.plot_plotly(savefig=False)
resolved_usetex = self._usetex if usetex is None else usetex
self.plot_plotly(savefig=False, usetex=resolved_usetex)
elif backend == "plotext":
figure = self.plot_plotext(
savefig=False,
Expand All @@ -827,7 +853,7 @@ def plot_matplotlib(
self,
savefig: bool = False,
layers: list | None = None,
usetex: bool = False,
usetex: bool | None = None,
verbose: bool = False,
):
"""
Expand All @@ -839,7 +865,8 @@ def plot_matplotlib(
if verbose:
print("Generating Matplotlib figure...")

tex_fonts = setup_tex_fonts(fontsize=self.fontsize, usetex=usetex)
resolved_usetex = self._usetex if usetex is None else usetex
tex_fonts = setup_tex_fonts(fontsize=self.fontsize, usetex=resolved_usetex)

setup_plotstyle(
tex_fonts=tex_fonts,
Expand Down Expand Up @@ -1003,18 +1030,28 @@ def plot_plotext(
self._plotext_figure = wrapped
return wrapped

def plot_plotly(self, show=True, savefig=None, usetex=False):
def plot_plotly(
self,
show=True,
savefig=None,
usetex: bool | None = None,
verbose: bool = False,
):
"""
Generate and optionally display the subplots using Plotly.

Parameters:
show (bool): Whether to display the plot.
savefig (str, optional): Filename to save the figure if provided.
verbose (bool): Whether to print verbose output.

"""

resolved_usetex = self._usetex if usetex is None else usetex

setup_tex_fonts(
fontsize=self.fontsize,
usetex=usetex,
usetex=resolved_usetex,
) # adjust or redefine for Plotly if needed

# Set default width and height if not specified
Expand Down Expand Up @@ -1106,6 +1143,10 @@ def label(self):
def figsize(self):
return self._figsize

@property
def usetex(self):
return self._usetex

@property
def subplot_matrix(self):
return self._subplot_matrix
Expand Down
43 changes: 43 additions & 0 deletions src/maxplotlib/tests/test_canvas.py
Original file line number Diff line number Diff line change
Expand Up @@ -270,5 +270,48 @@ def test_canvas_subplots_spacing_args_and_explicit_spacing_are_mutually_exclusiv
)


def test_canvas_usetex_reads_environment_default(monkeypatch):
from maxplotlib import Canvas

monkeypatch.setenv("MAXPLOTLIB_USETEX", "true")
canvas = Canvas()
assert canvas.usetex is True


def test_canvas_usetex_constructor_overrides_environment(monkeypatch):
from maxplotlib import Canvas

monkeypatch.setenv("MAXPLOTLIB_USETEX", "true")
canvas = Canvas(usetex=False)
assert canvas.usetex is False


def test_canvas_plot_usetex_precedence(monkeypatch):
import matplotlib.pyplot as plt

import maxplotlib.canvas.canvas as canvas_module
from maxplotlib import Canvas

captured: list[bool] = []

def fake_setup_tex_fonts(fontsize=14, usetex=False):
captured.append(usetex)
return {}

monkeypatch.setattr(canvas_module, "setup_tex_fonts", fake_setup_tex_fonts)

canvas = Canvas(usetex=True)
subplot = canvas.add_subplot()
subplot.plot([0, 1], [0, 1])

fig, _ = canvas.plot_matplotlib()
plt.close(fig)

fig, _ = canvas.plot_matplotlib(usetex=False)
plt.close(fig)

assert captured == [True, False]


if __name__ == "__main__":
test()
Loading