omlish-cext 0.0.0.dev493__tar.gz → 0.0.0.dev510__tar.gz

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: omlish-cext
3
- Version: 0.0.0.dev493
3
+ Version: 0.0.0.dev510
4
4
  Summary: omlish
5
5
  Author: wrmsr
6
6
  License-Expression: BSD-3-Clause
@@ -14,7 +14,7 @@ Classifier: Programming Language :: Python :: 3.13
14
14
  Requires-Python: >=3.13
15
15
  Description-Content-Type: text/markdown
16
16
  License-File: LICENSE
17
- Requires-Dist: omlish==0.0.0.dev493
17
+ Requires-Dist: omlish==0.0.0.dev510
18
18
  Dynamic: license-file
19
19
 
20
20
  # Overview
@@ -158,8 +158,8 @@ dependencies of any kind**.
158
158
  - **[plugins](https://github.com/wrmsr/omlish/blob/master/omlish/testing/pytest/plugins)** - Various other plugins.
159
159
 
160
160
  - **[typedvalues](https://github.com/wrmsr/omlish/blob/master/omlish/typedvalues)** - A little toolkit around 'boxed'
161
- values, whose 'box' types convey more information than the bare values themselves. A rebellion against kwargs and env
162
- vars: instead of `foo(bar=1, baz=2)`, you do `foo(Bar(1), Baz(2))`.
161
+ values, whose 'box' types convey more information than the bare values themselves. A rebellion against kwargs / env
162
+ vars / giant config objects: instead of `foo(bar=1, baz=2)`, you do `foo(Bar(1), Baz(2))`.
163
163
 
164
164
  - **[lite](https://github.com/wrmsr/omlish/blob/master/omlish/lite)** - The standard library of 'lite' code. This is the
165
165
  only package beneath `lang`, and parts of it are re-exported by it for deduplication. On top of miscellaneous
@@ -139,8 +139,8 @@ dependencies of any kind**.
139
139
  - **[plugins](https://github.com/wrmsr/omlish/blob/master/omlish/testing/pytest/plugins)** - Various other plugins.
140
140
 
141
141
  - **[typedvalues](https://github.com/wrmsr/omlish/blob/master/omlish/typedvalues)** - A little toolkit around 'boxed'
142
- values, whose 'box' types convey more information than the bare values themselves. A rebellion against kwargs and env
143
- vars: instead of `foo(bar=1, baz=2)`, you do `foo(Bar(1), Baz(2))`.
142
+ values, whose 'box' types convey more information than the bare values themselves. A rebellion against kwargs / env
143
+ vars / giant config objects: instead of `foo(bar=1, baz=2)`, you do `foo(Bar(1), Baz(2))`.
144
144
 
145
145
  - **[lite](https://github.com/wrmsr/omlish/blob/master/omlish/lite)** - The standard library of 'lite' code. This is the
146
146
  only package beneath `lang`, and parts of it are re-exported by it for deduplication. On top of miscellaneous
@@ -0,0 +1,209 @@
1
+ // @omlish-cext
2
+ #define PY_SSIZE_T_CLEAN
3
+ #include "Python.h"
4
+ #include "structmember.h"
5
+
6
+ #include <vector>
7
+
8
+ //
9
+
10
+ #define _MODULE_NAME "_check"
11
+ #define _PACKAGE_NAME "omlish"
12
+ #define _MODULE_FULL_NAME _PACKAGE_NAME "." _MODULE_NAME
13
+
14
+ typedef struct check_state {
15
+ PyObject *typing_any;
16
+ PyTypeObject *nonetype;
17
+ } check_state;
18
+
19
+ static inline check_state * get_check_state(PyObject *module)
20
+ {
21
+ void *state = PyModule_GetState(module);
22
+ assert(state != NULL);
23
+ return (check_state *)state;
24
+ }
25
+
26
+ //
27
+
28
+ PyDoc_STRVAR(unpack_isinstance_spec_doc, "unpack_isinstance_spec(spec)");
29
+
30
+ static PyObject * unpack_isinstance_spec(PyObject *module, PyObject *spec)
31
+ {
32
+ check_state *state = get_check_state(module);
33
+
34
+ // If spec is a type, return (spec,)
35
+ if (PyType_Check(spec)) {
36
+ return PyTuple_Pack(1, spec);
37
+ }
38
+
39
+ PyObject *tuple_spec = nullptr;
40
+
41
+ // If not a tuple, wrap it in a tuple
42
+ if (!PyTuple_Check(spec)) {
43
+ tuple_spec = PyTuple_Pack(1, spec);
44
+ if (tuple_spec == nullptr) {
45
+ return nullptr;
46
+ }
47
+ } else {
48
+ // It's already a tuple, so we'll work with it
49
+ tuple_spec = spec;
50
+ Py_INCREF(tuple_spec);
51
+ }
52
+
53
+ // Check if None is in spec
54
+ Py_ssize_t size = PyTuple_Size(tuple_spec);
55
+ if (size < 0) {
56
+ Py_DECREF(tuple_spec);
57
+ return nullptr;
58
+ }
59
+
60
+ bool has_none = false;
61
+ bool has_any = false;
62
+
63
+ for (Py_ssize_t i = 0; i < size; i++) {
64
+ PyObject *item = PyTuple_GetItem(tuple_spec, i); // borrowed reference
65
+ if (item == nullptr) {
66
+ Py_DECREF(tuple_spec);
67
+ return nullptr;
68
+ }
69
+
70
+ if (item == Py_None) {
71
+ has_none = true;
72
+ }
73
+
74
+ // Check if item is typing.Any
75
+ if (state->typing_any != nullptr) {
76
+ int cmp = PyObject_RichCompareBool(item, state->typing_any, Py_EQ);
77
+ if (cmp < 0) {
78
+ Py_DECREF(tuple_spec);
79
+ return nullptr;
80
+ }
81
+ if (cmp) {
82
+ has_any = true;
83
+ }
84
+ }
85
+ }
86
+
87
+ // If typing.Any is in spec, return (object,)
88
+ if (has_any) {
89
+ Py_DECREF(tuple_spec);
90
+ return PyTuple_Pack(1, &PyBaseObject_Type);
91
+ }
92
+
93
+ // If None is in spec, filter it out and add NoneType
94
+ if (has_none) {
95
+ std::vector<PyObject*> filtered;
96
+ filtered.reserve(size);
97
+
98
+ for (Py_ssize_t i = 0; i < size; i++) {
99
+ PyObject *item = PyTuple_GetItem(tuple_spec, i); // borrowed reference
100
+ if (item != Py_None) {
101
+ filtered.push_back(item);
102
+ }
103
+ }
104
+
105
+ // Add NoneType
106
+ filtered.push_back((PyObject *)state->nonetype);
107
+
108
+ // Create new tuple
109
+ PyObject *result = PyTuple_New(filtered.size());
110
+ if (result == nullptr) {
111
+ Py_DECREF(tuple_spec);
112
+ return nullptr;
113
+ }
114
+
115
+ for (size_t i = 0; i < filtered.size(); i++) {
116
+ Py_INCREF(filtered[i]);
117
+ PyTuple_SET_ITEM(result, i, filtered[i]);
118
+ }
119
+
120
+ Py_DECREF(tuple_spec);
121
+ return result;
122
+ }
123
+
124
+ // Return the tuple as-is
125
+ return tuple_spec;
126
+ }
127
+
128
+ //
129
+
130
+ PyDoc_STRVAR(check_doc, "Native C++ implementations for omlish.lite.check");
131
+
132
+ static int check_exec(PyObject *module)
133
+ {
134
+ check_state *state = get_check_state(module);
135
+
136
+ // Get typing.Any
137
+ PyObject *typing_module = PyImport_ImportModule("typing");
138
+ if (typing_module == nullptr) {
139
+ // If typing module is not available, just set to nullptr
140
+ PyErr_Clear();
141
+ state->typing_any = nullptr;
142
+ } else {
143
+ state->typing_any = PyObject_GetAttrString(typing_module, "Any");
144
+ Py_DECREF(typing_module);
145
+ if (state->typing_any == nullptr) {
146
+ PyErr_Clear();
147
+ }
148
+ }
149
+
150
+ // Get NoneType (type(None))
151
+ state->nonetype = Py_TYPE(Py_None);
152
+ Py_INCREF(state->nonetype);
153
+
154
+ return 0;
155
+ }
156
+
157
+ static int check_traverse(PyObject *module, visitproc visit, void *arg)
158
+ {
159
+ check_state *state = get_check_state(module);
160
+ Py_VISIT(state->typing_any);
161
+ Py_VISIT(state->nonetype);
162
+ return 0;
163
+ }
164
+
165
+ static int check_clear(PyObject *module)
166
+ {
167
+ check_state *state = get_check_state(module);
168
+ Py_CLEAR(state->typing_any);
169
+ Py_CLEAR(state->nonetype);
170
+ return 0;
171
+ }
172
+
173
+ static void check_free(void *module)
174
+ {
175
+ check_clear((PyObject *)module);
176
+ }
177
+
178
+ static PyMethodDef check_methods[] = {
179
+ {"unpack_isinstance_spec", (PyCFunction)unpack_isinstance_spec, METH_O, unpack_isinstance_spec_doc},
180
+ {NULL, NULL, 0, NULL}
181
+ };
182
+
183
+ static struct PyModuleDef_Slot check_slots[] = {
184
+ {Py_mod_exec, (void *) check_exec},
185
+ {Py_mod_gil, Py_MOD_GIL_NOT_USED},
186
+ {Py_mod_multiple_interpreters, Py_MOD_MULTIPLE_INTERPRETERS_SUPPORTED},
187
+ {0, NULL}
188
+ };
189
+
190
+ static struct PyModuleDef check_module = {
191
+ .m_base = PyModuleDef_HEAD_INIT,
192
+ .m_name = _MODULE_NAME,
193
+ .m_doc = check_doc,
194
+ .m_size = sizeof(check_state),
195
+ .m_methods = check_methods,
196
+ .m_slots = check_slots,
197
+ .m_traverse = check_traverse,
198
+ .m_clear = check_clear,
199
+ .m_free = check_free,
200
+ };
201
+
202
+ extern "C" {
203
+
204
+ PyMODINIT_FUNC PyInit__check(void)
205
+ {
206
+ return PyModuleDef_Init(&check_module);
207
+ }
208
+
209
+ }