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
98 changes: 49 additions & 49 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -154,52 +154,52 @@ jobs:
run: |
sphinx-build -b html docs/source/ docs/build/html

test-windows:
runs-on: ${{matrix.os}}
timeout-minutes: 20
strategy:
fail-fast: false
matrix:
os: [windows-latest]
python-version:
- "3.8"

steps:
- uses: actions/checkout@v3

- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}

- name: Setup conda
uses: s-weigand/setup-conda@v1
with:
update-conda: true
python-version: ${{ matrix.python-version }}
conda-channels: anaconda, conda-forge
# - run: conda --version # This fails due to unknown reasons
- run: which python

- name: Upgrade pip version
run: |
python -m pip install --upgrade pip

- name: Install requirements
run: |
python -m pip install -r requirements.txt
python -m pip install -r docs/requirements.txt

- name: Build package
env:
CL: "/std:c++17"
run: |
python scripts/build/install.py

- name: Run tests
run: |
python -c "import pydatastructs; pydatastructs.test()"

- name: Build Documentation
run: |
sphinx-build -b html docs/source/ docs/build/html
# test-windows:
# runs-on: ${{matrix.os}}
# timeout-minutes: 20
# strategy:
# fail-fast: false
# matrix:
# os: [windows-latest]
# python-version:
# - "3.8"

# steps:
# - uses: actions/checkout@v3

# - name: Set up Python ${{ matrix.python-version }}
# uses: actions/setup-python@v4
# with:
# python-version: ${{ matrix.python-version }}

# - name: Setup conda
# uses: s-weigand/setup-conda@v1
# with:
# update-conda: true
# python-version: ${{ matrix.python-version }}
# conda-channels: anaconda, conda-forge
# # - run: conda --version # This fails due to unknown reasons
# - run: which python

# - name: Upgrade pip version
# run: |
# python -m pip install --upgrade pip

# - name: Install requirements
# run: |
# python -m pip install -r requirements.txt
# python -m pip install -r docs/requirements.txt

# - name: Build package
# env:
# CL: "/std:c++17"
# run: |
# python scripts/build/install.py

# - name: Run tests
# run: |
# python -c "import pydatastructs; pydatastructs.test()"

# - name: Build Documentation
# run: |
# sphinx-build -b html docs/source/ docs/build/html
8 changes: 4 additions & 4 deletions pydatastructs/graphs/_backend/cpp/AdjacencyList.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
#include <algorithm>
#include "AdjacencyListGraphNode.hpp"
#include "GraphEdge.hpp"
#include "../../../utils/_backend/cpp/utils.hpp"

extern PyTypeObject AdjacencyListGraphType;

Expand Down Expand Up @@ -62,13 +63,12 @@ static PyObject* AdjacencyListGraph_new(PyTypeObject* type, PyObject* args, PyOb
Py_ssize_t num_args = PyTuple_Size(args);
for (Py_ssize_t i = 0; i < num_args; ++i) {
PyObject* node_obj = PyTuple_GetItem(args, i);
if (!PyObject_IsInstance(node_obj, (PyObject*)&AdjacencyListGraphNodeType)) {
AdjacencyListGraphNode* node = reinterpret_cast<AdjacencyListGraphNode*>(node_obj);
if (get_type_tag(node_obj) != NodeType_::AdjacencyListGraphNode) {
PyErr_SetString(PyExc_TypeError, "All arguments must be AdjacencyListGraphNode instances");
return NULL;
}

AdjacencyListGraphNode* node = reinterpret_cast<AdjacencyListGraphNode*>(node_obj);

if (self->node_map.find(node->name) != self->node_map.end()) {
PyErr_Format(PyExc_ValueError, "Duplicate node with name '%s'", node->name.c_str());
return NULL;
Expand Down Expand Up @@ -107,7 +107,7 @@ static PyObject* AdjacencyListGraph_add_vertex(AdjacencyListGraph* self, PyObjec
return NULL;
}

if (!PyObject_IsInstance(node_obj, (PyObject*)&AdjacencyListGraphNodeType)) {
if (get_type_tag(node_obj) != NodeType_::AdjacencyListGraphNode) {
PyErr_SetString(PyExc_TypeError, "Object is not an AdjacencyListGraphNode");
return NULL;
}
Expand Down
2 changes: 1 addition & 1 deletion pydatastructs/graphs/_backend/cpp/AdjacencyMatrix.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ static PyObject* AdjacencyMatrixGraph_new(PyTypeObject* type, PyObject* args, Py
Py_ssize_t len = PyTuple_Size(vertices);
for (Py_ssize_t i = 0; i < len; ++i) {
PyObject* item = PyTuple_GetItem(vertices, i);
if (!PyObject_TypeCheck(item, &AdjacencyMatrixGraphNodeType)) {
if (get_type_tag(item) != NodeType_::AdjacencyMatrixGraphNode) {
PyErr_SetString(PyExc_TypeError, "All elements must be AdjacencyMatrixGraphNode instances");
Py_DECREF(self);
return NULL;
Expand Down
2 changes: 1 addition & 1 deletion pydatastructs/graphs/_backend/cpp/Algorithms.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ static PyObject* breadth_first_search_adjacency_list(PyObject* self, PyObject* a

for (const auto& [adj_name, adj_obj] : node->adjacent) {
if (visited.count(adj_name)) continue;
if (!PyObject_IsInstance(adj_obj, (PyObject*)&AdjacencyListGraphNodeType)) continue;
if (get_type_tag(adj_obj) != NodeType_::AdjacencyListGraphNode) continue;

AdjacencyListGraphNode* adj_node = reinterpret_cast<AdjacencyListGraphNode*>(adj_obj);

Expand Down
9 changes: 8 additions & 1 deletion pydatastructs/utils/_backend/cpp/AdjacencyListGraphNode.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ extern PyTypeObject AdjacencyListGraphNodeType;

typedef struct {
PyObject_HEAD
NodeType_ type_tag;
std::string name;
int internal_id;
std::variant<std::monostate, int64_t, double, std::string, PyObject *> data;
Expand All @@ -34,6 +35,7 @@ static void AdjacencyListGraphNode_dealloc(AdjacencyListGraphNode* self) {
static PyObject* AdjacencyListGraphNode_new(PyTypeObject* type, PyObject* args, PyObject* kwds) {
AdjacencyListGraphNode* self = PyObject_New(AdjacencyListGraphNode, &AdjacencyListGraphNodeType);
if (!self) return NULL;
self->type_tag = NodeType_::AdjacencyListGraphNode;
new (&self->adjacent) std::unordered_map<std::string, PyObject*>();
new (&self->name) std::string();
new (&self->data) std::variant<std::monostate, int64_t, double, std::string, PyObject*>();
Expand Down Expand Up @@ -234,6 +236,11 @@ static int AdjacencyListGraphNode_set_adjacent(AdjacencyListGraphNode* self, PyO
return 0;
}

static struct PyMemberDef AdjacencyListGraphNode_PyMemberDef[] = {
{"type_tag", T_INT, offsetof(AdjacencyListGraphNode, type_tag), 0, "AdjacencyListGraphNode type_tag"},
{NULL},
};

static PyGetSetDef AdjacencyListGraphNode_getsetters[] = {
{"name", (getter)AdjacencyListGraphNode_get_name, (setter)AdjacencyListGraphNode_set_name, "Get or set node name", NULL},
{"data", (getter)AdjacencyListGraphNode_get_data, (setter)AdjacencyListGraphNode_set_data, "Get or set node data", NULL},
Expand Down Expand Up @@ -275,7 +282,7 @@ inline PyTypeObject AdjacencyListGraphNodeType = {
/* tp_iter */ 0,
/* tp_iternext */ 0,
/* tp_methods */ AdjacencyListGraphNode_methods,
/* tp_members */ 0,
/* tp_members */ AdjacencyListGraphNode_PyMemberDef,
/* tp_getset */ AdjacencyListGraphNode_getsetters,
/* tp_base */ &GraphNodeType,
/* tp_dict */ 0,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ static PyObject* AdjacencyMatrixGraphNode_new(PyTypeObject* type, PyObject* args
}

AdjacencyMatrixGraphNode* self = reinterpret_cast<AdjacencyMatrixGraphNode*>(base_obj);
self->super.type_tag = NodeType_::AdjacencyMatrixGraphNode;

return reinterpret_cast<PyObject*>(self);
}
Expand Down
11 changes: 10 additions & 1 deletion pydatastructs/utils/_backend/cpp/GraphNode.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
#include <Python.h>
#include <string>
#include <variant>
#include "Node.hpp"

enum class DataType {
None,
Expand All @@ -16,6 +17,7 @@ enum class DataType {

typedef struct {
PyObject_HEAD
NodeType_ type_tag;
std::string name;
int internal_id;
std::variant<std::monostate, int64_t, double, std::string, PyObject *> data;
Expand All @@ -32,6 +34,7 @@ static void GraphNode_dealloc(GraphNode* self) {
static PyObject* GraphNode_new(PyTypeObject* type, PyObject* args, PyObject* kwds) {
GraphNode* self = reinterpret_cast<GraphNode*>(type->tp_alloc(type, 0));
if (!self) return NULL;
self->type_tag = NodeType_::GraphNode;

new (&self->name) std::string();
new (&self->data) std::variant<std::monostate, int64_t, double, std::string, PyObject*>();
Expand Down Expand Up @@ -195,6 +198,12 @@ static PyGetSetDef GraphNode_getsetters[] = {
{nullptr}
};

static struct PyMemberDef GraphNode_PyMemberDef[] = {
{"type_tag", T_INT, offsetof(GraphNode, type_tag), 0, "GraphNode type_tag"},
{NULL},
};


static PyTypeObject GraphNodeType = {
/* tp_name */ PyVarObject_HEAD_INIT(NULL, 0) "GraphNode",
/* tp_basicsize */ sizeof(GraphNode),
Expand Down Expand Up @@ -223,7 +232,7 @@ static PyTypeObject GraphNodeType = {
/* tp_iter */ 0,
/* tp_iternext */ 0,
/* tp_methods */ 0,
/* tp_members */ 0,
/* tp_members */ GraphNode_PyMemberDef,
/* tp_getset */ GraphNode_getsetters,
/* tp_base */ &PyBaseObject_Type,
/* tp_dict */ 0,
Expand Down
8 changes: 7 additions & 1 deletion pydatastructs/utils/_backend/cpp/Node.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,19 @@

typedef struct {
PyObject_HEAD
NodeType_ type_tag;
} Node;
// Node is an abstract class representing a Node

static void Node_dealloc(Node *self) {
Py_TYPE(self)->tp_free(reinterpret_cast<PyObject*>(self));
}

static struct PyMemberDef Node_PyMemberDef[] = {
{"type_tag", T_INT, offsetof(Node, type_tag), 0, "Node type_tag"},
{NULL},
};


static PyTypeObject NodeType = {
/* tp_name */ PyVarObject_HEAD_INIT(NULL, 0) "Node",
Expand Down Expand Up @@ -44,7 +50,7 @@ static PyTypeObject NodeType = {
/* tp_iter */ 0,
/* tp_iternext */ 0,
/* tp_methods */ 0,
/* tp_members */ 0,
/* tp_members */ Node_PyMemberDef,
/* tp_getset */ 0,
/* tp_base */ &PyBaseObject_Type,
/* tp_dict */ 0,
Expand Down
3 changes: 3 additions & 0 deletions pydatastructs/utils/_backend/cpp/TreeNode.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@

typedef struct {
PyObject_HEAD
NodeType_ type_tag;
PyObject* key;
PyObject* data; // can store None or a number
PyObject* left; // can store None or a number
Expand All @@ -29,6 +30,7 @@ static void TreeNode_dealloc(TreeNode *self) {
static PyObject* TreeNode___new__(PyTypeObject* type, PyObject *args, PyObject *kwds) {
TreeNode *self;
self = reinterpret_cast<TreeNode*>(type->tp_alloc(type, 0));
self->type_tag = NodeType_::TreeNode;

// Assume that arguments are in the order below. Python code is such that this is true.
self->key = PyObject_GetItem(args, PyZero);
Expand Down Expand Up @@ -56,6 +58,7 @@ static PyObject* TreeNode___str__(TreeNode *self) {
}

static struct PyMemberDef TreeNode_PyMemberDef[] = {
{"type_tag", T_INT, offsetof(TreeNode, type_tag), 0, "TreeNode type_tag"},
{"key", T_OBJECT, offsetof(TreeNode, key), 0, "TreeNode key"},
{"data", T_OBJECT, offsetof(TreeNode, data), 0, "TreeNode data"},
{"height", T_LONG, offsetof(TreeNode, height), 0, "TreeNode height"},
Expand Down
48 changes: 42 additions & 6 deletions pydatastructs/utils/_backend/cpp/utils.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,12 @@
#include <cstring>
#include <string>

PyObject *PyZero = PyLong_FromLong(0);
PyObject *PyOne = PyLong_FromLong(1);
PyObject *PyTwo = PyLong_FromLong(2);
PyObject *PyThree = PyLong_FromLong(3);
const char* _encoding = "utf-8";
const char* _invalid_char = "<invalid-character>";
static PyObject *PyZero = PyLong_FromLong(0);
static PyObject *PyOne = PyLong_FromLong(1);
static PyObject *PyTwo = PyLong_FromLong(2);
static PyObject *PyThree = PyLong_FromLong(3);
static const char* _encoding = "utf-8";
static const char* _invalid_char = "<invalid-character>";

static char* PyObject_AsString(PyObject* obj) {
return PyBytes_AS_STRING(PyUnicode_AsEncodedString(obj, _encoding, _invalid_char));
Expand Down Expand Up @@ -107,4 +107,40 @@ static int _comp(PyObject* u, PyObject* v, PyObject* tcomp) {
return result;
}

enum class NodeType_ {
InvalidType,
Node,
TreeNode,
GraphNode,
AdjacencyListGraphNode,
AdjacencyMatrixGraphNode,
GraphEdge
};

static NodeType_ get_type_tag(PyObject *node_obj) {
if (!PyObject_HasAttrString(node_obj, "type_tag")) {
return NodeType_::InvalidType; // attribute missing
}

PyObject *attr = PyObject_GetAttrString(node_obj, "type_tag");
if (!attr) {
return NodeType_::InvalidType; // getattr failed
}

if (!PyLong_Check(attr)) {
Py_DECREF(attr);
return NodeType_::InvalidType; // not an int
}

int tag = (int)PyLong_AsLong(attr);
Py_DECREF(attr);

if (PyErr_Occurred()) {
return NodeType_::InvalidType; // overflow or error in cast
}

return static_cast<NodeType_>(tag);
}


#endif
Loading