Skip to content

Commit 63562a0

Browse files
authored
Merge pull request #266 from ax3l/topic-pyAttributable
Python: Expose Attributes Fully
2 parents a3b897c + 89c8e7e commit 63562a0

22 files changed

+153
-41
lines changed

CHANGELOG.rst

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,8 @@ Features
2525
- Python:
2626

2727
- unit tests #249
28-
- expose attribute keys #256
28+
- expose attributes #256 #266
29+
- use lists for offsets & extents #266
2930
- C++:
3031

3132
- ``setAttribute`` signature changed to const ref #268

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -303,6 +303,7 @@ if(openPMD_HAVE_PYTHON)
303303
pybind11_add_module(openPMD.py MODULE
304304
src/binding/python/openPMD.cpp
305305
src/binding/python/AccessType.cpp
306+
src/binding/python/Attributable.cpp
306307
src/binding/python/BaseRecord.cpp
307308
src/binding/python/BaseRecordComponent.cpp
308309
src/binding/python/Container.cpp

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ Where supported, openPMD-api implements both serial and MPI parallel I/O capabil
3333

3434
// ...
3535

36-
auto s = openPMD::Series("output_files/data%T.h5", openPMD::AccessType::READ_ONLY);
36+
auto s = openPMD::Series("samples/git-sample/data%T.h5", openPMD::AccessType::READ_ONLY);
3737

3838
for( auto const& i : s.iterations ) {
3939
std::cout << "Iteration: " << i.first << "\n";
@@ -59,7 +59,7 @@ import openPMD
5959

6060
# ...
6161

62-
series = openPMD.Series("output_files/data%T.h5", openPMD.Access_Type.read_only)
62+
series = openPMD.Series("samples/git-sample/data%T.h5", openPMD.Access_Type.read_only)
6363

6464
for k_i, i in series.iterations.items():
6565
print("Iteration: {0}".format(k_i))

examples/2_read_serial.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@
3737
print("Field E.x has shape {0} and datatype {1}".format(
3838
shape, E_x.dtype))
3939

40-
offset = openPMD.Extent([1, 1, 1])
41-
extent = openPMD.Extent([2, 2, 1])
40+
offset = [1, 1, 1]
41+
extent = [2, 2, 1]
4242
# TODO buffer protocol / numpy bindings
4343
# chunk_data = E_x[1:3, 1:3, 1:2]
4444
chunk_data = E_x.load_chunk(offset, extent)

examples/3_write_serial.py

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,7 @@
3434

3535
datatype = openPMD.Datatype.DOUBLE
3636
# datatype = openPMD.determineDatatype(global_data)
37-
extent = openPMD.Extent([size, size])
37+
extent = [size, size]
3838
dataset = openPMD.Dataset(datatype, extent)
3939

4040
print("Created a Dataset of size {0}x{1} and Datatype {2}".format(
@@ -47,8 +47,7 @@
4747
series.flush()
4848
print("File structure has been written")
4949

50-
# offset = openPMD.Offset([0, 0])
51-
offset = openPMD.Extent([0, 0])
50+
offset = [0, 0]
5251
# TODO implement slicing protocol
5352
# E[offset[0]:extent[0], offset[1]:extent[1]] = global_data
5453
rho.store_chunk(offset, extent, global_data)

src/binding/python/AccessType.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
* If not, see <http://www.gnu.org/licenses/>.
2020
*/
2121
#include <pybind11/pybind11.h>
22+
#include <pybind11/stl.h>
2223

2324
#include "openPMD/IO/AccessType.hpp"
2425

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
/* Copyright 2018 Axel Huebl
2+
*
3+
* This file is part of openPMD-api.
4+
*
5+
* openPMD-api is free software: you can redistribute it and/or modify
6+
* it under the terms of of either the GNU General Public License or
7+
* the GNU Lesser General Public License as published by
8+
* the Free Software Foundation, either version 3 of the License, or
9+
* (at your option) any later version.
10+
*
11+
* openPMD-api is distributed in the hope that it will be useful,
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14+
* GNU General Public License and the GNU Lesser General Public License
15+
* for more details.
16+
*
17+
* You should have received a copy of the GNU General Public License
18+
* and the GNU Lesser General Public License along with openPMD-api.
19+
* If not, see <http://www.gnu.org/licenses/>.
20+
*/
21+
#include <pybind11/pybind11.h>
22+
#include <pybind11/stl_bind.h>
23+
#include <pybind11/stl.h>
24+
25+
#include "openPMD/backend/Attributable.hpp"
26+
#include "openPMD/backend/Attribute.hpp"
27+
#include "openPMD/auxiliary/Variant.hpp"
28+
29+
#include <string>
30+
#include <vector>
31+
#include <array>
32+
33+
34+
// std::variant
35+
// https://pybind11.readthedocs.io/en/stable/advanced/cast/stl.html
36+
namespace pybind11 {
37+
namespace detail {
38+
template< typename... Ts >
39+
struct type_caster< variantSrc::variant< Ts... > > :
40+
variant_caster< variantSrc::variant< Ts... > >
41+
{};
42+
43+
} // namespace detail
44+
} // namespace pybind11
45+
46+
namespace py = pybind11;
47+
using namespace openPMD;
48+
49+
using PyAttributeKeys = std::vector< std::string >;
50+
//PYBIND11_MAKE_OPAQUE(PyAttributeKeys)
51+
52+
void init_Attributable(py::module &m) {
53+
py::class_<Attributable>(m, "Attributable")
54+
.def(py::init<>())
55+
.def(py::init<Attributable const &>())
56+
57+
.def("__repr__",
58+
[](Attributable const & attr) {
59+
return "<openPMD.Attributable with'" + std::to_string(attr.numAttributes()) + "' attributes>";
60+
}
61+
)
62+
63+
.def_property_readonly(
64+
"attributes",
65+
[]( Attributable & attr )
66+
{
67+
return attr.attributes();
68+
},
69+
// ref + keepalive
70+
py::return_value_policy::reference_internal
71+
)
72+
73+
// C++ pass-through API
74+
.def("set_attribute", &Attributable::setAttribute< char >)
75+
.def("set_attribute", &Attributable::setAttribute< unsigned char >)
76+
.def("set_attribute", &Attributable::setAttribute< int16_t >)
77+
.def("set_attribute", &Attributable::setAttribute< int32_t >)
78+
.def("set_attribute", &Attributable::setAttribute< int64_t >)
79+
.def("set_attribute", &Attributable::setAttribute< uint16_t >)
80+
.def("set_attribute", &Attributable::setAttribute< uint32_t >)
81+
.def("set_attribute", &Attributable::setAttribute< uint64_t >)
82+
.def("set_attribute", &Attributable::setAttribute< float >)
83+
.def("set_attribute", &Attributable::setAttribute< double >)
84+
.def("set_attribute", &Attributable::setAttribute< long double >)
85+
.def("set_attribute", &Attributable::setAttribute< std::string >)
86+
.def("set_attribute", &Attributable::setAttribute< std::vector< char > >)
87+
.def("set_attribute", &Attributable::setAttribute< std::vector< unsigned char > >)
88+
.def("set_attribute", &Attributable::setAttribute< std::vector< int16_t > >)
89+
.def("set_attribute", &Attributable::setAttribute< std::vector< int32_t > >)
90+
.def("set_attribute", &Attributable::setAttribute< std::vector< int64_t > >)
91+
.def("set_attribute", &Attributable::setAttribute< std::vector< uint16_t > >)
92+
.def("set_attribute", &Attributable::setAttribute< std::vector< uint32_t > >)
93+
.def("set_attribute", &Attributable::setAttribute< std::vector< uint64_t > >)
94+
.def("set_attribute", &Attributable::setAttribute< std::vector< float > >)
95+
.def("set_attribute", &Attributable::setAttribute< std::vector< double > >)
96+
.def("set_attribute", &Attributable::setAttribute< std::vector< long double > >)
97+
.def("set_attribute", &Attributable::setAttribute< std::vector< std::string > >)
98+
.def("set_attribute", &Attributable::setAttribute< std::array< double, 7 > >)
99+
.def("set_attribute", &Attributable::setAttribute< bool >)
100+
101+
.def("get_attribute", []( Attributable & attr, std::string const& key ) {
102+
auto v = attr.getAttribute(key);
103+
return v.getResource();
104+
})
105+
.def("delete_attribute", &Attributable::deleteAttribute)
106+
.def("contains_attribute", &Attributable::containsAttribute)
107+
108+
.def("__len__", &Attributable::numAttributes)
109+
110+
// @todo _ipython_key_completions_ if we find a way to add a [] interface
111+
112+
.def_property_readonly("comment", &Attributable::comment)
113+
.def("set_comment", &Attributable::setComment)
114+
;
115+
116+
py::bind_vector< PyAttributeKeys >(
117+
m,
118+
"Attribute_Keys"
119+
);
120+
}

src/binding/python/BaseRecord.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
* If not, see <http://www.gnu.org/licenses/>.
2020
*/
2121
#include <pybind11/pybind11.h>
22+
#include <pybind11/stl.h>
2223

2324
#include "openPMD/backend/BaseRecord.hpp"
2425
#include "openPMD/backend/Container.hpp"

src/binding/python/BaseRecordComponent.cpp

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
*/
2121
#include <pybind11/pybind11.h>
2222
#include <pybind11/numpy.h>
23+
#include <pybind11/stl.h>
2324

2425
#include "openPMD/backend/BaseRecordComponent.hpp"
2526
#include "openPMD/Datatype.hpp"
@@ -31,7 +32,7 @@ using namespace openPMD;
3132

3233

3334
void init_BaseRecordComponent(py::module &m) {
34-
py::class_<BaseRecordComponent>(m, "Base_Record_Component")
35+
py::class_<BaseRecordComponent, Attributable>(m, "Base_Record_Component")
3536
.def("__repr__",
3637
[](BaseRecordComponent const & brc) {
3738
std::stringstream ss;

src/binding/python/Container.cpp

Lines changed: 3 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -26,9 +26,9 @@
2626

2727
#include <pybind11/pybind11.h>
2828
#include <pybind11/stl_bind.h>
29+
#include <pybind11/stl.h>
2930

3031
#include "openPMD/backend/Container.hpp"
31-
//include "openPMD/backend/BaseRecord.hpp"
3232
#include "openPMD/Iteration.hpp"
3333
#include "openPMD/Mesh.hpp"
3434
#include "openPMD/ParticleSpecies.hpp"
@@ -38,7 +38,6 @@
3838
#include <string>
3939
#include <memory>
4040
#include <utility>
41-
#include <vector>
4241

4342
namespace py = pybind11;
4443
using namespace openPMD;
@@ -70,7 +69,8 @@ namespace detail
7069
using MappedType = typename Map::mapped_type;
7170
using Class_ = py::class_<
7271
Map,
73-
holder_type
72+
holder_type,
73+
Attributable
7474
>;
7575

7676
// If either type is a non-module-local bound type then make the map
@@ -143,16 +143,6 @@ namespace detail
143143
>()
144144
);
145145

146-
cl.def_property_readonly(
147-
"attributes",
148-
[]( Map & m )
149-
{
150-
return m.attributes();
151-
},
152-
// ref + keepalive
153-
py::return_value_policy::reference_internal
154-
);
155-
156146
// keep same policy as Container class: missing keys are created
157147
cl.def(
158148
"__getitem__",
@@ -213,14 +203,11 @@ using PyMeshContainer = Container< Mesh >;
213203
using PyPartContainer = Container< ParticleSpecies >;
214204
using PyRecordContainer = Container< Record >;
215205
using PyMeshRecordComponentContainer = Container< MeshRecordComponent >;
216-
using PyAttributeKeys = std::vector< std::string >;
217206
PYBIND11_MAKE_OPAQUE(PyIterationContainer)
218207
PYBIND11_MAKE_OPAQUE(PyMeshContainer)
219208
PYBIND11_MAKE_OPAQUE(PyPartContainer)
220209
PYBIND11_MAKE_OPAQUE(PyRecordContainer)
221210
PYBIND11_MAKE_OPAQUE(PyMeshRecordComponentContainer)
222-
PYBIND11_MAKE_OPAQUE(PyAttributeKeys)
223-
224211

225212
void init_Container( py::module & m ) {
226213
detail::bind_container< PyIterationContainer >(
@@ -243,8 +230,4 @@ void init_Container( py::module & m ) {
243230
m,
244231
"Mesh_Record_Component_Container"
245232
);
246-
py::bind_vector< PyAttributeKeys >(
247-
m,
248-
"Attribute_Keys"
249-
);
250233
}

0 commit comments

Comments
 (0)