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
|
@@ -1,8 +1,6 @@
|
|
|
1
|
-
import inspect
|
|
2
1
|
from typing import Any, Optional, Sequence
|
|
3
2
|
|
|
4
|
-
import
|
|
5
|
-
import pixeltable.exceptions as excs
|
|
3
|
+
from pixeltable import exceptions as excs, exprs
|
|
6
4
|
|
|
7
5
|
from .function import Function
|
|
8
6
|
from .signature import Signature
|
|
@@ -15,13 +13,11 @@ class ExprTemplate:
|
|
|
15
13
|
`CallableFunction`.)
|
|
16
14
|
"""
|
|
17
15
|
|
|
18
|
-
expr: '
|
|
16
|
+
expr: 'exprs.Expr'
|
|
19
17
|
signature: Signature
|
|
20
|
-
param_exprs: dict[str, '
|
|
21
|
-
|
|
22
|
-
def __init__(self, expr: 'pixeltable.exprs.Expr', signature: Signature):
|
|
23
|
-
from pixeltable import exprs
|
|
18
|
+
param_exprs: dict[str, 'exprs.Variable']
|
|
24
19
|
|
|
20
|
+
def __init__(self, expr: 'exprs.Expr', signature: Signature):
|
|
25
21
|
self.expr = expr
|
|
26
22
|
self.signature = signature
|
|
27
23
|
|
|
@@ -59,9 +55,7 @@ class ExprTemplateFunction(Function):
|
|
|
59
55
|
assert not self.is_polymorphic
|
|
60
56
|
return self.templates[0]
|
|
61
57
|
|
|
62
|
-
def instantiate(self, args: Sequence[Any], kwargs: dict[str, Any]) -> '
|
|
63
|
-
from pixeltable import exprs
|
|
64
|
-
|
|
58
|
+
def instantiate(self, args: Sequence[Any], kwargs: dict[str, Any]) -> 'exprs.Expr':
|
|
65
59
|
assert not self.is_polymorphic
|
|
66
60
|
template = self.template
|
|
67
61
|
bound_args = self.signature.py_signature.bind(*args, **kwargs).arguments
|
|
@@ -86,14 +80,12 @@ class ExprTemplateFunction(Function):
|
|
|
86
80
|
return result
|
|
87
81
|
|
|
88
82
|
def _docstring(self) -> Optional[str]:
|
|
89
|
-
from pixeltable import exprs
|
|
90
|
-
|
|
91
83
|
if isinstance(self.templates[0].expr, exprs.FunctionCall):
|
|
92
84
|
return self.templates[0].expr.fn._docstring()
|
|
93
85
|
return None
|
|
94
86
|
|
|
95
87
|
def exec(self, args: Sequence[Any], kwargs: dict[str, Any]) -> Any:
|
|
96
|
-
from pixeltable import exec
|
|
88
|
+
from pixeltable import exec
|
|
97
89
|
|
|
98
90
|
assert not self.is_polymorphic
|
|
99
91
|
expr = self.instantiate(args, kwargs)
|
|
@@ -130,7 +122,5 @@ class ExprTemplateFunction(Function):
|
|
|
130
122
|
if 'expr' not in d:
|
|
131
123
|
return super()._from_dict(d)
|
|
132
124
|
assert 'signature' in d and 'name' in d
|
|
133
|
-
import pixeltable.exprs as exprs
|
|
134
|
-
|
|
135
125
|
template = ExprTemplate(exprs.Expr.from_dict(d['expr']), Signature.from_dict(d['signature']))
|
|
136
126
|
return cls([template], name=d['name'])
|
pixeltable/func/function.py
CHANGED
|
@@ -246,7 +246,7 @@ class Function(ABC):
|
|
|
246
246
|
# `None` when any of its non-nullable inputs are `None`.
|
|
247
247
|
for arg_name, arg in bound_args.items():
|
|
248
248
|
param = self.signature.parameters[arg_name]
|
|
249
|
-
if param.kind in
|
|
249
|
+
if param.kind in {inspect.Parameter.VAR_POSITIONAL, inspect.Parameter.VAR_KEYWORD}:
|
|
250
250
|
continue
|
|
251
251
|
if arg.col_type.nullable and not param.col_type.nullable:
|
|
252
252
|
return_type = return_type.copy(nullable=True)
|
|
@@ -304,13 +304,12 @@ class Function(ABC):
|
|
|
304
304
|
callable_args[param.name] = arg
|
|
305
305
|
else:
|
|
306
306
|
return None
|
|
307
|
-
|
|
307
|
+
elif isinstance(arg, exprs.Literal):
|
|
308
308
|
# The callable is expecting `param.name` to be a constant Python value. Unpack a Literal if we find
|
|
309
309
|
# one; otherwise return None.
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
return None
|
|
310
|
+
callable_args[param.name] = arg.val
|
|
311
|
+
else:
|
|
312
|
+
return None
|
|
314
313
|
|
|
315
314
|
return callable_args
|
|
316
315
|
|
|
@@ -386,10 +385,10 @@ class Function(ABC):
|
|
|
386
385
|
else:
|
|
387
386
|
var = exprs.Variable(name, param.col_type)
|
|
388
387
|
bindings[name] = var
|
|
389
|
-
if args_ok and param.kind in
|
|
388
|
+
if args_ok and param.kind in {
|
|
390
389
|
inspect.Parameter.POSITIONAL_ONLY,
|
|
391
390
|
inspect.Parameter.POSITIONAL_OR_KEYWORD,
|
|
392
|
-
|
|
391
|
+
}:
|
|
393
392
|
template_args.append(var)
|
|
394
393
|
else:
|
|
395
394
|
template_kwargs[name] = var
|
|
@@ -436,6 +435,9 @@ class Function(ABC):
|
|
|
436
435
|
return False
|
|
437
436
|
return self.self_path == other.self_path
|
|
438
437
|
|
|
438
|
+
def __hash__(self) -> int:
|
|
439
|
+
return hash(self.self_path)
|
|
440
|
+
|
|
439
441
|
def source(self) -> None:
|
|
440
442
|
"""Print source code"""
|
|
441
443
|
print('source not available')
|
|
@@ -9,9 +9,7 @@ from uuid import UUID
|
|
|
9
9
|
|
|
10
10
|
import sqlalchemy as sql
|
|
11
11
|
|
|
12
|
-
import
|
|
13
|
-
import pixeltable.exceptions as excs
|
|
14
|
-
import pixeltable.type_system as ts
|
|
12
|
+
from pixeltable import env, exceptions as excs, type_system as ts
|
|
15
13
|
from pixeltable.metadata import schema
|
|
16
14
|
|
|
17
15
|
from .function import Function
|
|
@@ -1,13 +1,9 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import inspect
|
|
4
|
-
from typing import TYPE_CHECKING, Any, Callable, Optional,
|
|
4
|
+
from typing import TYPE_CHECKING, Any, Callable, Optional, overload
|
|
5
5
|
|
|
6
|
-
import
|
|
7
|
-
|
|
8
|
-
import pixeltable.exceptions as excs
|
|
9
|
-
import pixeltable.type_system as ts
|
|
10
|
-
from pixeltable import exprs
|
|
6
|
+
from pixeltable import exprs, type_system as ts
|
|
11
7
|
|
|
12
8
|
from .function import Function
|
|
13
9
|
from .signature import Signature
|
|
@@ -21,8 +17,6 @@ class QueryTemplateFunction(Function):
|
|
|
21
17
|
|
|
22
18
|
template_df: Optional['DataFrame']
|
|
23
19
|
self_name: Optional[str]
|
|
24
|
-
# conn: Optional[sql.engine.Connection]
|
|
25
|
-
defaults: dict[str, exprs.Literal]
|
|
26
20
|
|
|
27
21
|
@classmethod
|
|
28
22
|
def create(
|
|
@@ -50,20 +44,6 @@ class QueryTemplateFunction(Function):
|
|
|
50
44
|
self.self_name = name
|
|
51
45
|
self.template_df = template_df
|
|
52
46
|
|
|
53
|
-
# if we're running as part of an ongoing update operation, we need to use the same connection, otherwise
|
|
54
|
-
# we end up with a deadlock
|
|
55
|
-
# TODO: figure out a more general way to make execution state available
|
|
56
|
-
# self.conn = None
|
|
57
|
-
|
|
58
|
-
# convert defaults to Literals
|
|
59
|
-
self.defaults = {} # key: param name, value: default value converted to a Literal
|
|
60
|
-
param_types = self.template_df.parameters()
|
|
61
|
-
for param in [p for p in sig.parameters.values() if p.has_default()]:
|
|
62
|
-
assert param.name in param_types
|
|
63
|
-
param_type = param_types[param.name]
|
|
64
|
-
literal_default = exprs.Literal(param.default, col_type=param_type)
|
|
65
|
-
self.defaults[param.name] = literal_default
|
|
66
|
-
|
|
67
47
|
def _update_as_overload_resolution(self, signature_idx: int) -> None:
|
|
68
48
|
pass # only one signature supported for QueryTemplateFunction
|
|
69
49
|
|
|
@@ -76,7 +56,11 @@ class QueryTemplateFunction(Function):
|
|
|
76
56
|
bound_args = self.signature.py_signature.bind(*args, **kwargs).arguments
|
|
77
57
|
# apply defaults, otherwise we might have Parameters left over
|
|
78
58
|
bound_args.update(
|
|
79
|
-
{
|
|
59
|
+
{
|
|
60
|
+
param.name: param.default
|
|
61
|
+
for param in self.signature.parameters.values()
|
|
62
|
+
if param.has_default() and param.name not in bound_args
|
|
63
|
+
}
|
|
80
64
|
)
|
|
81
65
|
bound_df = self.template_df.bind(bound_args)
|
|
82
66
|
result = await bound_df._acollect()
|
|
@@ -91,7 +75,7 @@ class QueryTemplateFunction(Function):
|
|
|
91
75
|
return self.self_name
|
|
92
76
|
|
|
93
77
|
def _as_dict(self) -> dict:
|
|
94
|
-
return {'name': self.name, 'signature': self.
|
|
78
|
+
return {'name': self.name, 'signature': self.signature.as_dict(), 'df': self.template_df.as_dict()}
|
|
95
79
|
|
|
96
80
|
@classmethod
|
|
97
81
|
def _from_dict(cls, d: dict) -> Function:
|
pixeltable/func/signature.py
CHANGED
|
@@ -2,10 +2,9 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import dataclasses
|
|
4
4
|
import inspect
|
|
5
|
-
import json
|
|
6
5
|
import logging
|
|
7
6
|
import typing
|
|
8
|
-
from typing import TYPE_CHECKING, Any, Callable, Optional
|
|
7
|
+
from typing import TYPE_CHECKING, Any, Callable, ClassVar, Optional
|
|
9
8
|
|
|
10
9
|
import pixeltable.exceptions as excs
|
|
11
10
|
import pixeltable.type_system as ts
|
|
@@ -69,6 +68,9 @@ class Parameter:
|
|
|
69
68
|
py_default = self.default.val if self.default is not None else inspect.Parameter.empty
|
|
70
69
|
return inspect.Parameter(self.name, self.kind, default=py_default)
|
|
71
70
|
|
|
71
|
+
def __hash__(self) -> int:
|
|
72
|
+
return hash((self.name, self.col_type, self.kind, self.default, self.is_batched))
|
|
73
|
+
|
|
72
74
|
|
|
73
75
|
T = typing.TypeVar('T')
|
|
74
76
|
Batch = typing.Annotated[list[T], 'pxt-batch']
|
|
@@ -81,7 +83,7 @@ class Signature:
|
|
|
81
83
|
- self.is_batched: return type is a Batch[...] type
|
|
82
84
|
"""
|
|
83
85
|
|
|
84
|
-
SPECIAL_PARAM_NAMES = ['group_by', 'order_by']
|
|
86
|
+
SPECIAL_PARAM_NAMES: ClassVar[list[str]] = ['group_by', 'order_by']
|
|
85
87
|
|
|
86
88
|
def __init__(self, return_type: ts.ColumnType, parameters: list[Parameter], is_batched: bool = False):
|
|
87
89
|
assert isinstance(return_type, ts.ColumnType)
|
|
@@ -135,26 +137,28 @@ class Signature:
|
|
|
135
137
|
if (
|
|
136
138
|
param.kind != other_param.kind
|
|
137
139
|
or (param.col_type is None) != (other_param.col_type is None) # this can happen if they are varargs
|
|
138
|
-
or
|
|
139
|
-
|
|
140
|
+
or (
|
|
141
|
+
param.col_type is not None
|
|
142
|
+
and not other_param.col_type.is_supertype_of(param.col_type, ignore_nullable=True)
|
|
143
|
+
)
|
|
140
144
|
):
|
|
141
145
|
return False
|
|
142
146
|
|
|
143
147
|
# Check (iii)
|
|
144
|
-
for other_param in other.required_parameters:
|
|
148
|
+
for other_param in other.required_parameters: # noqa: SIM110
|
|
145
149
|
if other_param.name not in self.parameters:
|
|
146
150
|
return False
|
|
147
151
|
|
|
148
152
|
return True
|
|
149
153
|
|
|
150
154
|
def validate_args(self, bound_args: dict[str, Optional['exprs.Expr']], context: str = '') -> None:
|
|
151
|
-
if context
|
|
155
|
+
if context:
|
|
152
156
|
context = f' ({context})'
|
|
153
157
|
|
|
154
158
|
for param_name, arg in bound_args.items():
|
|
155
159
|
assert param_name in self.parameters
|
|
156
160
|
param = self.parameters[param_name]
|
|
157
|
-
is_var_param = param.kind in
|
|
161
|
+
is_var_param = param.kind in {inspect.Parameter.VAR_POSITIONAL, inspect.Parameter.VAR_KEYWORD}
|
|
158
162
|
if is_var_param:
|
|
159
163
|
continue
|
|
160
164
|
assert param.col_type is not None
|
|
@@ -191,6 +195,9 @@ class Signature:
|
|
|
191
195
|
return False
|
|
192
196
|
return True
|
|
193
197
|
|
|
198
|
+
def __hash__(self) -> int:
|
|
199
|
+
return hash((self.return_type, self.parameters))
|
|
200
|
+
|
|
194
201
|
def __str__(self) -> str:
|
|
195
202
|
param_strs: list[str] = []
|
|
196
203
|
for p in self.parameters.values():
|
|
@@ -199,8 +206,8 @@ class Signature:
|
|
|
199
206
|
elif p.kind == inspect.Parameter.VAR_KEYWORD:
|
|
200
207
|
param_strs.append(f'**{p.name}')
|
|
201
208
|
else:
|
|
202
|
-
param_strs.append(f'{p.name}: {
|
|
203
|
-
return f'({", ".join(param_strs)}) -> {
|
|
209
|
+
param_strs.append(f'{p.name}: {p.col_type}')
|
|
210
|
+
return f'({", ".join(param_strs)}) -> {self.get_return_type()}'
|
|
204
211
|
|
|
205
212
|
@classmethod
|
|
206
213
|
def _infer_type(cls, annotation: Optional[type]) -> tuple[Optional[ts.ColumnType], Optional[bool]]:
|
|
@@ -213,7 +220,7 @@ class Signature:
|
|
|
213
220
|
type_args = typing.get_args(annotation)
|
|
214
221
|
if len(type_args) == 2 and type_args[1] == 'pxt-batch':
|
|
215
222
|
# this is our Batch
|
|
216
|
-
assert typing.get_origin(type_args[0])
|
|
223
|
+
assert typing.get_origin(type_args[0]) is list
|
|
217
224
|
is_batched = True
|
|
218
225
|
py_type = typing.get_args(type_args[0])[0]
|
|
219
226
|
if py_type is None:
|
|
@@ -246,7 +253,7 @@ class Signature:
|
|
|
246
253
|
continue # skip 'self' or 'cls' parameter
|
|
247
254
|
if param.name in cls.SPECIAL_PARAM_NAMES:
|
|
248
255
|
raise excs.Error(f'{param.name!r} is a reserved parameter name')
|
|
249
|
-
if param.kind
|
|
256
|
+
if param.kind in {inspect.Parameter.VAR_POSITIONAL, inspect.Parameter.VAR_KEYWORD}:
|
|
250
257
|
parameters.append(Parameter(param.name, col_type=None, kind=param.kind))
|
|
251
258
|
continue
|
|
252
259
|
|
|
@@ -257,11 +264,8 @@ class Signature:
|
|
|
257
264
|
param_type = param_types[idx]
|
|
258
265
|
is_batched = False
|
|
259
266
|
else:
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
py_type = type_substitutions[param.annotation]
|
|
263
|
-
else:
|
|
264
|
-
py_type = param.annotation
|
|
267
|
+
# Look up the substitution for param.annotation, defaulting to param.annotation if there is none
|
|
268
|
+
py_type = type_substitutions.get(param.annotation, param.annotation)
|
|
265
269
|
param_type, is_batched = cls._infer_type(py_type)
|
|
266
270
|
if param_type is None:
|
|
267
271
|
raise excs.Error(f'Cannot infer pixeltable type for parameter {param.name!r}')
|
|
@@ -297,11 +301,8 @@ class Signature:
|
|
|
297
301
|
)
|
|
298
302
|
sig = inspect.signature(py_fn)
|
|
299
303
|
if return_type is None:
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
py_type = type_substitutions[sig.return_annotation]
|
|
303
|
-
else:
|
|
304
|
-
py_type = sig.return_annotation
|
|
304
|
+
# Look up the substitution for sig.return_annotation, defaulting to return_annotation if there is none
|
|
305
|
+
py_type = type_substitutions.get(sig.return_annotation, sig.return_annotation)
|
|
305
306
|
return_type, return_is_batched = cls._infer_type(py_type)
|
|
306
307
|
if return_type is None:
|
|
307
308
|
raise excs.Error('Cannot infer pixeltable return type')
|
pixeltable/func/tools.py
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import TYPE_CHECKING, Any, Callable,
|
|
1
|
+
from typing import TYPE_CHECKING, Any, Callable, Optional, TypeVar, Union
|
|
2
2
|
|
|
3
3
|
import pydantic
|
|
4
4
|
|
|
@@ -69,7 +69,7 @@ class Tool(pydantic.BaseModel):
|
|
|
69
69
|
return _extract_float_tool_arg(kwargs, param_name=param.name)
|
|
70
70
|
if param.col_type.is_bool_type():
|
|
71
71
|
return _extract_bool_tool_arg(kwargs, param_name=param.name)
|
|
72
|
-
|
|
72
|
+
raise AssertionError()
|
|
73
73
|
|
|
74
74
|
|
|
75
75
|
class ToolChoice(pydantic.BaseModel):
|
|
@@ -113,7 +113,7 @@ class Tools(pydantic.BaseModel):
|
|
|
113
113
|
)
|
|
114
114
|
tool_name = tool_obj.name or tool_obj.fn.name
|
|
115
115
|
except StopIteration:
|
|
116
|
-
raise excs.Error(f'That tool is not in the specified list of tools: {tool}')
|
|
116
|
+
raise excs.Error(f'That tool is not in the specified list of tools: {tool}') from None
|
|
117
117
|
return ToolChoice(auto=auto, required=required, tool=tool_name, parallel_tool_calls=parallel_tool_calls)
|
|
118
118
|
|
|
119
119
|
|
pixeltable/func/udf.py
CHANGED
|
@@ -146,7 +146,8 @@ def make_function(
|
|
|
146
146
|
raise excs.Error(f'Cannot specify both `is_method` and `is_property` (in function `{function_name}`)')
|
|
147
147
|
if is_property and len(sig.parameters) != 1:
|
|
148
148
|
raise excs.Error(
|
|
149
|
-
|
|
149
|
+
'`is_property=True` expects a UDF with exactly 1 parameter, but '
|
|
150
|
+
f'`{function_name}` has {len(sig.parameters)}'
|
|
150
151
|
)
|
|
151
152
|
if (is_method or is_property) and function_path is None:
|
|
152
153
|
raise excs.Error('Stored functions cannot be declared using `is_method` or `is_property`')
|
|
@@ -205,6 +206,8 @@ def expr_udf(*, param_types: Optional[list[ts.ColumnType]] = None) -> Callable[[
|
|
|
205
206
|
|
|
206
207
|
def expr_udf(*args: Any, **kwargs: Any) -> Any:
|
|
207
208
|
def make_expr_template(py_fn: Callable, param_types: Optional[list[ts.ColumnType]]) -> ExprTemplateFunction:
|
|
209
|
+
from pixeltable import exprs
|
|
210
|
+
|
|
208
211
|
if py_fn.__module__ != '__main__' and py_fn.__name__.isidentifier():
|
|
209
212
|
# this is a named function in a module
|
|
210
213
|
function_path = f'{py_fn.__module__}.{py_fn.__qualname__}'
|
|
@@ -216,7 +219,6 @@ def expr_udf(*args: Any, **kwargs: Any) -> Any:
|
|
|
216
219
|
|
|
217
220
|
# construct Signature from the function signature
|
|
218
221
|
sig = Signature.create(py_fn=py_fn, param_types=param_types, return_type=ts.InvalidType())
|
|
219
|
-
import pixeltable.exprs as exprs
|
|
220
222
|
|
|
221
223
|
var_exprs = [exprs.Variable(param.name, param.col_type) for param in sig.parameters.values()]
|
|
222
224
|
# call the function with the parameter expressions to construct an Expr with parameters
|
|
@@ -260,7 +262,7 @@ def from_table(
|
|
|
260
262
|
"""
|
|
261
263
|
from pixeltable import exprs
|
|
262
264
|
|
|
263
|
-
ancestors = [tbl
|
|
265
|
+
ancestors = [tbl, *tbl._bases]
|
|
264
266
|
ancestors.reverse() # We must traverse the ancestors in order from base to derived
|
|
265
267
|
|
|
266
268
|
subst: dict[exprs.Expr, exprs.Expr] = {}
|