Add mpl::format and cleanup mplutils · matplotlib/matplotlib@27a1c1e · GitHub
Skip to content

Commit 27a1c1e

Browse files
committed
Add mpl::format and cleanup mplutils
1 parent 003c1b5 commit 27a1c1e

9 files changed

Lines changed: 95 additions & 89 deletions

‎src/_backend_agg.h‎

Lines changed: 3 additions & 3 deletions

‎src/_backend_agg_wrapper.cpp‎

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,8 +129,8 @@ PyRendererAgg_draw_image(RendererAgg *self,
129129
// TODO: This really shouldn't be mutable, but Agg's renderer buffers aren't const.
130130
auto image = image_obj.mutable_unchecked<3>();
131131

132-
x = mpl_round(x);
133-
y = mpl_round(y);
132+
x = mpl::round(x);
133+
y = mpl::round(y);
134134

135135
self->draw_image(gc, x, y, image);
136136
}

‎src/_image_wrapper.cpp‎

Lines changed: 28 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -85,16 +85,16 @@ _get_transform_mesh(const py::object& transform, const py::ssize_t *dims)
8585
py::array_t<double, py::array::c_style | py::array::forcecast>(output_mesh);
8686

8787
if (output_mesh_array.ndim() != 2) {
88-
throw std::runtime_error(
89-
"Inverse transformed mesh array should be 2D not {}D"_s.format(
88+
throw std::runtime_error(mpl::format(
89+
"Inverse transformed mesh array should be 2D not {}D",
9090
output_mesh_array.ndim()));
9191
}
9292

9393
// An undersized mesh would be read out of bounds by the resampler.
9494
if (output_mesh_array.shape(0) != mesh_dims[0] ||
9595
output_mesh_array.shape(1) != mesh_dims[1]) {
96-
throw std::runtime_error(
97-
"Inverse transformed mesh array should have shape ({}, {}) not ({}, {})"_s.format(
96+
throw std::runtime_error(mpl::format(
97+
"Inverse transformed mesh array should have shape ({}, {}) not ({}, {})",
9898
mesh_dims[0], mesh_dims[1],
9999
output_mesh_array.shape(0), output_mesh_array.shape(1)));
100100
}
@@ -124,8 +124,8 @@ image_resample(py::array input_array,
124124
}
125125

126126
if (ndim == 3 && input_array.shape(2) != 4) {
127-
throw std::invalid_argument(
128-
"3D input array must be RGBA with shape (M, N, 4), has trailing dimension of {}"_s.format(
127+
throw std::invalid_argument(mpl::format(
128+
"3D input array must be RGBA with shape (M, N, 4), has trailing dimension of {}",
129129
input_array.shape(2)));
130130
}
131131

@@ -139,14 +139,14 @@ image_resample(py::array input_array,
139139
auto out_ndim = output_array.ndim();
140140

141141
if (out_ndim != ndim) {
142-
throw std::invalid_argument(
143-
"Input ({}D) and output ({}D) arrays have different dimensionalities"_s.format(
142+
throw std::invalid_argument(mpl::format(
143+
"Input ({}D) and output ({}D) arrays have different dimensionalities",
144144
ndim, out_ndim));
145145
}
146146

147147
if (out_ndim == 3 && output_array.shape(2) != 4) {
148-
throw std::invalid_argument(
149-
"3D output array must be RGBA with shape (M, N, 4), has trailing dimension of {}"_s.format(
148+
throw std::invalid_argument(mpl::format(
149+
"3D output array must be RGBA with shape (M, N, 4), has trailing dimension of {}",
150150
output_array.shape(2)));
151151
}
152152

@@ -220,6 +220,16 @@ image_resample(py::array input_array,
220220
}
221221

222222

223+
[[noreturn]] static void
224+
raise_image_comparison_failure(std::string msg)
225+
{
226+
auto exceptions = py::module_::import("matplotlib.testing.exceptions");
227+
auto ImageComparisonFailure = exceptions.attr("ImageComparisonFailure");
228+
py::set_error(ImageComparisonFailure.ptr(), msg.c_str());
229+
throw py::error_already_set();
230+
}
231+
232+
223233
// This is used by matplotlib.testing.compare to calculate RMS and a difference image.
224234
static py::tuple
225235
calculate_rms_and_diff(py::array_t<unsigned char> expected_image,
@@ -229,13 +239,9 @@ calculate_rms_and_diff(py::array_t<unsigned char> expected_image,
229239
std::pair{actual_image, "Actual"}})
230240
{
231241
if (image.ndim() != 3) {
232-
auto exceptions = py::module_::import("matplotlib.testing.exceptions");
233-
auto ImageComparisonFailure = exceptions.attr("ImageComparisonFailure");
234-
py::set_error(
235-
ImageComparisonFailure,
236-
"{name} image must be 3-dimensional, but is {ndim}-dimensional"_s.format(
237-
"name"_a=name, "ndim"_a=image.ndim()));
238-
throw py::error_already_set();
242+
raise_image_comparison_failure(mpl::format(
243+
"{} image must be 3-dimensional, but is {}-dimensional",
244+
name, image.ndim()));
239245
}
240246
}
241247

@@ -244,25 +250,16 @@ calculate_rms_and_diff(py::array_t<unsigned char> expected_image,
244250
auto depth = expected_image.shape(2);
245251

246252
if (depth != 3 && depth != 4) {
247-
auto exceptions = py::module_::import("matplotlib.testing.exceptions");
248-
auto ImageComparisonFailure = exceptions.attr("ImageComparisonFailure");
249-
py::set_error(
250-
ImageComparisonFailure,
251-
"Image must be RGB or RGBA but has depth {depth}"_s.format(
252-
"depth"_a=depth));
253-
throw py::error_already_set();
253+
raise_image_comparison_failure(mpl::format(
254+
"Image must be RGB or RGBA but has depth {}", depth));
254255
}
255256

256257
if (height != actual_image.shape(0) || width != actual_image.shape(1) ||
257258
depth != actual_image.shape(2)) {
258-
auto exceptions = py::module_::import("matplotlib.testing.exceptions");
259-
auto ImageComparisonFailure = exceptions.attr("ImageComparisonFailure");
260-
py::set_error(
261-
ImageComparisonFailure,
262-
"Image sizes do not match expected size: {expected_image.shape} "_s
263-
"actual size {actual_image.shape}"_s.format(
259+
raise_image_comparison_failure(mpl::format(
260+
"Image sizes do not match expected size: {expected_image.shape} "
261+
"actual size {actual_image.shape}",
264262
"expected_image"_a=expected_image, "actual_image"_a=actual_image));
265-
throw py::error_already_set();
266263
}
267264
auto expected = expected_image.unchecked<3>();
268265
auto actual = actual_image.unchecked<3>();

‎src/_qhull_wrapper.cpp‎

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ extern const char qh_version[];
2020
}
2121
#endif
2222

23+
#include "mplutils.h"
2324
#include "libqhull_r/qhull_ra.h"
2425
#include <cstdio>
2526
#include <vector>
@@ -195,13 +196,10 @@ delaunay_impl(py::ssize_t npoints, const double* x, const double* y,
195196
exitcode = qh_new_qhull(qh, ndim, (int)npoints, points.data(), False,
196197
(char*)"qhull d Qt Qbb Qc Qz", nullptr, error_file);
197198
if (exitcode != qh_ERRnone) {
198-
std::string msg =
199-
py::str("Error in qhull Delaunay triangulation calculation: {} (exitcode={})")
200-
.format(qhull_error_msg[exitcode], exitcode).cast<std::string>();
201-
if (hide_qhull_errors) {
202-
msg += "; use python verbose option (-v) to see original qhull error.";
203-
}
204-
throw std::runtime_error(msg);
199+
throw std::runtime_error(mpl::format(
200+
"Error in qhull Delaunay triangulation calculation: {} (exitcode={}){}",
201+
qhull_error_msg[exitcode], exitcode, hide_qhull_errors ?
202+
"; use python verbose option (-v) to see original qhull error." : ""));
205203
}
206204

207205
/* Split facets so that they only have 3 points each. */

‎src/_tkagg.cpp‎

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131

3232
#include <pybind11/pybind11.h>
3333
#include <pybind11/numpy.h>
34+
#include "mplutils.h"
35+
3436
namespace py = pybind11;
3537
using namespace pybind11::literals;
3638

@@ -108,18 +110,18 @@ mpl_tk_blit(py::object interp_obj, const char *photo_name,
108110

109111
auto data_ptr = data.mutable_unchecked<3>(); // Checks ndim and writeable flag.
110112
if (data.shape(2) != 4) {
111-
throw py::value_error(
112-
"Data pointer must be RGBA; last dimension is {}, not 4"_s.format(
113+
throw py::value_error(mpl::format(
114+
"Data pointer must be RGBA; last dimension is {}, not 4",
113115
data.shape(2)));
114116
}
115117
if (data.shape(0) > INT_MAX) { // Limited by Tk_PhotoPutBlock argument type.
116-
throw std::range_error(
117-
"Height ({}) exceeds maximum allowable size ({})"_s.format(
118+
throw std::range_error(mpl::format(
119+
"Height ({}) exceeds maximum allowable size ({})",
118120
data.shape(0), INT_MAX));
119121
}
120122
if (data.shape(1) > INT_MAX / 4) { // Limited by Tk_PhotoImageBlock.pitch field.
121-
throw std::range_error(
122-
"Width ({}) exceeds maximum allowable size ({})"_s.format(
123+
throw std::range_error(mpl::format(
124+
"Width ({}) exceeds maximum allowable size ({})",
123125
data.shape(1), INT_MAX / 4));
124126
}
125127
const auto height = static_cast<int>(data.shape(0));

‎src/ft2font.cpp‎

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -359,17 +359,17 @@ std::vector<raqm_glyph_t> FT2Font::layout(
359359
if (features) {
360360
for (auto const& feature : *features) {
361361
if (!raqm_add_font_feature(rq, feature.c_str(), feature.size())) {
362-
throw std::runtime_error("failed to set font feature {}"_s.format(feature));
362+
throw std::runtime_error(mpl::format(
363+
"failed to set font feature {}", feature));
363364
}
364365
}
365366
}
366367
if (languages) {
367368
for (auto & [lang_str, start, end] : *languages) {
368369
if (!raqm_set_language(rq, lang_str.c_str(), start, end - start)) {
369-
throw std::runtime_error(
370-
"failed to set language between {} and {} characters "_s
371-
"to {!r} for layout"_s.format(
372-
start, end, lang_str));
370+
throw std::runtime_error(mpl::format(
371+
"failed to set language between {} and {} characters "
372+
"to {!r} for layout", start, end, lang_str));
373373
}
374374
}
375375
}
@@ -439,18 +439,17 @@ std::vector<raqm_glyph_t> FT2Font::layout(
439439
if (features) {
440440
for (auto const& feature : *features) {
441441
if (!raqm_add_font_feature(rq, feature.c_str(), feature.size())) {
442-
throw std::runtime_error(
443-
"failed to set font feature {}"_s.format(feature));
442+
throw std::runtime_error(mpl::format(
443+
"failed to set font feature {}", feature));
444444
}
445445
}
446446
}
447447
if (languages) {
448448
for (auto & [lang_str, start, end] : *languages) {
449449
if (!raqm_set_language(rq, lang_str.c_str(), start, end - start)) {
450-
throw std::runtime_error(
451-
"failed to set language between {} and {} characters "_s
452-
"to {!r} for layout"_s.format(
453-
start, end, lang_str));
450+
throw std::runtime_error(mpl::format(
451+
"failed to set language between {} and {} characters "
452+
"to {!r} for layout", start, end, lang_str));
454453
}
455454
}
456455
}

‎src/ft2font_wrapper.cpp‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1660,9 +1660,9 @@ PYBIND11_MODULE(ft2font, m, py::mod_gil_not_used())
16601660
"The kerning between this item and the previous one.")
16611661
.def("__str__",
16621662
[](const LayoutItem& item) {
1663-
return
1664-
"LayoutItem(ft_object={}, char={!r}, glyph_index={}, "_s
1665-
"x={}, y={}, prev_kern={})"_s.format(
1663+
return py::str(
1664+
"LayoutItem(ft_object={}, char={!r}, glyph_index={}, "
1665+
"x={}, y={}, prev_kern={})").format(
16661666
PyFT2Font_fname(item.ft_object), item.character,
16671667
item.glyph_index, item.x, item.y, item.prev_kern);
16681668
});

‎src/mplutils.h‎

Lines changed: 35 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,15 @@
2727
#endif
2828
#endif
2929

30+
#include <pybind11/pybind11.h>
31+
#include <pybind11/numpy.h>
32+
#include <array>
33+
#include <cstddef>
34+
#include <string>
3035

31-
inline int mpl_round_to_int(double v)
32-
{
33-
return (int)(v + ((v >= 0.0) ? 0.5 : -0.5));
34-
}
36+
namespace py = pybind11;
37+
using namespace pybind11::literals;
3538

36-
inline double mpl_round(double v)
37-
{
38-
return (double)mpl_round_to_int(v);
39-
}
4039

4140
// 'kind' codes for paths.
4241
enum {
@@ -48,14 +47,27 @@ enum {
4847
CLOSEPOLY = 0x4f
4948
};
5049

51-
#ifdef __cplusplus // not for macosx.m
52-
#include <pybind11/pybind11.h>
53-
#include <pybind11/numpy.h>
54-
#include <array>
55-
#include <cstddef>
5650

57-
namespace py = pybind11;
58-
using namespace pybind11::literals;
51+
namespace mpl {
52+
53+
inline int round_to_int(double v)
54+
{
55+
return (int)(v + ((v >= 0.0) ? 0.5 : -0.5));
56+
}
57+
58+
inline double round(double v)
59+
{
60+
return (double)round_to_int(v);
61+
}
62+
63+
template <typename... Args>
64+
inline std::string format(const char *fmt, Args &&...args)
65+
{
66+
return std::string(py::str(fmt).format(std::forward<Args>(args)...));
67+
}
68+
69+
} // namespace mpl
70+
5971

6072
// Helper for std::visit.
6173
template<typename... Ts> struct overloaded : Ts... { using Ts::operator()...; };
@@ -67,17 +79,17 @@ template<typename T>
6779
inline void check_trailing_shape(T array, char const* name, long d1)
6880
{
6981
if (array.ndim() != 2) {
70-
throw py::value_error(
71-
"Expected 2-dimensional array, got {}"_s.format(array.ndim()));
82+
throw py::value_error(mpl::format(
83+
"Expected 2-dimensional array, got {}", array.ndim()));
7284
}
7385
if (array.size() == 0) {
7486
// Sometimes things come through as atleast_2d, etc., but they're empty, so
7587
// don't bother enforcing the trailing shape.
7688
return;
7789
}
7890
if (array.shape(1) != d1) {
79-
throw py::value_error(
80-
"{} must have shape (N, {}), got ({}, {})"_s.format(
91+
throw py::value_error(mpl::format(
92+
"{} must have shape (N, {}), got ({}, {})",
8193
name, d1, array.shape(0), array.shape(1)));
8294
}
8395
}
@@ -86,17 +98,17 @@ template<typename T>
8698
inline void check_trailing_shape(T array, char const* name, long d1, long d2)
8799
{
88100
if (array.ndim() != 3) {
89-
throw py::value_error(
90-
"Expected 3-dimensional array, got {}"_s.format(array.ndim()));
101+
throw py::value_error(mpl::format(
102+
"Expected 3-dimensional array, got {}", array.ndim()));
91103
}
92104
if (array.size() == 0) {
93105
// Sometimes things come through as atleast_3d, etc., but they're empty, so
94106
// don't bother enforcing the trailing shape.
95107
return;
96108
}
97109
if (array.shape(1) != d1 || array.shape(2) != d2) {
98-
throw py::value_error(
99-
"{} must have shape (N, {}, {}), got ({}, {}, {})"_s.format(
110+
throw py::value_error(mpl::format(
111+
"{} must have shape (N, {}, {}), got ({}, {}, {})",
100112
name, d1, d2, array.shape(0), array.shape(1), array.shape(2)));
101113
}
102114
}
@@ -127,5 +139,3 @@ safe_first_shape(const std::array<T, N> &)
127139
}
128140

129141
#endif
130-
131-
#endif

‎src/path_converters.h‎

Lines changed: 1 addition & 1 deletion

0 commit comments

Comments
 (0)