Skip to content

Commit e9f4abb

Browse files
committed
Accept std::filesystem::path as path argument in constructor
1 parent 56cbe3d commit e9f4abb

1 file changed

Lines changed: 136 additions & 100 deletions

File tree

src/binding/python/Series.cpp

Lines changed: 136 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@
2929
#include "openPMD/snapshots/StatefulIterator.hpp"
3030

3131
#include "openPMD/binding/python/Common.hpp"
32+
#include <filesystem>
3233
#include <optional>
3334

3435
#if openPMD_HAVE_MPI
@@ -41,6 +42,135 @@
4142
#include <sstream>
4243
#include <string>
4344

45+
namespace auxiliary
46+
{
47+
template <typename Functor, typename... Types>
48+
struct ForEachType;
49+
50+
template <typename Functor, typename FirstType, typename... OtherTypes>
51+
struct ForEachType<Functor, FirstType, OtherTypes...>
52+
{
53+
template <typename... Args>
54+
static void call(Args &&...args)
55+
{
56+
Functor::template call<FirstType>(args...);
57+
ForEachType<Functor, OtherTypes...>::template call<Args...>(args...);
58+
}
59+
};
60+
61+
template <typename Functor>
62+
struct ForEachType<Functor>
63+
{
64+
template <typename... Args>
65+
static constexpr void call(Args &&...)
66+
{ /* no-op */
67+
}
68+
};
69+
} // namespace auxiliary
70+
71+
namespace internal
72+
{
73+
struct DefineSeriesConstructorPerPathType
74+
{
75+
template <typename PathType>
76+
static void call(py::class_<Series, Attributable> &py_class)
77+
{
78+
py_class
79+
.def(
80+
py::init([](PathType const &filepath,
81+
Access at,
82+
std::string const &options) {
83+
py::gil_scoped_release release;
84+
return new Series(filepath, at, options);
85+
}),
86+
py::arg("filepath"),
87+
py::arg("access"),
88+
py::arg("options") = "{}",
89+
R"END(
90+
Construct a new Series. Parameters:
91+
92+
* filepath: The file path.
93+
* at: Access mode.
94+
* options: Advanced backend configuration via JSON.
95+
May be specified as a JSON-formatted string directly, or as a path
96+
to a JSON textfile, prepended by an at sign '@'.
97+
98+
For details on access modes, JSON/TOML configuration and iteration encoding,
99+
refer to:
100+
101+
* https://openpmd-api.readthedocs.io/en/latest/usage/workflow.html#access-modes
102+
* https://openpmd-api.readthedocs.io/en/latest/details/backendconfig.html
103+
* https://openpmd-api.readthedocs.io/en/latest/usage/concepts.html#iteration-and-series
104+
105+
In case of file-based iteration encoding, the file names for each
106+
iteration are determined by an expansion pattern that must be specified.
107+
It takes one out of two possible forms:
108+
109+
1. Simple form: %T is replaced with the iteration index, e.g.
110+
`simData_%T.bp` becomes `simData_50.bp`.
111+
2. Padded form: e.g. %06T is replaced with the iteration index padded to
112+
at least six digits. `simData_%06T.bp` becomes `simData_000050.bp`.
113+
114+
The backend is determined:
115+
116+
1. Explicitly via the JSON/TOML parameter `backend`, e.g. `{"backend":
117+
"adios2"}`.
118+
2. Otherwise implicitly from the filename extension, e.g.
119+
`simData_%T.h5`.
120+
121+
The filename extension can be replaced with a globbing pattern %E.
122+
It will be replaced with an automatically determined file name extension:
123+
124+
1. In CREATE mode: The extension is set to a backend-specific default
125+
extension. This requires that the backend is specified via JSON/TOML.
126+
2. In READ_ONLY, READ_WRITE and READ_LINEAR modes: These modes require
127+
that files already exist on disk. The disk will be scanned for files
128+
that match the pattern and the resulting file extension will be used.
129+
If the result is ambiguous or no such file is found, an error is
130+
raised.
131+
3. In APPEND mode: Like (2.), except if no matching file is found. In
132+
that case, the procedure of (1.) is used, owing to the fact that
133+
APPEND mode can be used to create new datasets.
134+
)END")
135+
#if openPMD_HAVE_MPI
136+
.def(
137+
py::init([](PathType const &filepath,
138+
Access at,
139+
py::object &comm,
140+
std::string const &options) {
141+
auto variant = pythonObjectAsMpiComm(comm);
142+
if (auto errorMsg = std::get_if<std::string>(&variant))
143+
{
144+
throw std::runtime_error("[Series] " + *errorMsg);
145+
}
146+
else
147+
{
148+
py::gil_scoped_release release;
149+
return new Series(
150+
filepath, at, std::get<MPI_Comm>(variant), options);
151+
}
152+
}),
153+
py::arg("filepath"),
154+
py::arg("access"),
155+
py::arg("mpi_communicator"),
156+
py::arg("options") = "{}",
157+
R"END(
158+
Construct a new Series. Parameters:
159+
160+
* filepath: The file path.
161+
* at: Access mode.
162+
* options: Advanced backend configuration via JSON.
163+
May be specified as a JSON-formatted string directly, or as a path
164+
to a JSON textfile, prepended by an at sign '@'.
165+
* mpi_communicator: The MPI communicator
166+
167+
For further details, refer to the non-MPI overload.
168+
)END");
169+
#endif
170+
}
171+
};
172+
} // namespace internal
173+
44174
struct StatefulIteratorPythonAdaptor : LegacyIteratorAdaptor
45175
{
46176
StatefulIteratorPythonAdaptor(LegacyIteratorAdaptor it)
@@ -166,107 +296,13 @@ not possible once it has been closed.
166296
// keep handle alive while iterator exists
167297
py::keep_alive<0, 1>());
168298

169-
// `clang-format on/off` doesn't help here.
170-
// Writing this without a macro would lead to a huge diff due to
171-
// clang-format.
172-
#define OPENPMD_AVOID_CLANG_FORMAT auto cl =
173-
OPENPMD_AVOID_CLANG_FORMAT
174-
#undef OPENPMD_AVOID_CLANG_FORMAT
175-
176-
py::class_<Series, Attributable>(m, "Series")
177-
178-
.def(
179-
py::init([](std::string const &filepath,
180-
Access at,
181-
std::string const &options) {
182-
py::gil_scoped_release release;
183-
return new Series(filepath, at, options);
184-
}),
185-
py::arg("filepath"),
186-
py::arg("access"),
187-
py::arg("options") = "{}",
188-
R"END(
189-
Construct a new Series. Parameters:
190-
191-
* filepath: The file path.
192-
* at: Access mode.
193-
* options: Advanced backend configuration via JSON.
194-
May be specified as a JSON-formatted string directly, or as a path
195-
to a JSON textfile, prepended by an at sign '@'.
196-
197-
For details on access modes, JSON/TOML configuration and iteration encoding,
198-
refer to:
199-
200-
* https://openpmd-api.readthedocs.io/en/latest/usage/workflow.html#access-modes
201-
* https://openpmd-api.readthedocs.io/en/latest/details/backendconfig.html
202-
* https://openpmd-api.readthedocs.io/en/latest/usage/concepts.html#iteration-and-series
203-
204-
In case of file-based iteration encoding, the file names for each
205-
iteration are determined by an expansion pattern that must be specified.
206-
It takes one out of two possible forms:
207-
208-
1. Simple form: %T is replaced with the iteration index, e.g.
209-
`simData_%T.bp` becomes `simData_50.bp`.
210-
2. Padded form: e.g. %06T is replaced with the iteration index padded to
211-
at least six digits. `simData_%06T.bp` becomes `simData_000050.bp`.
212-
213-
The backend is determined:
214-
215-
1. Explicitly via the JSON/TOML parameter `backend`, e.g. `{"backend":
216-
"adios2"}`.
217-
2. Otherwise implicitly from the filename extension, e.g.
218-
`simData_%T.h5`.
219-
220-
The filename extension can be replaced with a globbing pattern %E.
221-
It will be replaced with an automatically determined file name extension:
222-
223-
1. In CREATE mode: The extension is set to a backend-specific default
224-
extension. This requires that the backend is specified via JSON/TOML.
225-
2. In READ_ONLY, READ_WRITE and READ_LINEAR modes: These modes require
226-
that files already exist on disk. The disk will be scanned for files
227-
that match the pattern and the resulting file extension will be used.
228-
If the result is ambiguous or no such file is found, an error is
229-
raised.
230-
3. In APPEND mode: Like (2.), except if no matching file is found. In
231-
that case, the procedure of (1.) is used, owing to the fact that
232-
APPEND mode can be used to create new datasets.
233-
)END")
234-
#if openPMD_HAVE_MPI
235-
.def(
236-
py::init([](std::string const &filepath,
237-
Access at,
238-
py::object &comm,
239-
std::string const &options) {
240-
auto variant = pythonObjectAsMpiComm(comm);
241-
if (auto errorMsg = std::get_if<std::string>(&variant))
242-
{
243-
throw std::runtime_error("[Series] " + *errorMsg);
244-
}
245-
else
246-
{
247-
py::gil_scoped_release release;
248-
return new Series(
249-
filepath, at, std::get<MPI_Comm>(variant), options);
250-
}
251-
}),
252-
py::arg("filepath"),
253-
py::arg("access"),
254-
py::arg("mpi_communicator"),
255-
py::arg("options") = "{}",
256-
R"END(
257-
Construct a new Series. Parameters:
258-
259-
* filepath: The file path.
260-
* at: Access mode.
261-
* options: Advanced backend configuration via JSON.
262-
May be specified as a JSON-formatted string directly, or as a path
263-
to a JSON textfile, prepended by an at sign '@'.
264-
* mpi_communicator: The MPI communicator
299+
py::class_<Series, Attributable> cl(m, "Series");
300+
::auxiliary::ForEachType<
301+
::internal::DefineSeriesConstructorPerPathType,
302+
std::string,
303+
std::filesystem::path>::template call(cl);
265304

266-
For further details, refer to the non-MPI overload.
267-
)END")
268-
#endif
269-
.def("__bool__", &Series::operator bool)
305+
cl.def("__bool__", &Series::operator bool)
270306
.def("__len__", [](Series const &s) { return s.iterations.size(); })
271307
.def(
272308
"__repr__",

0 commit comments

Comments
 (0)