Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion dbzero/dbzero/interfaces.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,7 +201,7 @@ def fetch(self, id: Union[str, type], type: Optional[type] = None, prefix: Optio
"""
...

def find(self, *query_criteria: Union[Tag, List[Tag], Tuple[Tag], QueryObject, TagSet]) -> QueryObject:
def find(self, *query_criteria: Union[Tag, List[Tag], Tuple[Tag], QueryObject, TagSet], prefix: Optional[str] = None) -> QueryObject:
"""Query for memo objects based on search criteria such as tags, types, or subqueries.

Parameters
Expand All @@ -216,6 +216,9 @@ def find(self, *query_criteria: Union[Tag, List[Tag], Tuple[Tag], QueryObject, T
* Tuple of tags (AND): Objects with all of the specified tags
* QueryObject: Result of another query
* TagSet: Logical set operation.
prefix : str, optional
Optional data prefix to run the query on.
If omitted, the prefix to run the query is resolved based on query criteria.

Returns
-------
Expand Down
20 changes: 13 additions & 7 deletions dbzero/dbzero/reflection_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,17 +155,23 @@ def get_schema(self):

def all(self, snapshot=None, as_memo_base=False):
"""Find all instances of this Memo class."""
if not as_memo_base and self.get_class().is_known_type():

cls = self.get_class()
prefix_name = db0.get_prefix_of(cls).name

if not cls.type_exists():
as_memo_base = True

if as_memo_base:
if snapshot is not None:
return snapshot.find(self.get_class().type())
return snapshot.find(db0.MemoBase, cls, prefix=prefix_name)
else:
return db0.find(self.get_class().type())

# fall back to the base class if the actual model class is not imported
return db0.find(db0.MemoBase, cls, prefix=prefix_name)

if snapshot is not None:
return snapshot.find(db0.MemoBase, self.get_class())
return snapshot.find(cls, prefix=prefix_name)
else:
return db0.find(db0.MemoBase, self.get_class())
return db0.find(cls, prefix=prefix_name)

def get_instance_count(self):
"""Get number of instances of this Memo class."""
Expand Down
1 change: 1 addition & 0 deletions python_tests/test_reflection_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -356,3 +356,4 @@ def test_get_memo_class_of_instance(db0_fixture):
obj = MemoTestClass(123)
memo_class = db0.get_memo_class(obj)
assert memo_class is not None
assert db0.get_prefix_of(obj) == db0.get_prefix_of(memo_class.get_class())
31 changes: 31 additions & 0 deletions src/dbzero/bindings/python/ArgParse.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
#include "ArgParse.hpp"

namespace db0::python
{

const char* parseStringLikeArgument(PyObject *arg, const char *func_name, const char *arg_name) {
const char* result = nullptr;

if (PyUnicode_Check(arg)) {
result = PyUnicode_AsUTF8(arg);
if (!result) {
// Exception already set by PyUnicode_AsUTF8
return nullptr;
}
} else if (PyBytes_Check(arg)) {
result = PyBytes_AsString(arg);
if (!result) {
// Exception already set by PyBytes_AsString
return nullptr;
}
} else {
PyErr_Format(PyExc_TypeError,
"%s() argument '%s' must be str or bytes, not %s",
func_name, arg_name, Py_TYPE(arg)->tp_name);
return nullptr;
}

return result;
}

}
10 changes: 10 additions & 0 deletions src/dbzero/bindings/python/ArgParse.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#pragma once

#include <Python.h>

namespace db0::python
{

const char* parseStringLikeArgument(PyObject *arg, const char *func_name, const char *arg_name);

}
5 changes: 4 additions & 1 deletion src/dbzero/bindings/python/PyAPI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
#include <dbzero/bindings/python/collections/PyTuple.hpp>
#include <dbzero/bindings/python/types/PyEnum.hpp>
#include <dbzero/bindings/python/types/PyObjectId.hpp>
#include <dbzero/bindings/python/types/PyClass.hpp>
#include <dbzero/object_model/object/Object.hpp>
#include <dbzero/object_model/tags/TagIndex.hpp>
#include <dbzero/object_model/tags/QueryObserver.hpp>
Expand Down Expand Up @@ -392,7 +393,7 @@ namespace db0::python
if (prefix_name) {
PyToolkit::getPyWorkspace().getWorkspace().close(db0::PrefixName(prefix_name));
} else {
PyToolkit::getPyWorkspace().close();
PyToolkit::getPyWorkspace().close();
}
Py_RETURN_NONE;
}
Expand Down Expand Up @@ -458,6 +459,8 @@ namespace db0::python
fixture = reinterpret_cast<PyObjectIterable*>(py_object)->ext().getFixture();
} else if (PyObjectIterator_Check(py_object)) {
fixture = reinterpret_cast<PyObjectIterator*>(py_object)->ext().getFixture();
} else if (PyClassObject_Check(py_object)) {
fixture = reinterpret_cast<ClassObject*>(py_object)->ext().getFixture();
} else {
fixture = getFixtureOf(py_object);
}
Expand Down
29 changes: 24 additions & 5 deletions src/dbzero/bindings/python/PySnapshot.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "PyTagsAPI.hpp"
#include "Memo.hpp"
#include <dbzero/object_model/object/Object.hpp>
#include <dbzero/bindings/python/ArgParse.hpp>

namespace db0::python

Expand Down Expand Up @@ -120,16 +121,34 @@ namespace db0::python
return tryFetchFrom(snapshot, py_id, type, prefix_name).steal();
}

PyObject *tryPySnapshot_find(PyObject *self, PyObject *const *args, Py_ssize_t nargs)
PyObject *tryPySnapshot_find(PyObject *self, PyObject *args, PyObject *kwargs)
{
if (!PySnapshot_Check(self)) {
PyErr_SetString(PyExc_TypeError, "Invalid argument type");
return NULL;
}

Py_ssize_t num_args = PyTuple_Size(args);
std::vector<PyObject*> args_data(num_args);
for (Py_ssize_t i = 0; i < num_args; ++i) {
args_data[i] = PyTuple_GetItem(args, i);
}

const char prefix_arg[] = "prefix";
const char *prefix_name = nullptr;
if (kwargs) {
PyObject *py_prefix_name = PyDict_GetItemString(kwargs, prefix_arg);
if (py_prefix_name) {
prefix_name = parseStringLikeArgument(py_prefix_name, "find", prefix_arg);
if (!prefix_name) {
return nullptr;
}
}
}

auto &snapshot = reinterpret_cast<PySnapshotObject*>(self)->modifyExt();
// NOTE: self attached as context
return findIn(snapshot, args, nargs, self);
return findIn(snapshot, (PyObject* const*)args_data.data(), num_args, self, prefix_name);
}

PyObject *tryGetStateNum(db0::Snapshot &snapshot, PyObject *args, PyObject *kwargs)
Expand Down Expand Up @@ -176,10 +195,10 @@ namespace db0::python
return runSafe(tryPySnapshot_fetch, self, py_id, reinterpret_cast<PyTypeObject*>(py_type), prefix_name);
}

PyObject *PyAPI_PySnapshot_find(PyObject *self, PyObject *const *args, Py_ssize_t nargs)
PyObject *PyAPI_PySnapshot_find(PyObject *self, PyObject *args, PyObject *kwargs)
{
PY_API_FUNC
return runSafe(tryPySnapshot_find, self, args, nargs);
return runSafe(tryPySnapshot_find, self, args, kwargs);
}

PyObject *PyAPI_PySnapshot_enter(PyObject *self, PyObject *)
Expand Down Expand Up @@ -276,7 +295,7 @@ namespace db0::python
static PyMethodDef PySnapshot_methods[] =
{
{"fetch", (PyCFunction)&PyAPI_PySnapshot_fetch, METH_VARARGS | METH_KEYWORDS, "Fetch dbzero object instance by its ID or type (in case of a singleton)"},
{"find", (PyCFunction)&PyAPI_PySnapshot_find, METH_FASTCALL, ""},
{"find", (PyCFunction)&PyAPI_PySnapshot_find, METH_VARARGS | METH_KEYWORDS, ""},
{"deserialize", (PyCFunction)&PyAPI_PySnapshot_deserialize, METH_FASTCALL, "Deserialize from bytes within the snapshot's context"},
{"close", &PyAPI_PySnapshot_close, METH_NOARGS, "Close dbzero snapshot"},
{"get_state_num", (PyCFunction)&PyAPI_PySnapshot_GetStateNum, METH_VARARGS | METH_KEYWORDS, "Get state number of the snapshot"},
Expand Down
19 changes: 7 additions & 12 deletions src/dbzero/bindings/python/iter/PyJoinIterable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <dbzero/workspace/Workspace.hpp>
#include <dbzero/bindings/python/PyInternalAPI.hpp>
#include <dbzero/bindings/python/PyTagsAPI.hpp>
#include <dbzero/bindings/python/ArgParse.hpp>

namespace db0::python

Expand Down Expand Up @@ -96,7 +97,7 @@ namespace db0::python
for (Py_ssize_t i = 0; i < num_args; ++i) {
args_data[i] = PyTuple_GetItem(args, i);
}
const char *prefix_name = nullptr;

if (!kwargs) {
// The "join" function requires "on" as keyword argument
PyErr_SetString(PyExc_TypeError, "join() missing 1 required keyword argument: 'on'");
Expand All @@ -109,19 +110,13 @@ namespace db0::python
return NULL;
}

const char prefix_arg[] = "prefix";
const char *prefix_name = nullptr;
PyObject *py_prefix_name = PyDict_GetItemString(kwargs, "prefix");
if (py_prefix_name) {
if (!PyUnicode_Check(py_prefix_name) && !PyBytes_Check(py_prefix_name)) {
std::stringstream err_msg;
err_msg << "Expected 'prefix' argument to be a string or bytes, got: " << (py_prefix_name ? Py_TYPE(py_prefix_name)->tp_name : "None");
PyErr_SetString(PyExc_TypeError, err_msg.str().c_str());
return NULL;
}
if (PyUnicode_Check(py_prefix_name)) {
prefix_name = PyUnicode_AsUTF8(py_prefix_name);
} else {
assert(PyBytes_Check(py_prefix_name));
prefix_name = PyBytes_AsString(py_prefix_name);
prefix_name = parseStringLikeArgument(py_prefix_name, "join", prefix_arg);
if (!prefix_name) {
return nullptr;
}
}

Expand Down
18 changes: 6 additions & 12 deletions src/dbzero/bindings/python/iter/PyObjectIterable.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include <dbzero/bindings/python/PyInternalAPI.hpp>
#include <dbzero/bindings/python/PyTagsAPI.hpp>
#include <dbzero/bindings/python/PyToolkit.hpp>
#include <dbzero/bindings/python/ArgParse.hpp>
#include <dbzero/core/utils/base32.hpp>

namespace db0::python
Expand Down Expand Up @@ -251,21 +252,14 @@ namespace db0::python
args_data[i] = PyTuple_GetItem(args, i);
}

const char prefix_arg[] = "prefix";
const char *prefix_name = nullptr;
if (kwargs) {
PyObject *py_prefix_name = PyDict_GetItemString(kwargs, "prefix");
PyObject *py_prefix_name = PyDict_GetItemString(kwargs, prefix_arg);
if (py_prefix_name) {
if (!PyUnicode_Check(py_prefix_name) && !PyBytes_Check(py_prefix_name)) {
std::stringstream err_msg;
err_msg << "Expected 'prefix' argument to be a string or bytes, got: " << (py_prefix_name ? Py_TYPE(py_prefix_name)->tp_name : "None");
PyErr_SetString(PyExc_TypeError, err_msg.str().c_str());
return NULL;
}
if (PyUnicode_Check(py_prefix_name)) {
prefix_name = PyUnicode_AsUTF8(py_prefix_name);
} else {
assert(PyBytes_Check(py_prefix_name));
prefix_name = PyBytes_AsString(py_prefix_name);
prefix_name = parseStringLikeArgument(py_prefix_name, "find", prefix_arg);
if (!prefix_name) {
return nullptr;
}
}
}
Expand Down
4 changes: 2 additions & 2 deletions src/dbzero/bindings/python/types/PyClass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,14 +77,14 @@ namespace db0::python
return py_list.steal();
}

PyObject *tryGetAttributes(PyObject *self) {
PyObject *tryGetPyClassAttributes(PyObject *self) {
return tryGetClassAttributes(reinterpret_cast<ClassObject*>(self)->ext());
}

PyObject *PyAPI_PyClass_get_attributes(PyObject *self, PyObject *)
{
PY_API_FUNC
return runSafe(tryGetAttributes, self);
return runSafe(tryGetPyClassAttributes, self);
}

PyObject *PyAPI_PyClass_type_info(PyObject *self, PyObject *)
Expand Down
2 changes: 1 addition & 1 deletion src/dbzero/bindings/python/types/PyClass.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ namespace db0::python
ClassObject *makeClass(std::shared_ptr<db0::object_model::Class>);
bool PyClassObject_Check(PyObject *);

PyObject *tryGetAttributes(PyObject *);

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Change due to name collision.

PyObject *tryGetPyClassAttributes(PyObject *);
PyObject *tryGetClassAttributes(const db0::object_model::Class &);
PyObject *tryGetTypeInfo(const db0::object_model::Class &);

Expand Down
2 changes: 1 addition & 1 deletion src/dbzero/object_model/tags/TagIndex.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1014,7 +1014,7 @@ namespace db0::object_model
std::size_t nargs, const char *prefix_name)
{
if (prefix_name) {
return workspace.getFixture(prefix_name);
return workspace.getFixture(prefix_name, std::nullopt);
}

std::uint64_t fixture_uuid = 0;
Expand Down