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/_check.cc
ADDED
|
@@ -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
|
+
}
|
omlish/check.py
CHANGED
|
@@ -26,6 +26,17 @@ _callable = callable
|
|
|
26
26
|
##
|
|
27
27
|
|
|
28
28
|
|
|
29
|
+
try:
|
|
30
|
+
from . import _check # type: ignore
|
|
31
|
+
except ImportError:
|
|
32
|
+
pass
|
|
33
|
+
else:
|
|
34
|
+
setattr(Checks, '_unpack_isinstance_spec', _check.unpack_isinstance_spec)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
##
|
|
38
|
+
|
|
39
|
+
|
|
29
40
|
def register_on_raise(fn: OnRaiseFn) -> None:
|
|
30
41
|
check.register_on_raise(fn)
|
|
31
42
|
|
omlish/dataclasses/__init__.py
CHANGED
|
@@ -79,6 +79,9 @@ class FrozenGenerator(Generator[FrozenPlan]):
|
|
|
79
79
|
if not ctx.cs.frozen:
|
|
80
80
|
return None
|
|
81
81
|
|
|
82
|
+
if issubclass(ctx.cls, BaseException):
|
|
83
|
+
raise TypeError('cannot use frozen=True with subclass of BaseException')
|
|
84
|
+
|
|
82
85
|
return PlanResult(FrozenPlan(
|
|
83
86
|
fields=tuple(f.name for f in ctx.cs.fields),
|
|
84
87
|
allow_dynamic_dunder_attrs=ctx.cs.allow_dynamic_dunder_attrs,
|
|
@@ -118,7 +121,7 @@ class FrozenGenerator(Generator[FrozenPlan]):
|
|
|
118
121
|
f'}}',
|
|
119
122
|
f'',
|
|
120
123
|
])
|
|
121
|
-
condition.append(f'
|
|
124
|
+
condition.append(f'or name in {set_ident}')
|
|
122
125
|
|
|
123
126
|
return AddMethodOp(
|
|
124
127
|
f'__{mth}__',
|
|
@@ -1,9 +1,4 @@
|
|
|
1
|
-
"""
|
|
2
|
-
TODO:
|
|
3
|
-
- sha1 is slow :/ key by repr but name by sha1
|
|
4
|
-
"""
|
|
5
1
|
import dataclasses as dc
|
|
6
|
-
import re
|
|
7
2
|
import typing as ta
|
|
8
3
|
|
|
9
4
|
from .... import lang
|
|
@@ -21,15 +16,5 @@ class Plans:
|
|
|
21
16
|
return iter(self.tup)
|
|
22
17
|
|
|
23
18
|
@lang.cached_function(no_wrapper_update=True)
|
|
24
|
-
def
|
|
25
|
-
return
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
##
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
_WS_PAT = re.compile(r'\s+')
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
def _render(plans: Plans) -> str:
|
|
35
|
-
return _WS_PAT.sub('', repr(plans.tup))
|
|
19
|
+
def repr(self) -> str:
|
|
20
|
+
return repr(self)
|
|
@@ -126,7 +126,7 @@ class GeneratorProcessor(Processor):
|
|
|
126
126
|
])
|
|
127
127
|
|
|
128
128
|
if (vo := gp._ctx.option(Verbosity)) is not None and vo.debug: # noqa
|
|
129
|
-
print(gp.prepare().plans.
|
|
129
|
+
print(gp.prepare().plans.repr(), file=sys.stderr)
|
|
130
130
|
print(file=sys.stderr)
|
|
131
131
|
print(comp_src, file=sys.stderr)
|
|
132
132
|
print(file=sys.stderr)
|
|
@@ -235,7 +235,7 @@ class GeneratorProcessor(Processor):
|
|
|
235
235
|
#
|
|
236
236
|
|
|
237
237
|
prep = self.prepare()
|
|
238
|
-
prep_plan_repr =
|
|
238
|
+
prep_plan_repr = prep.plans.repr()
|
|
239
239
|
|
|
240
240
|
#
|
|
241
241
|
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import contextlib
|
|
2
2
|
import contextvars
|
|
3
|
+
import sys
|
|
3
4
|
import typing as ta
|
|
4
5
|
|
|
5
6
|
from .... import lang
|
|
@@ -36,6 +37,14 @@ def processing_options_context(*opts: ProcessingOption) -> ta.Iterator[None]:
|
|
|
36
37
|
##
|
|
37
38
|
|
|
38
39
|
|
|
40
|
+
def _is_pkg_init_mod(mod_name: str) -> bool:
|
|
41
|
+
if (mod_obj := sys.modules.get(mod_name)) is None:
|
|
42
|
+
return False
|
|
43
|
+
if (mod_spec := getattr(mod_obj, '__spec__', None)) is None:
|
|
44
|
+
return False
|
|
45
|
+
return bool(mod_spec.submodule_search_locations)
|
|
46
|
+
|
|
47
|
+
|
|
39
48
|
def drive_cls_processing(
|
|
40
49
|
cls: type,
|
|
41
50
|
cs: ClassSpec,
|
|
@@ -53,7 +62,10 @@ def drive_cls_processing(
|
|
|
53
62
|
#
|
|
54
63
|
|
|
55
64
|
cls_mod = cls.__module__
|
|
56
|
-
|
|
65
|
+
if _is_pkg_init_mod(cls_mod):
|
|
66
|
+
cls_pkg = cls_mod
|
|
67
|
+
else:
|
|
68
|
+
cls_pkg = cls_mod.rpartition('.')[0]
|
|
57
69
|
pkg_cfg = lang.coalesce(PACKAGE_CONFIG_CACHE.get(cls_pkg), DEFAULT_NAMED_PACKAGE_CONFIG)
|
|
58
70
|
|
|
59
71
|
#
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import dataclasses as dc
|
|
2
|
+
import operator
|
|
2
3
|
import typing as ta
|
|
3
4
|
|
|
4
5
|
|
|
@@ -15,3 +16,29 @@ def deep_replace(o: T, *args: str | ta.Callable[[ta.Any], ta.Mapping[str, ta.Any
|
|
|
15
16
|
return dc.replace(o, **args[0](o)) # type: ignore
|
|
16
17
|
else:
|
|
17
18
|
return dc.replace(o, **{args[0]: deep_replace(getattr(o, args[0]), *args[1:])}) # type: ignore
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
##
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def replace_if(
|
|
25
|
+
o: T,
|
|
26
|
+
fn: ta.Callable[[ta.Any, ta.Any], bool] = operator.eq,
|
|
27
|
+
/,
|
|
28
|
+
**kwargs: ta.Any,
|
|
29
|
+
) -> T:
|
|
30
|
+
dct: dict[str, ta.Any] = {}
|
|
31
|
+
for k, v in kwargs.items():
|
|
32
|
+
if fn(getattr(o, k), v):
|
|
33
|
+
dct[k] = v # noqa
|
|
34
|
+
if not dct:
|
|
35
|
+
return o
|
|
36
|
+
return dc.replace(o, **dct) # type: ignore
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def replace_ne(o: T, **kwargs: ta.Any) -> T:
|
|
40
|
+
return replace_if(o, operator.ne, **kwargs)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def replace_is_not(o: T, **kwargs: ta.Any) -> T:
|
|
44
|
+
return replace_if(o, operator.is_not, **kwargs)
|
omlish/diag/_pycharm/runhack.py
CHANGED
|
@@ -95,7 +95,7 @@ _BOOL_ENV_VAR_VALUES = {
|
|
|
95
95
|
def _get_opt_env_bool(n, d): # type: (str | None, bool) -> bool
|
|
96
96
|
if n is None or n not in os.environ:
|
|
97
97
|
return d
|
|
98
|
-
return _BOOL_ENV_VAR_VALUES[os.environ[n]]
|
|
98
|
+
return _BOOL_ENV_VAR_VALUES[os.environ[n].lower()]
|
|
99
99
|
|
|
100
100
|
|
|
101
101
|
def _get_env_path_list(k): # type: (str) -> list[str]
|
omlish/dispatch/functions.py
CHANGED
|
@@ -43,7 +43,7 @@ def function(func): # noqa
|
|
|
43
43
|
wrapper.dispatch = disp.dispatch # type: ignore
|
|
44
44
|
|
|
45
45
|
else:
|
|
46
|
-
from x.c._dispatch import function_wrapper #
|
|
46
|
+
from x.c._dispatch import function_wrapper # type: ignore
|
|
47
47
|
wrapper = function_wrapper(
|
|
48
48
|
disp.dispatch,
|
|
49
49
|
**{k: getattr(func, k) for k in functools.WRAPPER_ASSIGNMENTS if hasattr(func, k)},
|
|
@@ -338,7 +338,6 @@ class JsonStreamLexer(GenMachine[str, Token]):
|
|
|
338
338
|
self._line = line
|
|
339
339
|
self._col = col
|
|
340
340
|
|
|
341
|
-
last = None
|
|
342
341
|
while True:
|
|
343
342
|
c: str | None = None
|
|
344
343
|
|
|
@@ -358,7 +357,7 @@ class JsonStreamLexer(GenMachine[str, Token]):
|
|
|
358
357
|
ofs += skip_to - char_in_str_pos
|
|
359
358
|
if (np := char_in_str.rfind('\n', char_in_str_pos, skip_to)) >= 0:
|
|
360
359
|
line += char_in_str.count('\n', char_in_str_pos, skip_to)
|
|
361
|
-
col = np -
|
|
360
|
+
col = skip_to - np - 1
|
|
362
361
|
else:
|
|
363
362
|
col += skip_to - char_in_str_pos
|
|
364
363
|
buf.write(char_in_str[char_in_str_pos:skip_to])
|
|
@@ -406,9 +405,18 @@ class JsonStreamLexer(GenMachine[str, Token]):
|
|
|
406
405
|
self._raise(f'Unterminated string literal: {buf.getvalue()}')
|
|
407
406
|
|
|
408
407
|
buf.write(c)
|
|
409
|
-
if c == q
|
|
410
|
-
|
|
411
|
-
|
|
408
|
+
if c == q:
|
|
409
|
+
# Count consecutive backslashes before this quote
|
|
410
|
+
backslash_count = 0
|
|
411
|
+
buf_val = buf.getvalue()
|
|
412
|
+
check_pos = len(buf_val) - 2 # -2 because we just wrote the quote
|
|
413
|
+
while check_pos >= 0 and buf_val[check_pos] == '\\':
|
|
414
|
+
backslash_count += 1
|
|
415
|
+
check_pos -= 1
|
|
416
|
+
|
|
417
|
+
# Quote is escaped only if preceded by odd number of backslashes
|
|
418
|
+
if backslash_count % 2 == 0:
|
|
419
|
+
break
|
|
412
420
|
|
|
413
421
|
restore_state()
|
|
414
422
|
|
|
@@ -215,7 +215,7 @@ class JsonStreamParser(GenMachine[Token, Event]):
|
|
|
215
215
|
except GeneratorExit:
|
|
216
216
|
raise JsonStreamParseError('Expected object body') from None
|
|
217
217
|
|
|
218
|
-
if tok.kind == 'STRING' or (self.
|
|
218
|
+
if tok.kind == 'STRING' or (self._allow_extended_idents and tok.kind == 'IDENT'):
|
|
219
219
|
k = tok.value
|
|
220
220
|
|
|
221
221
|
try:
|