bsonsearch 2.1.2__cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
bsonsearch/__init__.py ADDED
@@ -0,0 +1,191 @@
1
+ """
2
+ wrapper for bsonsearch
3
+ """
4
+
5
+ from .matcher_module import Matcher, Document, Utils
6
+
7
+
8
+ try:
9
+ import yara
10
+ import io
11
+ import bson
12
+
13
+ def YARA_DICT(compiled_rule):
14
+ compiled_binary_rule = io.BytesIO()
15
+ compiled_rule.save(file=compiled_binary_rule)
16
+ return {"$yara":bson.Binary(compiled_binary_rule.getvalue())}
17
+
18
+ def YARA_LOAD(fn):
19
+ compiled_rule = yara.load(fn)
20
+ return YARA_DICT(compiled_rule)
21
+
22
+ def YARA_COMPILE(fn):
23
+ compiled_rule = yara.compile(fn)
24
+ return YARA_DICT(compiled_rule)
25
+
26
+ def YARA_COMPILE_STR(source):
27
+ compiled_rule = yara.compile(source=source)
28
+ return YARA_DICT(compiled_rule)
29
+
30
+ def YARA_SOURCE_STR(source):
31
+ assert isinstance(source, bytes)
32
+ return {"$yara": {"source": source}}
33
+
34
+ except ImportError:
35
+ yara = None
36
+
37
+
38
+ try:
39
+ import discodb
40
+ import bson
41
+ DISCO_VALUE_ONLY = "$valueonly"
42
+ DISCO_KEY_EXISTS = "$keyexists"
43
+ DISCO_VALUE_IS = "$valueis"
44
+ DISCO_CNF_QUERY = "$Q"
45
+ DISCO_DDB_FILE = "$ddb"
46
+
47
+ def DISCO_VALUE_IS_CONFIG(**kwargs):
48
+ spec = {
49
+ kwargs["ns"]: {
50
+ "$module": {
51
+ "name": "disco",
52
+ "config": {
53
+ DISCO_VALUE_IS: kwargs['value'],
54
+ DISCO_DDB_FILE: kwargs["ddb"]
55
+ }
56
+ }
57
+ }
58
+ }
59
+ return spec
60
+
61
+ def DISCO_VALUE_ONLY_CONFIG(**kwargs):
62
+ spec = {
63
+ kwargs["ns"]: {
64
+ "$module": {
65
+ "name": "disco",
66
+ "config": {
67
+ DISCO_VALUE_ONLY: kwargs['value'],
68
+ DISCO_DDB_FILE: kwargs["ddb"]
69
+ }
70
+ }
71
+ }
72
+ }
73
+ return spec
74
+
75
+ def DISCO_KEY_EXISTS_CONFIG(**kwargs):
76
+ spec = {
77
+ kwargs["ns"]: {
78
+ "$module": {
79
+ "name": "disco",
80
+ "config": {
81
+ DISCO_KEY_EXISTS: 0,
82
+ DISCO_DDB_FILE: kwargs["ddb"]
83
+ }
84
+ }
85
+ }
86
+ }
87
+ return spec
88
+
89
+
90
+ def DISCO_QUERY_CONFIG(**kwargs):
91
+ config = bson.son.SON()
92
+ config['$ddb'] = kwargs["ddb"]
93
+ config[DISCO_CNF_QUERY] = discodb.Q.parse(kwargs["cnf"]).deploy()
94
+ config['precache'] = kwargs.get("precache", False)
95
+ spec = {
96
+ kwargs["ns"]: {
97
+ "$module": {
98
+ "name": "disco",
99
+ "config": config
100
+ }
101
+ }
102
+ }
103
+ return spec
104
+
105
+ def DISCODB_CNF_TO_DICT(cnf_string):
106
+ return discodb.Q.parse(cnf_string).deploy()
107
+ except ImportError:
108
+ discodb = None
109
+
110
+
111
+ try:
112
+ import IPy
113
+ import struct
114
+ from bson.binary import Binary
115
+ def pack_ip(ip_string):
116
+ '''
117
+
118
+ :param ip_string: String representation of ipv4 or ipv6 address ("127.0.0.1" or "::1"
119
+ :return: Binary encapsulated and packed 16 byte integer
120
+ '''
121
+ ip = IPy.IP(ip_string)
122
+ ip_int = ip.int()
123
+ ip_bin = Binary(struct.pack(">QQ", ip_int/(2**64), ip_int%(2**64)), 0x80+ip.version())
124
+ return ip_bin
125
+
126
+ def ip_inrange_query(namespace, ip_string, netmask):
127
+ """
128
+ builds the $inIPRange
129
+ :param namespace:
130
+ :param ip_string:
131
+ :param netmask:
132
+ :return:
133
+ """
134
+ assert namespace
135
+ ip_bin = pack_ip(ip_string)
136
+ nm_bin = pack_ip(netmask)
137
+ assert ip_bin.subtype == nm_bin.subtype
138
+ return {namespace: {"$inIPrange": [ip_bin, nm_bin]}}
139
+
140
+ def ip_inrangeset_query(namespace, list_of_ip_netmask_tuples):
141
+ """
142
+
143
+ :param namespace:
144
+ :param list_of_ip_netmask_tuples: [(ip1,mask1), (ip2,mask2)...]
145
+ :return:dict
146
+ """
147
+ setlist = []
148
+ assert namespace
149
+ for ip_string, netmask in list_of_ip_netmask_tuples:
150
+ ip_bin = pack_ip(ip_string)
151
+ nm_bin = pack_ip(netmask)
152
+ setlist.append([ip_bin, nm_bin])
153
+ assert ip_bin.subtype == nm_bin.subtype
154
+ return {namespace: {"$inIPrangeset": setlist}}
155
+ except ImportError:
156
+ pack_ip = None
157
+
158
+
159
+
160
+ # list_of_tuples = unroll("", [], highly_embedded_dict)
161
+ def unroll(current_key, output_map, entry, keys_to_append=None):
162
+ """
163
+
164
+ :param current_key:
165
+ :param output_map:
166
+ :param entry:
167
+ :param keys_to_append:
168
+ :return:
169
+ """
170
+ def unroll_dict(current_key, output_map, entry, keys_to_append=None):
171
+ for key, value in entry.items():
172
+ unroll(".".join([current_key, key]).lstrip("."),
173
+ output_map,
174
+ value,
175
+ keys_to_append=keys_to_append)
176
+
177
+ def unroll_list(current_key, output_map, entry, keys_to_append=None):
178
+ for item in entry:
179
+ unroll(current_key,
180
+ output_map,
181
+ item,
182
+ keys_to_append=keys_to_append)
183
+
184
+ if isinstance(entry, dict):
185
+ unroll_dict(current_key, output_map, entry, keys_to_append=keys_to_append)
186
+ elif isinstance(entry, list):
187
+ unroll_list(current_key, output_map, entry, keys_to_append=keys_to_append)
188
+ else: # not iterable
189
+ if not keys_to_append or current_key in keys_to_append:
190
+ output_map.append((current_key, entry))
191
+ return output_map
@@ -0,0 +1,551 @@
1
+ #define WITH_MODULES
2
+
3
+ //The modules we know to be enabled from the build
4
+ #define WITH_DUKJS
5
+ #define WITH_DISCO
6
+ #define WITH_MATH
7
+
8
+ #define WITH_UTILS
9
+ #define WITH_PROJECTION
10
+ #define ALLOW_FILESYSTEM
11
+ #define WITH_CONDITIONAL
12
+
13
+ #define PY_SSIZE_T_CLEAN
14
+ #include <Python.h>
15
+ #include "bsoncompare.h"
16
+ #include "mongoc-matcher-private.h"
17
+ #include "matcher-module-duk.h"
18
+
19
+ typedef struct {
20
+ PyObject_HEAD
21
+ Py_ssize_t value;
22
+ } Utils;
23
+
24
+ typedef struct {
25
+ PyObject_HEAD
26
+ mongoc_matcher_t *matcher;
27
+ const char *json;
28
+ Py_ssize_t value;
29
+ } Matcher;
30
+
31
+ typedef struct {
32
+ PyObject_HEAD
33
+ bson_t *document;
34
+ const char *json;
35
+ Py_ssize_t value;
36
+ } Document;
37
+
38
+
39
+ // ---- UTILS FUNCTIONS ----
40
+ static PyObject *
41
+ Utils_startup(Utils *self, PyObject *args, PyObject *kwds)
42
+ {
43
+ int result = bsonsearch_startup();
44
+ return PyLong_FromLong(result);
45
+ }
46
+
47
+ static PyObject *
48
+ Utils_shutdown(Utils *self, PyObject *args, PyObject *kwds)
49
+ {
50
+ int result = bsonsearch_shutdown();
51
+ return PyLong_FromLong(result);
52
+ }
53
+
54
+ static PyObject *
55
+ Utils_regex_destroy(Utils *self, PyObject *args, PyObject *kwds)
56
+ {
57
+ int result = regex_destroy();
58
+ return PyLong_FromLong(result);
59
+ }
60
+
61
+ static PyObject *
62
+ Utils_to_bson(Utils *self, PyObject *args, PyObject *kwds)
63
+ {
64
+ const char * json = NULL;
65
+ Py_ssize_t json_len;
66
+ if (!PyArg_ParseTuple(args, "s#", &json, &json_len)) {
67
+ return NULL;
68
+ }
69
+ bson_t *bson_object = generate_doc_from_json((const uint8_t*)json, json_len);
70
+ const uint8_t *doc_bson = bson_get_data(bson_object);
71
+
72
+ PyObject * result = Py_BuildValue("y#", doc_bson, bson_object->len);
73
+ bson_destroy(bson_object);
74
+ bson_free((void *)doc_bson);
75
+
76
+ return result;
77
+ }
78
+
79
+ static PyObject *
80
+ Utils_haversine_distance(Utils *self, PyObject *args, PyObject *kwds)
81
+ {
82
+ double lon1;
83
+ double lat1;
84
+ double lon2;
85
+ double lat2;
86
+ if (!PyArg_ParseTuple(args, "dddd", &lon1, &lat1, &lon2, &lat2)) {
87
+ return NULL; // Error occurred, exception already set
88
+ }
89
+ double result = bsonsearch_haversine_distance(
90
+ lon1, lat1, lon2, lat2
91
+ );
92
+ return PyFloat_FromDouble(result);
93
+ }
94
+
95
+
96
+ static PyObject *
97
+ Utils_haversine_distance_degrees(Utils *self, PyObject *args, PyObject *kwds)
98
+ {
99
+ double lon1;
100
+ double lat1;
101
+ double lon2;
102
+ double lat2;
103
+ if (!PyArg_ParseTuple(args, "dddd", &lon1, &lat1, &lon2, &lat2)) {
104
+ return NULL; // Error occurred, exception already set
105
+ }
106
+ double result = bsonsearch_haversine_distance_degrees(
107
+ lon1, lat1, lon2, lat2
108
+ );
109
+ return PyFloat_FromDouble(result);
110
+ }
111
+
112
+ static PyObject *
113
+ Utils_crossarc_degrees(Utils *self, PyObject *args, PyObject *kwds)
114
+ {
115
+ double lon1;
116
+ double lat1;
117
+ double lon2;
118
+ double lat2;
119
+ double lon3;
120
+ double lat3;
121
+ if (!PyArg_ParseTuple(args, "dddddd", &lon1, &lat1, &lon2, &lat2, &lon3, &lat3)) {
122
+ return NULL; // Error occurred, exception already set
123
+ }
124
+ double result = bsonsearch_get_crossarc_degrees(
125
+ lon1, lat1, lon2, lat2, lon3, lat3
126
+ );
127
+ return PyFloat_FromDouble(result);
128
+ }
129
+
130
+ static PyObject *
131
+ Utils_get_value(Utils *self, PyObject *Py_UNUSED(ignored))
132
+ {
133
+ return PyLong_FromLong(self->value);
134
+ }
135
+
136
+ // Method definitions for Matcher
137
+ static PyMethodDef Utils_methods[] = {
138
+ {"get_value", (PyCFunction)Utils_get_value, METH_NOARGS, "Return the value of the Document instance."},
139
+ {"regex_destroy", (PyCFunction)Utils_regex_destroy, METH_VARARGS, "Frees internal regex cache"},
140
+ {"startup", (PyCFunction)Utils_startup, METH_VARARGS, "Prep the module internally"},
141
+ {"shutdown", (PyCFunction)Utils_shutdown, METH_VARARGS, "Cleanup the module internally"},
142
+ {"to_bson", (PyCFunction)Utils_to_bson, METH_VARARGS, "convert a json document to bson document"},
143
+ {"haversine_distance", (PyCFunction)Utils_haversine_distance, METH_VARARGS, "Haversine distance using radians"},
144
+ {"haversine_distance_degrees", (PyCFunction)Utils_haversine_distance_degrees, METH_VARARGS, "Haversine distance using degrees"},
145
+ {"crossarc_degrees", (PyCFunction)Utils_crossarc_degrees, METH_VARARGS, "Crossarc"},
146
+ {NULL} /* Sentinel */
147
+ };
148
+
149
+ // Matcher_new: Constructor (allocates and initializes the C struct)
150
+ static PyObject *
151
+ Utils_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
152
+ {
153
+ Utils *self;
154
+ self = (Utils *)type->tp_alloc(type, 0);
155
+ if (self != NULL) {
156
+ self->value = 0; // Default value
157
+ }
158
+ return (PyObject *)self;
159
+ }
160
+
161
+ // Matcher_init: Initializer (sets instance attributes)
162
+ static int
163
+ Utils_init(Utils *self, PyObject *args, PyObject *kwds)
164
+ {
165
+ return 0;
166
+ }
167
+
168
+ // Matcher_dealloc: Destructor (frees resources)
169
+ static void
170
+ Utils_dealloc(Document *self)
171
+ {
172
+ Py_TYPE(self)->tp_free((PyObject *)self);
173
+ }
174
+
175
+
176
+ // 6. Create the PyTypeObject
177
+ static PyTypeObject UtilsClassType = {
178
+ PyVarObject_HEAD_INIT(NULL, 0)
179
+ .tp_name = "bsonsearch.matcher_module.Utils",
180
+ .tp_doc = "matchermodule",
181
+ .tp_basicsize = sizeof(Utils),
182
+ .tp_itemsize = 0,
183
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
184
+ .tp_new = Utils_new, // Use the custom tp_new
185
+ .tp_init = (initproc)Utils_init,
186
+ .tp_dealloc = (destructor)Utils_dealloc,
187
+ .tp_methods = Utils_methods,
188
+ };
189
+
190
+ // ---- Document FUNCTIONS ----
191
+
192
+ static PyObject *
193
+ Document_get_value(Document *self, PyObject *Py_UNUSED(ignored))
194
+ {
195
+ return PyLong_FromLong(self->value);
196
+ }
197
+
198
+ static PyObject *
199
+ Document_as_bson(Document *self, PyObject *Py_UNUSED(ignored))
200
+ {
201
+ const uint8_t *doc_bson = bson_get_data(self->document);
202
+ PyObject * result = Py_BuildValue("y#", doc_bson, self->document->len);
203
+ return result;
204
+ }
205
+
206
+ static PyObject *
207
+ Document_as_json(Document *self, PyObject *Py_UNUSED(ignored))
208
+ {
209
+ Py_ssize_t len;
210
+ char * str = bson_as_legacy_extended_json(self->document, &len);
211
+ PyObject * result = Py_BuildValue("s#", str, len);
212
+ bson_free(str);
213
+ return result;
214
+ }
215
+
216
+ static PyObject *
217
+ Document_as_canonical_json(Document *self, PyObject *Py_UNUSED(ignored))
218
+ {
219
+ Py_ssize_t len;
220
+ char * str = bson_as_canonical_extended_json(self->document, &len);
221
+ PyObject * result = Py_BuildValue("s#", str, len);
222
+ bson_free(str);
223
+ return result;
224
+ }
225
+
226
+ // Method definitions for Matcher
227
+ static PyMethodDef Document_methods[] = {
228
+ {"get_value", (PyCFunction)Document_get_value, METH_NOARGS, "Return the value of the Document instance."},
229
+ {"as_bson", (PyCFunction)Document_as_bson, METH_NOARGS, "Return the BSON bytes"},
230
+ {"as_json", (PyCFunction)Document_as_json, METH_NOARGS, "Return the JSON string"},
231
+ {"as_canonical_json", (PyCFunction)Document_as_canonical_json, METH_NOARGS, "Return the canonical JSON string"},
232
+ {NULL} /* Sentinel */
233
+ };
234
+
235
+ // Matcher_new: Constructor (allocates and initializes the C struct)
236
+ static PyObject *
237
+ Document_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
238
+ {
239
+ Document *self;
240
+ self = (Document *)type->tp_alloc(type, 0);
241
+ if (self != NULL) {
242
+ self->value = 0; // Default value
243
+ }
244
+ return (PyObject *)self;
245
+ }
246
+
247
+ // Matcher_init: Initializer (sets instance attributes)
248
+ static int
249
+ Document_init(Document *self, PyObject *args, PyObject *kwds)
250
+ {
251
+ PyObject* input_obj;
252
+ if (!PyArg_ParseTuple(args, "O", &input_obj)) {
253
+ return 0; // Error handling done by PyArg_ParseTuple
254
+ }
255
+ if (PyUnicode_Check(input_obj)) {
256
+ char* buffer = PyUnicode_AsUTF8AndSize(input_obj, &self->value);
257
+ if (buffer == NULL) {
258
+ PyErr_SetString(PyExc_TypeError, "Expected a JSON string");
259
+ return -1; // Error
260
+ }
261
+ self->document = generate_doc_from_json((const uint8_t *)buffer, (uint32_t)self->value);
262
+ if (self->document == NULL) {
263
+ PyErr_SetString(PyExc_TypeError, "Expected valid JSON string");
264
+ return -1;
265
+ }
266
+
267
+ } else if (PyBytes_Check(input_obj)) {
268
+ Py_ssize_t length;
269
+ char* buffer = NULL;
270
+ PyBytes_AsStringAndSize(input_obj, &buffer, &length);
271
+ self->document = bson_new_from_data((const uint8_t*)buffer, (uint32_t)length);
272
+ if (self->document == NULL) {
273
+ PyErr_SetString(PyExc_TypeError, "Expected a valid BSON document");
274
+ return -1;
275
+ }
276
+ } else {
277
+ PyErr_SetString(PyExc_TypeError, "Expected string or bytes");
278
+ return -1;
279
+ }
280
+
281
+ return 0;
282
+ }
283
+
284
+ // Matcher_dealloc: Destructor (frees resources)
285
+ static void
286
+ Document_dealloc(Document *self)
287
+ {
288
+ doc_destroy(self->document);
289
+ Py_TYPE(self)->tp_free((PyObject *)self);
290
+ }
291
+
292
+
293
+ // 6. Create the PyTypeObject
294
+ static PyTypeObject DocumentClassType = {
295
+ PyVarObject_HEAD_INIT(NULL, 0)
296
+ .tp_name = "bsonsearch.matcher_module.Document",
297
+ .tp_doc = "matchermodule",
298
+ .tp_basicsize = sizeof(Matcher),
299
+ .tp_itemsize = 0,
300
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
301
+ .tp_new = Document_new, // Use the custom tp_new
302
+ .tp_init = (initproc)Document_init,
303
+ .tp_dealloc = (destructor)Document_dealloc,
304
+ .tp_methods = Document_methods,
305
+ };
306
+
307
+
308
+
309
+
310
+ // ---- MATCHER FUNCTIONS ----
311
+ static PyObject *
312
+ Matcher_get_value(Matcher *self, PyObject *Py_UNUSED(ignored))
313
+ {
314
+ return PyLong_FromLong(self->value);
315
+ }
316
+
317
+ static PyObject *
318
+ Matcher_match_json(Matcher *self, PyObject *args, PyObject *kwds)
319
+ {
320
+ const char *c_string_ptr;
321
+ Py_ssize_t c_string_len;
322
+ if (!PyArg_ParseTuple(args, "s#", &c_string_ptr, &c_string_len)) {
323
+ return NULL;
324
+ }
325
+ bson_t *doc = generate_doc_from_json((const uint8_t*)c_string_ptr, c_string_len);
326
+ if (!doc) {
327
+ return NULL;
328
+ }
329
+ long result = matcher_compare_doc(self->matcher, doc);
330
+ doc_destroy(doc);
331
+ return PyBool_FromLong(result);
332
+ }
333
+
334
+ static PyObject *
335
+ Matcher_match_doc(Matcher *self, PyObject *args, PyObject *kwds)
336
+ {
337
+ PyObject *my_obj;
338
+ if (!PyArg_ParseTuple(args, "O!", &DocumentClassType, &my_obj)) {
339
+ return NULL; // PyArg_ParseTuple handles the error
340
+ }
341
+ Document *document = (Document *)my_obj;
342
+ long result = matcher_compare_doc(self->matcher, document->document);
343
+ return PyBool_FromLong(result);
344
+ }
345
+
346
+ static PyObject *
347
+ Matcher_project_json(Matcher *self, PyObject *args, PyObject *kwds)
348
+ {
349
+ PyObject *my_obj;
350
+ if (!PyArg_ParseTuple(args, "O!", &DocumentClassType, &my_obj)) {
351
+ return NULL; // PyArg_ParseTuple handles the error
352
+ }
353
+ Document *document = (Document *)my_obj;
354
+ char *result = bsonsearch_project_json(self->matcher, document->document);
355
+ PyObject *returnable = Py_BuildValue("s", result);
356
+ bson_free(result);
357
+ return returnable;
358
+ }
359
+
360
+ static PyObject *
361
+ Matcher_project_canonical_json(Matcher *self, PyObject *args, PyObject *kwds)
362
+ {
363
+ PyObject *my_obj;
364
+ if (!PyArg_ParseTuple(args, "O!", &DocumentClassType, &my_obj)) {
365
+ return NULL; // PyArg_ParseTuple handles the error
366
+ }
367
+ Document *document = (Document *)my_obj;
368
+ char *result = bsonsearch_project_canonical_json(self->matcher, document->document);
369
+ PyObject *returnable = Py_BuildValue("s", result);
370
+ bson_free(result);
371
+ return returnable;
372
+ }
373
+
374
+ static PyObject *
375
+ Matcher_project_bson(Matcher *self, PyObject *args, PyObject *kwds)
376
+ {
377
+ PyObject *my_obj;
378
+ if (!PyArg_ParseTuple(args, "O!", &DocumentClassType, &my_obj)) {
379
+ return NULL; // PyArg_ParseTuple handles the error
380
+ }
381
+ Document *document = (Document *)my_obj;
382
+ bson_t *projected = bsonsearch_project_bson(self->matcher, document->document);
383
+ const uint8_t *doc_bson = bson_get_data(projected);
384
+ PyObject *returnable = Py_BuildValue("y#", doc_bson, projected->len);
385
+ bson_destroy(projected);
386
+ return returnable;
387
+ }
388
+
389
+ static PyObject *
390
+ Matcher_as_bson(Matcher *self, PyObject *Py_UNUSED(ignored))
391
+ {
392
+ const uint8_t *doc_bson = bson_get_data(&self->matcher->query);
393
+ PyObject * result = Py_BuildValue("y#", doc_bson, self->matcher->query.len);
394
+ return result;
395
+ }
396
+
397
+ static PyObject *
398
+ Matcher_as_json(Matcher *self, PyObject *Py_UNUSED(ignored))
399
+ {
400
+ Py_ssize_t len;
401
+ char * str = bson_as_legacy_extended_json(&self->matcher->query, &len);
402
+ PyObject * result = Py_BuildValue("s#", str, len);
403
+ bson_free(str);
404
+ return result;
405
+ }
406
+
407
+ static PyObject *
408
+ Matcher_as_canonical_json(Matcher *self, PyObject *Py_UNUSED(ignored))
409
+ {
410
+ Py_ssize_t len;
411
+ char * str = bson_as_canonical_extended_json(&self->matcher->query, &len);
412
+ PyObject * result = Py_BuildValue("s#", str, len);
413
+ bson_free(str);
414
+ return result;
415
+ }
416
+
417
+ // Method definitions for Matcher
418
+ static PyMethodDef Matcher_methods[] = {
419
+ {"get_value", (PyCFunction)Matcher_get_value, METH_NOARGS, "Return the value of the Matcher instance."},
420
+ {"as_bson", (PyCFunction)Matcher_as_bson, METH_NOARGS, "gets the BSON representation of the matcher."},
421
+ {"as_json", (PyCFunction)Matcher_as_json, METH_NOARGS, "gets the JSON representation of the matcher."},
422
+ {"as_canonical_json", (PyCFunction)Matcher_as_canonical_json, METH_NOARGS, "gets the canonical JSON representation of the matcher."},
423
+ {"match_json", (PyCFunction)Matcher_match_json, METH_VARARGS, "Returns whether the doc matches the spec"},
424
+ {"match_doc", (PyCFunction)Matcher_match_doc, METH_VARARGS, "Returns whether the doc matches the spec"},
425
+ {"project_json", (PyCFunction)Matcher_project_json, METH_VARARGS, "projects data into a json document"},
426
+ {"project_canonical_json", (PyCFunction)Matcher_project_canonical_json, METH_VARARGS, "projects data into a canonical json document"},
427
+ {"project_bson", (PyCFunction)Matcher_project_bson, METH_VARARGS, "projects data into a bson document"},
428
+ {NULL} /* Sentinel */
429
+ };
430
+
431
+ // Matcher_new: Constructor (allocates and initializes the C struct)
432
+ static PyObject *
433
+ Matcher_new(PyTypeObject *type, PyObject *args, PyObject *kwds)
434
+ {
435
+ Matcher *self;
436
+ self = (Matcher *)type->tp_alloc(type, 0);
437
+ if (self != NULL) {
438
+ self->value = 0; // Default value
439
+ }
440
+ return (PyObject *)self;
441
+ }
442
+
443
+ // Matcher_init: Initializer (sets instance attributes)
444
+ static int
445
+ Matcher_init(Matcher *self, PyObject *args, PyObject *kwds)
446
+ {
447
+ PyObject* input_obj;
448
+ if (!PyArg_ParseTuple(args, "O", &input_obj)) {
449
+ return 0; // Error handling done by PyArg_ParseTuple
450
+ }
451
+ if (PyUnicode_Check(input_obj)) {
452
+ self->json = PyUnicode_AsUTF8AndSize(input_obj, &self->value);
453
+ if (self->json == NULL) {
454
+ PyErr_SetString(PyExc_TypeError, "Expected a JSON string");
455
+ return 0; // Error
456
+ }
457
+ self->matcher = generate_matcher_from_json((const uint8_t *)self->json, (uint32_t)self->value);
458
+ if (self->matcher == NULL) {
459
+ return -2;
460
+ }
461
+ } else if (PyBytes_Check(input_obj)) {
462
+ Py_ssize_t length;
463
+ char* buffer = NULL;
464
+ PyBytes_AsStringAndSize(input_obj, &buffer, &length);
465
+ self->matcher = generate_matcher((const uint8_t*)buffer, (uint32_t)length);
466
+ if (self->matcher == NULL) {
467
+ PyErr_SetString(PyExc_TypeError, "Expected a valid BSON document");
468
+ return 0;
469
+ }
470
+ } else {
471
+ PyErr_SetString(PyExc_TypeError, "Expected string or bytes");
472
+ return -1;
473
+ }
474
+ return 0;
475
+ }
476
+
477
+ // Matcher_dealloc: Destructor (frees resources)
478
+ static void
479
+ Matcher_dealloc(Matcher *self)
480
+ {
481
+ matcher_destroy(self->matcher);
482
+ Py_TYPE(self)->tp_free((PyObject *)self);
483
+ }
484
+
485
+
486
+ // 6. Create the PyTypeObject
487
+ static PyTypeObject MatcherClassType = {
488
+ PyVarObject_HEAD_INIT(NULL, 0)
489
+ .tp_name = "bsonsearch.matcher_module.Matcher",
490
+ .tp_doc = "matchermodule",
491
+ .tp_basicsize = sizeof(Matcher),
492
+ .tp_itemsize = 0,
493
+ .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
494
+ .tp_new = Matcher_new, // Use the custom tp_new
495
+ .tp_init = (initproc)Matcher_init,
496
+ .tp_dealloc = (destructor)Matcher_dealloc,
497
+ .tp_methods = Matcher_methods,
498
+ };
499
+
500
+
501
+
502
+ // ---- Module FUNCTIONS (unlikely to need editing below here) ----
503
+
504
+
505
+ static struct PyModuleDef matcher_module_def = {
506
+ PyModuleDef_HEAD_INIT,
507
+ .m_name = "bsonsearch.matcher_module",
508
+ .m_doc = "The module.",
509
+ .m_size = -1
510
+ };
511
+
512
+
513
+ // Module initialization function
514
+ PyMODINIT_FUNC
515
+ PyInit_matcher_module(void)
516
+ {
517
+ PyObject *m;
518
+
519
+ if (PyType_Ready(&MatcherClassType) < 0)
520
+ return NULL;
521
+
522
+ if (PyType_Ready(&DocumentClassType) < 0)
523
+ return NULL;
524
+
525
+ if (PyType_Ready(&UtilsClassType) < 0)
526
+ return NULL;
527
+
528
+ m = PyModule_Create(&matcher_module_def);
529
+ if (m == NULL)
530
+ return NULL;
531
+
532
+ Py_INCREF(&MatcherClassType);
533
+ if (PyModule_AddObject(m, "Matcher", (PyObject *)&MatcherClassType) < 0) {
534
+ Py_DECREF(&MatcherClassType);
535
+ Py_DECREF(m);
536
+ return NULL;
537
+ }
538
+ Py_INCREF(&DocumentClassType);
539
+ if (PyModule_AddObject(m, "Document", (PyObject *)&DocumentClassType) < 0) {
540
+ Py_DECREF(&DocumentClassType);
541
+ Py_DECREF(m);
542
+ return NULL;
543
+ }
544
+ Py_INCREF(&UtilsClassType);
545
+ if (PyModule_AddObject(m, "Utils", (PyObject *)&UtilsClassType) < 0) {
546
+ Py_DECREF(&UtilsClassType);
547
+ Py_DECREF(m);
548
+ return NULL;
549
+ }
550
+ return m;
551
+ }
@@ -0,0 +1,9 @@
1
+ Metadata-Version: 2.4
2
+ Name: bsonsearch
3
+ Version: 2.1.2
4
+ Summary: compare BSON/JSON documents locally
5
+ Author: Dan Bauman
6
+ Maintainer: Dan Bauman
7
+ Requires-Python: >=3.11
8
+ License-File: LICENSE
9
+ Dynamic: license-file
@@ -0,0 +1,14 @@
1
+ bsonsearch/__init__.py,sha256=HwyXH3os6-2kCbVKL3SUUgz_p38bAaBFpERzgPHzzcc,5376
2
+ bsonsearch/matcher_module.cpython-314-x86_64-linux-gnu.so,sha256=nuXhMIBjtnLaB1_GrEPzD7t3gjVPS-fO17Mum5GCpmI,71097
3
+ bsonsearch/matcher_ext/matcher_module.c,sha256=j9qCKULhmS7uPzdgl8IjKPhi2dsGQnvhULKcCs3VSfM,16944
4
+ bsonsearch.libs/libbson2-bf0e8588.so.2.1.2,sha256=Z3djfzeEB-URJSzWaSh7RuCSPPXFcBm_9DJk6npT78c,742785
5
+ bsonsearch.libs/libbsonsearch-f7a0a394.so,sha256=Wp8aN-dhDI1OMkQvnQ2RIR4ljzV7s3HkS3eJBewHaBQ,127713
6
+ bsonsearch.libs/libcmph-3f220d00.so.0.0.0,sha256=nBfFpgHtvvtyFciqyTFjhc5OXgxtB9N_p8K3TuL1utA,568601
7
+ bsonsearch.libs/libdiscodb-1a6a5160.so,sha256=ANqdJZ6uoMEjAcQ7sils5VJYvgMbj6kX2Q9qWZZDkFw,63377
8
+ bsonsearch.libs/libduktape-9faedf1e.so.207.20700,sha256=BqFn2Au4nQQ9Yx7Os_wGgBVINMdJNa-ZpQdK0XtRNro,351937
9
+ bsonsearch.libs/libpcre-3dcc71ac.so.1.2.13,sha256=2o02DZLn3toKuZuBxw89YeR1AFuXTg4OXN0ysoI75AA,2065633
10
+ bsonsearch-2.1.2.dist-info/METADATA,sha256=Q0gu93DeoPesbpLa-sHUNiTBlDK4zh26QUAQJssrL8g,209
11
+ bsonsearch-2.1.2.dist-info/WHEEL,sha256=DMni2HWXXG4oat8Y6bAVuu-xlkB-grHQiqsTu-lLHls,190
12
+ bsonsearch-2.1.2.dist-info/top_level.txt,sha256=yKvan_PZbgnwFJu7aiO90uulDgZEUkdIbqdARreIdAU,11
13
+ bsonsearch-2.1.2.dist-info/RECORD,,
14
+ bsonsearch-2.1.2.dist-info/licenses/LICENSE,sha256=Kn6aS7NZYk5QKVM90hlXPEC32aK9Z4ioFCkLvdGlKP4,1081
@@ -0,0 +1,7 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (80.9.0)
3
+ Root-Is-Purelib: false
4
+ Tag: cp314-cp314-manylinux_2_17_x86_64
5
+ Tag: cp314-cp314-manylinux2014_x86_64
6
+ Tag: cp314-cp314-manylinux_2_28_x86_64
7
+
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014-2016 Dan Bauman
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ bsonsearch
Binary file