icplot 0.3.5__tar.gz → 0.5.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 (90) hide show
  1. icplot-0.5.0/LICENSE +21 -0
  2. icplot-0.5.0/PKG-INFO +172 -0
  3. {icplot-0.3.5 → icplot-0.5.0}/README.md +29 -4
  4. icplot-0.5.0/pyproject.toml +124 -0
  5. {icplot-0.3.5 → icplot-0.5.0}/src/icplot/charts/tree.py +1 -1
  6. {icplot-0.3.5 → icplot-0.5.0}/src/icplot/converter/image.py +14 -9
  7. {icplot-0.3.5 → icplot-0.5.0}/src/icplot/geometry/mesh/mesh.py +0 -1
  8. {icplot-0.3.5 → icplot-0.5.0}/src/icplot/geometry/mesh/openfoam/blockmesh.py +3 -4
  9. {icplot-0.3.5 → icplot-0.5.0}/src/icplot/geometry/mesh/openfoam/foamfile.py +1 -1
  10. {icplot-0.3.5 → icplot-0.5.0}/src/icplot/geometry/mesh/openfoam/polymesh.py +4 -1
  11. {icplot-0.3.5 → icplot-0.5.0}/src/icplot/geometry/mesh/shapes.py +0 -2
  12. {icplot-0.3.5 → icplot-0.5.0}/src/icplot/geometry/mesh/vertex.py +0 -2
  13. {icplot-0.3.5 → icplot-0.5.0}/src/icplot/geometry/shapes.py +0 -2
  14. {icplot-0.3.5 → icplot-0.5.0}/src/icplot/graph/plot_group.py +0 -1
  15. {icplot-0.3.5 → icplot-0.5.0}/src/icplot/graph/series.py +0 -3
  16. {icplot-0.3.5 → icplot-0.5.0}/src/icplot/renderer/charts/pgfgantt.py +1 -1
  17. {icplot-0.3.5 → icplot-0.5.0}/src/icplot/renderer/formats/mermaid.py +5 -4
  18. {icplot-0.3.5 → icplot-0.5.0}/src/icplot/renderer/formats/tex.py +7 -4
  19. {icplot-0.3.5 → icplot-0.5.0}/src/icplot/renderer/graph/matplotlib.py +15 -6
  20. {icplot-0.3.5 → icplot-0.5.0}/src/icplot/renderer/graph/mpl.py +3 -1
  21. {icplot-0.3.5 → icplot-0.5.0}/src/icplot/renderer/graph/renderer.py +9 -5
  22. {icplot-0.3.5 → icplot-0.5.0}/src/icplot/renderer/render.py +0 -1
  23. {icplot-0.3.5 → icplot-0.5.0}/src/icplot/theme/color.py +1 -1
  24. {icplot-0.3.5 → icplot-0.5.0}/src/icplot/theme/theme_generator.py +2 -0
  25. icplot-0.5.0/src/icplot.egg-info/PKG-INFO +172 -0
  26. icplot-0.5.0/src/icplot.egg-info/requires.txt +33 -0
  27. {icplot-0.3.5 → icplot-0.5.0}/test/test_image.py +0 -1
  28. icplot-0.3.5/LICENSE +0 -232
  29. icplot-0.3.5/PKG-INFO +0 -142
  30. icplot-0.3.5/pyproject.toml +0 -164
  31. icplot-0.3.5/src/icplot.egg-info/PKG-INFO +0 -142
  32. icplot-0.3.5/src/icplot.egg-info/requires.txt +0 -23
  33. {icplot-0.3.5 → icplot-0.5.0}/setup.cfg +0 -0
  34. {icplot-0.3.5 → icplot-0.5.0}/src/icplot/__init__.py +0 -0
  35. {icplot-0.3.5 → icplot-0.5.0}/src/icplot/charts/__init__.py +0 -0
  36. {icplot-0.3.5 → icplot-0.5.0}/src/icplot/charts/gantt.py +0 -0
  37. {icplot-0.3.5 → icplot-0.5.0}/src/icplot/converter/__init__.py +0 -0
  38. {icplot-0.3.5 → icplot-0.5.0}/src/icplot/converter/cli.py +0 -0
  39. {icplot-0.3.5 → icplot-0.5.0}/src/icplot/converter/convert.py +0 -0
  40. {icplot-0.3.5 → icplot-0.5.0}/src/icplot/geometry/__init__.py +0 -0
  41. {icplot-0.3.5 → icplot-0.5.0}/src/icplot/geometry/base.py +0 -0
  42. {icplot-0.3.5 → icplot-0.5.0}/src/icplot/geometry/mesh/__init__.py +0 -0
  43. {icplot-0.3.5 → icplot-0.5.0}/src/icplot/geometry/mesh/cell.py +0 -0
  44. {icplot-0.3.5 → icplot-0.5.0}/src/icplot/geometry/mesh/edge.py +0 -0
  45. {icplot-0.3.5 → icplot-0.5.0}/src/icplot/geometry/mesh/openfoam/__init__.py +0 -0
  46. {icplot-0.3.5 → icplot-0.5.0}/src/icplot/geometry/mesh/operations.py +0 -0
  47. {icplot-0.3.5 → icplot-0.5.0}/src/icplot/geometry/mesh/vtk.py +0 -0
  48. {icplot-0.3.5 → icplot-0.5.0}/src/icplot/geometry/operations.py +0 -0
  49. {icplot-0.3.5 → icplot-0.5.0}/src/icplot/geometry/transform.py +0 -0
  50. {icplot-0.3.5 → icplot-0.5.0}/src/icplot/graph/__init__.py +0 -0
  51. {icplot-0.3.5 → icplot-0.5.0}/src/icplot/graph/axis.py +0 -0
  52. {icplot-0.3.5 → icplot-0.5.0}/src/icplot/graph/plot.py +0 -0
  53. {icplot-0.3.5 → icplot-0.5.0}/src/icplot/main_cli.py +0 -0
  54. {icplot-0.3.5 → icplot-0.5.0}/src/icplot/py.typed +0 -0
  55. {icplot-0.3.5 → icplot-0.5.0}/src/icplot/renderer/__init__.py +0 -0
  56. {icplot-0.3.5 → icplot-0.5.0}/src/icplot/renderer/charts/svggantt.py +0 -0
  57. {icplot-0.3.5 → icplot-0.5.0}/src/icplot/renderer/cli.py +0 -0
  58. {icplot-0.3.5 → icplot-0.5.0}/src/icplot/renderer/config.py +0 -0
  59. {icplot-0.3.5 → icplot-0.5.0}/src/icplot/renderer/formats/__init__.py +0 -0
  60. {icplot-0.3.5 → icplot-0.5.0}/src/icplot/renderer/formats/plantuml.py +0 -0
  61. {icplot-0.3.5 → icplot-0.5.0}/src/icplot/renderer/frontmatter.py +0 -0
  62. {icplot-0.3.5 → icplot-0.5.0}/src/icplot/renderer/graph/__init__.py +0 -0
  63. {icplot-0.3.5 → icplot-0.5.0}/src/icplot/renderer/graph/color.py +0 -0
  64. {icplot-0.3.5 → icplot-0.5.0}/src/icplot/renderer/graph/vtk.py +0 -0
  65. {icplot-0.3.5 → icplot-0.5.0}/src/icplot/renderer/scene/cairo.py +0 -0
  66. {icplot-0.3.5 → icplot-0.5.0}/src/icplot/renderer/video.py +0 -0
  67. {icplot-0.3.5 → icplot-0.5.0}/src/icplot/scene.py +0 -0
  68. {icplot-0.3.5 → icplot-0.5.0}/src/icplot/theme/__init__.py +0 -0
  69. {icplot-0.3.5 → icplot-0.5.0}/src/icplot/theme/cli.py +0 -0
  70. {icplot-0.3.5 → icplot-0.5.0}/src/icplot/theme/templates/puml.j2 +0 -0
  71. {icplot-0.3.5 → icplot-0.5.0}/src/icplot/theme/templates/tikz.j2 +0 -0
  72. {icplot-0.3.5 → icplot-0.5.0}/src/icplot/theme/theme.json +0 -0
  73. {icplot-0.3.5 → icplot-0.5.0}/src/icplot/theme/theme.py +0 -0
  74. {icplot-0.3.5 → icplot-0.5.0}/src/icplot.egg-info/SOURCES.txt +0 -0
  75. {icplot-0.3.5 → icplot-0.5.0}/src/icplot.egg-info/dependency_links.txt +0 -0
  76. {icplot-0.3.5 → icplot-0.5.0}/src/icplot.egg-info/entry_points.txt +0 -0
  77. {icplot-0.3.5 → icplot-0.5.0}/src/icplot.egg-info/top_level.txt +0 -0
  78. {icplot-0.3.5 → icplot-0.5.0}/test/test_cairo.py +0 -0
  79. {icplot-0.3.5 → icplot-0.5.0}/test/test_frontmatter.py +0 -0
  80. {icplot-0.3.5 → icplot-0.5.0}/test/test_gantt_chart.py +0 -0
  81. {icplot-0.3.5 → icplot-0.5.0}/test/test_geometry.py +0 -0
  82. {icplot-0.3.5 → icplot-0.5.0}/test/test_line_plots.py +0 -0
  83. {icplot-0.3.5 → icplot-0.5.0}/test/test_mermaid.py +0 -0
  84. {icplot-0.3.5 → icplot-0.5.0}/test/test_meshing.py +0 -0
  85. {icplot-0.3.5 → icplot-0.5.0}/test/test_openfoam.py +0 -0
  86. {icplot-0.3.5 → icplot-0.5.0}/test/test_plot_group.py +0 -0
  87. {icplot-0.3.5 → icplot-0.5.0}/test/test_polymesh.py +0 -0
  88. {icplot-0.3.5 → icplot-0.5.0}/test/test_puml.py +0 -0
  89. {icplot-0.3.5 → icplot-0.5.0}/test/test_tex.py +0 -0
  90. {icplot-0.3.5 → icplot-0.5.0}/test/test_tree.py +0 -0
icplot-0.5.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.
icplot-0.5.0/PKG-INFO ADDED
@@ -0,0 +1,172 @@
1
+ Metadata-Version: 2.4
2
+ Name: icplot
3
+ Version: 0.5.0
4
+ Summary: Utilities for generating plots and graphics for technical reports.
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
9
+ Keywords: Publishing,Technical Reports,Graphics
10
+ Classifier: Development Status :: 3 - Alpha
11
+ Classifier: Programming Language :: Python :: 3
12
+ Classifier: License :: OSI Approved :: MIT License
13
+ Classifier: Operating System :: OS Independent
14
+ Classifier: Topic :: System :: Distributed Computing
15
+ Requires-Python: >=3.10
16
+ Description-Content-Type: text/markdown
17
+ License-File: LICENSE
18
+ Requires-Dist: iccore==0.2.3
19
+ Requires-Dist: graphviz
20
+ Requires-Dist: Jinja2
21
+ Provides-Extra: cairo
22
+ Requires-Dist: CairoSVG; extra == "cairo"
23
+ Provides-Extra: scene
24
+ Requires-Dist: pycairo; extra == "scene"
25
+ Provides-Extra: imagemagick
26
+ Requires-Dist: wand; extra == "imagemagick"
27
+ Provides-Extra: plot
28
+ Requires-Dist: matplotlib; extra == "plot"
29
+ Provides-Extra: vtk
30
+ Requires-Dist: vtk; extra == "vtk"
31
+ Provides-Extra: geometry
32
+ Requires-Dist: scipy; extra == "geometry"
33
+ Provides-Extra: video
34
+ Requires-Dist: moviepy; extra == "video"
35
+ Provides-Extra: all
36
+ Requires-Dist: CairoSVG; extra == "all"
37
+ Requires-Dist: pycairo; extra == "all"
38
+ Requires-Dist: wand; extra == "all"
39
+ Requires-Dist: matplotlib; extra == "all"
40
+ Requires-Dist: vtk; extra == "all"
41
+ Requires-Dist: scipy; extra == "all"
42
+ Requires-Dist: moviepy; extra == "all"
43
+ Dynamic: license-file
44
+
45
+ # icplot
46
+
47
+ 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.
48
+
49
+ # Scope #
50
+
51
+ The library is **frozen to what its consumers use** — the `ichec-handbook`
52
+ build tool and `ichec-flow`. That means `converter`, `renderer`, `theme` and
53
+ `graph`: fixes and maintenance yes, new capability no (planning#136). The
54
+ `geometry`/`mesh` 3D and OpenFOAM code is still here but is scheduled to move
55
+ to a module of its own, and its optional-dependency weight goes with it.
56
+
57
+ # Features #
58
+
59
+ The project has three primary modules:
60
+
61
+ `converter`: Converts between image formats, such as pdf, svg and png
62
+ `renderer`: Render text based inputs as svg, pdf or png images or videos
63
+ `theme`: Generate and apply themes from a design system
64
+
65
+
66
+ ## Converter ##
67
+
68
+ To covert between image formats you can do:
69
+
70
+ ``` shell
71
+ icplot convert --source my_image.svg --target my_image.png
72
+ ```
73
+
74
+ Supported conversions are:
75
+
76
+ * svg -> png
77
+ * svg -> pdf
78
+ * pdf -> png
79
+
80
+ ## Renderer ##
81
+
82
+ Render a range of text based formats to images or videos. For example,
83
+
84
+ ``` shell
85
+ icplot render --file uml_diagram.puml
86
+ ```
87
+
88
+ renders the PlantUML file to png and svg.
89
+
90
+ Supported render inputs and targets are:
91
+
92
+ * PlantUml
93
+ * SVG
94
+ * PNG
95
+ * Latex/Tikz
96
+ * PDF
97
+ * SVG
98
+ * PNG
99
+ * Mermaid
100
+ * SVG
101
+ * PNG
102
+ * Custom JSON
103
+ * Graph
104
+ * Backends:
105
+ * Matplotlib
106
+ * VTK
107
+ * Output formats:
108
+ * SVG
109
+ * PNG
110
+ * MP4
111
+ * Tree
112
+ * Backends:
113
+ * Graphviz
114
+ * Output formats:
115
+ * SVG
116
+ * PNG
117
+ * Gantt
118
+ * Backends:
119
+ * Cairo
120
+ * PgfGantt
121
+ * Output formats:
122
+ * SVG
123
+ * PNG
124
+ * Scene
125
+ * Backends:
126
+ * Cairo
127
+ * VTK
128
+ * Output formats:
129
+ * SVG
130
+ * VTK
131
+
132
+ ## Themes ##
133
+
134
+ The library supports themeing with an Angular Material 3 motivated design system. Plots can be themed by adapting a version of `theme.json` included in this module's source. Currently Tikz and PlantUML support themeing.
135
+
136
+ # Installation #
137
+
138
+ The package is available on PyPI. The core install is deliberately light — it
139
+ does not pull the heavy rendering backends, so consumers that only need part of
140
+ the library (e.g. a docs build that just converts svg to png) stay small:
141
+
142
+ ``` shell
143
+ pip install icplot
144
+ ```
145
+
146
+ Backends are opt-in via extras:
147
+
148
+ | Extra | Pulls | Enables |
149
+ |---|---|---|
150
+ | `cairo` | CairoSVG | svg → png / pdf conversion |
151
+ | `scene` | pycairo | vector scene drawing (`renderer.scene.cairo`) |
152
+ | `imagemagick` | wand | pdf → png conversion (falls back to the `magick` CLI) |
153
+ | `plot` | matplotlib | 2D plotting (`renderer.graph`, `charts`) |
154
+ | `vtk` | vtk | 3D / mesh rendering (`geometry.mesh`) |
155
+ | `geometry` | scipy | geometry transforms (`geometry.operations`) |
156
+ | `video` | moviepy | video assembly (`renderer.video`) |
157
+ | `all` | all of the above | everything (previous default behaviour) |
158
+
159
+ For example, `pip install icplot[cairo]` for svg conversion, or
160
+ `pip install icplot[all]` for the full toolkit. The native `cairo` and
161
+ `imagemagick` system libraries are also required for those backends; on Mac:
162
+
163
+ ``` shell
164
+ brew install imagemagick cairo
165
+ ```
166
+
167
+ # Copyright #
168
+
169
+ Copyright 2026 Irish Centre for High End Computing
170
+
171
+ 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.
172
+
@@ -2,6 +2,14 @@
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 (planning#136). The
10
+ `geometry`/`mesh` 3D and OpenFOAM code is still here but is scheduled to move
11
+ to a module of its own, and its optional-dependency weight goes with it.
12
+
5
13
  # Features #
6
14
 
7
15
  The project has three primary modules:
@@ -83,13 +91,30 @@ The library supports themeing with an Angular Material 3 motivated design system
83
91
 
84
92
  # Installation #
85
93
 
86
- The package is available on PyPI. For a minimal installation you can do:
94
+ The package is available on PyPI. The core install is deliberately light — it
95
+ does not pull the heavy rendering backends, so consumers that only need part of
96
+ the library (e.g. a docs build that just converts svg to png) stay small:
87
97
 
88
98
  ``` shell
89
99
  pip install icplot
90
100
  ```
91
101
 
92
- For full functionality, particularly conversion of image formats, `imagemagick` and `cairo` are required. On Mac you can install them with:
102
+ Backends are opt-in via extras:
103
+
104
+ | Extra | Pulls | Enables |
105
+ |---|---|---|
106
+ | `cairo` | CairoSVG | svg → png / pdf conversion |
107
+ | `scene` | pycairo | vector scene drawing (`renderer.scene.cairo`) |
108
+ | `imagemagick` | wand | pdf → png conversion (falls back to the `magick` CLI) |
109
+ | `plot` | matplotlib | 2D plotting (`renderer.graph`, `charts`) |
110
+ | `vtk` | vtk | 3D / mesh rendering (`geometry.mesh`) |
111
+ | `geometry` | scipy | geometry transforms (`geometry.operations`) |
112
+ | `video` | moviepy | video assembly (`renderer.video`) |
113
+ | `all` | all of the above | everything (previous default behaviour) |
114
+
115
+ For example, `pip install icplot[cairo]` for svg conversion, or
116
+ `pip install icplot[all]` for the full toolkit. The native `cairo` and
117
+ `imagemagick` system libraries are also required for those backends; on Mac:
93
118
 
94
119
  ``` shell
95
120
  brew install imagemagick cairo
@@ -97,7 +122,7 @@ brew install imagemagick cairo
97
122
 
98
123
  # Copyright #
99
124
 
100
- Copyright 2024 Irish Centre for High End Computing
125
+ Copyright 2026 Irish Centre for High End Computing
101
126
 
102
- 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.
127
+ 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.
103
128
 
@@ -0,0 +1,124 @@
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/vtk/scipy/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). CairoSVG pulls
35
+ # cairocffi, which dlopens libcairo at runtime (no C build step needed).
36
+ cairo = [
37
+ "CairoSVG"
38
+ ]
39
+
40
+ # vector scene drawing (renderer.scene.cairo). pycairo (module `cairo`)
41
+ # builds against libcairo — needs the cairo headers + a C toolchain at install.
42
+ scene = ["pycairo"]
43
+
44
+ # raster conversion via ImageMagick bindings (converter.pdf_to_png);
45
+ # falls back to the `magick` CLI if wand is absent
46
+ imagemagick = ["wand"]
47
+
48
+ # 2D plotting backend (icplot.renderer.graph / icplot.charts)
49
+ plot = ["matplotlib"]
50
+
51
+ # 3D / mesh backend (icplot.geometry.mesh, vtk graph backend)
52
+ vtk = ["vtk"]
53
+
54
+ # geometry transforms (icplot.geometry.operations)
55
+ geometry = ["scipy"]
56
+
57
+ # video assembly (icplot.renderer.video)
58
+ video = ["moviepy"]
59
+
60
+ # convenience aggregate — every backend (pre-split behaviour)
61
+ all = [
62
+ "CairoSVG",
63
+ "pycairo",
64
+ "wand",
65
+ "matplotlib",
66
+ "vtk",
67
+ "scipy",
68
+ "moviepy"
69
+ ]
70
+
71
+ [build-system]
72
+ requires = ["setuptools>=64", "setuptools-scm>=8"]
73
+ build-backend = "setuptools.build_meta"
74
+
75
+ # Version derived from the git tag (setuptools-scm). fallback_version keeps
76
+ # non-release jobs building on the shallow CI checkout, which has no tags.
77
+ [tool.setuptools_scm]
78
+ fallback_version = "0.0.0"
79
+
80
+ # Development toolchain (uv-native, PEP 735). CI installs it with
81
+ # `uv sync --group dev`. Deliberately free of the rendering backends: the lint
82
+ # job runs in the standard slim image, and pycairo has no wheel there — it
83
+ # builds against libcairo headers. The test job adds the backends with
84
+ # `--extra all` in an image that has those headers.
85
+ [dependency-groups]
86
+ dev = [
87
+ "ruff",
88
+ "mypy",
89
+ "pytest",
90
+ "pytest-cov",
91
+ "pytest-sugar",
92
+ "types-PyYAML",
93
+ ]
94
+
95
+ [project.scripts]
96
+ icplot = "icplot.main_cli:main_cli"
97
+
98
+ [tool.setuptools.package-data]
99
+ "icplot" = ["py.typed", "controlDict", "theme/theme.json", "theme/templates/*.j2"]
100
+
101
+ [tool.setuptools.packages.find]
102
+ where = ["src"]
103
+
104
+ [tool.mypy]
105
+ ignore_missing_imports = true
106
+
107
+ [tool.ruff]
108
+ line-length = 88
109
+ target-version = "py310"
110
+ src = ["src", "test"]
111
+
112
+ [tool.ruff.lint]
113
+ # E/W pycodestyle, F pyflakes, I import-sort (was isort), N pep8-naming,
114
+ # UP pyupgrade, B bugbear. Extend deliberately, not by pulling in "ALL".
115
+ select = ["E", "W", "F", "I", "N", "UP", "B"]
116
+
117
+ [tool.ruff.lint.isort]
118
+ known-first-party = ["icplot"]
119
+
120
+ [tool.pytest.ini_options]
121
+ testpaths = ["test",]
122
+ log_cli = 1
123
+ log_cli_level = "debug"
124
+ 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):
@@ -27,7 +27,6 @@ class Patch:
27
27
 
28
28
  @dataclass(frozen=True)
29
29
  class Mesh:
30
-
31
30
  vertices: tuple[Vertex, ...]
32
31
  cells: tuple[Cell, ...]
33
32
  scale: float = 1.0
@@ -125,8 +125,7 @@ def generate_mesh(mesh: Mesh, case_dir: Path):
125
125
  if not control_dict.exists():
126
126
  shutil.copy(Path(__file__).parent / "controlDict", control_dict)
127
127
 
128
- cmd = f"blockMesh -case {case_dir}"
129
- subprocess.run(cmd, cwd=case_dir, shell=True, check=True)
128
+ cmd = ["blockMesh", "-case", str(case_dir)]
129
+ subprocess.run(cmd, cwd=case_dir, check=True)
130
130
 
131
- cmd = f"blockMesh -case {case_dir} -write-vtk"
132
- subprocess.run(cmd, cwd=case_dir, shell=True, check=True)
131
+ subprocess.run(cmd + ["-write-vtk"], cwd=case_dir, check=True)
@@ -114,5 +114,5 @@ def parse(content: str) -> FoamFile:
114
114
 
115
115
  def read_foamfile(path: Path) -> FoamFile:
116
116
 
117
- with open(path, "r", encoding="utf-8") as f:
117
+ with open(path, encoding="utf-8") as f:
118
118
  return parse(f.read())
@@ -53,7 +53,10 @@ class Polymesh:
53
53
  class PolymeshPatch:
54
54
  name: str
55
55
  faces: tuple[Face, ...]
56
- startFace: int
56
+ # Mirrors the field name in OpenFOAM's own polyMesh/boundary file, which
57
+ # this class both parses and writes back out — renaming it to snake_case
58
+ # would put the Python attribute out of step with the format on disk.
59
+ startFace: int # noqa: N815
57
60
  type: str = "patch"
58
61
 
59
62
  def has_face(self, face: Face) -> bool:
@@ -102,7 +102,6 @@ def _close_circle(inner_mesh: Mesh, outer_mesh: Mesh, num_circumferential: int)
102
102
  new_cells = []
103
103
  count = 0
104
104
  for idx in range(num_circumferential):
105
-
106
105
  next_id = idx + 1
107
106
  if next_id == num_circumferential:
108
107
  next_id = 0
@@ -317,7 +316,6 @@ def mesh_cuboids(
317
316
  for cuboid in cuboids:
318
317
  vert_ids = []
319
318
  for p in cuboid.points:
320
-
321
319
  if not verts:
322
320
  verts.append(Vertex.from_point(p, 0))
323
321
  vert_ids.append(0)
@@ -57,7 +57,6 @@ def find_closest(verts: list[Vertex], point: Point) -> int:
57
57
  min_id = -1
58
58
  min_distance = 0.0
59
59
  for idx, v in enumerate(verts):
60
-
61
60
  distance = v.get_distance(point)
62
61
 
63
62
  if idx == 0:
@@ -80,7 +79,6 @@ def find_closest_vertex(verts: tuple[Vertex, ...], v: Vertex) -> Vertex:
80
79
  min_id = -1
81
80
  min_distance = 0.0
82
81
  for idx, vert in enumerate(verts):
83
-
84
82
  distance = vert.get_vertex_distance(v)
85
83
 
86
84
  if idx == 0:
@@ -26,13 +26,11 @@ class Shape:
26
26
 
27
27
  @dataclass(frozen=True)
28
28
  class Circle(Shape):
29
-
30
29
  radius: float = 1.0
31
30
 
32
31
 
33
32
  @dataclass(frozen=True)
34
33
  class Annulus(Shape):
35
-
36
34
  outer_radius: float = 1.0
37
35
  inner_radius: float = 0.5
38
36
  angle: float = 360 # degrees
@@ -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)