Skip to content

Commit 965d1f5

Browse files
committed
Fix construction of G3FrameObject maps and vectors
Pybind11 performs a series of type conversions when passing python arguments to C++ functions. These conversions do not behave appropriately when the argument is a base G3FrameObject. This commit overrides all of the functions in the G3MapFrameObject and G3VectorFrameObject classes that take G3FrameObject objects as input arguments. Type conversion is disabled on any argument that is a G3FrameObject, and the overridden functions are used in place of the standard ones.
1 parent b816d10 commit 965d1f5

File tree

4 files changed

+145
-6
lines changed

4 files changed

+145
-6
lines changed

core/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ add_spt3g_test(pipeline_module)
106106
add_spt3g_test(multiproc)
107107
add_spt3g_test(frames)
108108
add_spt3g_test(frame_types)
109+
add_spt3g_test(frameobjects)
109110
add_spt3g_test(datatypes)
110111
if(FLAC_FOUND)
111112
add_spt3g_test(ts_nanencoding)

core/src/G3Map.cxx

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -255,8 +255,39 @@ PYBINDINGS("core", scope) {
255255
register_g3map<G3MapVectorTime>(scope, "G3MapVectorTime", "Mapping from "
256256
"strings to lists of G3 time objects.");
257257

258-
register_g3map<G3MapFrameObject>(scope, "G3MapFrameObject", "Mapping "
259-
"strings to generic frame objects. Can lead to a variety of "
258+
auto cls = register_g3map<G3MapFrameObject>(scope, "G3MapFrameObject",
259+
"Mapping strings to generic frame objects. Can lead to a variety of "
260260
"paradoxes; please avoid general use of this class.");
261+
262+
// overrides to avoid type conversion
263+
cls.attr("__init__") = py::cpp_function(
264+
[](py::detail::value_and_holder &v_h) {
265+
v_h.value_ptr() = new G3MapFrameObject();
266+
}, py::name("__init__"), py::is_method(cls),
267+
py::detail::is_new_style_constructor());
268+
cls.def(py::init<const G3MapFrameObject&>(), "Copy constructor");
269+
cls.def(py::init([](const py::iterable &d) {
270+
auto v = py::cast(G3MapFrameObjectPtr(new G3MapFrameObject()));
271+
for (auto item: py::dict(d))
272+
v.attr("__setitem__")(item.first, item.second);
273+
return py::cast<G3MapFrameObject>(v);
274+
}), "Iterable constructor");
275+
276+
cls.attr("__setitem__") = py::cpp_function(
277+
[](G3MapFrameObject &m, const std::string &k, G3FrameObjectPtr v) {
278+
m[k] = v;
279+
}, py::name("__setitem__"), py::is_method(cls),
280+
py::arg("key"), py::arg("item").noconvert());
281+
282+
cls.attr("update") = py::cpp_function(
283+
[](py::object &m, const py::iterable &v, py::kwargs kw) {
284+
for (auto it: py::dict(v))
285+
m.attr("__setitem__")(it.first, it.second);
286+
for (auto it: kw)
287+
m.attr("__setitem__")(it.first, it.second);
288+
}, py::name("update"), py::is_method(cls),
289+
py::arg("items")=py::list(),
290+
"Update mapping from iterable/mapping.");
291+
261292
}
262293

core/src/G3Vector.cxx

Lines changed: 52 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -189,16 +189,64 @@ PYBINDINGS("core", scope) {
189189
register_g3vector<G3VectorVectorString>(scope, "G3VectorVectorString",
190190
"List of lists of strings.");
191191

192-
register_g3vector<G3VectorFrameObject>(scope, "G3VectorFrameObject",
193-
"List of generic frame objects. Can lead to paradoxes; avoid use of "
194-
"this class unless you are sure you need it.");
195-
196192
register_vector_of<unsigned char>(scope, "UnsignedChar");
197193
register_g3vector<G3VectorUnsignedChar>(scope, "G3VectorUnsignedChar",
198194
"List of 8-bit integers");
199195

200196
register_vector_of<G3Time>(scope, "G3Time", py::buffer_protocol());
201197
register_g3vector<G3VectorTime>(scope, "G3VectorTime", py::buffer_protocol(),
202198
"List of times.");
199+
200+
auto cls = register_g3vector<G3VectorFrameObject>(scope, "G3VectorFrameObject",
201+
"List of generic frame objects. Can lead to paradoxes; avoid use of "
202+
"this class unless you are sure you need it.");
203+
204+
// overrides to avoid type conversion
205+
cls.attr("__init__") = py::cpp_function(
206+
[](py::detail::value_and_holder &v_h) {
207+
v_h.value_ptr() = new G3VectorFrameObject();
208+
}, py::name("__init__"), py::is_method(cls),
209+
py::detail::is_new_style_constructor());
210+
cls.def(py::init<const G3VectorFrameObject&>(), "Copy constructor");
211+
cls.def(py::init([](const py::iterable &d) {
212+
auto v = py::cast(G3VectorFrameObjectPtr(new G3VectorFrameObject()));
213+
for (auto item: d)
214+
v.attr("append")(item);
215+
return py::cast<G3VectorFrameObject>(v);
216+
}), "Iterable constructor");
217+
218+
cls.def("__setitem__",
219+
[](G3VectorFrameObject &v, ssize_t i, G3FrameObjectPtr x) {
220+
if (i < 0)
221+
i += v.size();
222+
if (i < 0 || (size_t) i >= v.size())
223+
throw py::index_error();
224+
v[(size_t)i] = x;
225+
}, py::arg("i"), py::arg("x").noconvert(), py::prepend());
226+
227+
cls.attr("insert") = py::cpp_function(
228+
[](G3VectorFrameObject &v, ssize_t i, G3FrameObjectPtr x) {
229+
if (i < 0)
230+
i += v.size();
231+
if (i < 0 || (size_t) i > v.size())
232+
throw py::index_error();
233+
v.insert(v.begin() + i, x);
234+
}, py::name("insert"), py::is_method(cls),
235+
py::arg("i"), py::arg("x").noconvert(),
236+
"Insert an item at a given position.");
237+
238+
cls.attr("append") = py::cpp_function(
239+
[](G3VectorFrameObject &v, G3FrameObjectPtr x) {
240+
v.push_back(x);
241+
}, py::name("append"), py::is_method(cls),
242+
py::arg("x").noconvert(),
243+
"Add an item to the end of the list");
244+
245+
cls.attr("extend") = py::cpp_function(
246+
[](G3VectorFrameObject &v, const G3VectorFrameObject &src) {
247+
v.insert(v.end(), src.begin(), src.end());
248+
}, py::name("extend"), py::is_method(cls),
249+
py::arg("L").noconvert(),
250+
"Extend the list by appending all the items in the given list");
203251
}
204252

core/tests/frameobjects.py

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
from spt3g import core
2+
import numpy as np
3+
4+
vi = core.G3VectorInt([1, 2, 3])
5+
mi = core.G3MapInt({"a": 1, "b": 2, "c": 3})
6+
7+
# test default constructor
8+
m = core.G3MapFrameObject()
9+
10+
# test modifiers
11+
m["v"] = vi
12+
assert isinstance(m["v"], core.G3VectorInt)
13+
np.testing.assert_array_equal(m["v"], vi)
14+
m.update(m=mi)
15+
assert isinstance(m["m"], core.G3MapInt)
16+
np.testing.assert_array_equal(list(m["m"].keys()), list(mi.keys()))
17+
np.testing.assert_array_equal(list(m["m"].values()), list(mi.values()))
18+
19+
# test constructor from iterable
20+
m2 = core.G3MapFrameObject({"v": vi, "m": mi})
21+
assert isinstance(m2["v"], core.G3VectorInt)
22+
assert isinstance(m2["m"], core.G3MapInt)
23+
np.testing.assert_array_equal(list(m.keys()), list(m2.keys()))
24+
np.testing.assert_array_equal(list(m.values()), list(m2.values()))
25+
26+
# test copy constructor
27+
m3 = core.G3MapFrameObject(m2)
28+
assert isinstance(m3["v"], core.G3VectorInt)
29+
assert isinstance(m3["m"], core.G3MapInt)
30+
np.testing.assert_array_equal(list(m3.keys()), list(m2.keys()))
31+
np.testing.assert_array_equal(list(m3.values()), list(m2.values()))
32+
33+
# test default constructor
34+
v = core.G3VectorFrameObject()
35+
36+
# test modifiers
37+
v.append(vi)
38+
assert isinstance(v[0], core.G3VectorInt)
39+
np.testing.assert_array_equal(v[0], vi)
40+
v.insert(0, mi)
41+
assert isinstance(v[0], core.G3MapInt)
42+
np.testing.assert_array_equal(list(v[0].keys()), list(mi.keys()))
43+
np.testing.assert_array_equal(list(v[0].values()), list(mi.values()))
44+
v[0] = vi
45+
assert isinstance(v[0], core.G3VectorInt)
46+
v[1] = mi
47+
assert isinstance(v[1], core.G3MapInt)
48+
49+
# test constructor from iterable
50+
v2 = core.G3VectorFrameObject([vi, mi])
51+
assert isinstance(v2[0], core.G3VectorInt)
52+
assert isinstance(v2[1], core.G3MapInt)
53+
np.testing.assert_array_equal(v2, v)
54+
55+
# test copy constructor
56+
v3 = core.G3VectorFrameObject(v2)
57+
assert isinstance(v3[0], core.G3VectorInt)
58+
assert isinstance(v3[1], core.G3MapInt)
59+
np.testing.assert_array_equal(v3, v)

0 commit comments

Comments
 (0)