Skip to content

Commit 429f80f

Browse files
authored
Merge pull request #151 from quantopian/table-from-object
BUG: Update from_object dispatch for table
2 parents 5f91ac4 + ba84f00 commit 429f80f

3 files changed

Lines changed: 49 additions & 17 deletions

File tree

Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,10 @@ ifeq ($(OS),Darwin)
7777
ifeq ($(COMPILER),GCC)
7878
# #sparseahash uses realloc which osx+gcc are not happy about
7979
CXXFLAGS += -Wno-class-memaccess
80+
GCCVERSIONLT9 := $(shell test `$(COMPILER) -dumpversion | cut -f1 -d.` -lt 9 && echo true)
81+
ifeq ($(GCCVERSIONLT9),true)
82+
LDFLAGS += -lstdc++fs
83+
endif
8084
endif
8185
else
8286
CXXFLAGS += -fstack-protector-strong

include/libpy/table.h

Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1140,39 +1140,39 @@ struct from_object<py::table_view<columns...>> {
11401140
using type = py::table_view<columns...>;
11411141

11421142
template<typename Column>
1143-
static auto pop_column(PyObject* t) {
1143+
static auto pop_column(py::borrowed_ref<> t) {
11441144
auto text = py::cs::to_array(typename Column::key{});
11451145
auto column_name = py::to_object(
11461146
*reinterpret_cast<std::array<char, text.size() - 1>*>(text.data()));
11471147
if (!column_name) {
11481148
throw py::exception();
11491149
}
1150-
PyObject* column_ob = PyDict_GetItem(t, column_name.get());
1150+
PyObject* column_ob = PyDict_GetItem(t.get(), column_name.get());
11511151
if (!column_ob) {
11521152
throw py::exception(PyExc_ValueError, "missing column: ", column_name);
11531153
}
1154-
if (PyDict_DelItem(t, column_name.get())) {
1154+
if (PyDict_DelItem(t.get(), column_name.get())) {
11551155
// pop the item to track which columns we used
11561156
throw py::exception();
11571157
}
11581158
return py::from_object<py::array_view<typename Column::value>>(column_ob);
11591159
}
11601160

11611161
public:
1162-
static type f(PyObject* t) {
1163-
if (!PyDict_Check(t)) {
1162+
static type f(py::borrowed_ref<> t) {
1163+
if (!PyDict_Check(t.get())) {
11641164
throw py::exception(
11651165
PyExc_TypeError,
11661166
"from_object<table_view<...>> input must be a Python dictionary, got: ",
1167-
Py_TYPE(t)->tp_name);
1167+
Py_TYPE(t.get())->tp_name);
11681168
}
11691169

1170-
py::owned_ref copy(PyDict_Copy(t));
1170+
py::owned_ref copy(PyDict_Copy(t.get()));
11711171
if (!copy) {
11721172
throw py::exception();
11731173
}
11741174

1175-
type out(pop_column<py::detail::unwrap_column<columns>>(copy.get())...);
1175+
type out(pop_column<py::detail::unwrap_column<columns>>(copy)...);
11761176
if (PyDict_Size(copy.get())) {
11771177
py::owned_ref keys(PyDict_Keys(copy.get()));
11781178
if (!keys) {
@@ -1190,39 +1190,39 @@ struct from_object<py::row<columns...>> {
11901190
using type = py::row<columns...>;
11911191

11921192
template<typename Column>
1193-
static auto pop_column(PyObject* t) {
1193+
static auto pop_column(py::borrowed_ref<> t) {
11941194
auto text = py::cs::to_array(typename Column::key{});
11951195
auto column_name = py::to_object(
11961196
*reinterpret_cast<std::array<char, text.size() - 1>*>(text.data()));
11971197
if (!column_name) {
11981198
throw py::exception();
11991199
}
1200-
PyObject* column_ob = PyDict_GetItem(t, column_name.get());
1200+
PyObject* column_ob = PyDict_GetItem(t.get(), column_name.get());
12011201
if (!column_ob) {
12021202
throw py::exception(PyExc_ValueError, "missing column: ", column_name);
12031203
}
1204-
if (PyDict_DelItem(t, column_name.get())) {
1204+
if (PyDict_DelItem(t.get(), column_name.get())) {
12051205
// pop the item to track which columns we used
12061206
throw py::exception();
12071207
}
12081208
return py::from_object<typename Column::value>(column_ob);
12091209
}
12101210

12111211
public:
1212-
static type f(PyObject* t) {
1213-
if (!PyDict_Check(t)) {
1212+
static type f(py::borrowed_ref<> t) {
1213+
if (!PyDict_Check(t.get())) {
12141214
throw py::exception(
12151215
PyExc_TypeError,
1216-
"from_object<table_view<...>> input must be a Python dictionary, got: ",
1217-
Py_TYPE(t)->tp_name);
1216+
"from_object<row<...>> input must be a Python dictionary, got: ",
1217+
Py_TYPE(t.get())->tp_name);
12181218
}
12191219

1220-
py::owned_ref copy(PyDict_Copy(t));
1220+
py::owned_ref copy(PyDict_Copy(t.get()));
12211221
if (!copy) {
12221222
throw py::exception();
12231223
}
12241224

1225-
type out(pop_column<py::detail::unwrap_column<columns>>(copy.get())...);
1225+
type out(pop_column<py::detail::unwrap_column<columns>>(copy)...);
12261226
if (PyDict_Size(copy.get())) {
12271227
py::owned_ref keys(PyDict_Keys(copy.get()));
12281228
if (!keys) {

tests/test_from_object.cc

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,14 @@
99
#include "libpy/numpy_utils.h"
1010
#include "libpy/object_map_key.h"
1111
#include "libpy/owned_ref.h"
12+
#include "libpy/table.h"
1213
#include "libpy/util.h"
1314

1415
#include "test_utils.h"
1516

1617
namespace test_from_object {
18+
using namespace py::cs::literals;
19+
1720
class from_object : public with_python_interpreter {};
1821

1922
template<typename T>
@@ -243,6 +246,31 @@ TEST_F(from_object, ndarray_view_any_ref) {
243246
}
244247
}
245248

249+
TEST_F(from_object, table_view) {
250+
py::owned_ref ns = RUN_PYTHON(R"(
251+
import numpy as np
252+
table_dict = {b'a': np.array([1] * 4, dtype='i8'),
253+
b'b': np.array([2] * 4, dtype='f8')}
254+
)");
255+
ASSERT_TRUE(ns);
256+
257+
py::borrowed_ref table_dict = PyDict_GetItemString(ns.get(), "table_dict");
258+
ASSERT_TRUE(table_dict);
259+
260+
using my_table_view = py::table_view<py::C<std::int64_t>("a"_cs),
261+
py::C<double>("b"_cs)>;
262+
auto table_view = py::from_object<my_table_view>(table_dict);
263+
ASSERT_EQ(table_view.size(), 4ul);
264+
265+
for (auto row_view : table_view) {
266+
const auto& a = row_view.get("a"_cs);
267+
const auto& b = row_view.get("b"_cs);
268+
269+
EXPECT_EQ(a, 1);
270+
EXPECT_EQ(b, 2.0);
271+
}
272+
}
273+
246274
TEST_F(from_object, object_map_key) {
247275
PyObject* ob = Py_None;
248276
Py_ssize_t starting_ref_count = Py_REFCNT(ob);

0 commit comments

Comments
 (0)