pixeltable 0.3.7__py3-none-any.whl → 0.3.9__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 pixeltable might be problematic. Click here for more details.
- pixeltable/__version__.py +2 -2
- pixeltable/catalog/catalog.py +509 -103
- pixeltable/catalog/column.py +1 -0
- pixeltable/catalog/dir.py +15 -6
- pixeltable/catalog/path.py +15 -0
- pixeltable/catalog/schema_object.py +7 -12
- pixeltable/catalog/table.py +3 -12
- pixeltable/catalog/table_version.py +5 -0
- pixeltable/catalog/view.py +0 -4
- pixeltable/env.py +14 -8
- pixeltable/exprs/__init__.py +2 -0
- pixeltable/exprs/arithmetic_expr.py +7 -11
- pixeltable/exprs/array_slice.py +1 -1
- pixeltable/exprs/column_property_ref.py +3 -3
- pixeltable/exprs/column_ref.py +5 -6
- pixeltable/exprs/comparison.py +2 -5
- pixeltable/exprs/compound_predicate.py +4 -4
- pixeltable/exprs/expr.py +32 -19
- pixeltable/exprs/expr_dict.py +3 -3
- pixeltable/exprs/expr_set.py +1 -1
- pixeltable/exprs/function_call.py +28 -41
- pixeltable/exprs/globals.py +3 -3
- pixeltable/exprs/in_predicate.py +1 -1
- pixeltable/exprs/inline_expr.py +3 -3
- pixeltable/exprs/is_null.py +1 -1
- pixeltable/exprs/json_mapper.py +5 -5
- pixeltable/exprs/json_path.py +27 -15
- pixeltable/exprs/literal.py +1 -1
- pixeltable/exprs/method_ref.py +2 -2
- pixeltable/exprs/row_builder.py +3 -5
- pixeltable/exprs/rowid_ref.py +4 -7
- pixeltable/exprs/similarity_expr.py +5 -5
- pixeltable/exprs/sql_element_cache.py +1 -1
- pixeltable/exprs/type_cast.py +2 -3
- pixeltable/exprs/variable.py +2 -2
- pixeltable/ext/__init__.py +2 -0
- pixeltable/ext/functions/__init__.py +2 -0
- pixeltable/ext/functions/yolox.py +3 -3
- pixeltable/func/__init__.py +2 -0
- pixeltable/func/aggregate_function.py +9 -9
- pixeltable/func/callable_function.py +7 -5
- pixeltable/func/expr_template_function.py +6 -16
- pixeltable/func/function.py +10 -8
- pixeltable/func/function_registry.py +1 -3
- pixeltable/func/query_template_function.py +8 -24
- pixeltable/func/signature.py +23 -22
- pixeltable/func/tools.py +3 -3
- pixeltable/func/udf.py +5 -3
- pixeltable/globals.py +118 -260
- pixeltable/share/__init__.py +2 -0
- pixeltable/share/packager.py +3 -3
- pixeltable/share/publish.py +3 -5
- pixeltable/utils/coco.py +4 -4
- pixeltable/utils/console_output.py +1 -3
- pixeltable/utils/coroutine.py +41 -0
- pixeltable/utils/description_helper.py +1 -1
- pixeltable/utils/documents.py +3 -3
- pixeltable/utils/filecache.py +18 -8
- pixeltable/utils/formatter.py +2 -3
- pixeltable/utils/media_store.py +1 -1
- pixeltable/utils/pytorch.py +1 -1
- pixeltable/utils/sql.py +4 -4
- pixeltable/utils/transactional_directory.py +2 -1
- {pixeltable-0.3.7.dist-info → pixeltable-0.3.9.dist-info}/METADATA +1 -1
- {pixeltable-0.3.7.dist-info → pixeltable-0.3.9.dist-info}/RECORD +68 -67
- {pixeltable-0.3.7.dist-info → pixeltable-0.3.9.dist-info}/LICENSE +0 -0
- {pixeltable-0.3.7.dist-info → pixeltable-0.3.9.dist-info}/WHEEL +0 -0
- {pixeltable-0.3.7.dist-info → pixeltable-0.3.9.dist-info}/entry_points.txt +0 -0
|
@@ -3,16 +3,12 @@ from __future__ import annotations
|
|
|
3
3
|
import inspect
|
|
4
4
|
import logging
|
|
5
5
|
import sys
|
|
6
|
-
import warnings
|
|
7
6
|
from textwrap import dedent
|
|
8
7
|
from typing import Any, Optional, Sequence, Union
|
|
9
8
|
|
|
10
9
|
import sqlalchemy as sql
|
|
11
10
|
|
|
12
|
-
import
|
|
13
|
-
import pixeltable.exceptions as excs
|
|
14
|
-
import pixeltable.func as func
|
|
15
|
-
import pixeltable.type_system as ts
|
|
11
|
+
from pixeltable import catalog, exceptions as excs, func, type_system as ts
|
|
16
12
|
|
|
17
13
|
from .data_row import DataRow
|
|
18
14
|
from .expr import Expr
|
|
@@ -156,22 +152,18 @@ class FunctionCall(Expr):
|
|
|
156
152
|
return self.fn.name
|
|
157
153
|
|
|
158
154
|
def _equals(self, other: FunctionCall) -> bool:
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
if self.group_by_stop_idx != other.group_by_stop_idx:
|
|
168
|
-
return False
|
|
169
|
-
if self.order_by_start_idx != other.order_by_start_idx:
|
|
170
|
-
return False
|
|
171
|
-
return True
|
|
155
|
+
return (
|
|
156
|
+
self.fn == other.fn
|
|
157
|
+
and self.arg_idxs == other.arg_idxs
|
|
158
|
+
and self.kwarg_idxs == other.kwarg_idxs
|
|
159
|
+
and self.group_by_start_idx == other.group_by_start_idx
|
|
160
|
+
and self.group_by_stop_idx == other.group_by_stop_idx
|
|
161
|
+
and self.order_by_start_idx == other.order_by_start_idx
|
|
162
|
+
)
|
|
172
163
|
|
|
173
164
|
def _id_attrs(self) -> list[tuple[str, Any]]:
|
|
174
|
-
return
|
|
165
|
+
return [
|
|
166
|
+
*super()._id_attrs(),
|
|
175
167
|
('fn', id(self.fn)), # use the function pointer, not the fqn, which isn't set for lambdas
|
|
176
168
|
('args', self.arg_idxs),
|
|
177
169
|
('kwargs', self.kwarg_idxs),
|
|
@@ -192,12 +184,12 @@ class FunctionCall(Expr):
|
|
|
192
184
|
if self.is_method_call:
|
|
193
185
|
return f'{self.components[0]}.{self.fn.name}({self._print_args(1, inline)})'
|
|
194
186
|
else:
|
|
195
|
-
fn_name = self.fn.display_name
|
|
187
|
+
fn_name = self.fn.display_name or 'anonymous_fn'
|
|
196
188
|
return f'{fn_name}({self._print_args()})'
|
|
197
189
|
|
|
198
190
|
def _print_args(self, start_idx: int = 0, inline: bool = True) -> str:
|
|
199
191
|
arg_strs = [str(self.components[idx]) for idx in self.arg_idxs[start_idx:]]
|
|
200
|
-
arg_strs.extend([f'{param_name}={
|
|
192
|
+
arg_strs.extend([f'{param_name}={self.components[idx]}' for param_name, idx in self.kwarg_idxs.items()])
|
|
201
193
|
if len(self.order_by) > 0:
|
|
202
194
|
assert isinstance(self.fn, func.AggregateFunction)
|
|
203
195
|
if self.fn.requires_order_by:
|
|
@@ -297,7 +289,7 @@ class FunctionCall(Expr):
|
|
|
297
289
|
if (
|
|
298
290
|
val is None
|
|
299
291
|
and parameters_by_pos[idx].kind
|
|
300
|
-
in
|
|
292
|
+
in {inspect.Parameter.POSITIONAL_ONLY, inspect.Parameter.POSITIONAL_OR_KEYWORD}
|
|
301
293
|
and not parameters_by_pos[idx].col_type.nullable
|
|
302
294
|
):
|
|
303
295
|
return None
|
|
@@ -310,7 +302,7 @@ class FunctionCall(Expr):
|
|
|
310
302
|
if (
|
|
311
303
|
val is None
|
|
312
304
|
and parameters[param_name].kind
|
|
313
|
-
in
|
|
305
|
+
in {inspect.Parameter.KEYWORD_ONLY, inspect.Parameter.POSITIONAL_OR_KEYWORD}
|
|
314
306
|
and not parameters[param_name].col_type.nullable
|
|
315
307
|
):
|
|
316
308
|
return None
|
|
@@ -368,10 +360,7 @@ class FunctionCall(Expr):
|
|
|
368
360
|
return
|
|
369
361
|
args, kwargs = args_kwargs
|
|
370
362
|
|
|
371
|
-
if
|
|
372
|
-
# optimization: avoid additional level of indirection we'd get from calling Function.exec()
|
|
373
|
-
data_row[self.slot_idx] = self.fn.py_fn(*args, **kwargs)
|
|
374
|
-
elif self.is_window_fn_call:
|
|
363
|
+
if self.is_window_fn_call:
|
|
375
364
|
assert isinstance(self.fn, func.AggregateFunction)
|
|
376
365
|
agg_cls = self.fn.agg_class
|
|
377
366
|
if self.has_group_by():
|
|
@@ -466,20 +455,18 @@ class FunctionCall(Expr):
|
|
|
466
455
|
# the call_return_type that we just inferred (which matches the deserialization behavior prior to
|
|
467
456
|
# version 25).
|
|
468
457
|
return_type = call_return_type
|
|
469
|
-
|
|
470
|
-
# There is a return_type stored in metadata (schema version >= 25)
|
|
471
|
-
#
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
"""
|
|
482
|
-
).strip()
|
|
458
|
+
elif not return_type.is_supertype_of(call_return_type, ignore_nullable=True):
|
|
459
|
+
# There is a return_type stored in metadata (schema version >= 25),
|
|
460
|
+
# and the stored return_type of the UDF call doesn't match the column type of the FunctionCall.
|
|
461
|
+
validation_error = dedent(
|
|
462
|
+
f"""
|
|
463
|
+
The return type stored in the database for a UDF call to {fn.self_path!r} no longer
|
|
464
|
+
matches its return type as currently defined in the code. This probably means that the
|
|
465
|
+
code for {fn.self_path!r} has changed in a backward-incompatible way.
|
|
466
|
+
Return type of UDF call in the database: {return_type}
|
|
467
|
+
Return type of UDF as currently defined in code: {call_return_type}
|
|
468
|
+
"""
|
|
469
|
+
).strip()
|
|
483
470
|
|
|
484
471
|
fn_call = cls(
|
|
485
472
|
resolved_fn,
|
pixeltable/exprs/globals.py
CHANGED
|
@@ -36,7 +36,7 @@ class ComparisonOperator(enum.Enum):
|
|
|
36
36
|
return '>'
|
|
37
37
|
if self == self.GE:
|
|
38
38
|
return '>='
|
|
39
|
-
|
|
39
|
+
raise AssertionError()
|
|
40
40
|
|
|
41
41
|
def reverse(self) -> ComparisonOperator:
|
|
42
42
|
if self == self.LT:
|
|
@@ -62,7 +62,7 @@ class LogicalOperator(enum.Enum):
|
|
|
62
62
|
return '|'
|
|
63
63
|
if self == self.NOT:
|
|
64
64
|
return '~'
|
|
65
|
-
|
|
65
|
+
raise AssertionError()
|
|
66
66
|
|
|
67
67
|
|
|
68
68
|
class ArithmeticOperator(enum.Enum):
|
|
@@ -86,4 +86,4 @@ class ArithmeticOperator(enum.Enum):
|
|
|
86
86
|
return '%'
|
|
87
87
|
if self == self.FLOORDIV:
|
|
88
88
|
return '//'
|
|
89
|
-
|
|
89
|
+
raise AssertionError()
|
pixeltable/exprs/in_predicate.py
CHANGED
|
@@ -71,7 +71,7 @@ class InPredicate(Expr):
|
|
|
71
71
|
return self.value_list == other.value_list
|
|
72
72
|
|
|
73
73
|
def _id_attrs(self) -> list[tuple[str, Any]]:
|
|
74
|
-
return super()._id_attrs()
|
|
74
|
+
return [*super()._id_attrs(), ('value_list', self.value_list)]
|
|
75
75
|
|
|
76
76
|
def sql_expr(self, sql_elements: SqlElementCache) -> Optional[sql.ColumnElement]:
|
|
77
77
|
lhs_sql_exprs = sql_elements.get(self.components[0])
|
pixeltable/exprs/inline_expr.py
CHANGED
|
@@ -131,7 +131,7 @@ class InlineList(Expr):
|
|
|
131
131
|
def as_literal(self) -> Optional[Literal]:
|
|
132
132
|
if not all(isinstance(comp, Literal) for comp in self.components):
|
|
133
133
|
return None
|
|
134
|
-
return Literal(
|
|
134
|
+
return Literal([c.as_literal().val for c in self.components], self.col_type)
|
|
135
135
|
|
|
136
136
|
|
|
137
137
|
class InlineDict(Expr):
|
|
@@ -166,7 +166,7 @@ class InlineDict(Expr):
|
|
|
166
166
|
self.id = self._create_id()
|
|
167
167
|
|
|
168
168
|
def __repr__(self) -> str:
|
|
169
|
-
item_strs =
|
|
169
|
+
item_strs = [f"'{key}': {expr}" for key, expr in zip(self.keys, self.components)]
|
|
170
170
|
return '{' + ', '.join(item_strs) + '}'
|
|
171
171
|
|
|
172
172
|
def _equals(self, other: InlineDict) -> bool:
|
|
@@ -174,7 +174,7 @@ class InlineDict(Expr):
|
|
|
174
174
|
return self.keys == other.keys
|
|
175
175
|
|
|
176
176
|
def _id_attrs(self) -> list[tuple[str, Any]]:
|
|
177
|
-
return super()._id_attrs()
|
|
177
|
+
return [*super()._id_attrs(), ('keys', self.keys)]
|
|
178
178
|
|
|
179
179
|
def sql_expr(self, _: SqlElementCache) -> Optional[sql.ColumnElement]:
|
|
180
180
|
return None
|
pixeltable/exprs/is_null.py
CHANGED
pixeltable/exprs/json_mapper.py
CHANGED
|
@@ -48,9 +48,9 @@ class JsonMapper(Expr):
|
|
|
48
48
|
scope_anchor = ObjectRef(self.target_expr_scope, self)
|
|
49
49
|
self.components.append(scope_anchor)
|
|
50
50
|
|
|
51
|
-
def
|
|
52
|
-
self._src_expr.
|
|
53
|
-
self._target_expr.
|
|
51
|
+
def _bind_rel_paths(self, mapper: Optional[JsonMapper] = None) -> None:
|
|
52
|
+
self._src_expr._bind_rel_paths(mapper)
|
|
53
|
+
self._target_expr._bind_rel_paths(self)
|
|
54
54
|
self.parent_mapper = mapper
|
|
55
55
|
parent_scope = _GLOBAL_SCOPE if mapper is None else mapper.target_expr_scope
|
|
56
56
|
self.target_expr_scope.parent = parent_scope
|
|
@@ -81,12 +81,12 @@ class JsonMapper(Expr):
|
|
|
81
81
|
"""
|
|
82
82
|
We override equals() because we need to avoid comparing our scope anchor.
|
|
83
83
|
"""
|
|
84
|
-
if type(self)
|
|
84
|
+
if type(self) is not type(other):
|
|
85
85
|
return False
|
|
86
86
|
return self._src_expr.equals(other._src_expr) and self._target_expr.equals(other._target_expr)
|
|
87
87
|
|
|
88
88
|
def __repr__(self) -> str:
|
|
89
|
-
return f'{
|
|
89
|
+
return f'{self._src_expr} >> {self._target_expr}'
|
|
90
90
|
|
|
91
91
|
@property
|
|
92
92
|
def _src_expr(self) -> Expr:
|
pixeltable/exprs/json_path.py
CHANGED
|
@@ -6,14 +6,13 @@ import jmespath
|
|
|
6
6
|
import sqlalchemy as sql
|
|
7
7
|
|
|
8
8
|
import pixeltable as pxt
|
|
9
|
-
import
|
|
10
|
-
import pixeltable.exceptions as excs
|
|
11
|
-
import pixeltable.type_system as ts
|
|
9
|
+
from pixeltable import catalog, exceptions as excs, type_system as ts
|
|
12
10
|
|
|
13
11
|
from .data_row import DataRow
|
|
14
12
|
from .expr import Expr
|
|
15
13
|
from .globals import print_slice
|
|
16
14
|
from .json_mapper import JsonMapper
|
|
15
|
+
from .object_ref import ObjectRef
|
|
17
16
|
from .row_builder import RowBuilder
|
|
18
17
|
from .sql_element_cache import SqlElementCache
|
|
19
18
|
|
|
@@ -50,8 +49,16 @@ class JsonPath(Expr):
|
|
|
50
49
|
return f'{anchor_str}{"." if isinstance(self.path_elements[0], str) else ""}{self._json_path()}'
|
|
51
50
|
|
|
52
51
|
def _as_dict(self) -> dict:
|
|
52
|
+
assert len(self.components) <= 1
|
|
53
|
+
components_dict: dict[str, Any]
|
|
54
|
+
if len(self.components) == 0 or isinstance(self.components[0], ObjectRef):
|
|
55
|
+
# If the anchor is an ObjectRef, it means this JsonPath is a bound relative path. We store it as a relative
|
|
56
|
+
# path, *not* a bound path (which has no meaning in the dict).
|
|
57
|
+
components_dict = {}
|
|
58
|
+
else:
|
|
59
|
+
components_dict = super()._as_dict()
|
|
53
60
|
path_elements = [[el.start, el.stop, el.step] if isinstance(el, slice) else el for el in self.path_elements]
|
|
54
|
-
return {'path_elements': path_elements, 'scope_idx': self.scope_idx, **
|
|
61
|
+
return {'path_elements': path_elements, 'scope_idx': self.scope_idx, **components_dict}
|
|
55
62
|
|
|
56
63
|
@classmethod
|
|
57
64
|
def _from_dict(cls, d: dict, components: list[Expr]) -> JsonPath:
|
|
@@ -73,29 +80,34 @@ class JsonPath(Expr):
|
|
|
73
80
|
def is_relative_path(self) -> bool:
|
|
74
81
|
return self._anchor is None
|
|
75
82
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
83
|
+
@property
|
|
84
|
+
def _has_relative_path(self) -> bool:
|
|
85
|
+
return self.is_relative_path() or super()._has_relative_path
|
|
86
|
+
|
|
87
|
+
def _bind_rel_paths(self, mapper: Optional['JsonMapper'] = None) -> None:
|
|
88
|
+
if self.is_relative_path():
|
|
89
|
+
# TODO: take scope_idx into account
|
|
90
|
+
self.set_anchor(mapper.scope_anchor)
|
|
91
|
+
else:
|
|
92
|
+
self._anchor._bind_rel_paths(mapper)
|
|
81
93
|
|
|
82
94
|
def __call__(self, *args: object, **kwargs: object) -> 'JsonPath':
|
|
83
95
|
"""
|
|
84
96
|
Construct a relative path that references an ancestor of the immediately enclosing JsonMapper.
|
|
85
97
|
"""
|
|
86
98
|
if not self.is_relative_path():
|
|
87
|
-
raise excs.Error(
|
|
99
|
+
raise excs.Error('() for an absolute path is invalid')
|
|
88
100
|
if len(args) != 1 or not isinstance(args[0], int) or args[0] >= 0:
|
|
89
|
-
raise excs.Error(
|
|
101
|
+
raise excs.Error('R() requires a negative index')
|
|
90
102
|
return JsonPath(None, [], args[0])
|
|
91
103
|
|
|
92
104
|
def __getattr__(self, name: str) -> 'JsonPath':
|
|
93
105
|
assert isinstance(name, str)
|
|
94
|
-
return JsonPath(self._anchor, self.path_elements
|
|
106
|
+
return JsonPath(self._anchor, [*self.path_elements, name])
|
|
95
107
|
|
|
96
108
|
def __getitem__(self, index: object) -> 'JsonPath':
|
|
97
109
|
if isinstance(index, (int, slice, str)):
|
|
98
|
-
return JsonPath(self._anchor, self.path_elements
|
|
110
|
+
return JsonPath(self._anchor, [*self.path_elements, index])
|
|
99
111
|
raise excs.Error(f'Invalid json list index: {index}')
|
|
100
112
|
|
|
101
113
|
def __rshift__(self, other: object) -> 'JsonMapper':
|
|
@@ -120,7 +132,7 @@ class JsonPath(Expr):
|
|
|
120
132
|
|
|
121
133
|
clean_name = ''.join(map(cleanup_char, ret_name))
|
|
122
134
|
clean_name = clean_name.lstrip('_') # remove leading underscore
|
|
123
|
-
if clean_name
|
|
135
|
+
if not clean_name: # Replace '' with None
|
|
124
136
|
clean_name = None
|
|
125
137
|
|
|
126
138
|
assert clean_name is None or catalog.is_valid_identifier(clean_name)
|
|
@@ -130,7 +142,7 @@ class JsonPath(Expr):
|
|
|
130
142
|
return self.path_elements == other.path_elements
|
|
131
143
|
|
|
132
144
|
def _id_attrs(self) -> list[tuple[str, Any]]:
|
|
133
|
-
return super()._id_attrs()
|
|
145
|
+
return [*super()._id_attrs(), ('path_elements', self.path_elements)]
|
|
134
146
|
|
|
135
147
|
def sql_expr(self, _: SqlElementCache) -> Optional[sql.ColumnElement]:
|
|
136
148
|
"""
|
pixeltable/exprs/literal.py
CHANGED
|
@@ -62,7 +62,7 @@ class Literal(Expr):
|
|
|
62
62
|
return self.val == other.val
|
|
63
63
|
|
|
64
64
|
def _id_attrs(self) -> list[tuple[str, Any]]:
|
|
65
|
-
return super()._id_attrs()
|
|
65
|
+
return [*super()._id_attrs(), ('val', self.val)]
|
|
66
66
|
|
|
67
67
|
def sql_expr(self, _: SqlElementCache) -> Optional[sql.ColumnElement]:
|
|
68
68
|
# Return a sql object so that constants can participate in SQL expressions
|
pixeltable/exprs/method_ref.py
CHANGED
|
@@ -53,13 +53,13 @@ class MethodRef(Expr):
|
|
|
53
53
|
return self.base_expr.id == other.base_expr.id and self.method_name == other.method_name
|
|
54
54
|
|
|
55
55
|
def _id_attrs(self) -> list[tuple[str, Any]]:
|
|
56
|
-
return super()._id_attrs()
|
|
56
|
+
return [*super()._id_attrs(), ('method_name', self.method_name)]
|
|
57
57
|
|
|
58
58
|
def sql_expr(self, _: SqlElementCache) -> Optional[sql.ColumnElement]:
|
|
59
59
|
return None
|
|
60
60
|
|
|
61
61
|
def eval(self, data_row: DataRow, row_builder: RowBuilder) -> None:
|
|
62
|
-
|
|
62
|
+
raise AssertionError('MethodRef cannot be evaluated directly')
|
|
63
63
|
|
|
64
64
|
def __repr__(self) -> str:
|
|
65
65
|
return f'{self.base_expr}.{self.method_name}'
|
pixeltable/exprs/row_builder.py
CHANGED
|
@@ -8,9 +8,7 @@ from uuid import UUID
|
|
|
8
8
|
|
|
9
9
|
import numpy as np
|
|
10
10
|
|
|
11
|
-
import
|
|
12
|
-
import pixeltable.exceptions as excs
|
|
13
|
-
import pixeltable.utils as utils
|
|
11
|
+
from pixeltable import catalog, exceptions as excs, utils
|
|
14
12
|
from pixeltable.env import Env
|
|
15
13
|
from pixeltable.utils.media_store import MediaStore
|
|
16
14
|
|
|
@@ -366,8 +364,8 @@ class RowBuilder:
|
|
|
366
364
|
def set_exc(self, data_row: DataRow, slot_idx: int, exc: Exception) -> None:
|
|
367
365
|
"""Record an exception in data_row and propagate it to dependents"""
|
|
368
366
|
data_row.set_exc(slot_idx, exc)
|
|
369
|
-
for
|
|
370
|
-
data_row.set_exc(
|
|
367
|
+
for idx in self._exc_dependents[slot_idx]:
|
|
368
|
+
data_row.set_exc(idx, exc)
|
|
371
369
|
|
|
372
370
|
def eval(
|
|
373
371
|
self,
|
pixeltable/exprs/rowid_ref.py
CHANGED
|
@@ -1,21 +1,17 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from typing import
|
|
3
|
+
from typing import Any, Optional, cast
|
|
4
4
|
from uuid import UUID
|
|
5
5
|
|
|
6
6
|
import sqlalchemy as sql
|
|
7
7
|
|
|
8
|
-
import
|
|
9
|
-
import pixeltable.type_system as ts
|
|
8
|
+
from pixeltable import catalog, type_system as ts
|
|
10
9
|
|
|
11
10
|
from .data_row import DataRow
|
|
12
11
|
from .expr import Expr
|
|
13
12
|
from .row_builder import RowBuilder
|
|
14
13
|
from .sql_element_cache import SqlElementCache
|
|
15
14
|
|
|
16
|
-
if TYPE_CHECKING:
|
|
17
|
-
from pixeltable import store
|
|
18
|
-
|
|
19
15
|
|
|
20
16
|
class RowidRef(Expr):
|
|
21
17
|
"""A reference to a part of a table rowid
|
|
@@ -68,7 +64,8 @@ class RowidRef(Expr):
|
|
|
68
64
|
)
|
|
69
65
|
|
|
70
66
|
def _id_attrs(self) -> list[tuple[str, Any]]:
|
|
71
|
-
return
|
|
67
|
+
return [
|
|
68
|
+
*super()._id_attrs(),
|
|
72
69
|
('normalized_base_id', self.normalized_base_id),
|
|
73
70
|
('idx', self.rowid_component_idx),
|
|
74
71
|
]
|
|
@@ -62,14 +62,14 @@ class SimilarityExpr(Expr):
|
|
|
62
62
|
return f'{self.components[0]}.similarity({self.components[1]})'
|
|
63
63
|
|
|
64
64
|
def _id_attrs(self):
|
|
65
|
-
return super()._id_attrs()
|
|
65
|
+
return [*super()._id_attrs(), ('idx_name', self.idx_info.name)]
|
|
66
66
|
|
|
67
67
|
def default_column_name(self) -> str:
|
|
68
68
|
return 'similarity'
|
|
69
69
|
|
|
70
70
|
def sql_expr(self, _: SqlElementCache) -> Optional[sql.ColumnElement]:
|
|
71
71
|
if not isinstance(self.components[1], Literal):
|
|
72
|
-
raise excs.Error(
|
|
72
|
+
raise excs.Error('similarity(): requires a string or a PIL.Image.Image object, not an expression')
|
|
73
73
|
item = self.components[1].val
|
|
74
74
|
from pixeltable import index
|
|
75
75
|
|
|
@@ -78,7 +78,7 @@ class SimilarityExpr(Expr):
|
|
|
78
78
|
|
|
79
79
|
def as_order_by_clause(self, is_asc: bool) -> Optional[sql.ColumnElement]:
|
|
80
80
|
if not isinstance(self.components[1], Literal):
|
|
81
|
-
raise excs.Error(
|
|
81
|
+
raise excs.Error('similarity(): requires a string or a PIL.Image.Image object, not an expression')
|
|
82
82
|
item = self.components[1].val
|
|
83
83
|
from pixeltable import index
|
|
84
84
|
|
|
@@ -87,14 +87,14 @@ class SimilarityExpr(Expr):
|
|
|
87
87
|
|
|
88
88
|
def eval(self, data_row: DataRow, row_builder: RowBuilder) -> None:
|
|
89
89
|
# this should never get called
|
|
90
|
-
|
|
90
|
+
raise AssertionError()
|
|
91
91
|
|
|
92
92
|
def _as_dict(self) -> dict:
|
|
93
93
|
return {'idx_name': self.idx_info.name, **super()._as_dict()}
|
|
94
94
|
|
|
95
95
|
@classmethod
|
|
96
96
|
def _from_dict(cls, d: dict, components: list[Expr]) -> 'SimilarityExpr':
|
|
97
|
-
iname = d
|
|
97
|
+
iname = d.get('idx_name')
|
|
98
98
|
assert len(components) == 2
|
|
99
99
|
assert isinstance(components[0], ColumnRef)
|
|
100
100
|
return cls(components[0], components[1], idx_name=iname)
|
pixeltable/exprs/type_cast.py
CHANGED
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
from typing import
|
|
1
|
+
from typing import Optional
|
|
2
2
|
|
|
3
3
|
import sqlalchemy as sql
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
import pixeltable.type_system as ts
|
|
5
|
+
from pixeltable import type_system as ts
|
|
7
6
|
|
|
8
7
|
from .expr import DataRow, Expr
|
|
9
8
|
from .literal import Literal
|
pixeltable/exprs/variable.py
CHANGED
|
@@ -2,7 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
from typing import Any, NoReturn
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
from pixeltable import type_system as ts
|
|
6
6
|
|
|
7
7
|
from .data_row import DataRow
|
|
8
8
|
from .expr import Expr
|
|
@@ -22,7 +22,7 @@ class Variable(Expr):
|
|
|
22
22
|
self.id = self._create_id()
|
|
23
23
|
|
|
24
24
|
def _id_attrs(self) -> list[tuple[str, Any]]:
|
|
25
|
-
return super()._id_attrs()
|
|
25
|
+
return [*super()._id_attrs(), ('name', self.name)]
|
|
26
26
|
|
|
27
27
|
def default_column_name(self) -> NoReturn:
|
|
28
28
|
raise NotImplementedError()
|
pixeltable/ext/__init__.py
CHANGED
|
@@ -111,10 +111,10 @@ def _images_to_tensors(images: Iterable[PIL.Image.Image], exp: 'Exp') -> Iterato
|
|
|
111
111
|
import torch
|
|
112
112
|
from yolox.data import ValTransform # type: ignore[import-untyped]
|
|
113
113
|
|
|
114
|
-
|
|
114
|
+
val_transform = ValTransform(legacy=False)
|
|
115
115
|
for image in images:
|
|
116
|
-
|
|
117
|
-
image_transform, _ =
|
|
116
|
+
normalized_image = normalize_image_mode(image)
|
|
117
|
+
image_transform, _ = val_transform(np.array(normalized_image), None, exp.test_size)
|
|
118
118
|
yield torch.from_numpy(image_transform)
|
|
119
119
|
|
|
120
120
|
|
pixeltable/func/__init__.py
CHANGED
|
@@ -2,7 +2,7 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import abc
|
|
4
4
|
import inspect
|
|
5
|
-
from typing import TYPE_CHECKING, Any, Callable, Optional, Sequence, overload
|
|
5
|
+
from typing import TYPE_CHECKING, Any, Callable, ClassVar, Optional, Sequence, overload
|
|
6
6
|
|
|
7
7
|
import pixeltable.exceptions as excs
|
|
8
8
|
import pixeltable.type_system as ts
|
|
@@ -16,11 +16,11 @@ if TYPE_CHECKING:
|
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
class Aggregator(abc.ABC):
|
|
19
|
-
|
|
20
|
-
|
|
19
|
+
@abc.abstractmethod
|
|
20
|
+
def update(self, *args: Any, **kwargs: Any) -> None: ...
|
|
21
21
|
|
|
22
|
-
|
|
23
|
-
|
|
22
|
+
@abc.abstractmethod
|
|
23
|
+
def value(self) -> Any: ...
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
class AggregateFunction(Function):
|
|
@@ -32,9 +32,9 @@ class AggregateFunction(Function):
|
|
|
32
32
|
allows_window: if True, the aggregate function can be used with a window
|
|
33
33
|
"""
|
|
34
34
|
|
|
35
|
-
ORDER_BY_PARAM = 'order_by'
|
|
36
|
-
GROUP_BY_PARAM = 'group_by'
|
|
37
|
-
RESERVED_PARAMS = {ORDER_BY_PARAM, GROUP_BY_PARAM}
|
|
35
|
+
ORDER_BY_PARAM: ClassVar[str] = 'order_by'
|
|
36
|
+
GROUP_BY_PARAM: ClassVar[str] = 'group_by'
|
|
37
|
+
RESERVED_PARAMS: ClassVar[set[str]] = {ORDER_BY_PARAM, GROUP_BY_PARAM}
|
|
38
38
|
|
|
39
39
|
agg_classes: list[type[Aggregator]] # classes for each signature, in signature order
|
|
40
40
|
init_param_names: list[list[str]] # names of the __init__ parameters for each signature
|
|
@@ -124,7 +124,7 @@ class AggregateFunction(Function):
|
|
|
124
124
|
)
|
|
125
125
|
for i, p in enumerate(py_init_params)
|
|
126
126
|
]
|
|
127
|
-
duplicate_params =
|
|
127
|
+
duplicate_params = {p.name for p in init_params} & {p.name for p in update_params}
|
|
128
128
|
if len(duplicate_params) > 0:
|
|
129
129
|
raise excs.Error(
|
|
130
130
|
f'__init__() and update() cannot have parameters with the same name: {", ".join(duplicate_params)}'
|
|
@@ -8,6 +8,7 @@ from uuid import UUID
|
|
|
8
8
|
import cloudpickle # type: ignore[import-untyped]
|
|
9
9
|
|
|
10
10
|
import pixeltable.exceptions as excs
|
|
11
|
+
from pixeltable.utils.coroutine import run_coroutine_synchronously
|
|
11
12
|
|
|
12
13
|
from .function import Function
|
|
13
14
|
from .signature import Signature
|
|
@@ -93,16 +94,17 @@ class CallableFunction(Function):
|
|
|
93
94
|
batched_kwargs = {k: [v] for k, v in kwargs.items() if k not in constant_param_names}
|
|
94
95
|
result: list[Any]
|
|
95
96
|
if inspect.iscoroutinefunction(self.py_fn):
|
|
96
|
-
|
|
97
|
+
# TODO: This is temporary (see note in utils/coroutine.py)
|
|
98
|
+
result = run_coroutine_synchronously(self.py_fn(*batched_args, **constant_kwargs, **batched_kwargs))
|
|
97
99
|
else:
|
|
98
100
|
result = self.py_fn(*batched_args, **constant_kwargs, **batched_kwargs)
|
|
99
101
|
assert len(result) == 1
|
|
100
102
|
return result[0]
|
|
103
|
+
elif inspect.iscoroutinefunction(self.py_fn):
|
|
104
|
+
# TODO: This is temporary (see note in utils/coroutine.py)
|
|
105
|
+
return run_coroutine_synchronously(self.py_fn(*args, **kwargs))
|
|
101
106
|
else:
|
|
102
|
-
|
|
103
|
-
return asyncio.run(self.py_fn(*args, **kwargs))
|
|
104
|
-
else:
|
|
105
|
-
return self.py_fn(*args, **kwargs)
|
|
107
|
+
return self.py_fn(*args, **kwargs)
|
|
106
108
|
|
|
107
109
|
async def aexec_batch(self, *args: Any, **kwargs: Any) -> list:
|
|
108
110
|
"""Execute the function with the given arguments and return the result.
|