Changes for version 0.313 - 2026-09-15

  • A "plot.type" that isn't defined
    • A misspelled `plot.type` now says what you wrote and which of the defined types it resembles, the way a misspelled option has since 0.313: `'plot.type' => 'barr'` dies with `"barr" isn't a defined plot.type, perhaps you meant one of these defined plot types: (bar, barh)`. It is matched by the same rules as an option name, so separators are ignored (`hist_2d` finds `hist2d`, `coloredtable` finds `colored_table`), two transposed characters count as one mistake (`pye` finds `pie`), and a type reached for by part of its name is found (`box` finds `boxplot`). When nothing resembles what was typed, all fifteen types are printed.
    • A misspelled `plot.type` at a subplot no longer dies inside perl. It reached the dispatch table as an undefined code reference and came out as `Can't use an undefined value as a subroutine reference at .../Simple.pm line 2862`, which names neither the type nor the subplot; the same mistake in a single plot printed the whole arguments hash through `Data::Printer` and died `barr isn't defined`. Both now get the message above, and a subplot's says which subplot: `"violn" isn't a defined plot.type at subplot 1`.
    • The type given for an `add` graph is checked the same way, and says which graph it belongs to -- `"pye" isn't a defined plot.type for an "add" graph at subplot 1` -- rather than the previous `"pye" isn't a known plot.type at ax = 1`, which offered nothing.
    • Every subplot's type is checked before the figure is written rather than as each subplot is reached, so a bad type at the last subplot is reported without the earlier ones having been generated first.
    • The type is checked before the options are. With an unrecognised type there is no per-type option list to check the options against, so `plt` fell back to the union of every type's, and `'plot.type' => 'barr', notch => 'True'` reported neither the type nor the option: `notch` is on some type's list, so it passed, and the caller was told the type was wrong only much later, by `Data::Printer`. The message is now about the type, which is the thing that does not exist.
  • Bug fixes
    • The subplot indices reported by "The above subplot indices are missing "plot.type"" are now the indices of the subplots that are missing one. The counter was incremented only on the subplots that had already failed the test, so three subplots with the third missing its type reported index 0.
  • Testing
    • `t/04.options.t` covers the above: the eight misspellings above at a single plot, the three nested places a type can be given, a type resembling nothing, and a misspelled type given together with another type's option.
    • An `unlike()` in `t/04.options.t` was matching against the `Devel::Confess` stack trace as well as the message. Every frame in that trace prints its own arguments, so an option named in the call appears in `$@` whether or not the module mentioned it, and an assertion that the module did *not* name something could fail on a message that was right. The two assertions of that shape now match the message alone, as `t/03.coverage.t` has since 0.312.
  • Error messages for an option that isn't defined
    • An option that no plot type accepts, or that this plot type does not accept, now says what you wrote and what it resembles: `xlim => '0, 20000'` at a bar chart dies with `"xlim" isn't defined for plot.type "bar", perhaps you meant one of these defined keywords: (clim, ylim, set_xlim)`. Previously the module printed the offending key, then dumped all 450-odd accepted keywords through `Data::Printer` and died with "The above args are accepted" -- a list long enough that the answer in it was no easier to find than in the documentation. The full list is still printed, but only when nothing resembles what was typed.
    • The suggestions are drawn from the list the plot type in hand actually accepts, not from the union of every type's, so `bins_` is offered `bins` at a `hist` and is not offered it at a `boxplot`, which has no `bins`. Separators are ignored when matching, so `key_order` finds `key.order` and `show_legend` finds `show.legend`; and two transposed characters count as one mistake, so `widht` finds `width`.
    • An option that is real but belongs to another plot type is named as such: `plot(..., notch => 'True')` now adds `"notch" is a defined keyword, but for plot.type boxplot`. This is the commonest way to land here -- `stacked`, `bins`, `notch` and `whiskers` are all somebody's option -- and the bare refusal read as though the documentation were wrong.
    • `plt` checks a single plot's options against that plot type's list rather than against every type's, so the refusal comes with the suggestions for the type asked for. Nothing new is refused: the helper the plot dispatches to was already checking the same hash against the same list, one step later.
    • `colored_table` was checking its own internal arguments (`fh`, `plot`, `ax`) against the option list, and never checking the caller's options at all. An option belonging to another plot type -- `notch`, say -- therefore passed `plt`, which accepts any type's option, and was then silently ignored rather than refused. `colored_table`'s options are now checked like every other plot type's.
  • `autodie` removed from the module
    • `lib/Matplotlib/Simple.pm` no longer does `use autodie ':all'`. The two builtins it covered now check their own return values, so nothing that used to die returns false and carries on: `binmode` keeps autodie's wording, and the `system` that runs the generated script reports the same three cases autodie told apart -- failed to start, died to a signal, and a non-zero exit -- naming the script each time. `autodie` and `IPC::System::Simple` stay in the prerequisites because `t/01.all.tests.t` still uses them.
    • A generated script that fails to run now prints python's own error. autodie raised the failure from inside `capture`, so the branch that prints the captured STDOUT and STDERR was never reached and the traceback was thrown away: a script that would not parse reported only `"python3" unexpectedly returned exit value 1 at .../Capture/Tiny.pm line 382`, which says nothing about which option was at fault.
    • The generated script is now run as `system('python3', $file)` rather than `system("python3 $file")`. The one-argument form goes through the shell and splits on whitespace, so a temp directory with a space in its name -- ordinary under `C:\Users\<name>\AppData\Local\Temp` on MSWin32 -- ran python3 against a truncated path.
  • `hist` reports how tall its bars are
    • Every subplot drawn with `plot.type => 'hist'` now says the range of its bin heights on STDOUT as it is drawn: `plot 0 hist range = [1, 12]`, over all of the sets drawn into that subplot, `plot 1` being the second subplot of the figure. The heights are matplotlib's -- this module hands it the data and the bins and is told nothing back -- so reading them off the figure by eye was the only way to have them. Nothing is passed to matplotlib to report them, and the file written to `output.file` is byte-for-byte the file that was written before.
    • A successful run's STDOUT is no longer thrown away. The generated script is run inside `capture`, and what it printed was passed on only when it *failed*: `hist2d` has printed its density range since it was added, in 0.09 (commit 651c239), and nobody has ever seen one. Both ranges now reach the terminal.
  • Packaging
    • `IPC::System::Simple` is no longer a prerequisite. Nothing used it directly; it was pulled in by `use autodie ':all'` in `t/01.all.tests.t`, which loads it at compile time whether or not a `system()` is ever fatalised. That test calls only `open`, `close`, `mkdir` and `unlink`, all covered by `:default`, so it now asks for `:default` and the module -- which is not core -- no longer has to be installed to use or test this distribution.
    • `Test::More` and `Test::Exception` moved from the runtime prerequisites to the test prerequisites, where they belong: both are used only by `t/`, and neither is loaded by `lib/Matplotlib/Simple.pm`. Installing the module no longer asks for them. `[Prereqs]` in `dist.ini` is now exactly what the module loads, which was checked by hiding every test-only prerequisite from `@INC` and rendering a plot.
    • `autodie` moved from the runtime prerequisites to the test prerequisites, which is what it now is: `lib/Matplotlib/Simple.pm` stopped using it in this release. It is still declared rather than assumed, because it is core only from perl 5.010001 and this distribution supports 5.010.
    • `META.yml` and `META.json` now carry a `provides` field naming the one module the distribution installs, `Matplotlib::Simple`, and the version it installs. Both files shipped without one, which is the CPANTS `meta_yml_has_provides` failure: a tool that wants to know what this tarball claims to index had to unpack it and scan `lib/` to find out. `dist.ini` gained `[MetaProvides::Package]`, which fills the field in from the gathered `.pm` files.
  • Security policy
    • The distribution now ships a `SECURITY.md`, generated by `Software::Security::Policy::Individual` and following the CPAN Security Group's guidelines, saying where to report a vulnerability and what is supported. `security.policy.pl` in the git repository regenerates it and records why each value in it was chosen; it is not part of the tarball.

Modules

Access Matplotlib from Perl; providing consistent user interface between different plot types