omlish 0.0.0.dev484__py3-none-any.whl → 0.0.0.dev506__py3-none-any.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.
Potentially problematic release.
This version of omlish might be problematic. Click here for more details.
- omlish/CODESTYLE.md +345 -0
- omlish/README.md +199 -0
- omlish/__about__.py +12 -5
- omlish/_check.cc +209 -0
- omlish/check.py +11 -0
- omlish/dataclasses/__init__.py +4 -0
- omlish/dataclasses/impl/concerns/frozen.py +4 -1
- omlish/dataclasses/impl/generation/plans.py +2 -17
- omlish/dataclasses/impl/generation/processor.py +2 -2
- omlish/dataclasses/impl/processing/driving.py +13 -1
- omlish/dataclasses/tools/replace.py +27 -0
- omlish/diag/_pycharm/runhack.py +1 -1
- omlish/dispatch/functions.py +1 -1
- omlish/formats/json/stream/lexing.py +13 -5
- omlish/formats/json/stream/parsing.py +1 -1
- omlish/inject/README.md +430 -0
- omlish/inject/__init__.py +20 -11
- omlish/inject/_dataclasses.py +1545 -1383
- omlish/inject/binder.py +7 -4
- omlish/inject/eagers.py +2 -4
- omlish/inject/elements.py +4 -0
- omlish/inject/helpers/late.py +76 -0
- omlish/inject/{managed.py → helpers/managed.py} +37 -34
- omlish/inject/impl/elements.py +7 -4
- omlish/inject/impl/injector.py +14 -26
- omlish/inject/impl/inspect.py +0 -8
- omlish/inject/impl/origins.py +1 -0
- omlish/inject/impl/privates.py +2 -6
- omlish/inject/impl/providers.py +0 -4
- omlish/inject/impl/scopes.py +14 -18
- omlish/inject/inspect.py +10 -1
- omlish/inject/multis.py +0 -3
- omlish/inject/scopes.py +7 -5
- omlish/io/buffers.py +35 -8
- omlish/lang/__init__.py +10 -0
- omlish/lang/classes/simple.py +2 -1
- omlish/lang/iterables.py +6 -0
- omlish/lang/objects.py +13 -0
- omlish/lang/outcomes.py +1 -1
- omlish/lang/recursion.py +1 -1
- omlish/lang/sequences.py +33 -0
- omlish/lifecycles/README.md +30 -0
- omlish/lifecycles/__init__.py +87 -13
- omlish/lifecycles/_dataclasses.py +1388 -0
- omlish/lifecycles/base.py +178 -64
- omlish/lifecycles/contextmanagers.py +113 -4
- omlish/lifecycles/controller.py +150 -87
- omlish/lifecycles/injection.py +143 -0
- omlish/lifecycles/listeners.py +56 -0
- omlish/lifecycles/managed.py +142 -0
- omlish/lifecycles/manager.py +218 -93
- omlish/lifecycles/states.py +2 -0
- omlish/lifecycles/transitions.py +3 -0
- omlish/lifecycles/unwrap.py +57 -0
- omlish/lite/maybes.py +7 -0
- omlish/lite/typing.py +33 -0
- omlish/logs/_amalg.py +1 -1
- omlish/logs/all.py +36 -11
- omlish/logs/asyncs.py +73 -0
- omlish/logs/base.py +101 -12
- omlish/logs/bisync.py +99 -0
- omlish/logs/contexts.py +4 -1
- omlish/logs/lists.py +125 -0
- omlish/logs/modules.py +19 -1
- omlish/logs/std/loggers.py +6 -1
- omlish/logs/std/noisy.py +11 -9
- omlish/logs/{standard.py → std/standard.py} +3 -4
- omlish/logs/utils.py +16 -1
- omlish/marshal/_dataclasses.py +813 -813
- omlish/reflect/__init__.py +43 -26
- omlish/reflect/ops.py +10 -1
- omlish/specs/jmespath/_dataclasses.py +597 -597
- omlish/specs/jsonschema/keywords/_dataclasses.py +244 -244
- omlish/sql/__init__.py +24 -5
- omlish/sql/api/dbapi.py +1 -1
- omlish/sql/dbapi/__init__.py +15 -0
- omlish/sql/{dbapi.py → dbapi/drivers.py} +2 -2
- omlish/sql/queries/__init__.py +3 -0
- omlish/testing/pytest/plugins/asyncs/plugin.py +2 -0
- omlish/text/docwrap/cli.py +5 -0
- omlish/typedvalues/_collection.cc +500 -0
- omlish/typedvalues/collection.py +159 -62
- omlish/typedvalues/generic.py +5 -4
- omlish/typedvalues/values.py +6 -0
- {omlish-0.0.0.dev484.dist-info → omlish-0.0.0.dev506.dist-info}/METADATA +14 -9
- {omlish-0.0.0.dev484.dist-info → omlish-0.0.0.dev506.dist-info}/RECORD +92 -77
- omlish/lifecycles/abstract.py +0 -86
- /omlish/inject/{impl → helpers}/proxy.py +0 -0
- /omlish/sql/{abc.py → dbapi/abc.py} +0 -0
- {omlish-0.0.0.dev484.dist-info → omlish-0.0.0.dev506.dist-info}/WHEEL +0 -0
- {omlish-0.0.0.dev484.dist-info → omlish-0.0.0.dev506.dist-info}/entry_points.txt +0 -0
- {omlish-0.0.0.dev484.dist-info → omlish-0.0.0.dev506.dist-info}/licenses/LICENSE +0 -0
- {omlish-0.0.0.dev484.dist-info → omlish-0.0.0.dev506.dist-info}/top_level.txt +0 -0
omlish/sql/__init__.py
CHANGED
|
@@ -7,18 +7,37 @@ with _lang.auto_proxy_init(globals()):
|
|
|
7
7
|
|
|
8
8
|
from . import api # noqa
|
|
9
9
|
|
|
10
|
+
from . import dbapi # noqa
|
|
11
|
+
|
|
12
|
+
from . import queries # noqa
|
|
13
|
+
|
|
14
|
+
#
|
|
15
|
+
|
|
10
16
|
from .dbs import ( # noqa
|
|
11
|
-
DbLoc,
|
|
12
|
-
DbSpec,
|
|
13
17
|
DbType,
|
|
14
18
|
DbTypes,
|
|
15
|
-
|
|
19
|
+
|
|
20
|
+
DbLoc,
|
|
16
21
|
UrlDbLoc,
|
|
22
|
+
HostDbLoc,
|
|
23
|
+
|
|
24
|
+
DbSpec,
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
from .params import ( # noqa
|
|
28
|
+
ParamKey,
|
|
29
|
+
ParamsPreparer,
|
|
30
|
+
LinearParamsPreparer,
|
|
31
|
+
NumericParamsPreparer,
|
|
32
|
+
NamedParamsPreparer,
|
|
33
|
+
|
|
34
|
+
ParamStyle,
|
|
35
|
+
make_params_preparer,
|
|
36
|
+
|
|
37
|
+
substitute_params,
|
|
17
38
|
)
|
|
18
39
|
|
|
19
40
|
from .qualifiedname import ( # noqa
|
|
20
41
|
QualifiedName,
|
|
21
42
|
qn,
|
|
22
43
|
)
|
|
23
|
-
|
|
24
|
-
from . import queries # noqa
|
omlish/sql/api/dbapi.py
CHANGED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from .abc import ( # noqa
|
|
2
|
+
DbapiTypeCode,
|
|
3
|
+
DbapiColumnDescription,
|
|
4
|
+
DbapiColumnDescription_,
|
|
5
|
+
DbapiConnection,
|
|
6
|
+
DbapiCursor,
|
|
7
|
+
DbapiThreadSafety,
|
|
8
|
+
DbapiModule,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
from .drivers import ( # noqa
|
|
12
|
+
DbapiDialect,
|
|
13
|
+
DbapiDriver,
|
|
14
|
+
DbapiDrivers,
|
|
15
|
+
)
|
omlish/sql/queries/__init__.py
CHANGED
omlish/text/docwrap/cli.py
CHANGED
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
"""
|
|
2
2
|
Usable as a jetbrains external tool:
|
|
3
3
|
`om docwrap -i "$FilePath$" -s "$SelectionStartLine$" -e "$SelectionEndLine$"`
|
|
4
|
+
|
|
5
|
+
TODO:
|
|
6
|
+
- -> omdev.tools
|
|
7
|
+
- at least file extension awareness, preserve say # prefixes in py comment blocks
|
|
8
|
+
- maybe treesitter? or just special case py
|
|
4
9
|
"""
|
|
5
10
|
import argparse
|
|
6
11
|
import sys
|
|
@@ -0,0 +1,500 @@
|
|
|
1
|
+
// @omlish-cext
|
|
2
|
+
// @omlish-llm-author "claude-sonnet-4-5-20250929"
|
|
3
|
+
#define PY_SSIZE_T_CLEAN
|
|
4
|
+
#include "Python.h"
|
|
5
|
+
#include "structmember.h"
|
|
6
|
+
|
|
7
|
+
#include <memory>
|
|
8
|
+
#include <unordered_map>
|
|
9
|
+
#include <variant>
|
|
10
|
+
#include <vector>
|
|
11
|
+
|
|
12
|
+
//
|
|
13
|
+
|
|
14
|
+
#define _MODULE_NAME "_collection"
|
|
15
|
+
#define _PACKAGE_NAME "omlish.typedvalues"
|
|
16
|
+
#define _MODULE_FULL_NAME _PACKAGE_NAME "." _MODULE_NAME
|
|
17
|
+
|
|
18
|
+
typedef struct collection_state {
|
|
19
|
+
PyObject *typed_value_type;
|
|
20
|
+
PyObject *unique_typed_value_type;
|
|
21
|
+
PyObject *duplicate_error_type;
|
|
22
|
+
} collection_state;
|
|
23
|
+
|
|
24
|
+
static inline collection_state * get_collection_state(PyObject *module)
|
|
25
|
+
{
|
|
26
|
+
void *state = PyModule_GetState(module);
|
|
27
|
+
assert(state != NULL);
|
|
28
|
+
return (collection_state *)state;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
//
|
|
32
|
+
|
|
33
|
+
// Helper struct to track unique typed values during processing
|
|
34
|
+
struct UniqueInfo {
|
|
35
|
+
PyObject *unique_tv_cls; // The unique class (borrowed from map key)
|
|
36
|
+
PyObject *tv; // The typed value (borrowed from args)
|
|
37
|
+
size_t idx; // Index in unique_lst when added
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
using TmpItem = std::variant<PyObject*, std::unique_ptr<UniqueInfo>>;
|
|
41
|
+
|
|
42
|
+
PyDoc_STRVAR(init_typed_values_collection_doc,
|
|
43
|
+
"init_typed_values_collection(*tvs, override=False, check_type=None)\n"
|
|
44
|
+
"\n"
|
|
45
|
+
"Initialize a typed values collection.\n"
|
|
46
|
+
"\n"
|
|
47
|
+
"Returns a tuple of (tuple, dict, dict2) where:\n"
|
|
48
|
+
"- tuple: ordered sequence of typed values\n"
|
|
49
|
+
"- dict: mapping of type -> typed value or tuple of typed values\n"
|
|
50
|
+
"- dict2: extended dict including instance types for unique values");
|
|
51
|
+
|
|
52
|
+
static PyObject * init_typed_values_collection(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject *kwnames)
|
|
53
|
+
{
|
|
54
|
+
collection_state *state = get_collection_state(module);
|
|
55
|
+
|
|
56
|
+
// Parse arguments
|
|
57
|
+
bool override = false;
|
|
58
|
+
PyObject *check_type = nullptr;
|
|
59
|
+
|
|
60
|
+
// Handle keyword arguments
|
|
61
|
+
Py_ssize_t nkwargs = kwnames ? PyTuple_GET_SIZE(kwnames) : 0;
|
|
62
|
+
|
|
63
|
+
for (Py_ssize_t i = 0; i < nkwargs; i++) {
|
|
64
|
+
PyObject *key = PyTuple_GET_ITEM(kwnames, i);
|
|
65
|
+
PyObject *value = args[nargs + i];
|
|
66
|
+
const char *key_str = PyUnicode_AsUTF8(key);
|
|
67
|
+
|
|
68
|
+
if (key_str == nullptr) {
|
|
69
|
+
return nullptr;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (strcmp(key_str, "override") == 0) {
|
|
73
|
+
int override_result = PyObject_IsTrue(value);
|
|
74
|
+
if (override_result == -1) {
|
|
75
|
+
return nullptr;
|
|
76
|
+
}
|
|
77
|
+
override = override_result;
|
|
78
|
+
} else if (strcmp(key_str, "check_type") == 0) {
|
|
79
|
+
check_type = value;
|
|
80
|
+
} else {
|
|
81
|
+
PyErr_Format(PyExc_TypeError, "unexpected keyword argument: %s", key_str);
|
|
82
|
+
return nullptr;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// If no positional arguments, return empty tuple and dicts
|
|
87
|
+
if (nargs == 0) {
|
|
88
|
+
PyObject *empty_tuple = PyTuple_New(0);
|
|
89
|
+
if (empty_tuple == nullptr) {
|
|
90
|
+
return nullptr;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
PyObject *empty_dict = PyDict_New();
|
|
94
|
+
if (empty_dict == nullptr) {
|
|
95
|
+
Py_DECREF(empty_tuple);
|
|
96
|
+
return nullptr;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
PyObject *empty_dict2 = PyDict_New();
|
|
100
|
+
if (empty_dict2 == nullptr) {
|
|
101
|
+
Py_DECREF(empty_tuple);
|
|
102
|
+
Py_DECREF(empty_dict);
|
|
103
|
+
return nullptr;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
PyObject *result = PyTuple_Pack(3, empty_tuple, empty_dict, empty_dict2);
|
|
107
|
+
Py_DECREF(empty_tuple);
|
|
108
|
+
Py_DECREF(empty_dict);
|
|
109
|
+
Py_DECREF(empty_dict2);
|
|
110
|
+
return result;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Temporary storage
|
|
114
|
+
std::vector<TmpItem> tmp_lst;
|
|
115
|
+
tmp_lst.reserve(nargs);
|
|
116
|
+
|
|
117
|
+
// Map from unique type to list of typed values
|
|
118
|
+
std::unordered_map<PyObject*, std::vector<PyObject*>> unique_dct;
|
|
119
|
+
|
|
120
|
+
struct UniqueKeysCleaner {
|
|
121
|
+
std::unordered_map<PyObject*, std::vector<PyObject*>> ↦
|
|
122
|
+
~UniqueKeysCleaner() { for (auto &pair : map) Py_DECREF(pair.first); }
|
|
123
|
+
} keys_cleaner{unique_dct};
|
|
124
|
+
|
|
125
|
+
// Process each typed value
|
|
126
|
+
for (Py_ssize_t i = 0; i < nargs; i++) {
|
|
127
|
+
PyObject *tv = args[i];
|
|
128
|
+
|
|
129
|
+
// Check type if requested
|
|
130
|
+
if (check_type != nullptr && check_type != Py_None) {
|
|
131
|
+
int result = PyObject_IsInstance(tv, check_type);
|
|
132
|
+
if (result == -1) {
|
|
133
|
+
return nullptr;
|
|
134
|
+
}
|
|
135
|
+
if (!result) {
|
|
136
|
+
PyErr_SetObject(PyExc_TypeError, tv);
|
|
137
|
+
return nullptr;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// Check if it's a UniqueTypedValue
|
|
142
|
+
int is_unique = PyObject_IsInstance(tv, state->unique_typed_value_type);
|
|
143
|
+
if (is_unique == -1) {
|
|
144
|
+
return nullptr;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (is_unique) {
|
|
148
|
+
// Get the _unique_typed_value_cls attribute
|
|
149
|
+
PyObject *unique_tv_cls = PyObject_GetAttrString(tv, "_unique_typed_value_cls");
|
|
150
|
+
if (unique_tv_cls == nullptr) {
|
|
151
|
+
return nullptr;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
// Check for duplicates if not override
|
|
155
|
+
if (!override) {
|
|
156
|
+
auto it = unique_dct.find(unique_tv_cls);
|
|
157
|
+
if (it != unique_dct.end() && !it->second.empty()) {
|
|
158
|
+
// Duplicate found - raise error
|
|
159
|
+
PyObject *old_tv = it->second[0];
|
|
160
|
+
|
|
161
|
+
// Create DuplicateUniqueTypedValueError
|
|
162
|
+
PyObject *error = PyObject_CallFunction(
|
|
163
|
+
state->duplicate_error_type,
|
|
164
|
+
"OOO",
|
|
165
|
+
unique_tv_cls,
|
|
166
|
+
tv,
|
|
167
|
+
old_tv
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
Py_DECREF(unique_tv_cls);
|
|
171
|
+
|
|
172
|
+
if (error == nullptr) {
|
|
173
|
+
return nullptr;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
PyErr_SetObject(state->duplicate_error_type, error);
|
|
177
|
+
Py_DECREF(error);
|
|
178
|
+
return nullptr;
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Add to unique_dct
|
|
183
|
+
// Use insert to avoid reference leak: if key already exists, we need to decref the new reference
|
|
184
|
+
auto insertion = unique_dct.insert({unique_tv_cls, std::vector<PyObject*>()});
|
|
185
|
+
if (!insertion.second) {
|
|
186
|
+
// Key already existed, decref the newly acquired reference
|
|
187
|
+
Py_DECREF(unique_tv_cls);
|
|
188
|
+
}
|
|
189
|
+
std::vector<PyObject*> &unique_lst = insertion.first->second;
|
|
190
|
+
unique_lst.push_back(tv);
|
|
191
|
+
|
|
192
|
+
// Add to tmp_lst using make_unique
|
|
193
|
+
// unique_ptr automatically handles deletion, variant handles moves
|
|
194
|
+
tmp_lst.emplace_back(std::make_unique<UniqueInfo>(UniqueInfo{
|
|
195
|
+
unique_tv_cls, // Borrowed from map
|
|
196
|
+
tv,
|
|
197
|
+
unique_lst.size()
|
|
198
|
+
}));
|
|
199
|
+
|
|
200
|
+
} else {
|
|
201
|
+
// Check if it's a TypedValue
|
|
202
|
+
int is_typed_value = PyObject_IsInstance(tv, state->typed_value_type);
|
|
203
|
+
if (is_typed_value == -1) {
|
|
204
|
+
return nullptr;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
if (!is_typed_value) {
|
|
208
|
+
PyErr_SetObject(PyExc_TypeError, tv);
|
|
209
|
+
return nullptr;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// Non-unique typed value
|
|
213
|
+
tmp_lst.emplace_back(tv);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// Build output structures
|
|
218
|
+
PyObject *lst = PyList_New(0);
|
|
219
|
+
if (lst == nullptr) {
|
|
220
|
+
return nullptr;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
PyObject *tmp_dct = PyDict_New();
|
|
224
|
+
if (tmp_dct == nullptr) {
|
|
225
|
+
Py_DECREF(lst);
|
|
226
|
+
return nullptr;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// Process tmp_lst to build output list and tmp_dct
|
|
230
|
+
for (auto &item : tmp_lst) {
|
|
231
|
+
// Check if item holds unique_ptr<UniqueInfo>
|
|
232
|
+
if (auto *uniq_ptr_wrapper = std::get_if<std::unique_ptr<UniqueInfo>>(&item)) {
|
|
233
|
+
UniqueInfo *info = uniq_ptr_wrapper->get();
|
|
234
|
+
|
|
235
|
+
// Look up the vector now (after all insertions are done, no more rehashing)
|
|
236
|
+
auto it = unique_dct.find(info->unique_tv_cls);
|
|
237
|
+
assert(it != unique_dct.end());
|
|
238
|
+
|
|
239
|
+
// Last-in-wins: only include if this is the last one
|
|
240
|
+
if (info->idx == it->second.size()) {
|
|
241
|
+
// Add to output list
|
|
242
|
+
if (PyList_Append(lst, info->tv) == -1) {
|
|
243
|
+
Py_DECREF(lst);
|
|
244
|
+
Py_DECREF(tmp_dct);
|
|
245
|
+
return nullptr;
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
// Add to tmp_dct (scalar value for unique types)
|
|
249
|
+
if (PyDict_SetItem(tmp_dct, info->unique_tv_cls, info->tv) == -1) {
|
|
250
|
+
Py_DECREF(lst);
|
|
251
|
+
Py_DECREF(tmp_dct);
|
|
252
|
+
return nullptr;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
} else {
|
|
256
|
+
// Must be PyObject*
|
|
257
|
+
PyObject *tv = std::get<PyObject*>(item);
|
|
258
|
+
|
|
259
|
+
// Add to output list
|
|
260
|
+
if (PyList_Append(lst, tv) == -1) {
|
|
261
|
+
Py_DECREF(lst);
|
|
262
|
+
Py_DECREF(tmp_dct);
|
|
263
|
+
return nullptr;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
// Add to tmp_dct (accumulating list for non-unique types)
|
|
267
|
+
PyObject *tv_type = (PyObject *)Py_TYPE(tv);
|
|
268
|
+
PyObject *existing = PyDict_GetItem(tmp_dct, tv_type); // Borrowed reference
|
|
269
|
+
|
|
270
|
+
if (existing == nullptr) {
|
|
271
|
+
// Check if an error occurred during lookup
|
|
272
|
+
if (PyErr_Occurred()) {
|
|
273
|
+
Py_DECREF(lst);
|
|
274
|
+
Py_DECREF(tmp_dct);
|
|
275
|
+
return nullptr;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// Create new list
|
|
279
|
+
PyObject *new_list = PyList_New(0);
|
|
280
|
+
if (new_list == nullptr) {
|
|
281
|
+
Py_DECREF(lst);
|
|
282
|
+
Py_DECREF(tmp_dct);
|
|
283
|
+
return nullptr;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
if (PyList_Append(new_list, tv) == -1) {
|
|
287
|
+
Py_DECREF(new_list);
|
|
288
|
+
Py_DECREF(lst);
|
|
289
|
+
Py_DECREF(tmp_dct);
|
|
290
|
+
return nullptr;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
if (PyDict_SetItem(tmp_dct, tv_type, new_list) == -1) {
|
|
294
|
+
Py_DECREF(new_list);
|
|
295
|
+
Py_DECREF(lst);
|
|
296
|
+
Py_DECREF(tmp_dct);
|
|
297
|
+
return nullptr;
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
Py_DECREF(new_list);
|
|
301
|
+
} else {
|
|
302
|
+
// Append to existing list
|
|
303
|
+
if (PyList_Append(existing, tv) == -1) {
|
|
304
|
+
Py_DECREF(lst);
|
|
305
|
+
Py_DECREF(tmp_dct);
|
|
306
|
+
return nullptr;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// Convert list to tuple
|
|
313
|
+
PyObject *result_tuple = PyList_AsTuple(lst);
|
|
314
|
+
Py_DECREF(lst);
|
|
315
|
+
if (result_tuple == nullptr) {
|
|
316
|
+
Py_DECREF(tmp_dct);
|
|
317
|
+
return nullptr;
|
|
318
|
+
}
|
|
319
|
+
|
|
320
|
+
// Build dct: convert lists to tuples
|
|
321
|
+
PyObject *dct = PyDict_New();
|
|
322
|
+
if (dct == nullptr) {
|
|
323
|
+
Py_DECREF(result_tuple);
|
|
324
|
+
Py_DECREF(tmp_dct);
|
|
325
|
+
return nullptr;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
PyObject *key, *value;
|
|
329
|
+
Py_ssize_t pos = 0;
|
|
330
|
+
|
|
331
|
+
while (PyDict_Next(tmp_dct, &pos, &key, &value)) {
|
|
332
|
+
if (PyList_Check(value)) {
|
|
333
|
+
PyObject *tuple_value = PyList_AsTuple(value);
|
|
334
|
+
if (tuple_value == nullptr) {
|
|
335
|
+
Py_DECREF(result_tuple);
|
|
336
|
+
Py_DECREF(tmp_dct);
|
|
337
|
+
Py_DECREF(dct);
|
|
338
|
+
return nullptr;
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
if (PyDict_SetItem(dct, key, tuple_value) == -1) {
|
|
342
|
+
Py_DECREF(tuple_value);
|
|
343
|
+
Py_DECREF(result_tuple);
|
|
344
|
+
Py_DECREF(tmp_dct);
|
|
345
|
+
Py_DECREF(dct);
|
|
346
|
+
return nullptr;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
Py_DECREF(tuple_value);
|
|
350
|
+
} else {
|
|
351
|
+
// Scalar value (unique type)
|
|
352
|
+
if (PyDict_SetItem(dct, key, value) == -1) {
|
|
353
|
+
Py_DECREF(result_tuple);
|
|
354
|
+
Py_DECREF(tmp_dct);
|
|
355
|
+
Py_DECREF(dct);
|
|
356
|
+
return nullptr;
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
Py_DECREF(tmp_dct);
|
|
362
|
+
|
|
363
|
+
// Build dct2: dct + unique values keyed by instance type
|
|
364
|
+
PyObject *dct2 = PyDict_Copy(dct);
|
|
365
|
+
if (dct2 == nullptr) {
|
|
366
|
+
Py_DECREF(result_tuple);
|
|
367
|
+
Py_DECREF(dct);
|
|
368
|
+
return nullptr;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
// Add unique values by their instance type
|
|
372
|
+
pos = 0;
|
|
373
|
+
while (PyDict_Next(dct, &pos, &key, &value)) {
|
|
374
|
+
// Check if value is a UniqueTypedValue (not a tuple)
|
|
375
|
+
if (!PyTuple_Check(value)) {
|
|
376
|
+
int is_unique = PyObject_IsInstance(value, state->unique_typed_value_type);
|
|
377
|
+
if (is_unique == -1) {
|
|
378
|
+
Py_DECREF(result_tuple);
|
|
379
|
+
Py_DECREF(dct);
|
|
380
|
+
Py_DECREF(dct2);
|
|
381
|
+
return nullptr;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
if (is_unique) {
|
|
385
|
+
PyObject *value_type = (PyObject *)Py_TYPE(value);
|
|
386
|
+
if (PyDict_SetItem(dct2, value_type, value) == -1) {
|
|
387
|
+
Py_DECREF(result_tuple);
|
|
388
|
+
Py_DECREF(dct);
|
|
389
|
+
Py_DECREF(dct2);
|
|
390
|
+
return nullptr;
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
// Build final result
|
|
397
|
+
PyObject *result = PyTuple_Pack(3, result_tuple, dct, dct2);
|
|
398
|
+
Py_DECREF(result_tuple);
|
|
399
|
+
Py_DECREF(dct);
|
|
400
|
+
Py_DECREF(dct2);
|
|
401
|
+
|
|
402
|
+
return result;
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
//
|
|
406
|
+
|
|
407
|
+
PyDoc_STRVAR(collection_doc, "Native C++ implementations for omlish.typedvalues.collection");
|
|
408
|
+
|
|
409
|
+
static int collection_exec(PyObject *module)
|
|
410
|
+
{
|
|
411
|
+
collection_state *state = get_collection_state(module);
|
|
412
|
+
|
|
413
|
+
// Import TypedValue
|
|
414
|
+
PyObject *values_module = PyImport_ImportModule("omlish.typedvalues.values");
|
|
415
|
+
if (values_module == nullptr) {
|
|
416
|
+
return -1;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
state->typed_value_type = PyObject_GetAttrString(values_module, "TypedValue");
|
|
420
|
+
if (state->typed_value_type == nullptr) {
|
|
421
|
+
Py_DECREF(values_module);
|
|
422
|
+
return -1;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
state->unique_typed_value_type = PyObject_GetAttrString(values_module, "UniqueTypedValue");
|
|
426
|
+
Py_DECREF(values_module);
|
|
427
|
+
if (state->unique_typed_value_type == nullptr) {
|
|
428
|
+
return -1;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// Import DuplicateUniqueTypedValueError
|
|
432
|
+
PyObject *collection_module = PyImport_ImportModule("omlish.typedvalues.collection");
|
|
433
|
+
if (collection_module == nullptr) {
|
|
434
|
+
return -1;
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
state->duplicate_error_type = PyObject_GetAttrString(collection_module, "DuplicateUniqueTypedValueError");
|
|
438
|
+
Py_DECREF(collection_module);
|
|
439
|
+
if (state->duplicate_error_type == nullptr) {
|
|
440
|
+
return -1;
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
return 0;
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
static int collection_traverse(PyObject *module, visitproc visit, void *arg)
|
|
447
|
+
{
|
|
448
|
+
collection_state *state = get_collection_state(module);
|
|
449
|
+
Py_VISIT(state->typed_value_type);
|
|
450
|
+
Py_VISIT(state->unique_typed_value_type);
|
|
451
|
+
Py_VISIT(state->duplicate_error_type);
|
|
452
|
+
return 0;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
static int collection_clear(PyObject *module)
|
|
456
|
+
{
|
|
457
|
+
collection_state *state = get_collection_state(module);
|
|
458
|
+
Py_CLEAR(state->typed_value_type);
|
|
459
|
+
Py_CLEAR(state->unique_typed_value_type);
|
|
460
|
+
Py_CLEAR(state->duplicate_error_type);
|
|
461
|
+
return 0;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
static void collection_free(void *module)
|
|
465
|
+
{
|
|
466
|
+
collection_clear((PyObject *)module);
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
static PyMethodDef collection_methods[] = {
|
|
470
|
+
{"init_typed_values_collection", (PyCFunction)(void(*)(void))init_typed_values_collection, METH_FASTCALL | METH_KEYWORDS, init_typed_values_collection_doc},
|
|
471
|
+
{NULL, NULL, 0, NULL}
|
|
472
|
+
};
|
|
473
|
+
|
|
474
|
+
static struct PyModuleDef_Slot collection_slots[] = {
|
|
475
|
+
{Py_mod_exec, (void *) collection_exec},
|
|
476
|
+
{Py_mod_gil, Py_MOD_GIL_NOT_USED},
|
|
477
|
+
{Py_mod_multiple_interpreters, Py_MOD_MULTIPLE_INTERPRETERS_SUPPORTED},
|
|
478
|
+
{0, NULL}
|
|
479
|
+
};
|
|
480
|
+
|
|
481
|
+
static struct PyModuleDef collection_module = {
|
|
482
|
+
.m_base = PyModuleDef_HEAD_INIT,
|
|
483
|
+
.m_name = _MODULE_NAME,
|
|
484
|
+
.m_doc = collection_doc,
|
|
485
|
+
.m_size = sizeof(collection_state),
|
|
486
|
+
.m_methods = collection_methods,
|
|
487
|
+
.m_slots = collection_slots,
|
|
488
|
+
.m_traverse = collection_traverse,
|
|
489
|
+
.m_clear = collection_clear,
|
|
490
|
+
.m_free = collection_free,
|
|
491
|
+
};
|
|
492
|
+
|
|
493
|
+
extern "C" {
|
|
494
|
+
|
|
495
|
+
PyMODINIT_FUNC PyInit__collection(void)
|
|
496
|
+
{
|
|
497
|
+
return PyModuleDef_Init(&collection_module);
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
}
|