mgplot 0.2.23__tar.gz → 0.2.25__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 (58) hide show
  1. mgplot-0.2.25/.python-version +1 -0
  2. {mgplot-0.2.23 → mgplot-0.2.25}/CHANGELOG.md +60 -0
  3. {mgplot-0.2.23 → mgplot-0.2.25}/PKG-INFO +69 -1
  4. {mgplot-0.2.23 → mgplot-0.2.25}/README.md +68 -0
  5. {mgplot-0.2.23 → mgplot-0.2.25}/docs/mgplot/finalise_plot.html +346 -338
  6. {mgplot-0.2.23 → mgplot-0.2.25}/docs/mgplot/summary_plot.html +356 -348
  7. {mgplot-0.2.23 → mgplot-0.2.25}/docs/mgplot.html +443 -378
  8. mgplot-0.2.25/docs/search.js +46 -0
  9. {mgplot-0.2.23 → mgplot-0.2.25}/pyproject.toml +1 -1
  10. {mgplot-0.2.23 → mgplot-0.2.25}/src/mgplot/__init__.py +4 -2
  11. {mgplot-0.2.23 → mgplot-0.2.25}/src/mgplot/axis_utils.py +64 -7
  12. {mgplot-0.2.23 → mgplot-0.2.25}/src/mgplot/bar_plot.py +10 -5
  13. {mgplot-0.2.23 → mgplot-0.2.25}/src/mgplot/fill_between_plot.py +8 -1
  14. {mgplot-0.2.23 → mgplot-0.2.25}/src/mgplot/finalise_plot.py +10 -3
  15. {mgplot-0.2.23 → mgplot-0.2.25}/src/mgplot/growth_plot.py +8 -1
  16. {mgplot-0.2.23 → mgplot-0.2.25}/src/mgplot/keyword_checking.py +4 -1
  17. {mgplot-0.2.23 → mgplot-0.2.25}/src/mgplot/line_plot.py +8 -2
  18. {mgplot-0.2.23 → mgplot-0.2.25}/src/mgplot/run_plot.py +6 -1
  19. {mgplot-0.2.23 → mgplot-0.2.25}/src/mgplot/settings.py +31 -0
  20. {mgplot-0.2.23 → mgplot-0.2.25}/src/mgplot/summary_plot.py +7 -3
  21. mgplot-0.2.25/test/test_chart_subdir.py +82 -0
  22. mgplot-0.2.25/test/test_tick_relabel.py +90 -0
  23. {mgplot-0.2.23 → mgplot-0.2.25}/uv.lock +471 -400
  24. mgplot-0.2.23/docs/search.js +0 -46
  25. {mgplot-0.2.23 → mgplot-0.2.25}/.claude/settings.local.json +0 -0
  26. {mgplot-0.2.23 → mgplot-0.2.25}/.gitignore +0 -0
  27. {mgplot-0.2.23 → mgplot-0.2.25}/.pylintrc +0 -0
  28. {mgplot-0.2.23 → mgplot-0.2.25}/LICENSE +0 -0
  29. {mgplot-0.2.23 → mgplot-0.2.25}/build-all.sh +0 -0
  30. {mgplot-0.2.23 → mgplot-0.2.25}/build-docs.sh +0 -0
  31. {mgplot-0.2.23 → mgplot-0.2.25}/docs/index.html +0 -0
  32. {mgplot-0.2.23 → mgplot-0.2.25}/docs/mgplot/bar_plot.html +0 -0
  33. {mgplot-0.2.23 → mgplot-0.2.25}/docs/mgplot/fill_between_plot.html +0 -0
  34. {mgplot-0.2.23 → mgplot-0.2.25}/docs/mgplot/growth_plot.html +0 -0
  35. {mgplot-0.2.23 → mgplot-0.2.25}/docs/mgplot/line_plot.html +0 -0
  36. {mgplot-0.2.23 → mgplot-0.2.25}/docs/mgplot/postcovid_plot.html +0 -0
  37. {mgplot-0.2.23 → mgplot-0.2.25}/docs/mgplot/revision_plot.html +0 -0
  38. {mgplot-0.2.23 → mgplot-0.2.25}/docs/mgplot/run_plot.html +0 -0
  39. {mgplot-0.2.23 → mgplot-0.2.25}/docs/mgplot/seastrend_plot.html +0 -0
  40. {mgplot-0.2.23 → mgplot-0.2.25}/src/mgplot/colors.py +0 -0
  41. {mgplot-0.2.23 → mgplot-0.2.25}/src/mgplot/finalisers.py +0 -0
  42. {mgplot-0.2.23 → mgplot-0.2.25}/src/mgplot/lint-all.sh +0 -0
  43. {mgplot-0.2.23 → mgplot-0.2.25}/src/mgplot/multi_plot.py +0 -0
  44. {mgplot-0.2.23 → mgplot-0.2.25}/src/mgplot/postcovid_plot.py +0 -0
  45. {mgplot-0.2.23 → mgplot-0.2.25}/src/mgplot/py.typed +0 -0
  46. {mgplot-0.2.23 → mgplot-0.2.25}/src/mgplot/revision_plot.py +0 -0
  47. {mgplot-0.2.23 → mgplot-0.2.25}/src/mgplot/seastrend_plot.py +0 -0
  48. {mgplot-0.2.23 → mgplot-0.2.25}/src/mgplot/utilities.py +0 -0
  49. {mgplot-0.2.23 → mgplot-0.2.25}/test/test-executed.ipynb +0 -0
  50. {mgplot-0.2.23 → mgplot-0.2.25}/test/test.ipynb +0 -0
  51. {mgplot-0.2.23 → mgplot-0.2.25}/test/test_bar_string_index.py +0 -0
  52. {mgplot-0.2.23 → mgplot-0.2.25}/test/test_multi_series_ticks.py +0 -0
  53. {mgplot-0.2.23 → mgplot-0.2.25}/test/test_splat_sequences.py +0 -0
  54. {mgplot-0.2.23 → mgplot-0.2.25}/test/test_zorder.py +0 -0
  55. {mgplot-0.2.23 → mgplot-0.2.25}/test/zz-test-data/ocr_rba.csv +0 -0
  56. {mgplot-0.2.23 → mgplot-0.2.25}/test/zz-test-data/revisions.csv +0 -0
  57. {mgplot-0.2.23 → mgplot-0.2.25}/test/zz-test-data/summary.csv +0 -0
  58. {mgplot-0.2.23 → mgplot-0.2.25}/uv-upgrade.sh +0 -0
@@ -0,0 +1 @@
1
+ 3.14
@@ -1,3 +1,63 @@
1
+ Version 0.2.25 - released 5-Jun-2026 (Canberra, Australia)
2
+
3
+ * enhancement
4
+ - added tick_relabel keyword argument to line_plot(), bar_plot(),
5
+ growth_plot(), fill_between_plot() and run_plot() (and their
6
+ *_finalise variants): a callable applied to each generated x-axis
7
+ tick label after the contextual labellers have run (e.g. shorten
8
+ 4-digit years to 2 digits)
9
+ - tick-label options (max_ticks, label rotation, tick_relabel) are
10
+ now stashed on the Axes by set_labels() and honoured when
11
+ finalise_plot() refreshes the labels before saving. Previously the
12
+ refresh regenerated labels with hard-coded defaults (max_ticks=10,
13
+ rotation=0), silently discarding per-plot settings
14
+ - added register_label_options() / get_label_options() to
15
+ axis_utils.py to support the above
16
+ - keyword checking now validates Callable annotations (checks
17
+ callability; previously a parameterized Callable would error)
18
+ - added test/test_tick_relabel.py covering tick_relabel, max_ticks
19
+ and label_rotation persistence through finalise_plot(), and
20
+ correct-figure close behaviour
21
+ - documented tick labelling and the multi-panel axes_only pattern
22
+ in README.md (new "Axis Tick Labels" and "Multi-Panel Figures"
23
+ sections)
24
+
25
+ * bug fix
26
+ - bar_plot() applied label_rotation via plt.xticks(), which targets
27
+ pyplot's current axes rather than the axes being plotted (wrong
28
+ panel in multi-axes figures), and the rotation was then lost when
29
+ finalise_plot() refreshed PeriodIndex labels. Rotation now goes
30
+ via the stashed label options (PeriodIndex) or set_xticklabels()
31
+ (string index)
32
+ - finalise_plot() closed pyplot's current figure (plt.close()) rather
33
+ than the figure belonging to the axes being finalised; it now closes
34
+ the correct figure (walking up from a SubFigure if necessary)
35
+
36
+ ---
37
+
38
+ Version 0.2.24 - released 4-Jun-2026 (Canberra, Australia)
39
+
40
+ * enhancement
41
+ - added the chart_subdir() context manager to settings.py. It
42
+ temporarily redirects chart output to a subdirectory of the current
43
+ chart_dir, optionally clearing the subdirectory of image files on
44
+ entry (clear=True), and restores the previous chart directory on
45
+ exit, even if an exception is raised. Yields the subdirectory path.
46
+ Useful in notebooks that group saved charts into per-topic
47
+ subdirectories
48
+ - added test/test_chart_subdir.py covering redirect/restore,
49
+ exception safety, clear=True semantics and nested contexts
50
+ - sorted __all__ in __init__.py (removed a duplicate "run_plot"
51
+ entry), fixing a pre-existing RUF022 lint error
52
+ - resolved all remaining mypy/pyright errors with runtime type
53
+ narrowing (no casts): plot_latest_datapoint() in summary_plot.py
54
+ now verifies datapoints are numeric (raising TypeError otherwise)
55
+ before calling float(), and apply_splat_kwargs() in
56
+ finalise_plot.py narrows dynamically-fetched kwarg values before
57
+ passing them to _apply_splat()
58
+
59
+ ---
60
+
1
61
  Version 0.2.23 - released 24-Apr-2026 (Canberra, Australia)
2
62
 
3
63
  * bug fix
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: mgplot
3
- Version: 0.2.23
3
+ Version: 0.2.25
4
4
  Summary: mgplot is a time-series/PeriodIndex frontend for matplotlib
5
5
  Project-URL: Repository, https://github.com/bpalmer4/mgplot
6
6
  Project-URL: Homepage, https://github.com/bpalmer4/mgplot
@@ -94,6 +94,74 @@ ax = mg.line_plot(data)
94
94
  mg.finalise_plot(ax, title="My Chart", ylabel="Units", tag="my_chart")
95
95
  ```
96
96
 
97
+ Axis Tick Labels
98
+ ----------------
99
+ For PeriodIndex data, x-axis tick labels are generated contextually:
100
+ the tick density is chosen to fit within `max_ticks`, and labels show
101
+ the period with years marked at transitions (e.g. a monthly axis shows
102
+ `Feb Mar ... 2024 ... Feb`, a quarterly axis shows `Q2 Q3 2025 Q2`).
103
+
104
+ Three keyword arguments control the labels on the period-indexed plot
105
+ functions (`line_plot`, `bar_plot`, `growth_plot`, `fill_between_plot`,
106
+ `run_plot`, and their `*_finalise` variants):
107
+
108
+ - `max_ticks` -- the maximum number of ticks (suggestive, not exact).
109
+ The global default is `mg.get_setting("max_ticks")`.
110
+ - `tick_relabel` -- a callable applied to each generated label string,
111
+ after the contextual labelling has run. Use it to restyle labels
112
+ without losing the transition logic.
113
+ - `label_rotation` -- (`bar_plot` only) rotates the x-axis tick labels.
114
+
115
+ For example, to convert 4-digit year labels to 2-digit years:
116
+
117
+ ```python
118
+ import re
119
+
120
+ def two_digit_years(label: str) -> str:
121
+ """Shorten 4-digit years to 2 digits (e.g. 2024 -> 24)."""
122
+ return re.sub(r"\b(?:19|20)(\d{2})\b", r"\1", label)
123
+
124
+ # default labels: 2010 2012 2014 ... 2024 2026
125
+ # with tick_relabel: 10 12 14 ... 24 26
126
+ mg.line_plot_finalise(data, title="My Chart", tick_relabel=two_digit_years)
127
+ ```
128
+
129
+ Because `tick_relabel` operates on the label strings, this works
130
+ unchanged on quarterly or monthly axes too: a label such as `2024`
131
+ marking a year transition becomes `24`, while the `Q2`/`Mar` labels
132
+ between transitions pass through untouched.
133
+
134
+ These options are stashed on the matplotlib Axes when the plot is drawn,
135
+ and `finalise_plot()` honours them when it refreshes the tick labels
136
+ just before saving. Editing tick labels directly on the Axes (e.g. with
137
+ `set_xticklabels()`) does not survive that refresh -- use `tick_relabel`
138
+ instead.
139
+
140
+ Multi-Panel Figures
141
+ -------------------
142
+ `finalise_plot()` works on a single Axes. For a figure with several
143
+ panels, finalise each panel with `axes_only=True` (axes-level styling
144
+ only: titles, labels, legends), then make the last call a normal
145
+ `finalise_plot()` carrying the figure-level arguments (`suptitle`,
146
+ `lfooter`, `rfooter`, `figsize`, ...), which also saves and closes
147
+ the figure:
148
+
149
+ ```python
150
+ fig, (ax_left, ax_right) = plt.subplots(1, 2)
151
+ mg.line_plot(left_data, ax=ax_left)
152
+ mg.line_plot(right_data, ax=ax_right)
153
+ mg.finalise_plot(ax_left, title="Left Panel", ylabel="Index", axes_only=True)
154
+ mg.finalise_plot(
155
+ ax_right,
156
+ title="Right Panel",
157
+ ylabel="Index",
158
+ suptitle="Both Panels Together", # also used for the filename
159
+ lfooter="Australia. Seasonally adjusted.",
160
+ rfooter="Source: ABS",
161
+ figsize=(9, 4.5),
162
+ )
163
+ ```
164
+
97
165
  Convenience Finalisers
98
166
  ----------------------
99
167
  For every plot function, there is a `*_finalise()` variant that combines
@@ -78,6 +78,74 @@ ax = mg.line_plot(data)
78
78
  mg.finalise_plot(ax, title="My Chart", ylabel="Units", tag="my_chart")
79
79
  ```
80
80
 
81
+ Axis Tick Labels
82
+ ----------------
83
+ For PeriodIndex data, x-axis tick labels are generated contextually:
84
+ the tick density is chosen to fit within `max_ticks`, and labels show
85
+ the period with years marked at transitions (e.g. a monthly axis shows
86
+ `Feb Mar ... 2024 ... Feb`, a quarterly axis shows `Q2 Q3 2025 Q2`).
87
+
88
+ Three keyword arguments control the labels on the period-indexed plot
89
+ functions (`line_plot`, `bar_plot`, `growth_plot`, `fill_between_plot`,
90
+ `run_plot`, and their `*_finalise` variants):
91
+
92
+ - `max_ticks` -- the maximum number of ticks (suggestive, not exact).
93
+ The global default is `mg.get_setting("max_ticks")`.
94
+ - `tick_relabel` -- a callable applied to each generated label string,
95
+ after the contextual labelling has run. Use it to restyle labels
96
+ without losing the transition logic.
97
+ - `label_rotation` -- (`bar_plot` only) rotates the x-axis tick labels.
98
+
99
+ For example, to convert 4-digit year labels to 2-digit years:
100
+
101
+ ```python
102
+ import re
103
+
104
+ def two_digit_years(label: str) -> str:
105
+ """Shorten 4-digit years to 2 digits (e.g. 2024 -> 24)."""
106
+ return re.sub(r"\b(?:19|20)(\d{2})\b", r"\1", label)
107
+
108
+ # default labels: 2010 2012 2014 ... 2024 2026
109
+ # with tick_relabel: 10 12 14 ... 24 26
110
+ mg.line_plot_finalise(data, title="My Chart", tick_relabel=two_digit_years)
111
+ ```
112
+
113
+ Because `tick_relabel` operates on the label strings, this works
114
+ unchanged on quarterly or monthly axes too: a label such as `2024`
115
+ marking a year transition becomes `24`, while the `Q2`/`Mar` labels
116
+ between transitions pass through untouched.
117
+
118
+ These options are stashed on the matplotlib Axes when the plot is drawn,
119
+ and `finalise_plot()` honours them when it refreshes the tick labels
120
+ just before saving. Editing tick labels directly on the Axes (e.g. with
121
+ `set_xticklabels()`) does not survive that refresh -- use `tick_relabel`
122
+ instead.
123
+
124
+ Multi-Panel Figures
125
+ -------------------
126
+ `finalise_plot()` works on a single Axes. For a figure with several
127
+ panels, finalise each panel with `axes_only=True` (axes-level styling
128
+ only: titles, labels, legends), then make the last call a normal
129
+ `finalise_plot()` carrying the figure-level arguments (`suptitle`,
130
+ `lfooter`, `rfooter`, `figsize`, ...), which also saves and closes
131
+ the figure:
132
+
133
+ ```python
134
+ fig, (ax_left, ax_right) = plt.subplots(1, 2)
135
+ mg.line_plot(left_data, ax=ax_left)
136
+ mg.line_plot(right_data, ax=ax_right)
137
+ mg.finalise_plot(ax_left, title="Left Panel", ylabel="Index", axes_only=True)
138
+ mg.finalise_plot(
139
+ ax_right,
140
+ title="Right Panel",
141
+ ylabel="Index",
142
+ suptitle="Both Panels Together", # also used for the filename
143
+ lfooter="Australia. Seasonally adjusted.",
144
+ rfooter="Source: ABS",
145
+ figsize=(9, 4.5),
146
+ )
147
+ ```
148
+
81
149
  Convenience Finalisers
82
150
  ----------------------
83
151
  For every plot function, there is a `*_finalise()` variant that combines