icplot 0.4.0__tar.gz → 0.6.0__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.
Files changed (89) hide show
  1. icplot-0.6.0/LICENSE +21 -0
  2. {icplot-0.4.0/src/icplot.egg-info → icplot-0.6.0}/PKG-INFO +21 -34
  3. {icplot-0.4.0 → icplot-0.6.0}/README.md +15 -10
  4. icplot-0.6.0/pyproject.toml +110 -0
  5. {icplot-0.4.0 → icplot-0.6.0}/src/icplot/charts/tree.py +1 -1
  6. {icplot-0.4.0 → icplot-0.6.0}/src/icplot/converter/image.py +14 -9
  7. {icplot-0.4.0 → icplot-0.6.0}/src/icplot/graph/plot_group.py +0 -1
  8. {icplot-0.4.0 → icplot-0.6.0}/src/icplot/graph/series.py +0 -3
  9. {icplot-0.4.0 → icplot-0.6.0}/src/icplot/renderer/charts/pgfgantt.py +1 -1
  10. {icplot-0.4.0 → icplot-0.6.0}/src/icplot/renderer/formats/mermaid.py +5 -4
  11. {icplot-0.4.0 → icplot-0.6.0}/src/icplot/renderer/formats/tex.py +7 -4
  12. {icplot-0.4.0 → icplot-0.6.0}/src/icplot/renderer/graph/matplotlib.py +15 -6
  13. {icplot-0.4.0 → icplot-0.6.0}/src/icplot/renderer/graph/mpl.py +3 -1
  14. {icplot-0.4.0 → icplot-0.6.0}/src/icplot/renderer/graph/renderer.py +16 -8
  15. {icplot-0.4.0 → icplot-0.6.0}/src/icplot/renderer/render.py +0 -1
  16. {icplot-0.4.0 → icplot-0.6.0}/src/icplot/renderer/scene/cairo.py +9 -4
  17. {icplot-0.4.0 → icplot-0.6.0}/src/icplot/theme/color.py +1 -1
  18. {icplot-0.4.0 → icplot-0.6.0}/src/icplot/theme/theme_generator.py +2 -0
  19. {icplot-0.4.0 → icplot-0.6.0/src/icplot.egg-info}/PKG-INFO +21 -34
  20. {icplot-0.4.0 → icplot-0.6.0}/src/icplot.egg-info/SOURCES.txt +1 -22
  21. icplot-0.6.0/src/icplot.egg-info/requires.txt +21 -0
  22. {icplot-0.4.0 → icplot-0.6.0}/test/test_cairo.py +1 -2
  23. {icplot-0.4.0 → icplot-0.6.0}/test/test_frontmatter.py +1 -2
  24. {icplot-0.4.0 → icplot-0.6.0}/test/test_image.py +1 -3
  25. {icplot-0.4.0 → icplot-0.6.0}/test/test_mermaid.py +1 -2
  26. {icplot-0.4.0 → icplot-0.6.0}/test/test_puml.py +1 -2
  27. {icplot-0.4.0 → icplot-0.6.0}/test/test_tex.py +1 -2
  28. icplot-0.6.0/test/test_theme_generator.py +33 -0
  29. {icplot-0.4.0 → icplot-0.6.0}/test/test_tree.py +1 -2
  30. icplot-0.4.0/LICENSE +0 -232
  31. icplot-0.4.0/pyproject.toml +0 -197
  32. icplot-0.4.0/src/icplot/geometry/__init__.py +0 -4
  33. icplot-0.4.0/src/icplot/geometry/base.py +0 -68
  34. icplot-0.4.0/src/icplot/geometry/mesh/__init__.py +0 -6
  35. icplot-0.4.0/src/icplot/geometry/mesh/cell.py +0 -74
  36. icplot-0.4.0/src/icplot/geometry/mesh/edge.py +0 -31
  37. icplot-0.4.0/src/icplot/geometry/mesh/mesh.py +0 -133
  38. icplot-0.4.0/src/icplot/geometry/mesh/openfoam/__init__.py +0 -3
  39. icplot-0.4.0/src/icplot/geometry/mesh/openfoam/blockmesh.py +0 -132
  40. icplot-0.4.0/src/icplot/geometry/mesh/openfoam/foamfile.py +0 -118
  41. icplot-0.4.0/src/icplot/geometry/mesh/openfoam/polymesh.py +0 -313
  42. icplot-0.4.0/src/icplot/geometry/mesh/operations.py +0 -120
  43. icplot-0.4.0/src/icplot/geometry/mesh/shapes.py +0 -403
  44. icplot-0.4.0/src/icplot/geometry/mesh/vertex.py +0 -95
  45. icplot-0.4.0/src/icplot/geometry/mesh/vtk.py +0 -47
  46. icplot-0.4.0/src/icplot/geometry/operations.py +0 -57
  47. icplot-0.4.0/src/icplot/geometry/shapes.py +0 -184
  48. icplot-0.4.0/src/icplot/geometry/transform.py +0 -66
  49. icplot-0.4.0/src/icplot/renderer/graph/vtk.py +0 -80
  50. icplot-0.4.0/src/icplot.egg-info/requires.txt +0 -44
  51. icplot-0.4.0/test/test_geometry.py +0 -65
  52. icplot-0.4.0/test/test_meshing.py +0 -132
  53. icplot-0.4.0/test/test_openfoam.py +0 -36
  54. icplot-0.4.0/test/test_polymesh.py +0 -132
  55. {icplot-0.4.0 → icplot-0.6.0}/setup.cfg +0 -0
  56. {icplot-0.4.0 → icplot-0.6.0}/src/icplot/__init__.py +0 -0
  57. {icplot-0.4.0 → icplot-0.6.0}/src/icplot/charts/__init__.py +0 -0
  58. {icplot-0.4.0 → icplot-0.6.0}/src/icplot/charts/gantt.py +0 -0
  59. {icplot-0.4.0 → icplot-0.6.0}/src/icplot/converter/__init__.py +0 -0
  60. {icplot-0.4.0 → icplot-0.6.0}/src/icplot/converter/cli.py +0 -0
  61. {icplot-0.4.0 → icplot-0.6.0}/src/icplot/converter/convert.py +0 -0
  62. {icplot-0.4.0 → icplot-0.6.0}/src/icplot/graph/__init__.py +0 -0
  63. {icplot-0.4.0 → icplot-0.6.0}/src/icplot/graph/axis.py +0 -0
  64. {icplot-0.4.0 → icplot-0.6.0}/src/icplot/graph/plot.py +0 -0
  65. {icplot-0.4.0 → icplot-0.6.0}/src/icplot/main_cli.py +0 -0
  66. {icplot-0.4.0 → icplot-0.6.0}/src/icplot/py.typed +0 -0
  67. {icplot-0.4.0 → icplot-0.6.0}/src/icplot/renderer/__init__.py +0 -0
  68. {icplot-0.4.0 → icplot-0.6.0}/src/icplot/renderer/charts/svggantt.py +0 -0
  69. {icplot-0.4.0 → icplot-0.6.0}/src/icplot/renderer/cli.py +0 -0
  70. {icplot-0.4.0 → icplot-0.6.0}/src/icplot/renderer/config.py +0 -0
  71. {icplot-0.4.0 → icplot-0.6.0}/src/icplot/renderer/formats/__init__.py +0 -0
  72. {icplot-0.4.0 → icplot-0.6.0}/src/icplot/renderer/formats/plantuml.py +0 -0
  73. {icplot-0.4.0 → icplot-0.6.0}/src/icplot/renderer/frontmatter.py +0 -0
  74. {icplot-0.4.0 → icplot-0.6.0}/src/icplot/renderer/graph/__init__.py +0 -0
  75. {icplot-0.4.0 → icplot-0.6.0}/src/icplot/renderer/graph/color.py +0 -0
  76. {icplot-0.4.0 → icplot-0.6.0}/src/icplot/renderer/video.py +0 -0
  77. {icplot-0.4.0 → icplot-0.6.0}/src/icplot/scene.py +0 -0
  78. {icplot-0.4.0 → icplot-0.6.0}/src/icplot/theme/__init__.py +0 -0
  79. {icplot-0.4.0 → icplot-0.6.0}/src/icplot/theme/cli.py +0 -0
  80. {icplot-0.4.0 → icplot-0.6.0}/src/icplot/theme/templates/puml.j2 +0 -0
  81. {icplot-0.4.0 → icplot-0.6.0}/src/icplot/theme/templates/tikz.j2 +0 -0
  82. {icplot-0.4.0 → icplot-0.6.0}/src/icplot/theme/theme.json +0 -0
  83. {icplot-0.4.0 → icplot-0.6.0}/src/icplot/theme/theme.py +0 -0
  84. {icplot-0.4.0 → icplot-0.6.0}/src/icplot.egg-info/dependency_links.txt +0 -0
  85. {icplot-0.4.0 → icplot-0.6.0}/src/icplot.egg-info/entry_points.txt +0 -0
  86. {icplot-0.4.0 → icplot-0.6.0}/src/icplot.egg-info/top_level.txt +0 -0
  87. {icplot-0.4.0 → icplot-0.6.0}/test/test_gantt_chart.py +0 -0
  88. {icplot-0.4.0 → icplot-0.6.0}/test/test_line_plots.py +0 -0
  89. {icplot-0.4.0 → icplot-0.6.0}/test/test_plot_group.py +0 -0
icplot-0.6.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Irish Centre for High End Computing
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -1,14 +1,15 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: icplot
3
- Version: 0.4.0
3
+ Version: 0.6.0
4
4
  Summary: Utilities for generating plots and graphics for technical reports.
5
- Author-email: "James Grogan, Irish Centre for High End Computing" <james.grogan@ichec.ie>
6
- Project-URL: Repository, https://git.ichec.ie/performance/toolshed/icplot
7
- Project-URL: Homepage, https://git.ichec.ie/performance/toolshed/icplot
5
+ Author: Irish Centre for High End Computing
6
+ License: MIT
7
+ Project-URL: Repository, https://git.ichec.ie/platform-engineering/modules/documentation/ichec-plot
8
+ Project-URL: Homepage, https://git.ichec.ie/platform-engineering/modules/documentation/ichec-plot
8
9
  Keywords: Publishing,Technical Reports,Graphics
9
10
  Classifier: Development Status :: 3 - Alpha
10
11
  Classifier: Programming Language :: Python :: 3
11
- Classifier: License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)
12
+ Classifier: License :: OSI Approved :: MIT License
12
13
  Classifier: Operating System :: OS Independent
13
14
  Classifier: Topic :: System :: Distributed Computing
14
15
  Requires-Python: >=3.10
@@ -17,37 +18,18 @@ License-File: LICENSE
17
18
  Requires-Dist: iccore==0.2.3
18
19
  Requires-Dist: graphviz
19
20
  Requires-Dist: Jinja2
20
- Provides-Extra: test
21
- Requires-Dist: pytest; extra == "test"
22
- Requires-Dist: pytest-cov; extra == "test"
23
- Requires-Dist: pytest-sugar; extra == "test"
24
- Requires-Dist: black; extra == "test"
25
- Requires-Dist: isort; extra == "test"
26
- Requires-Dist: mypy; extra == "test"
27
- Requires-Dist: flake8; extra == "test"
28
- Requires-Dist: pylint; extra == "test"
29
- Requires-Dist: types-PyYAML; extra == "test"
30
21
  Provides-Extra: cairo
31
22
  Requires-Dist: CairoSVG; extra == "cairo"
32
- Provides-Extra: scene
33
- Requires-Dist: pycairo; extra == "scene"
34
23
  Provides-Extra: imagemagick
35
24
  Requires-Dist: wand; extra == "imagemagick"
36
25
  Provides-Extra: plot
37
26
  Requires-Dist: matplotlib; extra == "plot"
38
- Provides-Extra: vtk
39
- Requires-Dist: vtk; extra == "vtk"
40
- Provides-Extra: geometry
41
- Requires-Dist: scipy; extra == "geometry"
42
27
  Provides-Extra: video
43
28
  Requires-Dist: moviepy; extra == "video"
44
29
  Provides-Extra: all
45
30
  Requires-Dist: CairoSVG; extra == "all"
46
- Requires-Dist: pycairo; extra == "all"
47
31
  Requires-Dist: wand; extra == "all"
48
32
  Requires-Dist: matplotlib; extra == "all"
49
- Requires-Dist: vtk; extra == "all"
50
- Requires-Dist: scipy; extra == "all"
51
33
  Requires-Dist: moviepy; extra == "all"
52
34
  Dynamic: license-file
53
35
 
@@ -55,6 +37,17 @@ Dynamic: license-file
55
37
 
56
38
  This is a library used at [The Irish Centre for High End Computing (ICHEC)](https://www.ichec.ie) for generating plots, diagrams and other graphics for technical documents.
57
39
 
40
+ # Scope #
41
+
42
+ The library is **frozen to what its consumers use** — the `ichec-handbook`
43
+ build tool and `ichec-flow`. That means `converter`, `renderer`, `theme` and
44
+ `graph`: fixes and maintenance yes, new capability no.
45
+
46
+ The `geometry`/`mesh` 3D and OpenFOAM code has moved to
47
+ [icmesh](https://git.ichec.ie/platform-engineering/modules/ichec-mesh), taking
48
+ VTK and scipy with it — including VTK plot output, which is now
49
+ `icmesh.plot`. Install `icmesh` if you want it.
50
+
58
51
  # Features #
59
52
 
60
53
  The project has three primary modules:
@@ -104,7 +97,6 @@ Supported render inputs and targets are:
104
97
  * Graph
105
98
  * Backends:
106
99
  * Matplotlib
107
- * VTK
108
100
  * Output formats:
109
101
  * SVG
110
102
  * PNG
@@ -125,10 +117,8 @@ Supported render inputs and targets are:
125
117
  * Scene
126
118
  * Backends:
127
119
  * Cairo
128
- * VTK
129
120
  * Output formats:
130
121
  * SVG
131
- * VTK
132
122
 
133
123
  ## Themes ##
134
124
 
@@ -148,14 +138,11 @@ Backends are opt-in via extras:
148
138
 
149
139
  | Extra | Pulls | Enables |
150
140
  |---|---|---|
151
- | `cairo` | CairoSVG | svg → png / pdf conversion |
152
- | `scene` | pycairo | vector scene drawing (`renderer.scene.cairo`) |
141
+ | `cairo` | CairoSVG | svg → png / pdf conversion, and vector scene drawing |
153
142
  | `imagemagick` | wand | pdf → png conversion (falls back to the `magick` CLI) |
154
143
  | `plot` | matplotlib | 2D plotting (`renderer.graph`, `charts`) |
155
- | `vtk` | vtk | 3D / mesh rendering (`geometry.mesh`) |
156
- | `geometry` | scipy | geometry transforms (`geometry.operations`) |
157
144
  | `video` | moviepy | video assembly (`renderer.video`) |
158
- | `all` | all of the above | everything (previous default behaviour) |
145
+ | `all` | all of the above | everything |
159
146
 
160
147
  For example, `pip install icplot[cairo]` for svg conversion, or
161
148
  `pip install icplot[all]` for the full toolkit. The native `cairo` and
@@ -167,7 +154,7 @@ brew install imagemagick cairo
167
154
 
168
155
  # Copyright #
169
156
 
170
- Copyright 2024 Irish Centre for High End Computing
157
+ Copyright 2026 Irish Centre for High End Computing
171
158
 
172
- The software in this repository can be used under the conditions of the GPLv3+ license, which is available for reading in the accompanying LICENSE file.
159
+ The software in this repository can be used under the conditions of the MIT license, which is available for reading in the accompanying LICENSE file.
173
160
 
@@ -2,6 +2,17 @@
2
2
 
3
3
  This is a library used at [The Irish Centre for High End Computing (ICHEC)](https://www.ichec.ie) for generating plots, diagrams and other graphics for technical documents.
4
4
 
5
+ # Scope #
6
+
7
+ The library is **frozen to what its consumers use** — the `ichec-handbook`
8
+ build tool and `ichec-flow`. That means `converter`, `renderer`, `theme` and
9
+ `graph`: fixes and maintenance yes, new capability no.
10
+
11
+ The `geometry`/`mesh` 3D and OpenFOAM code has moved to
12
+ [icmesh](https://git.ichec.ie/platform-engineering/modules/ichec-mesh), taking
13
+ VTK and scipy with it — including VTK plot output, which is now
14
+ `icmesh.plot`. Install `icmesh` if you want it.
15
+
5
16
  # Features #
6
17
 
7
18
  The project has three primary modules:
@@ -51,7 +62,6 @@ Supported render inputs and targets are:
51
62
  * Graph
52
63
  * Backends:
53
64
  * Matplotlib
54
- * VTK
55
65
  * Output formats:
56
66
  * SVG
57
67
  * PNG
@@ -72,10 +82,8 @@ Supported render inputs and targets are:
72
82
  * Scene
73
83
  * Backends:
74
84
  * Cairo
75
- * VTK
76
85
  * Output formats:
77
86
  * SVG
78
- * VTK
79
87
 
80
88
  ## Themes ##
81
89
 
@@ -95,14 +103,11 @@ Backends are opt-in via extras:
95
103
 
96
104
  | Extra | Pulls | Enables |
97
105
  |---|---|---|
98
- | `cairo` | CairoSVG | svg → png / pdf conversion |
99
- | `scene` | pycairo | vector scene drawing (`renderer.scene.cairo`) |
106
+ | `cairo` | CairoSVG | svg → png / pdf conversion, and vector scene drawing |
100
107
  | `imagemagick` | wand | pdf → png conversion (falls back to the `magick` CLI) |
101
108
  | `plot` | matplotlib | 2D plotting (`renderer.graph`, `charts`) |
102
- | `vtk` | vtk | 3D / mesh rendering (`geometry.mesh`) |
103
- | `geometry` | scipy | geometry transforms (`geometry.operations`) |
104
109
  | `video` | moviepy | video assembly (`renderer.video`) |
105
- | `all` | all of the above | everything (previous default behaviour) |
110
+ | `all` | all of the above | everything |
106
111
 
107
112
  For example, `pip install icplot[cairo]` for svg conversion, or
108
113
  `pip install icplot[all]` for the full toolkit. The native `cairo` and
@@ -114,7 +119,7 @@ brew install imagemagick cairo
114
119
 
115
120
  # Copyright #
116
121
 
117
- Copyright 2024 Irish Centre for High End Computing
122
+ Copyright 2026 Irish Centre for High End Computing
118
123
 
119
- The software in this repository can be used under the conditions of the GPLv3+ license, which is available for reading in the accompanying LICENSE file.
124
+ The software in this repository can be used under the conditions of the MIT license, which is available for reading in the accompanying LICENSE file.
120
125
 
@@ -0,0 +1,110 @@
1
+ [project]
2
+ name = "icplot"
3
+ dynamic = ["version"]
4
+ license = { text = "MIT" }
5
+ authors = [
6
+ { name="Irish Centre for High End Computing"},
7
+ ]
8
+ description = "Utilities for generating plots and graphics for technical reports."
9
+ readme = "README.md"
10
+ requires-python = ">=3.10"
11
+ classifiers = [
12
+ "Development Status :: 3 - Alpha",
13
+ "Programming Language :: Python :: 3",
14
+ "License :: OSI Approved :: MIT License",
15
+ "Operating System :: OS Independent",
16
+ "Topic :: System :: Distributed Computing"
17
+ ]
18
+ keywords = ["Publishing", "Technical Reports", "Graphics"]
19
+
20
+ # Core is deliberately light: only what every import path needs. The heavy
21
+ # rendering backends (matplotlib/moviepy/wand) are optional extras
22
+ # so consumers that only use the converter or diagram renderer (e.g. the
23
+ # handbook build) can install a small footprint. Use icplot[all] for the
24
+ # previous behaviour of pulling every backend.
25
+ dependencies = ["iccore==0.2.3",
26
+ "graphviz",
27
+ "Jinja2"]
28
+
29
+ [project.urls]
30
+ Repository = "https://git.ichec.ie/platform-engineering/modules/documentation/ichec-plot"
31
+ Homepage = "https://git.ichec.ie/platform-engineering/modules/documentation/ichec-plot"
32
+
33
+ [project.optional-dependencies]
34
+ # svg/pdf conversion (converter.svg_to_png / svg_to_pdf) and vector scene
35
+ # drawing (renderer.scene.cairo). CairoSVG pulls cairocffi, which dlopens
36
+ # libcairo at runtime — no C build step, and one cairo binding rather than two.
37
+ cairo = ["CairoSVG"]
38
+
39
+ # raster conversion via ImageMagick bindings (converter.pdf_to_png);
40
+ # falls back to the `magick` CLI if wand is absent
41
+ imagemagick = ["wand"]
42
+
43
+ # 2D plotting backend (icplot.renderer.graph / icplot.charts)
44
+ plot = ["matplotlib"]
45
+
46
+ # video assembly (renderer.video, driven from renderer.graph.mpl)
47
+ video = ["moviepy"]
48
+
49
+ # convenience aggregate — every backend
50
+ all = [
51
+ "CairoSVG",
52
+ "wand",
53
+ "matplotlib",
54
+ "moviepy",
55
+ ]
56
+
57
+ [build-system]
58
+ requires = ["setuptools>=64", "setuptools-scm>=8"]
59
+ build-backend = "setuptools.build_meta"
60
+
61
+ # Version derived from the git tag (setuptools-scm). fallback_version keeps
62
+ # non-release jobs building on the shallow CI checkout, which has no tags.
63
+ [tool.setuptools_scm]
64
+ fallback_version = "0.0.0"
65
+
66
+ # Development toolchain (uv-native, PEP 735). CI installs it with
67
+ # `uv sync --group dev`. Deliberately free of the rendering backends: the lint
68
+ # job runs in the standard slim image, and pycairo has no wheel there — it
69
+ # builds against libcairo headers. The test job adds the backends with
70
+ # `--extra all` in an image that has those headers.
71
+ [dependency-groups]
72
+ dev = [
73
+ "ruff",
74
+ "mypy",
75
+ "pytest",
76
+ "pytest-cov",
77
+ "pytest-sugar",
78
+ "types-PyYAML",
79
+ ]
80
+
81
+ [project.scripts]
82
+ icplot = "icplot.main_cli:main_cli"
83
+
84
+ [tool.setuptools.package-data]
85
+ "icplot" = ["py.typed", "controlDict", "theme/theme.json", "theme/templates/*.j2"]
86
+
87
+ [tool.setuptools.packages.find]
88
+ where = ["src"]
89
+
90
+ [tool.mypy]
91
+ ignore_missing_imports = true
92
+
93
+ [tool.ruff]
94
+ line-length = 88
95
+ target-version = "py310"
96
+ src = ["src", "test"]
97
+
98
+ [tool.ruff.lint]
99
+ # E/W pycodestyle, F pyflakes, I import-sort (was isort), N pep8-naming,
100
+ # UP pyupgrade, B bugbear. Extend deliberately, not by pulling in "ALL".
101
+ select = ["E", "W", "F", "I", "N", "UP", "B"]
102
+
103
+ [tool.ruff.lint.isort]
104
+ known-first-party = ["icplot", "utils"]
105
+
106
+ [tool.pytest.ini_options]
107
+ testpaths = ["test",]
108
+ log_cli = 1
109
+ log_cli_level = "debug"
110
+ addopts = "--cov=icplot --cov-report term --cov-fail-under=55 --cov-report xml:coverage.xml --cov-report html"
@@ -1,9 +1,9 @@
1
1
  import logging
2
2
  import os
3
3
  import shutil
4
+ from collections.abc import Callable
4
5
  from dataclasses import dataclass
5
6
  from pathlib import Path
6
- from typing import Callable
7
7
 
8
8
  import graphviz
9
9
 
@@ -45,19 +45,24 @@ def _get_out_filename(source: Path, target: Path | None, extension: str) -> Path
45
45
  return source.parent / f"{source.stem}.{extension}"
46
46
 
47
47
 
48
- # The order of ImageMagick arguments was changed so that `-alpha off`
49
- # is applied after loading the source file rather than before.
50
- # This ensures proper handling when converting PDFs/SVGs,
51
- # as some versions of ImageMagick ignore or misapply `-alpha off` if it
52
- # appears before the input file.
48
+ # `-alpha off` must come *after* the source file: some ImageMagick versions
49
+ # ignore or misapply it when it precedes the input.
53
50
  def _pdf_to_png(source: Path, target: Path | None = None, resolution: int = 300):
54
51
 
55
52
  outfile = _get_out_filename(source, target, "png")
56
53
  os.makedirs(outfile.parent, exist_ok=True)
57
- cmd = (
58
- f"magick -density {resolution} -background white {source} -alpha off {outfile}"
59
- )
60
- subprocess.run(cmd, shell=True, check=True)
54
+ cmd = [
55
+ "magick",
56
+ "-density",
57
+ str(resolution),
58
+ "-background",
59
+ "white",
60
+ str(source),
61
+ "-alpha",
62
+ "off",
63
+ str(outfile),
64
+ ]
65
+ subprocess.run(cmd, check=True)
61
66
 
62
67
 
63
68
  def pdf_to_png(source: Path, target: Path | None = None, resolution: int = 300):
@@ -42,7 +42,6 @@ class LineConfig:
42
42
 
43
43
 
44
44
  class PlotGroupBase(BaseModel, frozen=True):
45
-
46
45
  start_datetime: datetime | None = None
47
46
  end_datetime: datetime | None = None
48
47
  video: VideoConfig | None = None
@@ -16,13 +16,11 @@ from icplot.theme.color import Color
16
16
 
17
17
 
18
18
  class PlotSeriesBase(BaseModel, frozen=True):
19
-
20
19
  min_value: float | None = None
21
20
  max_value: float | None = None
22
21
 
23
22
 
24
23
  class PlotSeriesCreate(PlotSeriesBase, frozen=True):
25
-
26
24
  measurement: str
27
25
  unit_name: str = ""
28
26
 
@@ -36,7 +34,6 @@ class PlotSeriesCreate(PlotSeriesBase, frozen=True):
36
34
 
37
35
 
38
36
  class PlotSeriesPublic(PlotSeriesBase, frozen=True):
39
-
40
37
  measurement: MeasurementPublicWithUnits
41
38
  unit: UnitPublic
42
39
  x: MeasurementPublicWithUnits | None
@@ -40,7 +40,7 @@ def _ganttbar(milestone) -> str:
40
40
 
41
41
  def render(gantt: GanttChart, theme: "Theme | None" = None) -> str:
42
42
 
43
- with open(Path(__file__).parent / "pgfgantt.tex", "r", encoding="utf-8") as f:
43
+ with open(Path(__file__).parent / "pgfgantt.tex", encoding="utf-8") as f:
44
44
  template = f.read()
45
45
 
46
46
  milestones = gantt.milestones
@@ -6,6 +6,7 @@ Mermaid must already be set up on the system, eg. via the npm ecosystem.
6
6
 
7
7
  import logging
8
8
  import os
9
+ import shlex
9
10
  import shutil
10
11
  import subprocess
11
12
  from pathlib import Path
@@ -39,9 +40,9 @@ def render(ctx: RenderContext):
39
40
  if not browser_config and (env_path := os.environ.get("MMDC_PUPPETEER_CONFIG")):
40
41
  browser_config = Path(env_path)
41
42
 
42
- cmd = f"mmdc -i '{ctx.source}' -o '{output_path}'"
43
+ cmd = ["mmdc", "-i", str(ctx.source), "-o", str(output_path)]
43
44
  if browser_config:
44
- cmd += f" -p '{browser_config}'"
45
- logger.debug("Command: %s", cmd)
46
- subprocess.run(cmd, shell=True, check=True)
45
+ cmd += ["-p", str(browser_config)]
46
+ logger.debug("Command: %s", shlex.join(cmd))
47
+ subprocess.run(cmd, check=True)
47
48
  logger.info("Done: %s", output_path.name)
@@ -5,6 +5,7 @@ This module has functionality for publishing Tex documents
5
5
  import hashlib
6
6
  import logging
7
7
  import os
8
+ import shlex
8
9
  import shutil
9
10
  import subprocess
10
11
  from pathlib import Path
@@ -38,12 +39,11 @@ def _run_commands(ctx: RenderContext, work_dir: Path):
38
39
  logger.debug("Engine: %s | work_dir: %s", ctx.config.tex.build_engine, work_dir)
39
40
 
40
41
  if ctx.config.tex.build_engine == "pdflatex":
41
- cmd = f"{ctx.config.tex.build_engine} {' '.join(extra_flags)} '{ctx.source}'"
42
- logger.info("Command: %s", cmd.strip())
42
+ cmd = [ctx.config.tex.build_engine, *extra_flags, str(ctx.source)]
43
+ logger.info("Command: %s", shlex.join(cmd))
43
44
  with open(work_dir / "stdout.txt", "w", encoding="utf-8") as f:
44
45
  subprocess.run(
45
46
  cmd,
46
- shell=True,
47
47
  check=True,
48
48
  cwd=work_dir,
49
49
  env=tex_env,
@@ -91,7 +91,10 @@ def render(ctx: RenderContext):
91
91
  ctx.config.tex.build_engine,
92
92
  )
93
93
 
94
- path_hash = hashlib.md5(str(ctx.source).encode()).hexdigest()[:8]
94
+ # Names a build directory, so collision resistance is all that is wanted.
95
+ path_hash = hashlib.md5(
96
+ str(ctx.source).encode(), usedforsecurity=False
97
+ ).hexdigest()[:8]
95
98
  work_dir = ctx.build_dir / f"{ctx.source.stem}_{path_hash}"
96
99
  work_dir.mkdir(exist_ok=True, parents=True)
97
100
 
@@ -2,9 +2,9 @@ import logging
2
2
  from pathlib import Path
3
3
  from typing import cast
4
4
 
5
- from icplot.graph.series import LinePlotSeries # NOQA
6
5
  from icplot.graph.series import (
7
6
  ImageSeries,
7
+ LinePlotSeries, # NOQA
8
8
  PlotSeries,
9
9
  ScatterPlotSeries,
10
10
  )
@@ -33,6 +33,11 @@ class MatplotlibColorMap(ColorMap):
33
33
  super().__init__(label, mpl.colormaps[label])
34
34
 
35
35
 
36
+ # Module-level singleton rather than a constructed default argument, which
37
+ # would be built once at import time and shared silently across every call.
38
+ _DEFAULT_COLORMAP = MatplotlibColorMap("gist_rainbow")
39
+
40
+
36
41
  def str_exact_values_formatter(x, pos):
37
42
  """
38
43
  When using log scale, axis ticks come out as scientific notation by default,
@@ -158,9 +163,10 @@ def _render(fig, path: Path | None = None):
158
163
  plt.switch_backend("Agg")
159
164
 
160
165
 
161
- def render(
162
- plot: Plot, path: Path | None = None, cmap=MatplotlibColorMap("gist_rainbow")
163
- ):
166
+ def render(plot: Plot, path: Path | None = None, cmap=None):
167
+
168
+ if cmap is None:
169
+ cmap = _DEFAULT_COLORMAP
164
170
 
165
171
  colors = get_series_colors(cmap, plot)
166
172
 
@@ -169,7 +175,8 @@ def render(
169
175
  if len(plot.y_axes) > 1:
170
176
  axs.append(ax.twinx())
171
177
 
172
- for series, color in zip(plot.series, colors):
178
+ # colors is derived from plot.series, so the two are the same length.
179
+ for series, color in zip(plot.series, colors, strict=True):
173
180
  _plot_series(axs, series, color)
174
181
 
175
182
  _set_decorations(axs, plot)
@@ -185,7 +192,9 @@ def render_grid(
185
192
  rows, cols, series = plot.get_subplots(num_samples)
186
193
  fig, axs = plt.subplots(rows, cols)
187
194
 
188
- for ax, series_item in zip(axs, series):
195
+ # strict=False keeps the existing behaviour: plt.subplots(rows, cols) can
196
+ # return more axes than there are series when the grid is not full.
197
+ for ax, series_item in zip(axs, series, strict=False):
189
198
  _plot_series(ax, series_item)
190
199
 
191
200
  _render(fig, path)
@@ -151,7 +151,9 @@ def plot_array(path: Path, series: PlotSeriesPublic, data_series: Series):
151
151
  raise RuntimeError("Attempted to plot series with missing data")
152
152
 
153
153
  for idx, (x, values) in enumerate(
154
- zip(data_series.get_array(data_series.x).data, array.data)
154
+ # strict=False preserves the existing zip-to-shortest behaviour; the
155
+ # x array and the measurement array are not guaranteed equal length.
156
+ zip(data_series.get_array(data_series.x).data, array.data, strict=False)
155
157
  ):
156
158
  fig, ax = plt.subplots()
157
159
 
@@ -1,6 +1,8 @@
1
+ import logging
1
2
  import os
3
+ from collections.abc import Callable
2
4
  from pathlib import Path
3
- from typing import Any, Callable
5
+ from typing import Any
4
6
 
5
7
  from iccore.data import Series
6
8
  from iccore.data.series import convert_units
@@ -12,11 +14,12 @@ from icplot.graph.plot_group import PlotGroupPublic, PlotSeriesPublic
12
14
  from icplot.graph.series import LinePlotSeries
13
15
  from icplot.renderer.graph.matplotlib import render
14
16
 
15
- from . import mpl, vtk
17
+ from . import mpl
16
18
 
19
+ logger = logging.getLogger(__name__)
17
20
 
18
- class PlotConfig(BaseModel, frozen=True):
19
21
 
22
+ class PlotConfig(BaseModel, frozen=True):
20
23
  series_attr: str
21
24
  x_attr: str
22
25
  plots: dict[str, Plot]
@@ -39,7 +42,6 @@ def make_plots(
39
42
  # Each 'y attribute' in the config corresponds to a 'plot' of this
40
43
  # attribute's values.
41
44
  for y_attr, plot_template in config.plots.items():
42
-
43
45
  series = []
44
46
  for label in series_labels:
45
47
  # Get results for this series
@@ -55,7 +57,7 @@ def make_plots(
55
57
  )
56
58
  if not points:
57
59
  continue
58
- x, y = [list(xy) for xy in zip(*points)]
60
+ x, y = [list(xy) for xy in zip(*points, strict=True)]
59
61
 
60
62
  if label in config.colormap:
61
63
  series.append(
@@ -79,13 +81,18 @@ def plot(
79
81
  results: list[Any],
80
82
  config: PlotConfig,
81
83
  point_func: Callable,
82
- output_dir: Path = Path(os.getcwd()),
84
+ output_dir: Path | None = None,
83
85
  ):
84
86
  """
85
87
  For a given set of results and plot config generate the
86
88
  corresponding plots and render them to files.
87
89
  """
88
90
 
91
+ # Resolved per call, not once at import: the previous default froze the
92
+ # working directory at the moment this module was first imported.
93
+ if output_dir is None:
94
+ output_dir = Path(os.getcwd())
95
+
89
96
  for p in make_plots(results, config, point_func):
90
97
  render(p, output_dir / f"{p.name}.svg")
91
98
 
@@ -123,5 +130,6 @@ def plot_series(
123
130
  if "mpl" in group.formats:
124
131
  mpl.plot(output_path, group, series, converted_data_series)
125
132
 
126
- if "vtk" in group.formats and data_series.y:
127
- vtk.save(output_path, group, series, converted_data_series)
133
+ if "vtk" in group.formats:
134
+ # Moved to icmesh.plot warn rather than silently drop the request.
135
+ logger.warning("vtk plot output moved to icmesh; skipping %s", output_path)
@@ -114,7 +114,6 @@ def _setup_tasks(
114
114
 
115
115
  tasks = []
116
116
  for f in sources:
117
-
118
117
  ext = _get_extension(f)
119
118
 
120
119
  output_formats = (
@@ -1,6 +1,8 @@
1
1
  from pathlib import Path
2
2
 
3
- import cairo
3
+ # cairocffi, not pycairo: same drawing API, but it dlopens libcairo instead of
4
+ # building against its headers, and CairoSVG already pulls it in.
5
+ import cairocffi as cairo
4
6
 
5
7
  from icplot.scene import Rectangle, Scene
6
8
 
@@ -54,6 +56,9 @@ def draw_scene(cr, scene: Scene):
54
56
 
55
57
 
56
58
  def render(scene: Scene, path: Path):
57
- with cairo.SVGSurface(path, scene.size[0], scene.size[1]) as surface:
58
- cr = cairo.Context(surface)
59
- draw_scene(cr, scene)
59
+ # cairocffi surfaces take a str and are not context managers.
60
+ surface = cairo.SVGSurface(str(path), scene.size[0], scene.size[1])
61
+ try:
62
+ draw_scene(cairo.Context(surface), scene)
63
+ finally:
64
+ surface.finish()
@@ -3,7 +3,7 @@ This module handles colors
3
3
  """
4
4
 
5
5
  import re
6
- from typing import Callable
6
+ from collections.abc import Callable
7
7
 
8
8
  from pydantic import BaseModel, GetCoreSchemaHandler
9
9
  from pydantic_core import core_schema
@@ -100,6 +100,8 @@ def generate_theme(output_dir: Path, theme_path: Path, template_dir: Path) -> No
100
100
 
101
101
  env = Environment(
102
102
  loader=FileSystemLoader(str(template_dir)),
103
+ # Targets are PlantUML and TeX, not HTML — escaping would corrupt them.
104
+ autoescape=False,
103
105
  keep_trailing_newline=True,
104
106
  trim_blocks=True,
105
107
  lstrip_blocks=True,