pixeltable 0.3.9__py3-none-any.whl → 0.3.11__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 (122) hide show
  1. pixeltable/__init__.py +2 -3
  2. pixeltable/__version__.py +2 -2
  3. pixeltable/catalog/__init__.py +2 -1
  4. pixeltable/catalog/catalog.py +63 -36
  5. pixeltable/catalog/column.py +11 -4
  6. pixeltable/catalog/dir.py +5 -5
  7. pixeltable/catalog/globals.py +28 -14
  8. pixeltable/catalog/insertable_table.py +81 -43
  9. pixeltable/catalog/path.py +2 -2
  10. pixeltable/catalog/table.py +140 -109
  11. pixeltable/catalog/table_version.py +60 -43
  12. pixeltable/catalog/table_version_handle.py +3 -0
  13. pixeltable/catalog/table_version_path.py +1 -1
  14. pixeltable/catalog/view.py +17 -9
  15. pixeltable/dataframe.py +5 -3
  16. pixeltable/env.py +109 -43
  17. pixeltable/exec/__init__.py +2 -0
  18. pixeltable/exec/aggregation_node.py +6 -8
  19. pixeltable/exec/cache_prefetch_node.py +4 -7
  20. pixeltable/exec/component_iteration_node.py +1 -3
  21. pixeltable/exec/data_row_batch.py +1 -2
  22. pixeltable/exec/exec_context.py +1 -1
  23. pixeltable/exec/exec_node.py +2 -3
  24. pixeltable/exec/expr_eval/__init__.py +2 -0
  25. pixeltable/exec/expr_eval/evaluators.py +137 -20
  26. pixeltable/exec/expr_eval/expr_eval_node.py +43 -64
  27. pixeltable/exec/expr_eval/globals.py +68 -7
  28. pixeltable/exec/expr_eval/schedulers.py +25 -23
  29. pixeltable/exec/in_memory_data_node.py +8 -6
  30. pixeltable/exec/row_update_node.py +3 -4
  31. pixeltable/exec/sql_node.py +16 -17
  32. pixeltable/exprs/__init__.py +3 -2
  33. pixeltable/exprs/arithmetic_expr.py +2 -0
  34. pixeltable/exprs/column_property_ref.py +1 -1
  35. pixeltable/exprs/column_ref.py +39 -3
  36. pixeltable/exprs/compound_predicate.py +1 -1
  37. pixeltable/exprs/data_row.py +17 -1
  38. pixeltable/exprs/expr.py +51 -21
  39. pixeltable/exprs/function_call.py +34 -2
  40. pixeltable/exprs/globals.py +12 -0
  41. pixeltable/exprs/json_mapper.py +95 -48
  42. pixeltable/exprs/json_path.py +3 -10
  43. pixeltable/exprs/method_ref.py +2 -2
  44. pixeltable/exprs/object_ref.py +2 -2
  45. pixeltable/exprs/row_builder.py +33 -6
  46. pixeltable/exprs/similarity_expr.py +6 -21
  47. pixeltable/exprs/sql_element_cache.py +1 -1
  48. pixeltable/exprs/string_op.py +107 -0
  49. pixeltable/ext/__init__.py +1 -1
  50. pixeltable/ext/functions/__init__.py +1 -1
  51. pixeltable/ext/functions/whisperx.py +1 -1
  52. pixeltable/ext/functions/yolox.py +22 -65
  53. pixeltable/func/aggregate_function.py +1 -1
  54. pixeltable/func/callable_function.py +2 -5
  55. pixeltable/func/expr_template_function.py +22 -2
  56. pixeltable/func/function.py +4 -5
  57. pixeltable/func/function_registry.py +1 -1
  58. pixeltable/func/signature.py +1 -1
  59. pixeltable/func/tools.py +2 -2
  60. pixeltable/func/udf.py +2 -2
  61. pixeltable/functions/__init__.py +2 -2
  62. pixeltable/functions/anthropic.py +2 -2
  63. pixeltable/functions/audio.py +1 -1
  64. pixeltable/functions/deepseek.py +1 -1
  65. pixeltable/functions/fireworks.py +1 -1
  66. pixeltable/functions/globals.py +22 -11
  67. pixeltable/functions/huggingface.py +1 -1
  68. pixeltable/functions/image.py +1 -1
  69. pixeltable/functions/json.py +1 -1
  70. pixeltable/functions/llama_cpp.py +1 -1
  71. pixeltable/functions/math.py +1 -1
  72. pixeltable/functions/mistralai.py +1 -1
  73. pixeltable/functions/ollama.py +1 -1
  74. pixeltable/functions/openai.py +2 -2
  75. pixeltable/functions/replicate.py +1 -1
  76. pixeltable/functions/string.py +1 -1
  77. pixeltable/functions/timestamp.py +1 -1
  78. pixeltable/functions/together.py +1 -1
  79. pixeltable/functions/util.py +1 -1
  80. pixeltable/functions/video.py +2 -2
  81. pixeltable/functions/vision.py +2 -2
  82. pixeltable/globals.py +85 -33
  83. pixeltable/index/embedding_index.py +12 -1
  84. pixeltable/io/__init__.py +8 -5
  85. pixeltable/io/datarows.py +138 -0
  86. pixeltable/io/external_store.py +8 -5
  87. pixeltable/io/fiftyone.py +6 -7
  88. pixeltable/io/globals.py +7 -160
  89. pixeltable/io/hf_datasets.py +21 -98
  90. pixeltable/io/label_studio.py +21 -20
  91. pixeltable/io/pandas.py +35 -48
  92. pixeltable/io/parquet.py +17 -42
  93. pixeltable/io/table_data_conduit.py +569 -0
  94. pixeltable/io/utils.py +6 -21
  95. pixeltable/iterators/__init__.py +1 -1
  96. pixeltable/metadata/__init__.py +6 -4
  97. pixeltable/metadata/converters/convert_24.py +3 -3
  98. pixeltable/metadata/converters/convert_25.py +1 -1
  99. pixeltable/metadata/converters/convert_29.py +1 -1
  100. pixeltable/metadata/converters/convert_30.py +50 -0
  101. pixeltable/metadata/converters/util.py +26 -1
  102. pixeltable/metadata/notes.py +1 -0
  103. pixeltable/metadata/schema.py +3 -0
  104. pixeltable/store.py +2 -2
  105. pixeltable/type_system.py +19 -7
  106. pixeltable/utils/arrow.py +32 -7
  107. pixeltable/utils/console_output.py +3 -2
  108. pixeltable/utils/coroutine.py +3 -3
  109. pixeltable/utils/dbms.py +66 -0
  110. pixeltable/utils/documents.py +61 -67
  111. pixeltable/utils/filecache.py +1 -1
  112. pixeltable/utils/http_server.py +3 -2
  113. pixeltable/utils/pytorch.py +1 -1
  114. pixeltable/utils/sql.py +1 -1
  115. pixeltable-0.3.11.dist-info/METADATA +436 -0
  116. pixeltable-0.3.11.dist-info/RECORD +179 -0
  117. {pixeltable-0.3.9.dist-info → pixeltable-0.3.11.dist-info}/WHEEL +1 -1
  118. pixeltable/catalog/path_dict.py +0 -169
  119. pixeltable-0.3.9.dist-info/METADATA +0 -382
  120. pixeltable-0.3.9.dist-info/RECORD +0 -175
  121. {pixeltable-0.3.9.dist-info → pixeltable-0.3.11.dist-info}/LICENSE +0 -0
  122. {pixeltable-0.3.9.dist-info → pixeltable-0.3.11.dist-info}/entry_points.txt +0 -0
@@ -1,21 +1,15 @@
1
1
  import logging
2
- from pathlib import Path
3
- from typing import TYPE_CHECKING, Iterable, Iterator
4
- from urllib.request import urlretrieve
2
+ from typing import TYPE_CHECKING
5
3
 
6
- import numpy as np
7
4
  import PIL.Image
8
5
 
9
6
  import pixeltable as pxt
10
- from pixeltable import env
11
7
  from pixeltable.func import Batch
12
8
  from pixeltable.functions.util import normalize_image_mode
13
9
  from pixeltable.utils.code import local_public_names
14
10
 
15
11
  if TYPE_CHECKING:
16
- import torch
17
- from yolox.exp import Exp # type: ignore[import-untyped]
18
- from yolox.models import YOLOX # type: ignore[import-untyped]
12
+ from yolox.models import Yolox, YoloxProcessor # type: ignore[import-untyped]
19
13
 
20
14
  _logger = logging.getLogger('pixeltable')
21
15
 
@@ -30,7 +24,7 @@ def yolox(images: Batch[PIL.Image.Image], *, model_id: str, threshold: float = 0
30
24
 
31
25
  __Requirements__:
32
26
 
33
- - `pip install git+https://github.com/Megvii-BaseDetection/YOLOX`
27
+ - `pip install pixeltable-yolox`
34
28
 
35
29
  Args:
36
30
  model_id: one of: `yolox_nano`, `yolox_tiny`, `yolox_s`, `yolox_m`, `yolox_l`, `yolox_x`
@@ -46,31 +40,14 @@ def yolox(images: Batch[PIL.Image.Image], *, model_id: str, threshold: float = 0
46
40
  >>> tbl.add_computed_column(detections=yolox(tbl.image, model_id='yolox_m', threshold=0.8))
47
41
  """
48
42
  import torch
49
- from yolox.utils import postprocess # type: ignore[import-untyped]
50
-
51
- model, exp = _lookup_model(model_id, 'cpu')
52
- image_tensors = list(_images_to_tensors(images, exp))
53
- batch_tensor = torch.stack(image_tensors)
54
43
 
44
+ model = _lookup_model(model_id, 'cpu')
45
+ processor = _lookup_processor(model_id)
46
+ normalized_images = [normalize_image_mode(image) for image in images]
55
47
  with torch.no_grad():
56
- output_tensor = model(batch_tensor)
57
-
58
- outputs = postprocess(output_tensor, 80, threshold, exp.nmsthre, class_agnostic=False)
59
-
60
- results: list[dict] = []
61
- for image in images:
62
- ratio = min(exp.test_size[0] / image.height, exp.test_size[1] / image.width)
63
- if outputs[0] is None:
64
- results.append({'bboxes': [], 'scores': [], 'labels': []})
65
- else:
66
- results.append(
67
- {
68
- 'bboxes': [(output[:4] / ratio).tolist() for output in outputs[0]],
69
- 'scores': [output[4].item() * output[5].item() for output in outputs[0]],
70
- 'labels': [int(output[6]) for output in outputs[0]],
71
- }
72
- )
73
- return results
48
+ tensor = processor(normalized_images)
49
+ output = model(tensor)
50
+ return processor.postprocess(normalized_images, output, threshold=threshold)
74
51
 
75
52
 
76
53
  @pxt.udf
@@ -107,51 +84,31 @@ def yolo_to_coco(detections: dict) -> list:
107
84
  return result
108
85
 
109
86
 
110
- def _images_to_tensors(images: Iterable[PIL.Image.Image], exp: 'Exp') -> Iterator['torch.Tensor']:
111
- import torch
112
- from yolox.data import ValTransform # type: ignore[import-untyped]
113
-
114
- val_transform = ValTransform(legacy=False)
115
- for image in images:
116
- normalized_image = normalize_image_mode(image)
117
- image_transform, _ = val_transform(np.array(normalized_image), None, exp.test_size)
118
- yield torch.from_numpy(image_transform)
119
-
120
-
121
- def _lookup_model(model_id: str, device: str) -> tuple['YOLOX', 'Exp']:
122
- import torch
123
- from yolox.exp import get_exp
87
+ def _lookup_model(model_id: str, device: str) -> 'Yolox':
88
+ from yolox.models import Yolox
124
89
 
125
90
  key = (model_id, device)
126
- if key in _model_cache:
127
- return _model_cache[key]
91
+ if key not in _model_cache:
92
+ _model_cache[key] = Yolox.from_pretrained(model_id, device=device)
128
93
 
129
- weights_url = f'https://github.com/Megvii-BaseDetection/YOLOX/releases/download/0.1.1rc0/{model_id}.pth'
130
- weights_file = Path(f'{env.Env.get().tmp_dir}/{model_id}.pth')
131
- if not weights_file.exists():
132
- _logger.info(f'Downloading weights for YOLOX model {model_id}: from {weights_url} -> {weights_file}')
133
- urlretrieve(weights_url, weights_file)
94
+ return _model_cache[key]
134
95
 
135
- exp = get_exp(exp_name=model_id)
136
- model = exp.get_model().to(device)
137
96
 
138
- model.eval()
139
- model.head.training = False
140
- model.training = False
97
+ def _lookup_processor(model_id: str) -> 'YoloxProcessor':
98
+ from yolox.models import YoloxProcessor
141
99
 
142
- # Load in the weights from training
143
- weights = torch.load(weights_file, map_location=torch.device(device))
144
- model.load_state_dict(weights['model'])
100
+ if model_id not in _processor_cache:
101
+ _processor_cache[model_id] = YoloxProcessor(model_id)
145
102
 
146
- _model_cache[key] = (model, exp)
147
- return model, exp
103
+ return _processor_cache[model_id]
148
104
 
149
105
 
150
- _model_cache: dict[tuple[str, str], tuple['YOLOX', 'Exp']] = {}
106
+ _model_cache: dict[tuple[str, str], 'Yolox'] = {}
107
+ _processor_cache: dict[str, 'YoloxProcessor'] = {}
151
108
 
152
109
 
153
110
  __all__ = local_public_names(__name__)
154
111
 
155
112
 
156
- def __dir__():
113
+ def __dir__() -> list[str]:
157
114
  return __all__
@@ -252,7 +252,7 @@ def uda(
252
252
  ) -> Callable[[type[Aggregator]], AggregateFunction]: ...
253
253
 
254
254
 
255
- def uda(*args, **kwargs):
255
+ def uda(*args, **kwargs): # type: ignore[no-untyped-def]
256
256
  """Decorator for user-defined aggregate functions.
257
257
 
258
258
  The decorated class must inherit from Aggregator and implement the following methods:
@@ -1,6 +1,5 @@
1
1
  from __future__ import annotations
2
2
 
3
- import asyncio
4
3
  import inspect
5
4
  from typing import TYPE_CHECKING, Any, Callable, Optional, Sequence
6
5
  from uuid import UUID
@@ -127,12 +126,10 @@ class CallableFunction(Function):
127
126
  """
128
127
  assert self.is_batched
129
128
  assert not self.is_polymorphic
129
+ assert not self.is_async
130
130
  # Unpack the constant parameters
131
131
  constant_kwargs, batched_kwargs = self.create_batch_kwargs(kwargs)
132
- if inspect.iscoroutinefunction(self.py_fn):
133
- return asyncio.run(self.py_fn(*args, **constant_kwargs, **batched_kwargs))
134
- else:
135
- return self.py_fn(*args, **constant_kwargs, **batched_kwargs)
132
+ return self.py_fn(*args, **constant_kwargs, **batched_kwargs)
136
133
 
137
134
  def create_batch_kwargs(self, kwargs: dict[str, Any]) -> tuple[dict[str, Any], dict[str, list[Any]]]:
138
135
  """Converts kwargs containing lists into constant and batched kwargs in the format expected by a batched udf."""
@@ -1,6 +1,6 @@
1
1
  from typing import Any, Optional, Sequence
2
2
 
3
- from pixeltable import exceptions as excs, exprs
3
+ from pixeltable import exceptions as excs, exprs, type_system as ts
4
4
 
5
5
  from .function import Function
6
6
  from .signature import Signature
@@ -76,9 +76,25 @@ class ExprTemplateFunction(Function):
76
76
  arg_expr = arg
77
77
  arg_exprs[param_expr] = arg_expr
78
78
  result = result.substitute(arg_exprs)
79
- assert not result._contains(exprs.Variable)
80
79
  return result
81
80
 
81
+ def call_return_type(self, bound_args: dict[str, 'exprs.Expr']) -> ts.ColumnType:
82
+ """
83
+ The call_return_type of an ExprTemplateFunction is derived from the template expression's col_type after
84
+ substitution (unlike for UDFs, whose call_return_type is derived from an explicitly specified
85
+ conditional_return_type).
86
+ """
87
+ assert not self.is_polymorphic
88
+ template = self.template
89
+ with_defaults = bound_args.copy()
90
+ with_defaults.update(
91
+ {param_name: default for param_name, default in template.defaults.items() if param_name not in bound_args}
92
+ )
93
+ substituted_expr = self.template.expr.copy().substitute(
94
+ {template.param_exprs[name]: expr for name, expr in with_defaults.items()}
95
+ )
96
+ return substituted_expr.col_type
97
+
82
98
  def _docstring(self) -> Optional[str]:
83
99
  if isinstance(self.templates[0].expr, exprs.FunctionCall):
84
100
  return self.templates[0].expr.fn._docstring()
@@ -97,6 +113,10 @@ class ExprTemplateFunction(Function):
97
113
 
98
114
  @property
99
115
  def display_name(self) -> str:
116
+ if not self.self_name and isinstance(self.templates[0].expr, exprs.FunctionCall):
117
+ # In the common case where the templated expression is itself a FunctionCall,
118
+ # fall back on the display name of the underlying FunctionCall
119
+ return self.templates[0].expr.fn.display_name
100
120
  return self.self_name
101
121
 
102
122
  @property
@@ -10,7 +10,6 @@ from typing import TYPE_CHECKING, Any, Callable, Optional, Sequence, cast
10
10
  import sqlalchemy as sql
11
11
  from typing_extensions import Self
12
12
 
13
- import pixeltable as pxt
14
13
  import pixeltable.exceptions as excs
15
14
  import pixeltable.type_system as ts
16
15
 
@@ -155,7 +154,7 @@ class Function(ABC):
155
154
  """
156
155
  raise NotImplementedError()
157
156
 
158
- def __call__(self, *args: Any, **kwargs: Any) -> 'pxt.exprs.FunctionCall':
157
+ def __call__(self, *args: Any, **kwargs: Any) -> 'exprs.FunctionCall':
159
158
  from pixeltable import exprs
160
159
 
161
160
  args = [exprs.Expr.from_object(arg) for arg in args]
@@ -246,7 +245,7 @@ class Function(ABC):
246
245
  # `None` when any of its non-nullable inputs are `None`.
247
246
  for arg_name, arg in bound_args.items():
248
247
  param = self.signature.parameters[arg_name]
249
- if param.kind in {inspect.Parameter.VAR_POSITIONAL, inspect.Parameter.VAR_KEYWORD}:
248
+ if param.kind in (inspect.Parameter.VAR_POSITIONAL, inspect.Parameter.VAR_KEYWORD):
250
249
  continue
251
250
  if arg.col_type.nullable and not param.col_type.nullable:
252
251
  return_type = return_type.copy(nullable=True)
@@ -385,10 +384,10 @@ class Function(ABC):
385
384
  else:
386
385
  var = exprs.Variable(name, param.col_type)
387
386
  bindings[name] = var
388
- if args_ok and param.kind in {
387
+ if args_ok and param.kind in (
389
388
  inspect.Parameter.POSITIONAL_ONLY,
390
389
  inspect.Parameter.POSITIONAL_OR_KEYWORD,
391
- }:
390
+ ):
392
391
  template_args.append(var)
393
392
  else:
394
393
  template_kwargs[name] = var
@@ -31,7 +31,7 @@ class FunctionRegistry:
31
31
  cls._instance = FunctionRegistry()
32
32
  return cls._instance
33
33
 
34
- def __init__(self):
34
+ def __init__(self) -> None:
35
35
  self.stored_fns_by_id: dict[UUID, Function] = {}
36
36
  self.module_fns: dict[str, Function] = {} # fqn -> Function
37
37
  self.type_methods: dict[ts.ColumnType.Type, dict[str, Function]] = {}
@@ -253,7 +253,7 @@ class Signature:
253
253
  continue # skip 'self' or 'cls' parameter
254
254
  if param.name in cls.SPECIAL_PARAM_NAMES:
255
255
  raise excs.Error(f'{param.name!r} is a reserved parameter name')
256
- if param.kind in {inspect.Parameter.VAR_POSITIONAL, inspect.Parameter.VAR_KEYWORD}:
256
+ if param.kind in (inspect.Parameter.VAR_POSITIONAL, inspect.Parameter.VAR_KEYWORD):
257
257
  parameters.append(Parameter(param.name, col_type=None, kind=param.kind))
258
258
  continue
259
259
 
pixeltable/func/tools.py CHANGED
@@ -51,10 +51,10 @@ class Tool(pydantic.BaseModel):
51
51
  # The output of `tool_calls` must be a dict in standardized tool invocation format:
52
52
  # {tool_name: [{'args': {name1: value1, name2: value2, ...}}, ...], ...}
53
53
  def invoke(self, tool_calls: 'exprs.Expr') -> 'exprs.Expr':
54
- from pixeltable import exprs
54
+ import pixeltable.functions as pxtf
55
55
 
56
56
  func_name = self.name or self.fn.name
57
- return exprs.JsonMapper(tool_calls[func_name]['*'], self.__invoke_kwargs(exprs.RELATIVE_PATH_ROOT.args))
57
+ return pxtf.map(tool_calls[func_name]['*'], lambda x: self.__invoke_kwargs(x.args))
58
58
 
59
59
  def __invoke_kwargs(self, kwargs: 'exprs.Expr') -> 'exprs.FunctionCall':
60
60
  kwargs = {param.name: self.__extract_tool_arg(param, kwargs) for param in self.parameters.values()}
pixeltable/func/udf.py CHANGED
@@ -43,7 +43,7 @@ def udf(
43
43
  ) -> ExprTemplateFunction: ...
44
44
 
45
45
 
46
- def udf(*args, **kwargs):
46
+ def udf(*args, **kwargs): # type: ignore[no-untyped-def]
47
47
  """A decorator to create a Function from a function definition.
48
48
 
49
49
  Examples:
@@ -79,7 +79,7 @@ def udf(*args, **kwargs):
79
79
  if len(args) > 0:
80
80
  raise excs.Error('Unexpected @udf decorator arguments.')
81
81
 
82
- def decorator(decorated_fn: Callable):
82
+ def decorator(decorated_fn: Callable) -> CallableFunction:
83
83
  return make_function(
84
84
  decorated_fn,
85
85
  batch_size=batch_size,
@@ -24,10 +24,10 @@ from . import (
24
24
  vision,
25
25
  whisper,
26
26
  )
27
- from .globals import count, max, mean, min, sum
27
+ from .globals import count, map, max, mean, min, sum
28
28
 
29
29
  __all__ = local_public_names(__name__, exclude=['globals']) + local_public_names(globals.__name__)
30
30
 
31
31
 
32
- def __dir__():
32
+ def __dir__() -> list[str]:
33
33
  return __all__
@@ -39,7 +39,7 @@ def _anthropic_client() -> 'anthropic.AsyncAnthropic':
39
39
 
40
40
 
41
41
  class AnthropicRateLimitsInfo(env.RateLimitsInfo):
42
- def __init__(self):
42
+ def __init__(self) -> None:
43
43
  super().__init__(self._get_request_resources)
44
44
 
45
45
  def _get_request_resources(self, messages: dict, max_tokens: int) -> dict[str, int]:
@@ -236,5 +236,5 @@ def _opt(arg: _T) -> Union[_T, 'anthropic.NotGiven']:
236
236
  __all__ = local_public_names(__name__)
237
237
 
238
238
 
239
- def __dir__():
239
+ def __dir__() -> list[str]:
240
240
  return __all__
@@ -26,5 +26,5 @@ def get_metadata(audio: pxt.Audio) -> dict:
26
26
  __all__ = local_public_names(__name__)
27
27
 
28
28
 
29
- def __dir__():
29
+ def __dir__() -> list[str]:
30
30
  return __all__
@@ -117,5 +117,5 @@ async def chat_completions(
117
117
  __all__ = local_public_names(__name__)
118
118
 
119
119
 
120
- def __dir__():
120
+ def __dir__() -> list[str]:
121
121
  return __all__
@@ -131,5 +131,5 @@ async def chat_completions(
131
131
  __all__ = local_public_names(__name__)
132
132
 
133
133
 
134
- def __dir__():
134
+ def __dir__() -> list[str]:
135
135
  return __all__
@@ -1,15 +1,14 @@
1
1
  import builtins
2
2
  import typing
3
-
4
- from typing import _GenericAlias # type: ignore[attr-defined] # isort: skip
5
- from typing import Optional, Union
3
+ from typing import Any, Callable, Optional, Union
6
4
 
7
5
  import sqlalchemy as sql
8
6
 
9
- import pixeltable.type_system as ts
10
- from pixeltable import exprs, func
7
+ from pixeltable import exceptions as excs, exprs, func, type_system as ts
11
8
  from pixeltable.utils.code import local_public_names
12
9
 
10
+ from typing import _GenericAlias # type: ignore[attr-defined] # isort: skip
11
+
13
12
 
14
13
  # TODO: remove and replace calls with astype()
15
14
  def cast(expr: exprs.Expr, target_type: Union[ts.ColumnType, type, _GenericAlias]) -> exprs.Expr:
@@ -24,7 +23,7 @@ T = typing.TypeVar('T')
24
23
  class sum(func.Aggregator, typing.Generic[T]):
25
24
  """Sums the selected integers or floats."""
26
25
 
27
- def __init__(self):
26
+ def __init__(self) -> None:
28
27
  self.sum: T = None
29
28
 
30
29
  def update(self, val: T) -> None:
@@ -68,7 +67,7 @@ def _(val: sql.ColumnElement) -> Optional[sql.ColumnElement]:
68
67
  ),
69
68
  )
70
69
  class count(func.Aggregator, typing.Generic[T]):
71
- def __init__(self):
70
+ def __init__(self) -> None:
72
71
  self.count = 0
73
72
 
74
73
  def update(self, val: T) -> None:
@@ -89,7 +88,7 @@ def _(val: sql.ColumnElement) -> Optional[sql.ColumnElement]:
89
88
  type_substitutions=tuple({T: Optional[t]} for t in (str, int, float, bool, ts.Timestamp)), # type: ignore[misc]
90
89
  )
91
90
  class min(func.Aggregator, typing.Generic[T]):
92
- def __init__(self):
91
+ def __init__(self) -> None:
93
92
  self.val: T = None
94
93
 
95
94
  def update(self, val: T) -> None:
@@ -119,7 +118,7 @@ def _(val: sql.ColumnElement) -> Optional[sql.ColumnElement]:
119
118
  type_substitutions=tuple({T: Optional[t]} for t in (str, int, float, bool, ts.Timestamp)), # type: ignore[misc]
120
119
  )
121
120
  class max(func.Aggregator, typing.Generic[T]):
122
- def __init__(self):
121
+ def __init__(self) -> None:
123
122
  self.val: T = None
124
123
 
125
124
  def update(self, val: T) -> None:
@@ -144,7 +143,7 @@ def _(val: sql.ColumnElement) -> Optional[sql.ColumnElement]:
144
143
 
145
144
  @func.uda(type_substitutions=({T: Optional[int]}, {T: Optional[float]})) # type: ignore[misc]
146
145
  class mean(func.Aggregator, typing.Generic[T]):
147
- def __init__(self):
146
+ def __init__(self) -> None:
148
147
  self.sum: T = None
149
148
  self.count = 0
150
149
 
@@ -168,8 +167,20 @@ def _(val: sql.ColumnElement) -> Optional[sql.ColumnElement]:
168
167
  return sql.sql.func.avg(val)
169
168
 
170
169
 
170
+ def map(expr: exprs.Expr, fn: Callable[[exprs.Expr], Any]) -> exprs.Expr:
171
+ target_expr: exprs.Expr
172
+ try:
173
+ target_expr = exprs.Expr.from_object(fn(exprs.json_path.RELATIVE_PATH_ROOT))
174
+ except Exception as e:
175
+ raise excs.Error(
176
+ 'Failed to evaluate map function. '
177
+ '(The `fn` argument to `map()` must produce a valid Pixeltable expression.)'
178
+ ) from e
179
+ return exprs.JsonMapper(expr, target_expr)
180
+
181
+
171
182
  __all__ = local_public_names(__name__)
172
183
 
173
184
 
174
- def __dir__():
185
+ def __dir__() -> list[str]:
175
186
  return __all__
@@ -493,5 +493,5 @@ _processor_cache: dict[tuple[str, Callable], Any] = {}
493
493
  __all__ = local_public_names(__name__)
494
494
 
495
495
 
496
- def __dir__():
496
+ def __dir__() -> list[str]:
497
497
  return __all__
@@ -426,5 +426,5 @@ def mode(self: PIL.Image.Image) -> str:
426
426
  __all__ = local_public_names(__name__)
427
427
 
428
428
 
429
- def __dir__():
429
+ def __dir__() -> list[str]:
430
430
  return __all__
@@ -37,5 +37,5 @@ class make_list(pxt.Aggregator):
37
37
  __all__ = local_public_names(__name__)
38
38
 
39
39
 
40
- def __dir__():
40
+ def __dir__() -> list[str]:
41
41
  return __all__
@@ -100,5 +100,5 @@ _IS_GPU_AVAILABLE: Optional[bool] = None
100
100
  __all__ = local_public_names(__name__)
101
101
 
102
102
 
103
- def __dir__():
103
+ def __dir__() -> list[str]:
104
104
  return __all__
@@ -63,5 +63,5 @@ def _(self: sql.ColumnElement, digits: Optional[sql.ColumnElement] = None) -> sq
63
63
  __all__ = local_public_names(__name__)
64
64
 
65
65
 
66
- def __dir__():
66
+ def __dir__() -> list[str]:
67
67
  return __all__
@@ -193,5 +193,5 @@ def _opt(arg: Optional[_T]) -> Union[_T, 'mistralai.types.basemodel.Unset']:
193
193
  __all__ = local_public_names(__name__)
194
194
 
195
195
 
196
- def __dir__():
196
+ def __dir__() -> list[str]:
197
197
  return __all__
@@ -129,5 +129,5 @@ def embed(
129
129
  __all__ = local_public_names(__name__)
130
130
 
131
131
 
132
- def __dir__():
132
+ def __dir__() -> list[str]:
133
133
  return __all__
@@ -122,7 +122,7 @@ class OpenAIRateLimitsInfo(env.RateLimitsInfo):
122
122
  _header_duration_pattern = re.compile(r'(?:(\d+)d)?(?:(\d+)h)?(?:(\d+)ms)|(?:(\d+)m)?(?:([\d.]+)s)?')
123
123
 
124
124
 
125
- def _parse_header_duration(duration_str):
125
+ def _parse_header_duration(duration_str: str) -> datetime.timedelta:
126
126
  match = _header_duration_pattern.match(duration_str)
127
127
  if not match:
128
128
  raise ValueError('Invalid duration format')
@@ -837,5 +837,5 @@ def _opt(arg: _T) -> Union[_T, 'openai.NotGiven']:
837
837
  __all__ = local_public_names(__name__)
838
838
 
839
839
 
840
- def __dir__():
840
+ def __dir__() -> list[str]:
841
841
  return __all__
@@ -69,5 +69,5 @@ async def run(input: dict[str, Any], *, ref: str) -> dict[str, Any]:
69
69
  __all__ = local_public_names(__name__)
70
70
 
71
71
 
72
- def __dir__():
72
+ def __dir__() -> list[str]:
73
73
  return __all__
@@ -675,5 +675,5 @@ def zfill(self: str, width: int) -> str:
675
675
  __all__ = local_public_names(__name__)
676
676
 
677
677
 
678
- def __dir__():
678
+ def __dir__() -> list[str]:
679
679
  return __all__
@@ -312,5 +312,5 @@ def posix_timestamp(self: datetime) -> float:
312
312
  __all__ = local_public_names(__name__)
313
313
 
314
314
 
315
- def __dir__():
315
+ def __dir__() -> list[str]:
316
316
  return __all__
@@ -298,5 +298,5 @@ async def image_generations(
298
298
  __all__ = local_public_names(__name__)
299
299
 
300
300
 
301
- def __dir__():
301
+ def __dir__() -> list[str]:
302
302
  return __all__
@@ -21,7 +21,7 @@ def normalize_image_mode(image: PIL.Image.Image) -> PIL.Image.Image:
21
21
  Converts grayscale images to 3-channel for compatibility with models that only work with
22
22
  multichannel input.
23
23
  """
24
- if image.mode in {'1', 'L'}:
24
+ if image.mode in ('1', 'L'):
25
25
  return image.convert('RGB')
26
26
  if image.mode == 'LA':
27
27
  return image.convert('RGBA')
@@ -143,7 +143,7 @@ def _get_metadata(path: str) -> dict:
143
143
 
144
144
 
145
145
  def __get_stream_metadata(stream: av.stream.Stream) -> dict:
146
- if stream.type not in {'audio', 'video'}:
146
+ if stream.type not in ('audio', 'video'):
147
147
  return {'type': stream.type} # Currently unsupported
148
148
 
149
149
  codec_context = stream.codec_context
@@ -190,5 +190,5 @@ def __get_stream_metadata(stream: av.stream.Stream) -> dict:
190
190
  __all__ = local_public_names(__name__)
191
191
 
192
192
 
193
- def __dir__():
193
+ def __dir__() -> list[str]:
194
194
  return __all__
@@ -238,7 +238,7 @@ class mean_ap(pxt.Aggregator):
238
238
  - A `dict[int, float]` mapping each label class to an average precision (AP) value for that class.
239
239
  """
240
240
 
241
- def __init__(self):
241
+ def __init__(self) -> None:
242
242
  self.class_tpfp: dict[int, list[dict]] = defaultdict(list)
243
243
 
244
244
  def update(self, eval_dicts: list[dict]) -> None:
@@ -394,5 +394,5 @@ def draw_bounding_boxes(
394
394
  __all__ = local_public_names(__name__)
395
395
 
396
396
 
397
- def __dir__():
397
+ def __dir__() -> list[str]:
398
398
  return __all__