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.

Files changed (68) hide show
  1. pixeltable/__version__.py +2 -2
  2. pixeltable/catalog/catalog.py +509 -103
  3. pixeltable/catalog/column.py +1 -0
  4. pixeltable/catalog/dir.py +15 -6
  5. pixeltable/catalog/path.py +15 -0
  6. pixeltable/catalog/schema_object.py +7 -12
  7. pixeltable/catalog/table.py +3 -12
  8. pixeltable/catalog/table_version.py +5 -0
  9. pixeltable/catalog/view.py +0 -4
  10. pixeltable/env.py +14 -8
  11. pixeltable/exprs/__init__.py +2 -0
  12. pixeltable/exprs/arithmetic_expr.py +7 -11
  13. pixeltable/exprs/array_slice.py +1 -1
  14. pixeltable/exprs/column_property_ref.py +3 -3
  15. pixeltable/exprs/column_ref.py +5 -6
  16. pixeltable/exprs/comparison.py +2 -5
  17. pixeltable/exprs/compound_predicate.py +4 -4
  18. pixeltable/exprs/expr.py +32 -19
  19. pixeltable/exprs/expr_dict.py +3 -3
  20. pixeltable/exprs/expr_set.py +1 -1
  21. pixeltable/exprs/function_call.py +28 -41
  22. pixeltable/exprs/globals.py +3 -3
  23. pixeltable/exprs/in_predicate.py +1 -1
  24. pixeltable/exprs/inline_expr.py +3 -3
  25. pixeltable/exprs/is_null.py +1 -1
  26. pixeltable/exprs/json_mapper.py +5 -5
  27. pixeltable/exprs/json_path.py +27 -15
  28. pixeltable/exprs/literal.py +1 -1
  29. pixeltable/exprs/method_ref.py +2 -2
  30. pixeltable/exprs/row_builder.py +3 -5
  31. pixeltable/exprs/rowid_ref.py +4 -7
  32. pixeltable/exprs/similarity_expr.py +5 -5
  33. pixeltable/exprs/sql_element_cache.py +1 -1
  34. pixeltable/exprs/type_cast.py +2 -3
  35. pixeltable/exprs/variable.py +2 -2
  36. pixeltable/ext/__init__.py +2 -0
  37. pixeltable/ext/functions/__init__.py +2 -0
  38. pixeltable/ext/functions/yolox.py +3 -3
  39. pixeltable/func/__init__.py +2 -0
  40. pixeltable/func/aggregate_function.py +9 -9
  41. pixeltable/func/callable_function.py +7 -5
  42. pixeltable/func/expr_template_function.py +6 -16
  43. pixeltable/func/function.py +10 -8
  44. pixeltable/func/function_registry.py +1 -3
  45. pixeltable/func/query_template_function.py +8 -24
  46. pixeltable/func/signature.py +23 -22
  47. pixeltable/func/tools.py +3 -3
  48. pixeltable/func/udf.py +5 -3
  49. pixeltable/globals.py +118 -260
  50. pixeltable/share/__init__.py +2 -0
  51. pixeltable/share/packager.py +3 -3
  52. pixeltable/share/publish.py +3 -5
  53. pixeltable/utils/coco.py +4 -4
  54. pixeltable/utils/console_output.py +1 -3
  55. pixeltable/utils/coroutine.py +41 -0
  56. pixeltable/utils/description_helper.py +1 -1
  57. pixeltable/utils/documents.py +3 -3
  58. pixeltable/utils/filecache.py +18 -8
  59. pixeltable/utils/formatter.py +2 -3
  60. pixeltable/utils/media_store.py +1 -1
  61. pixeltable/utils/pytorch.py +1 -1
  62. pixeltable/utils/sql.py +4 -4
  63. pixeltable/utils/transactional_directory.py +2 -1
  64. {pixeltable-0.3.7.dist-info → pixeltable-0.3.9.dist-info}/METADATA +1 -1
  65. {pixeltable-0.3.7.dist-info → pixeltable-0.3.9.dist-info}/RECORD +68 -67
  66. {pixeltable-0.3.7.dist-info → pixeltable-0.3.9.dist-info}/LICENSE +0 -0
  67. {pixeltable-0.3.7.dist-info → pixeltable-0.3.9.dist-info}/WHEEL +0 -0
  68. {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 pixeltable.catalog as catalog
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
- if self.fn != other.fn:
160
- return False
161
- if self.arg_idxs != other.arg_idxs:
162
- return False
163
- if self.kwarg_idxs != other.kwarg_idxs:
164
- return False
165
- if self.group_by_start_idx != other.group_by_start_idx:
166
- return False
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 super()._id_attrs() + [
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 if self.fn.display_name != '' else 'anonymous_fn'
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}={str(self.components[idx])}' for param_name, idx in self.kwarg_idxs.items()])
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 (inspect.Parameter.POSITIONAL_ONLY, inspect.Parameter.POSITIONAL_OR_KEYWORD)
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 (inspect.Parameter.KEYWORD_ONLY, inspect.Parameter.POSITIONAL_OR_KEYWORD)
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 isinstance(self.fn, func.CallableFunction) and not self.fn.is_batched:
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
- else:
470
- # There is a return_type stored in metadata (schema version >= 25).
471
- # Check that the stored return_type of the UDF call matches the column type of the FunctionCall, and
472
- # fail-fast if it doesn't (otherwise we risk getting downstream database errors).
473
- if not return_type.is_supertype_of(call_return_type, ignore_nullable=True):
474
- validation_error = dedent(
475
- f"""
476
- The return type stored in the database for a UDF call to {fn.self_path!r} no longer
477
- matches its return type as currently defined in the code. This probably means that the
478
- code for {fn.self_path!r} has changed in a backward-incompatible way.
479
- Return type of UDF call in the database: {return_type}
480
- Return type of UDF as currently defined in code: {call_return_type}
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,
@@ -36,7 +36,7 @@ class ComparisonOperator(enum.Enum):
36
36
  return '>'
37
37
  if self == self.GE:
38
38
  return '>='
39
- assert False
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
- assert False
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
- assert False
89
+ raise AssertionError()
@@ -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() + [('value_list', self.value_list)]
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])
@@ -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(list(c.as_literal().val for c in self.components), self.col_type)
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 = list(f"'{key}': {str(expr)}" for key, expr in zip(self.keys, self.components))
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() + [('keys', self.keys)]
177
+ return [*super()._id_attrs(), ('keys', self.keys)]
178
178
 
179
179
  def sql_expr(self, _: SqlElementCache) -> Optional[sql.ColumnElement]:
180
180
  return None
@@ -19,7 +19,7 @@ class IsNull(Expr):
19
19
  self.id = self._create_id()
20
20
 
21
21
  def __repr__(self) -> str:
22
- return f'{str(self.components[0])} == None'
22
+ return f'{self.components[0]} == None'
23
23
 
24
24
  def _equals(self, other: IsNull) -> bool:
25
25
  return True
@@ -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 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)
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) != type(other):
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'{str(self._src_expr)} >> {str(self._target_expr)}'
89
+ return f'{self._src_expr} >> {self._target_expr}'
90
90
 
91
91
  @property
92
92
  def _src_expr(self) -> Expr:
@@ -6,14 +6,13 @@ import jmespath
6
6
  import sqlalchemy as sql
7
7
 
8
8
  import pixeltable as pxt
9
- import pixeltable.catalog as catalog
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, **super()._as_dict()}
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
- def bind_rel_paths(self, mapper: Optional['JsonMapper'] = None) -> None:
77
- if not self.is_relative_path():
78
- return
79
- # TODO: take scope_idx into account
80
- self.set_anchor(mapper.scope_anchor)
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(f'() for an absolute path is invalid')
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(f'R() requires a negative index')
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 + [name])
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 + [index])
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() + [('path_elements', self.path_elements)]
145
+ return [*super()._id_attrs(), ('path_elements', self.path_elements)]
134
146
 
135
147
  def sql_expr(self, _: SqlElementCache) -> Optional[sql.ColumnElement]:
136
148
  """
@@ -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() + [('val', self.val)]
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
@@ -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() + [('method_name', self.method_name)]
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
- assert False, 'MethodRef cannot be evaluated directly'
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}'
@@ -8,9 +8,7 @@ from uuid import UUID
8
8
 
9
9
  import numpy as np
10
10
 
11
- import pixeltable.catalog as catalog
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 slot_idx in self._exc_dependents[slot_idx]:
370
- data_row.set_exc(slot_idx, 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,
@@ -1,21 +1,17 @@
1
1
  from __future__ import annotations
2
2
 
3
- from typing import TYPE_CHECKING, Any, Optional, cast
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 pixeltable.catalog as catalog
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 super()._id_attrs() + [
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() + [('idx_name', self.idx_info.name)]
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(f'similarity(): requires a string or a PIL.Image.Image object, not an expression')
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(f'similarity(): requires a string or a PIL.Image.Image object, not an expression')
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
- assert False
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['idx_name'] if 'idx_name' in d else None
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)
@@ -1,4 +1,4 @@
1
- from typing import Iterable, Optional, Union, cast
1
+ from typing import Iterable, Optional
2
2
 
3
3
  import sqlalchemy as sql
4
4
 
@@ -1,9 +1,8 @@
1
- from typing import Any, Optional, Union
1
+ from typing import Optional
2
2
 
3
3
  import sqlalchemy as sql
4
4
 
5
- import pixeltable.exprs as exprs
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
@@ -2,7 +2,7 @@ from __future__ import annotations
2
2
 
3
3
  from typing import Any, NoReturn
4
4
 
5
- import pixeltable.type_system as ts
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() + [('name', self.name)]
25
+ return [*super()._id_attrs(), ('name', self.name)]
26
26
 
27
27
  def default_column_name(self) -> NoReturn:
28
28
  raise NotImplementedError()
@@ -4,6 +4,8 @@ are not intended for production use. Long-term support cannot be guaranteed, usu
4
4
  have dependencies whose future support is unclear.
5
5
  """
6
6
 
7
+ # ruff: noqa: F401
8
+
7
9
  from pixeltable.utils.code import local_public_names
8
10
 
9
11
  from . import functions
@@ -1,3 +1,5 @@
1
+ # ruff: noqa: F401
2
+
1
3
  from pixeltable.utils.code import local_public_names
2
4
 
3
5
  from . import whisperx, yolox
@@ -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
- _val_transform = ValTransform(legacy=False)
114
+ val_transform = ValTransform(legacy=False)
115
115
  for image in images:
116
- image = normalize_image_mode(image)
117
- image_transform, _ = _val_transform(np.array(image), None, exp.test_size)
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
 
@@ -1,3 +1,5 @@
1
+ # ruff: noqa: F401
2
+
1
3
  from .aggregate_function import AggregateFunction, Aggregator, uda
2
4
  from .callable_function import CallableFunction
3
5
  from .expr_template_function import ExprTemplateFunction
@@ -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
- def update(self, *args: Any, **kwargs: Any) -> None:
20
- pass
19
+ @abc.abstractmethod
20
+ def update(self, *args: Any, **kwargs: Any) -> None: ...
21
21
 
22
- def value(self) -> Any:
23
- pass
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 = set(p.name for p in init_params) & set(p.name for p in update_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
- result = asyncio.run(self.py_fn(*batched_args, **constant_kwargs, **batched_kwargs))
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
- if inspect.iscoroutinefunction(self.py_fn):
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.