omlish-cext 0.0.0.dev518__tar.gz → 0.0.0.dev519__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.
- {omlish_cext-0.0.0.dev518 → omlish_cext-0.0.0.dev519}/PKG-INFO +2 -2
- omlish_cext-0.0.0.dev519/omlish/dispatch/_dispatch.cc +349 -0
- omlish_cext-0.0.0.dev519/omlish/dispatch/_methods.cc +404 -0
- {omlish_cext-0.0.0.dev518 → omlish_cext-0.0.0.dev519}/omlish_cext.egg-info/PKG-INFO +2 -2
- {omlish_cext-0.0.0.dev518 → omlish_cext-0.0.0.dev519}/omlish_cext.egg-info/SOURCES.txt +2 -0
- omlish_cext-0.0.0.dev519/omlish_cext.egg-info/requires.txt +1 -0
- {omlish_cext-0.0.0.dev518 → omlish_cext-0.0.0.dev519}/pyproject.toml +2 -2
- {omlish_cext-0.0.0.dev518 → omlish_cext-0.0.0.dev519}/setup.py +10 -0
- omlish_cext-0.0.0.dev518/omlish_cext.egg-info/requires.txt +0 -1
- {omlish_cext-0.0.0.dev518 → omlish_cext-0.0.0.dev519}/LICENSE +0 -0
- {omlish_cext-0.0.0.dev518 → omlish_cext-0.0.0.dev519}/README.md +0 -0
- {omlish_cext-0.0.0.dev518 → omlish_cext-0.0.0.dev519}/omlish/_check.cc +0 -0
- {omlish_cext-0.0.0.dev518 → omlish_cext-0.0.0.dev519}/omlish/collections/hamt/_hamt.c +0 -0
- {omlish_cext-0.0.0.dev518 → omlish_cext-0.0.0.dev519}/omlish/lang/_asyncs.cc +0 -0
- {omlish_cext-0.0.0.dev518 → omlish_cext-0.0.0.dev519}/omlish/lang/imports/_capture.cc +0 -0
- {omlish_cext-0.0.0.dev518 → omlish_cext-0.0.0.dev519}/omlish/typedvalues/_collection.cc +0 -0
- {omlish_cext-0.0.0.dev518 → omlish_cext-0.0.0.dev519}/omlish_cext.egg-info/dependency_links.txt +0 -0
- {omlish_cext-0.0.0.dev518 → omlish_cext-0.0.0.dev519}/omlish_cext.egg-info/top_level.txt +0 -0
- {omlish_cext-0.0.0.dev518 → omlish_cext-0.0.0.dev519}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: omlish-cext
|
|
3
|
-
Version: 0.0.0.
|
|
3
|
+
Version: 0.0.0.dev519
|
|
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.
|
|
17
|
+
Requires-Dist: omlish==0.0.0.dev519
|
|
18
18
|
Dynamic: license-file
|
|
19
19
|
|
|
20
20
|
# Overview
|
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
// @omlish-cext
|
|
2
|
+
#define PY_SSIZE_T_CLEAN
|
|
3
|
+
#include "Python.h"
|
|
4
|
+
|
|
5
|
+
//
|
|
6
|
+
|
|
7
|
+
#define _MODULE_NAME "_dispatch"
|
|
8
|
+
#define _PACKAGE_NAME "omlish.dispatch"
|
|
9
|
+
#define _MODULE_FULL_NAME _PACKAGE_NAME "." _MODULE_NAME
|
|
10
|
+
|
|
11
|
+
typedef struct {
|
|
12
|
+
PyTypeObject *StrongCacheType;
|
|
13
|
+
PyObject *abc_get_cache_token;
|
|
14
|
+
} dispatch_state;
|
|
15
|
+
|
|
16
|
+
static inline dispatch_state * get_dispatch_state(PyObject *module)
|
|
17
|
+
{
|
|
18
|
+
void *state = PyModule_GetState(module);
|
|
19
|
+
assert(state != NULL);
|
|
20
|
+
return (dispatch_state *)state;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
//
|
|
24
|
+
|
|
25
|
+
typedef struct {
|
|
26
|
+
PyObject_HEAD
|
|
27
|
+
PyObject *token;
|
|
28
|
+
PyObject *dct;
|
|
29
|
+
PyObject *impls_by_arg_cls;
|
|
30
|
+
PyObject *find_impl;
|
|
31
|
+
PyObject *reset_cache_for_token;
|
|
32
|
+
PyObject *abc_get_cache_token;
|
|
33
|
+
} StrongCache;
|
|
34
|
+
|
|
35
|
+
static int StrongCache_traverse(StrongCache *self, visitproc visit, void *arg)
|
|
36
|
+
{
|
|
37
|
+
Py_VISIT(self->token);
|
|
38
|
+
Py_VISIT(self->dct);
|
|
39
|
+
Py_VISIT(self->impls_by_arg_cls);
|
|
40
|
+
Py_VISIT(self->find_impl);
|
|
41
|
+
Py_VISIT(self->reset_cache_for_token);
|
|
42
|
+
Py_VISIT(self->abc_get_cache_token);
|
|
43
|
+
return 0;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
static int StrongCache_clear(StrongCache *self)
|
|
47
|
+
{
|
|
48
|
+
Py_CLEAR(self->token);
|
|
49
|
+
Py_CLEAR(self->dct);
|
|
50
|
+
Py_CLEAR(self->impls_by_arg_cls);
|
|
51
|
+
Py_CLEAR(self->find_impl);
|
|
52
|
+
Py_CLEAR(self->reset_cache_for_token);
|
|
53
|
+
Py_CLEAR(self->abc_get_cache_token);
|
|
54
|
+
return 0;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
static void StrongCache_dealloc(StrongCache *self)
|
|
58
|
+
{
|
|
59
|
+
PyObject_GC_UnTrack(self);
|
|
60
|
+
StrongCache_clear(self);
|
|
61
|
+
Py_TYPE(self)->tp_free((PyObject *)self);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
static PyObject * StrongCache_dispatch(StrongCache *self, PyObject *cls)
|
|
65
|
+
{
|
|
66
|
+
// if token is not None and abc.get_cache_token() != token:
|
|
67
|
+
// reset_cache_for_token(self)
|
|
68
|
+
// return find_impl(cls, impls_by_arg_cls)
|
|
69
|
+
if (self->token != Py_None) {
|
|
70
|
+
PyObject *current_token = PyObject_CallNoArgs(self->abc_get_cache_token);
|
|
71
|
+
if (current_token == nullptr) {
|
|
72
|
+
return nullptr;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
int token_changed = PyObject_RichCompareBool(current_token, self->token, Py_NE);
|
|
76
|
+
Py_DECREF(current_token);
|
|
77
|
+
|
|
78
|
+
if (token_changed < 0) {
|
|
79
|
+
return nullptr;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (token_changed) {
|
|
83
|
+
// Call reset_cache_for_token(self)
|
|
84
|
+
PyObject *reset_result = PyObject_CallOneArg(self->reset_cache_for_token, (PyObject *)self);
|
|
85
|
+
if (reset_result == nullptr) {
|
|
86
|
+
return nullptr;
|
|
87
|
+
}
|
|
88
|
+
Py_DECREF(reset_result);
|
|
89
|
+
|
|
90
|
+
// Call find_impl(cls, impls_by_arg_cls)
|
|
91
|
+
PyObject *impl = PyObject_CallFunctionObjArgs(
|
|
92
|
+
self->find_impl,
|
|
93
|
+
cls,
|
|
94
|
+
self->impls_by_arg_cls,
|
|
95
|
+
nullptr
|
|
96
|
+
);
|
|
97
|
+
return impl;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// try:
|
|
102
|
+
// return dct[cls]
|
|
103
|
+
// except KeyError:
|
|
104
|
+
// pass
|
|
105
|
+
PyObject *cached = PyDict_GetItemWithError(self->dct, cls);
|
|
106
|
+
if (cached != nullptr) {
|
|
107
|
+
Py_INCREF(cached);
|
|
108
|
+
return cached;
|
|
109
|
+
}
|
|
110
|
+
if (PyErr_Occurred()) {
|
|
111
|
+
return nullptr;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// try:
|
|
115
|
+
// impl = impls_by_arg_cls[cls]
|
|
116
|
+
// except KeyError:
|
|
117
|
+
// impl = find_impl(cls, impls_by_arg_cls)
|
|
118
|
+
PyObject *impl = PyDict_GetItemWithError(self->impls_by_arg_cls, cls);
|
|
119
|
+
if (impl != nullptr) {
|
|
120
|
+
Py_INCREF(impl);
|
|
121
|
+
} else if (PyErr_Occurred()) {
|
|
122
|
+
return nullptr;
|
|
123
|
+
} else {
|
|
124
|
+
// Call find_impl(cls, impls_by_arg_cls)
|
|
125
|
+
impl = PyObject_CallFunctionObjArgs(
|
|
126
|
+
self->find_impl,
|
|
127
|
+
cls,
|
|
128
|
+
self->impls_by_arg_cls,
|
|
129
|
+
nullptr
|
|
130
|
+
);
|
|
131
|
+
if (impl == nullptr) {
|
|
132
|
+
return nullptr;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// dct[cls] = impl
|
|
137
|
+
if (PyDict_SetItem(self->dct, cls, impl) < 0) {
|
|
138
|
+
Py_DECREF(impl);
|
|
139
|
+
return nullptr;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
Py_DECREF(impl);
|
|
143
|
+
return impl;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
static PyObject * StrongCache_get_token(StrongCache *self, void *closure)
|
|
147
|
+
{
|
|
148
|
+
Py_INCREF(self->token);
|
|
149
|
+
return self->token;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
static PyObject * StrongCache_get_dct(StrongCache *self, void *closure)
|
|
153
|
+
{
|
|
154
|
+
Py_INCREF(self->dct);
|
|
155
|
+
return self->dct;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
static PyMethodDef StrongCache_methods[] = {
|
|
159
|
+
{"dispatch", (PyCFunction)StrongCache_dispatch, METH_O, "Dispatch to implementation for given class"},
|
|
160
|
+
{nullptr}
|
|
161
|
+
};
|
|
162
|
+
|
|
163
|
+
static PyGetSetDef StrongCache_getsets[] = {
|
|
164
|
+
{"token", (getter)StrongCache_get_token, nullptr, nullptr, nullptr},
|
|
165
|
+
{"dct", (getter)StrongCache_get_dct, nullptr, nullptr, nullptr},
|
|
166
|
+
{nullptr}
|
|
167
|
+
};
|
|
168
|
+
|
|
169
|
+
static PyType_Slot StrongCache_slots[] = {
|
|
170
|
+
{Py_tp_dealloc, (void *)StrongCache_dealloc},
|
|
171
|
+
{Py_tp_traverse, (void *)StrongCache_traverse},
|
|
172
|
+
{Py_tp_clear, (void *)StrongCache_clear},
|
|
173
|
+
{Py_tp_methods, StrongCache_methods},
|
|
174
|
+
{Py_tp_getset, StrongCache_getsets},
|
|
175
|
+
{Py_tp_doc, (void *)"Fast strong cache for dispatcher"},
|
|
176
|
+
{0, nullptr}
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
static PyType_Spec StrongCache_spec = {
|
|
180
|
+
.name = _MODULE_FULL_NAME ".StrongCache",
|
|
181
|
+
.basicsize = sizeof(StrongCache),
|
|
182
|
+
.itemsize = 0,
|
|
183
|
+
.flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
|
|
184
|
+
.slots = StrongCache_slots,
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
//
|
|
188
|
+
|
|
189
|
+
PyDoc_STRVAR(build_strong_dispatch_cache_doc,
|
|
190
|
+
"build_strong_dispatch_cache(impls_by_arg_cls, find_impl, reset_cache_for_token, token)\n\
|
|
191
|
+
\n\
|
|
192
|
+
Create a fast strong cache for dispatcher.\n\
|
|
193
|
+
\n\
|
|
194
|
+
Args:\n\
|
|
195
|
+
impls_by_arg_cls: Dictionary mapping types to implementations\n\
|
|
196
|
+
find_impl: Function to find implementation for a type\n\
|
|
197
|
+
reset_cache_for_token: Function to reset cache when token changes\n\
|
|
198
|
+
token: Current ABC cache token or None\n\
|
|
199
|
+
\n\
|
|
200
|
+
Returns:\n\
|
|
201
|
+
A StrongCache instance");
|
|
202
|
+
|
|
203
|
+
static PyObject * build_strong_dispatch_cache(PyObject *module, PyObject *args)
|
|
204
|
+
{
|
|
205
|
+
PyObject *impls_by_arg_cls;
|
|
206
|
+
PyObject *find_impl;
|
|
207
|
+
PyObject *reset_cache_for_token;
|
|
208
|
+
PyObject *token;
|
|
209
|
+
|
|
210
|
+
if (!PyArg_ParseTuple(args, "OOOO", &impls_by_arg_cls, &find_impl, &reset_cache_for_token, &token)) {
|
|
211
|
+
return nullptr;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (!PyDict_Check(impls_by_arg_cls)) {
|
|
215
|
+
PyErr_SetString(PyExc_TypeError, "impls_by_arg_cls must be a dictionary");
|
|
216
|
+
return nullptr;
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (!PyCallable_Check(find_impl)) {
|
|
220
|
+
PyErr_SetString(PyExc_TypeError, "find_impl must be callable");
|
|
221
|
+
return nullptr;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (!PyCallable_Check(reset_cache_for_token)) {
|
|
225
|
+
PyErr_SetString(PyExc_TypeError, "reset_cache_for_token must be callable");
|
|
226
|
+
return nullptr;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
dispatch_state *state = get_dispatch_state(module);
|
|
230
|
+
StrongCache *self = PyObject_GC_New(StrongCache, state->StrongCacheType);
|
|
231
|
+
if (self == nullptr) {
|
|
232
|
+
return nullptr;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
self->token = nullptr;
|
|
236
|
+
self->dct = nullptr;
|
|
237
|
+
self->impls_by_arg_cls = nullptr;
|
|
238
|
+
self->find_impl = nullptr;
|
|
239
|
+
self->reset_cache_for_token = nullptr;
|
|
240
|
+
self->abc_get_cache_token = nullptr;
|
|
241
|
+
|
|
242
|
+
self->token = Py_NewRef(token);
|
|
243
|
+
self->dct = PyDict_New();
|
|
244
|
+
if (self->dct == nullptr) {
|
|
245
|
+
Py_DECREF(self);
|
|
246
|
+
return nullptr;
|
|
247
|
+
}
|
|
248
|
+
self->impls_by_arg_cls = Py_NewRef(impls_by_arg_cls);
|
|
249
|
+
self->find_impl = Py_NewRef(find_impl);
|
|
250
|
+
self->reset_cache_for_token = Py_NewRef(reset_cache_for_token);
|
|
251
|
+
self->abc_get_cache_token = Py_NewRef(state->abc_get_cache_token);
|
|
252
|
+
|
|
253
|
+
PyObject_GC_Track(self);
|
|
254
|
+
return (PyObject *)self;
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
//
|
|
258
|
+
|
|
259
|
+
PyDoc_STRVAR(dispatch_doc, "Native C++ implementations for omlish.dispatch");
|
|
260
|
+
|
|
261
|
+
static int dispatch_exec(PyObject *module)
|
|
262
|
+
{
|
|
263
|
+
dispatch_state *state = get_dispatch_state(module);
|
|
264
|
+
|
|
265
|
+
// Import abc.get_cache_token
|
|
266
|
+
PyObject *abc_module = PyImport_ImportModule("abc");
|
|
267
|
+
if (abc_module == nullptr) {
|
|
268
|
+
return -1;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
state->abc_get_cache_token = PyObject_GetAttrString(abc_module, "get_cache_token");
|
|
272
|
+
Py_DECREF(abc_module);
|
|
273
|
+
if (state->abc_get_cache_token == nullptr) {
|
|
274
|
+
return -1;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Create the type dynamically
|
|
278
|
+
state->StrongCacheType = (PyTypeObject *)PyType_FromModuleAndSpec(
|
|
279
|
+
module,
|
|
280
|
+
&StrongCache_spec,
|
|
281
|
+
nullptr
|
|
282
|
+
);
|
|
283
|
+
if (state->StrongCacheType == nullptr) {
|
|
284
|
+
return -1;
|
|
285
|
+
}
|
|
286
|
+
Py_INCREF(state->StrongCacheType);
|
|
287
|
+
|
|
288
|
+
// Add the type to the module
|
|
289
|
+
if (PyModule_AddType(module, state->StrongCacheType) < 0) {
|
|
290
|
+
Py_CLEAR(state->StrongCacheType);
|
|
291
|
+
return -1;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
return 0;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
static int dispatch_traverse(PyObject *module, visitproc visit, void *arg)
|
|
298
|
+
{
|
|
299
|
+
dispatch_state *state = get_dispatch_state(module);
|
|
300
|
+
Py_VISIT(state->StrongCacheType);
|
|
301
|
+
Py_VISIT(state->abc_get_cache_token);
|
|
302
|
+
return 0;
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
static int dispatch_clear(PyObject *module)
|
|
306
|
+
{
|
|
307
|
+
dispatch_state *state = get_dispatch_state(module);
|
|
308
|
+
Py_CLEAR(state->StrongCacheType);
|
|
309
|
+
Py_CLEAR(state->abc_get_cache_token);
|
|
310
|
+
return 0;
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
static void dispatch_free(void *module)
|
|
314
|
+
{
|
|
315
|
+
dispatch_clear((PyObject *)module);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
static PyMethodDef dispatch_methods[] = {
|
|
319
|
+
{"build_strong_dispatch_cache", (PyCFunction)build_strong_dispatch_cache, METH_VARARGS, build_strong_dispatch_cache_doc},
|
|
320
|
+
{nullptr, nullptr, 0, nullptr}
|
|
321
|
+
};
|
|
322
|
+
|
|
323
|
+
static struct PyModuleDef_Slot dispatch_slots[] = {
|
|
324
|
+
{Py_mod_exec, (void *) dispatch_exec},
|
|
325
|
+
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
|
|
326
|
+
{Py_mod_multiple_interpreters, Py_MOD_MULTIPLE_INTERPRETERS_SUPPORTED},
|
|
327
|
+
{0, nullptr}
|
|
328
|
+
};
|
|
329
|
+
|
|
330
|
+
static struct PyModuleDef dispatch_module = {
|
|
331
|
+
.m_base = PyModuleDef_HEAD_INIT,
|
|
332
|
+
.m_name = _MODULE_NAME,
|
|
333
|
+
.m_doc = dispatch_doc,
|
|
334
|
+
.m_size = sizeof(dispatch_state),
|
|
335
|
+
.m_methods = dispatch_methods,
|
|
336
|
+
.m_slots = dispatch_slots,
|
|
337
|
+
.m_traverse = dispatch_traverse,
|
|
338
|
+
.m_clear = dispatch_clear,
|
|
339
|
+
.m_free = dispatch_free,
|
|
340
|
+
};
|
|
341
|
+
|
|
342
|
+
extern "C" {
|
|
343
|
+
|
|
344
|
+
PyMODINIT_FUNC PyInit__dispatch(void)
|
|
345
|
+
{
|
|
346
|
+
return PyModuleDef_Init(&dispatch_module);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
}
|
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
// @omlish-cext
|
|
2
|
+
#define PY_SSIZE_T_CLEAN
|
|
3
|
+
#include "Python.h"
|
|
4
|
+
#include "structmember.h"
|
|
5
|
+
|
|
6
|
+
//
|
|
7
|
+
|
|
8
|
+
#define _MODULE_NAME "_methods"
|
|
9
|
+
#define _PACKAGE_NAME "omlish.dispatch"
|
|
10
|
+
#define _MODULE_FULL_NAME _PACKAGE_NAME "." _MODULE_NAME
|
|
11
|
+
|
|
12
|
+
typedef struct {
|
|
13
|
+
PyTypeObject *MethodDispatchFuncType;
|
|
14
|
+
} methods_state;
|
|
15
|
+
|
|
16
|
+
static inline methods_state * get_methods_state(PyObject *module)
|
|
17
|
+
{
|
|
18
|
+
void *state = PyModule_GetState(module);
|
|
19
|
+
assert(state != NULL);
|
|
20
|
+
return (methods_state *)state;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
//
|
|
24
|
+
|
|
25
|
+
typedef struct {
|
|
26
|
+
PyObject_HEAD
|
|
27
|
+
PyObject *dispatch_func;
|
|
28
|
+
PyObject *base_func;
|
|
29
|
+
PyObject *func_name;
|
|
30
|
+
PyObject *dict;
|
|
31
|
+
} MethodDispatchFunc;
|
|
32
|
+
|
|
33
|
+
static int MethodDispatchFunc_traverse(MethodDispatchFunc *self, visitproc visit, void *arg)
|
|
34
|
+
{
|
|
35
|
+
Py_VISIT(self->dispatch_func);
|
|
36
|
+
Py_VISIT(self->base_func);
|
|
37
|
+
Py_VISIT(self->func_name);
|
|
38
|
+
Py_VISIT(self->dict);
|
|
39
|
+
return 0;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
static int MethodDispatchFunc_clear(MethodDispatchFunc *self)
|
|
43
|
+
{
|
|
44
|
+
Py_CLEAR(self->dispatch_func);
|
|
45
|
+
Py_CLEAR(self->base_func);
|
|
46
|
+
Py_CLEAR(self->func_name);
|
|
47
|
+
Py_CLEAR(self->dict);
|
|
48
|
+
return 0;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
static void MethodDispatchFunc_dealloc(MethodDispatchFunc *self)
|
|
52
|
+
{
|
|
53
|
+
PyObject_GC_UnTrack(self);
|
|
54
|
+
MethodDispatchFunc_clear(self);
|
|
55
|
+
Py_TYPE(self)->tp_free((PyObject *)self);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
static PyObject * MethodDispatchFunc_call(MethodDispatchFunc *self, PyObject *args, PyObject *kwargs)
|
|
59
|
+
{
|
|
60
|
+
// This is the unbound call - args[0] should be the instance (self)
|
|
61
|
+
// args[1:] are the actual method arguments
|
|
62
|
+
|
|
63
|
+
Py_ssize_t nargs = PyTuple_GET_SIZE(args);
|
|
64
|
+
if (nargs < 2) {
|
|
65
|
+
PyErr_Format(
|
|
66
|
+
PyExc_TypeError,
|
|
67
|
+
"%U requires at least 1 positional argument",
|
|
68
|
+
self->func_name
|
|
69
|
+
);
|
|
70
|
+
return nullptr;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Get the instance (first positional arg - the 'self')
|
|
74
|
+
PyObject *instance = PyTuple_GET_ITEM(args, 0); // borrowed reference
|
|
75
|
+
|
|
76
|
+
// Get type of first method arg directly: Py_TYPE(args[1])
|
|
77
|
+
PyObject *first_method_arg = PyTuple_GET_ITEM(args, 1); // borrowed reference
|
|
78
|
+
PyTypeObject *first_arg_type = Py_TYPE(first_method_arg);
|
|
79
|
+
|
|
80
|
+
// Call dispatch(type_(method_args[0]))
|
|
81
|
+
PyObject *impl_att = PyObject_CallOneArg(self->dispatch_func, (PyObject *)first_arg_type);
|
|
82
|
+
if (impl_att == nullptr) {
|
|
83
|
+
return nullptr;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
PyObject *result = nullptr;
|
|
87
|
+
|
|
88
|
+
// Check if impl_att is not None
|
|
89
|
+
if (impl_att != Py_None) {
|
|
90
|
+
// Use PyObject_GetAttr directly instead of calling getattr builtin
|
|
91
|
+
PyObject *impl_method = PyObject_GetAttr(instance, impl_att);
|
|
92
|
+
Py_DECREF(impl_att);
|
|
93
|
+
|
|
94
|
+
if (impl_method == nullptr) {
|
|
95
|
+
return nullptr;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
// Optimize for common case of single argument with no kwargs
|
|
99
|
+
Py_ssize_t method_nargs = nargs - 1;
|
|
100
|
+
if (method_nargs == 1 && (kwargs == nullptr || PyDict_GET_SIZE(kwargs) == 0)) {
|
|
101
|
+
// Fast path for single argument, no kwargs
|
|
102
|
+
result = PyObject_CallOneArg(impl_method, PyTuple_GET_ITEM(args, 1));
|
|
103
|
+
Py_DECREF(impl_method);
|
|
104
|
+
} else {
|
|
105
|
+
// Build method_args tuple (args[1:])
|
|
106
|
+
PyObject *method_args = PyTuple_New(method_nargs);
|
|
107
|
+
if (method_args == nullptr) {
|
|
108
|
+
Py_DECREF(impl_method);
|
|
109
|
+
return nullptr;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
for (Py_ssize_t i = 0; i < method_nargs; i++) {
|
|
113
|
+
PyObject *item = PyTuple_GET_ITEM(args, i + 1);
|
|
114
|
+
Py_INCREF(item);
|
|
115
|
+
PyTuple_SET_ITEM(method_args, i, item);
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Call impl_method(*method_args, **kwargs)
|
|
119
|
+
result = PyObject_Call(impl_method, method_args, kwargs);
|
|
120
|
+
Py_DECREF(impl_method);
|
|
121
|
+
Py_DECREF(method_args);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
} else {
|
|
125
|
+
Py_DECREF(impl_att);
|
|
126
|
+
|
|
127
|
+
// base_func.__get__(instance)
|
|
128
|
+
PyObject *get = PyObject_GetAttrString(self->base_func, "__get__"); // new ref or NULL
|
|
129
|
+
if (get == nullptr) {
|
|
130
|
+
return nullptr;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
PyObject *owner = (PyObject *)Py_TYPE(instance); // borrowed
|
|
134
|
+
PyObject *bound_func = PyObject_CallFunctionObjArgs(get, instance, owner, NULL);
|
|
135
|
+
Py_DECREF(get);
|
|
136
|
+
|
|
137
|
+
if (bound_func == nullptr) {
|
|
138
|
+
return nullptr;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Optimize for common case of single argument with no kwargs
|
|
142
|
+
Py_ssize_t method_nargs = nargs - 1;
|
|
143
|
+
if (method_nargs == 1 && (kwargs == nullptr || PyDict_GET_SIZE(kwargs) == 0)) {
|
|
144
|
+
// Fast path for single argument, no kwargs
|
|
145
|
+
result = PyObject_CallOneArg(bound_func, PyTuple_GET_ITEM(args, 1));
|
|
146
|
+
Py_DECREF(bound_func);
|
|
147
|
+
} else {
|
|
148
|
+
// Build method_args tuple (args[1:])
|
|
149
|
+
PyObject *method_args = PyTuple_New(method_nargs);
|
|
150
|
+
if (method_args == nullptr) {
|
|
151
|
+
Py_DECREF(bound_func);
|
|
152
|
+
return nullptr;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
for (Py_ssize_t i = 0; i < method_nargs; i++) {
|
|
156
|
+
PyObject *item = PyTuple_GET_ITEM(args, i + 1);
|
|
157
|
+
Py_INCREF(item);
|
|
158
|
+
PyTuple_SET_ITEM(method_args, i, item);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Call bound_func(*method_args, **kwargs)
|
|
162
|
+
result = PyObject_Call(bound_func, method_args, kwargs);
|
|
163
|
+
Py_DECREF(bound_func);
|
|
164
|
+
Py_DECREF(method_args);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return result;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
static PyObject * MethodDispatchFunc_get(MethodDispatchFunc *self, PyObject *instance, PyObject *owner)
|
|
172
|
+
{
|
|
173
|
+
// Implement descriptor protocol for binding
|
|
174
|
+
if (instance == Py_None || instance == nullptr) {
|
|
175
|
+
// Unbound access - return self
|
|
176
|
+
Py_INCREF(self);
|
|
177
|
+
return (PyObject *)self;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Bound access - return a bound method
|
|
181
|
+
return PyMethod_New((PyObject *)self, instance);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
static PyObject * MethodDispatchFunc_get_dict(MethodDispatchFunc *self, void *closure)
|
|
185
|
+
{
|
|
186
|
+
if (self->dict == nullptr) {
|
|
187
|
+
self->dict = PyDict_New();
|
|
188
|
+
if (self->dict == nullptr) {
|
|
189
|
+
return nullptr;
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
Py_INCREF(self->dict);
|
|
193
|
+
return self->dict;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
static int MethodDispatchFunc_set_dict(MethodDispatchFunc *self, PyObject *value, void *closure)
|
|
197
|
+
{
|
|
198
|
+
if (value == nullptr) {
|
|
199
|
+
PyErr_SetString(PyExc_TypeError, "Cannot delete __dict__");
|
|
200
|
+
return -1;
|
|
201
|
+
}
|
|
202
|
+
if (!PyDict_Check(value)) {
|
|
203
|
+
PyErr_SetString(PyExc_TypeError, "__dict__ must be a dictionary");
|
|
204
|
+
return -1;
|
|
205
|
+
}
|
|
206
|
+
Py_XSETREF(self->dict, Py_NewRef(value));
|
|
207
|
+
return 0;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
static PyObject * MethodDispatchFunc_getattro(MethodDispatchFunc *self, PyObject *name)
|
|
211
|
+
{
|
|
212
|
+
// First check instance dict
|
|
213
|
+
if (self->dict != nullptr) {
|
|
214
|
+
PyObject *res = PyDict_GetItemWithError(self->dict, name);
|
|
215
|
+
if (res != nullptr) {
|
|
216
|
+
Py_INCREF(res);
|
|
217
|
+
return res;
|
|
218
|
+
}
|
|
219
|
+
if (PyErr_Occurred()) {
|
|
220
|
+
return nullptr;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Fall back to type's getattro
|
|
225
|
+
return PyObject_GenericGetAttr((PyObject *)self, name);
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
static int MethodDispatchFunc_setattro(MethodDispatchFunc *self, PyObject *name, PyObject *value)
|
|
229
|
+
{
|
|
230
|
+
// Ensure dict exists
|
|
231
|
+
if (self->dict == nullptr) {
|
|
232
|
+
self->dict = PyDict_New();
|
|
233
|
+
if (self->dict == nullptr) {
|
|
234
|
+
return -1;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
// Set in instance dict
|
|
239
|
+
if (value == nullptr) {
|
|
240
|
+
return PyDict_DelItem(self->dict, name);
|
|
241
|
+
} else {
|
|
242
|
+
return PyDict_SetItem(self->dict, name, value);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
static PyGetSetDef MethodDispatchFunc_getsets[] = {
|
|
247
|
+
{"__dict__", (getter)MethodDispatchFunc_get_dict, (setter)MethodDispatchFunc_set_dict, nullptr, nullptr},
|
|
248
|
+
{nullptr}
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
static PyType_Slot MethodDispatchFunc_slots[] = {
|
|
252
|
+
{Py_tp_dealloc, (void *)MethodDispatchFunc_dealloc},
|
|
253
|
+
{Py_tp_call, (void *)MethodDispatchFunc_call},
|
|
254
|
+
{Py_tp_descr_get, (void *)MethodDispatchFunc_get},
|
|
255
|
+
{Py_tp_traverse, (void *)MethodDispatchFunc_traverse},
|
|
256
|
+
{Py_tp_clear, (void *)MethodDispatchFunc_clear},
|
|
257
|
+
{Py_tp_getattro, (void *)MethodDispatchFunc_getattro},
|
|
258
|
+
{Py_tp_setattro, (void *)MethodDispatchFunc_setattro},
|
|
259
|
+
{Py_tp_getset, MethodDispatchFunc_getsets},
|
|
260
|
+
{Py_tp_doc, (void *)"Fast dispatch function for Method instances"},
|
|
261
|
+
{0, nullptr}
|
|
262
|
+
};
|
|
263
|
+
|
|
264
|
+
static PyType_Spec MethodDispatchFunc_spec = {
|
|
265
|
+
.name = _MODULE_FULL_NAME ".MethodDispatchFunc",
|
|
266
|
+
.basicsize = sizeof(MethodDispatchFunc),
|
|
267
|
+
.itemsize = 0,
|
|
268
|
+
.flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_HAVE_GC,
|
|
269
|
+
.slots = MethodDispatchFunc_slots,
|
|
270
|
+
};
|
|
271
|
+
|
|
272
|
+
//
|
|
273
|
+
|
|
274
|
+
PyDoc_STRVAR(build_method_dispatch_func_doc,
|
|
275
|
+
"build_method_dispatch_func(dispatch_func, base_func, func_name)\n\
|
|
276
|
+
\n\
|
|
277
|
+
Create a fast dispatch function for Method instances.\n\
|
|
278
|
+
\n\
|
|
279
|
+
Args:\n\
|
|
280
|
+
dispatch_func: The dispatcher's dispatch callable\n\
|
|
281
|
+
base_func: The base function to call if no implementation is found\n\
|
|
282
|
+
func_name: The name of the function (for error messages)\n\
|
|
283
|
+
\n\
|
|
284
|
+
Returns:\n\
|
|
285
|
+
A callable that performs method dispatch");
|
|
286
|
+
|
|
287
|
+
static PyObject * build_method_dispatch_func(PyObject *module, PyObject *args)
|
|
288
|
+
{
|
|
289
|
+
PyObject *dispatch_func;
|
|
290
|
+
PyObject *base_func;
|
|
291
|
+
PyObject *func_name;
|
|
292
|
+
|
|
293
|
+
if (!PyArg_ParseTuple(args, "OOO", &dispatch_func, &base_func, &func_name)) {
|
|
294
|
+
return nullptr;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
if (!PyCallable_Check(dispatch_func)) {
|
|
298
|
+
PyErr_SetString(PyExc_TypeError, "dispatch_func must be callable");
|
|
299
|
+
return nullptr;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
if (!PyCallable_Check(base_func) && !PyObject_HasAttrString(base_func, "__get__")) {
|
|
303
|
+
PyErr_SetString(PyExc_TypeError, "base_func must be callable or a descriptor");
|
|
304
|
+
return nullptr;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
if (!PyUnicode_Check(func_name)) {
|
|
308
|
+
PyErr_SetString(PyExc_TypeError, "func_name must be a string");
|
|
309
|
+
return nullptr;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
methods_state *state = get_methods_state(module);
|
|
313
|
+
MethodDispatchFunc *self = PyObject_GC_New(MethodDispatchFunc, state->MethodDispatchFuncType);
|
|
314
|
+
if (self == nullptr) {
|
|
315
|
+
return nullptr;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
self->dispatch_func = Py_NewRef(dispatch_func);
|
|
319
|
+
self->base_func = Py_NewRef(base_func);
|
|
320
|
+
self->func_name = Py_NewRef(func_name);
|
|
321
|
+
self->dict = nullptr;
|
|
322
|
+
|
|
323
|
+
PyObject_GC_Track(self);
|
|
324
|
+
return (PyObject *)self;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
//
|
|
328
|
+
|
|
329
|
+
PyDoc_STRVAR(methods_doc, "Native C++ implementations for omlish.dispatch.methods");
|
|
330
|
+
|
|
331
|
+
static int methods_exec(PyObject *module)
|
|
332
|
+
{
|
|
333
|
+
methods_state *state = get_methods_state(module);
|
|
334
|
+
|
|
335
|
+
// Create the type dynamically
|
|
336
|
+
state->MethodDispatchFuncType = (PyTypeObject *)PyType_FromModuleAndSpec(
|
|
337
|
+
module,
|
|
338
|
+
&MethodDispatchFunc_spec,
|
|
339
|
+
nullptr
|
|
340
|
+
);
|
|
341
|
+
if (state->MethodDispatchFuncType == nullptr) {
|
|
342
|
+
return -1;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// Add the type to the module
|
|
346
|
+
if (PyModule_AddType(module, state->MethodDispatchFuncType) < 0) {
|
|
347
|
+
Py_CLEAR(state->MethodDispatchFuncType);
|
|
348
|
+
return -1;
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
return 0;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
static int methods_traverse(PyObject *module, visitproc visit, void *arg)
|
|
355
|
+
{
|
|
356
|
+
methods_state *state = get_methods_state(module);
|
|
357
|
+
Py_VISIT(state->MethodDispatchFuncType);
|
|
358
|
+
return 0;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
static int methods_clear(PyObject *module)
|
|
362
|
+
{
|
|
363
|
+
methods_state *state = get_methods_state(module);
|
|
364
|
+
Py_CLEAR(state->MethodDispatchFuncType);
|
|
365
|
+
return 0;
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
static void methods_free(void *module)
|
|
369
|
+
{
|
|
370
|
+
methods_clear((PyObject *)module);
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
static PyMethodDef methods_methods[] = {
|
|
374
|
+
{"build_method_dispatch_func", (PyCFunction)build_method_dispatch_func, METH_VARARGS, build_method_dispatch_func_doc},
|
|
375
|
+
{NULL, NULL, 0, NULL}
|
|
376
|
+
};
|
|
377
|
+
|
|
378
|
+
static struct PyModuleDef_Slot methods_slots[] = {
|
|
379
|
+
{Py_mod_exec, (void *) methods_exec},
|
|
380
|
+
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
|
|
381
|
+
{Py_mod_multiple_interpreters, Py_MOD_MULTIPLE_INTERPRETERS_SUPPORTED},
|
|
382
|
+
{0, NULL}
|
|
383
|
+
};
|
|
384
|
+
|
|
385
|
+
static struct PyModuleDef methods_module = {
|
|
386
|
+
.m_base = PyModuleDef_HEAD_INIT,
|
|
387
|
+
.m_name = _MODULE_NAME,
|
|
388
|
+
.m_doc = methods_doc,
|
|
389
|
+
.m_size = sizeof(methods_state),
|
|
390
|
+
.m_methods = methods_methods,
|
|
391
|
+
.m_slots = methods_slots,
|
|
392
|
+
.m_traverse = methods_traverse,
|
|
393
|
+
.m_clear = methods_clear,
|
|
394
|
+
.m_free = methods_free,
|
|
395
|
+
};
|
|
396
|
+
|
|
397
|
+
extern "C" {
|
|
398
|
+
|
|
399
|
+
PyMODINIT_FUNC PyInit__methods(void)
|
|
400
|
+
{
|
|
401
|
+
return PyModuleDef_Init(&methods_module);
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: omlish-cext
|
|
3
|
-
Version: 0.0.0.
|
|
3
|
+
Version: 0.0.0.dev519
|
|
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.
|
|
17
|
+
Requires-Dist: omlish==0.0.0.dev519
|
|
18
18
|
Dynamic: license-file
|
|
19
19
|
|
|
20
20
|
# Overview
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
omlish==0.0.0.dev519
|
|
@@ -13,7 +13,7 @@ urls = {source = 'https://github.com/wrmsr/omlish'}
|
|
|
13
13
|
license = 'BSD-3-Clause'
|
|
14
14
|
readme = 'README.md'
|
|
15
15
|
requires-python = '>=3.13'
|
|
16
|
-
version = '0.0.0.
|
|
16
|
+
version = '0.0.0.dev519'
|
|
17
17
|
classifiers = [
|
|
18
18
|
'Development Status :: 2 - Pre-Alpha',
|
|
19
19
|
'Intended Audience :: Developers',
|
|
@@ -24,7 +24,7 @@ classifiers = [
|
|
|
24
24
|
]
|
|
25
25
|
description = 'omlish'
|
|
26
26
|
dependencies = [
|
|
27
|
-
'omlish == 0.0.0.
|
|
27
|
+
'omlish == 0.0.0.dev519',
|
|
28
28
|
]
|
|
29
29
|
|
|
30
30
|
[tool.setuptools]
|
|
@@ -13,6 +13,16 @@ st.setup(
|
|
|
13
13
|
sources=['omlish/collections/hamt/_hamt.c'],
|
|
14
14
|
extra_compile_args=['-std=c11'],
|
|
15
15
|
),
|
|
16
|
+
st.Extension(
|
|
17
|
+
name='omlish.dispatch._dispatch',
|
|
18
|
+
sources=['omlish/dispatch/_dispatch.cc'],
|
|
19
|
+
extra_compile_args=['-std=c++20'],
|
|
20
|
+
),
|
|
21
|
+
st.Extension(
|
|
22
|
+
name='omlish.dispatch._methods',
|
|
23
|
+
sources=['omlish/dispatch/_methods.cc'],
|
|
24
|
+
extra_compile_args=['-std=c++20'],
|
|
25
|
+
),
|
|
16
26
|
st.Extension(
|
|
17
27
|
name='omlish.lang._asyncs',
|
|
18
28
|
sources=['omlish/lang/_asyncs.cc'],
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
omlish==0.0.0.dev518
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{omlish_cext-0.0.0.dev518 → omlish_cext-0.0.0.dev519}/omlish_cext.egg-info/dependency_links.txt
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|