pixeltable 0.2.23__py3-none-any.whl → 0.2.25__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 CHANGED
@@ -1,3 +1,3 @@
1
1
  # These version placeholders will be replaced during build.
2
- __version__ = "0.2.23"
3
- __version_tuple__ = (0, 2, 23)
2
+ __version__ = "0.2.25"
3
+ __version_tuple__ = (0, 2, 25)
@@ -28,6 +28,7 @@ from .globals import _ROWID_COLUMN_NAME, UpdateStatus, is_system_column_name, is
28
28
  from .schema_object import SchemaObject
29
29
  from .table_version import TableVersion
30
30
  from .table_version_path import TableVersionPath
31
+ from ..exprs import ColumnRef
31
32
 
32
33
  if TYPE_CHECKING:
33
34
  import torch.utils.data
@@ -199,6 +200,7 @@ class Table(SchemaObject):
199
200
  ) -> 'pxt.dataframe.DataFrameResultSet':
200
201
  """Return rows from this table.
201
202
  """
203
+ self._check_is_dropped()
202
204
  return self._df().show(*args, **kwargs)
203
205
 
204
206
  def head(
@@ -298,6 +300,10 @@ class Table(SchemaObject):
298
300
  return self._description_html()._repr_html_() # type: ignore[attr-defined]
299
301
 
300
302
  def _drop(self) -> None:
303
+ cat = catalog.Catalog.get()
304
+ # verify all dependents are deleted by now
305
+ for dep in cat.tbl_dependents[self._id]:
306
+ assert dep._is_dropped
301
307
  self._check_is_dropped()
302
308
  self._tbl_version.drop()
303
309
  self._is_dropped = True
@@ -598,31 +604,49 @@ class Table(SchemaObject):
598
604
  cls._verify_column(col, column_names)
599
605
  column_names.add(col.name)
600
606
 
601
- def drop_column(self, name: str) -> None:
607
+ def __check_column_name_exists(self, column_name: str, include_bases: bool = False) -> None:
608
+ col = self._tbl_version_path.get_column(column_name, include_bases)
609
+ if col is None:
610
+ raise excs.Error(f'Column {column_name!r} unknown')
611
+
612
+ def __check_column_ref_exists(self, col_ref: ColumnRef, include_bases: bool = False) -> None:
613
+ exists = self._tbl_version_path.has_column(col_ref.col, include_bases)
614
+ if not exists:
615
+ raise excs.Error(f'Unknown column: {col_ref.col.qualified_name}')
616
+
617
+ def drop_column(self, column: Union[str, ColumnRef]) -> None:
602
618
  """Drop a column from the table.
603
619
 
604
620
  Args:
605
- name: The name of the column to drop.
621
+ column: The name or reference of the column to drop.
606
622
 
607
623
  Raises:
608
624
  Error: If the column does not exist or if it is referenced by a dependent computed column.
609
625
 
610
626
  Examples:
611
- Drop the column `col` from the table `my_table`:
627
+ Drop the column `col` from the table `my_table` by column name:
612
628
 
613
629
  >>> tbl = pxt.get_table('my_table')
614
630
  ... tbl.drop_column('col')
631
+
632
+ Drop the column `col` from the table `my_table` by column reference:
633
+
634
+ >>> tbl = pxt.get_table('my_table')
635
+ ... tbl.drop_column(tbl.col)
615
636
  """
616
637
  self._check_is_dropped()
617
-
618
- if name not in self._tbl_version.cols_by_name:
619
- raise excs.Error(f'Unknown column: {name}')
620
- col = self._tbl_version.cols_by_name[name]
638
+ col: Column = None
639
+ if isinstance(column, str):
640
+ self.__check_column_name_exists(column)
641
+ col = self._tbl_version.cols_by_name[column]
642
+ else:
643
+ self.__check_column_ref_exists(column)
644
+ col = column.col
621
645
 
622
646
  dependent_user_cols = [c for c in col.dependent_cols if c.name is not None]
623
647
  if len(dependent_user_cols) > 0:
624
648
  raise excs.Error(
625
- f'Cannot drop column `{name}` because the following columns depend on it:\n'
649
+ f'Cannot drop column `{col.name}` because the following columns depend on it:\n'
626
650
  f'{", ".join(c.name for c in dependent_user_cols)}'
627
651
  )
628
652
 
@@ -640,7 +664,7 @@ class Table(SchemaObject):
640
664
  for view, store in dependent_stores
641
665
  ]
642
666
  raise excs.Error(
643
- f'Cannot drop column `{name}` because the following external stores depend on it:\n'
667
+ f'Cannot drop column `{col.name}` because the following external stores depend on it:\n'
644
668
  f'{", ".join(dependent_store_names)}'
645
669
  )
646
670
 
@@ -666,7 +690,7 @@ class Table(SchemaObject):
666
690
  self._tbl_version.rename_column(old_name, new_name)
667
691
 
668
692
  def add_embedding_index(
669
- self, col_name: str, *, idx_name: Optional[str] = None,
693
+ self, column: Union[str, ColumnRef], *, idx_name: Optional[str] = None,
670
694
  string_embed: Optional[pxt.Function] = None, image_embed: Optional[pxt.Function] = None,
671
695
  metric: str = 'cosine'
672
696
  ) -> None:
@@ -680,7 +704,7 @@ class Table(SchemaObject):
680
704
  of text over an image column.
681
705
 
682
706
  Args:
683
- col_name: The name of column to index; must be a `String` or `Image` column.
707
+ column: The name of, or reference to, the column to index; must be a `String` or `Image` column.
684
708
  idx_name: The name of index. If not specified, a name such as `'idx0'` will be generated automatically.
685
709
  If specified, the name must be unique for this table.
686
710
  string_embed: A function to embed text; required if the column is a `String` column.
@@ -692,11 +716,15 @@ class Table(SchemaObject):
692
716
  Error: If an index with that name already exists for the table, or if the specified column does not exist.
693
717
 
694
718
  Examples:
695
- Add an index to the `img` column of the table `my_table`:
719
+ Add an index to the `img` column of the table `my_table` by column name:
696
720
 
697
721
  >>> tbl = pxt.get_table('my_table')
698
722
  ... tbl.add_embedding_index('img', image_embed=my_image_func)
699
723
 
724
+ Add an index to the `img` column of the table `my_table` by column reference:
725
+ >>> tbl = pxt.get_table('my_table')
726
+ ... tbl.add_embedding_index(tbl.img, image_embed=my_image_func)
727
+
700
728
  Add another index to the `img` column, using the inner product as the distance metric,
701
729
  and with a specific name; `string_embed` is also specified in order to search with text:
702
730
 
@@ -707,13 +735,28 @@ class Table(SchemaObject):
707
735
  ... string_embed=my_string_func,
708
736
  ... metric='ip'
709
737
  ... )
738
+
739
+ Alternatively:
740
+
741
+ >>> tbl.add_embedding_index(
742
+ ... tbl.img,
743
+ ... idx_name='clip_idx',
744
+ ... image_embed=my_image_func,
745
+ ... string_embed=my_string_func,
746
+ ... metric='ip'
747
+ ... )
710
748
  """
711
749
  if self._tbl_version_path.is_snapshot():
712
750
  raise excs.Error('Cannot add an index to a snapshot')
713
751
  self._check_is_dropped()
714
- col = self._tbl_version_path.get_column(col_name, include_bases=True)
715
- if col is None:
716
- raise excs.Error(f'Column {col_name} unknown')
752
+ col: Column
753
+ if isinstance(column, str):
754
+ self.__check_column_name_exists(column, include_bases=True)
755
+ col = self._tbl_version_path.get_column(column, include_bases=True)
756
+ else:
757
+ self.__check_column_ref_exists(column, include_bases=True)
758
+ col = column.col
759
+
717
760
  if idx_name is not None and idx_name in self._tbl_version.idxs_by_name:
718
761
  raise excs.Error(f'Duplicate index name: {idx_name}')
719
762
  from pixeltable.index import EmbeddingIndex
@@ -724,82 +767,129 @@ class Table(SchemaObject):
724
767
  # TODO: how to deal with exceptions here? drop the index and raise?
725
768
  FileCache.get().emit_eviction_warnings()
726
769
 
727
- def drop_embedding_index(self, *, column_name: Optional[str] = None, idx_name: Optional[str] = None) -> None:
770
+ def drop_embedding_index(
771
+ self, *,
772
+ column: Union[str, ColumnRef, None] = None,
773
+ idx_name: Optional[str] = None) -> None:
728
774
  """
729
775
  Drop an embedding index from the table. Either a column name or an index name (but not both) must be
730
- specified. If a column name is specified, it must be a column containing exactly one embedding index;
731
- otherwise the specific index name must be provided instead.
776
+ specified. If a column name or reference is specified, it must be a column containing exactly one
777
+ embedding index; otherwise the specific index name must be provided instead.
732
778
 
733
779
  Args:
734
- column_name: The name of the column from which to drop the index. Invalid if the column has multiple
735
- embedding indices.
780
+ column: The name of, or reference to, the column from which to drop the index.
781
+ The column must have only one embedding index.
736
782
  idx_name: The name of the index to drop.
737
783
 
738
784
  Raises:
739
- Error: If `column_name` is specified, but the column does not exist, or it contains no embedding
785
+ Error: If `column` is specified, but the column does not exist, or it contains no embedding
740
786
  indices or multiple embedding indices.
741
787
  Error: If `idx_name` is specified, but the index does not exist or is not an embedding index.
742
788
 
743
789
  Examples:
744
- Drop the embedding index on the `img` column of the table `my_table`:
790
+ Drop the embedding index on the `img` column of the table `my_table` by column name:
745
791
 
746
792
  >>> tbl = pxt.get_table('my_table')
747
- ... tbl.drop_embedding_index(column_name='img')
748
- """
749
- self._drop_index(column_name=column_name, idx_name=idx_name, _idx_class=index.EmbeddingIndex)
793
+ ... tbl.drop_embedding_index(column='img')
794
+
795
+ Drop the embedding index on the `img` column of the table `my_table` by column reference:
796
+
797
+ >>> tbl = pxt.get_table('my_table')
798
+ ... tbl.drop_embedding_index(column=tbl.img)
799
+
800
+ Drop the embedding index `idx1` of the table `my_table` by index name:
801
+ >>> tbl = pxt.get_table('my_table')
802
+ ... tbl.drop_embedding_index(idx_name='idx1')
750
803
 
751
- def drop_index(self, *, column_name: Optional[str] = None, idx_name: Optional[str] = None) -> None:
804
+ """
805
+ if (column is None) == (idx_name is None):
806
+ raise excs.Error("Exactly one of 'column' or 'idx_name' must be provided")
807
+
808
+ col: Column = None
809
+ if idx_name is None:
810
+ if isinstance(column, str):
811
+ self.__check_column_name_exists(column, include_bases=True)
812
+ col = self._tbl_version_path.get_column(column, include_bases=True)
813
+ else:
814
+ self.__check_column_ref_exists(column, include_bases=True)
815
+ col = column.col
816
+ assert col is not None
817
+ self._drop_index(col=col, idx_name=idx_name, _idx_class=index.EmbeddingIndex)
818
+
819
+ def drop_index(
820
+ self, *,
821
+ column: Union[str, ColumnRef, None] = None,
822
+ idx_name: Optional[str] = None) -> None:
752
823
  """
753
824
  Drop an index from the table. Either a column name or an index name (but not both) must be
754
- specified. If a column name is specified, it must be a column containing exactly one index;
825
+ specified. If a column name or reference is specified, it must be a column containing exactly one index;
755
826
  otherwise the specific index name must be provided instead.
756
827
 
757
828
  Args:
758
- column_name: The name of the column from which to drop the index. Invalid if the column has multiple
759
- indices.
829
+ column: The name of, or reference to, the column from which to drop the index.
830
+ The column must have only one embedding index.
760
831
  idx_name: The name of the index to drop.
761
832
 
762
833
  Raises:
763
- Error: If `column_name` is specified, but the column does not exist, or it contains no
834
+ Error: If `column` is specified, but the column does not exist, or it contains no
764
835
  indices or multiple indices.
765
836
  Error: If `idx_name` is specified, but the index does not exist.
766
837
 
767
838
  Examples:
768
- Drop the index on the `img` column of the table `my_table`:
839
+ Drop the index on the `img` column of the table `my_table` by column name:
769
840
 
770
841
  >>> tbl = pxt.get_table('my_table')
771
842
  ... tbl.drop_index(column_name='img')
843
+
844
+ Drop the index on the `img` column of the table `my_table` by column reference:
845
+
846
+ >>> tbl = pxt.get_table('my_table')
847
+ ... tbl.drop_index(tbl.img)
848
+
849
+ Drop the index `idx1` of the table `my_table` by index name:
850
+ >>> tbl = pxt.get_table('my_table')
851
+ ... tbl.drop_index(idx_name='idx1')
852
+
772
853
  """
773
- self._drop_index(column_name=column_name, idx_name=idx_name)
854
+ if (column is None) == (idx_name is None):
855
+ raise excs.Error("Exactly one of 'column' or 'idx_name' must be provided")
856
+
857
+ col: Column = None
858
+ if idx_name is None:
859
+ if isinstance(column, str):
860
+ self.__check_column_name_exists(column, include_bases=True)
861
+ col = self._tbl_version_path.get_column(column, include_bases=True)
862
+ else:
863
+ self.__check_column_ref_exists(column, include_bases=True)
864
+ col = column.col
865
+ assert col is not None
866
+ self._drop_index(col=col, idx_name=idx_name)
774
867
 
775
868
  def _drop_index(
776
- self, *, column_name: Optional[str] = None, idx_name: Optional[str] = None,
869
+ self, *, col: Optional[Column] = None,
870
+ idx_name: Optional[str] = None,
777
871
  _idx_class: Optional[type[index.IndexBase]] = None
778
872
  ) -> None:
779
873
  if self._tbl_version_path.is_snapshot():
780
874
  raise excs.Error('Cannot drop an index from a snapshot')
781
875
  self._check_is_dropped()
782
- if (column_name is None) == (idx_name is None):
783
- raise excs.Error("Exactly one of 'column_name' or 'idx_name' must be provided")
876
+ assert (col is None) != (idx_name is None)
784
877
 
785
878
  if idx_name is not None:
786
879
  if idx_name not in self._tbl_version.idxs_by_name:
787
880
  raise excs.Error(f'Index {idx_name!r} does not exist')
788
881
  idx_id = self._tbl_version.idxs_by_name[idx_name].id
789
882
  else:
790
- col = self._tbl_version_path.get_column(column_name, include_bases=True)
791
- if col is None:
792
- raise excs.Error(f'Column {column_name!r} unknown')
793
883
  if col.tbl.id != self._tbl_version.id:
794
884
  raise excs.Error(
795
- f'Column {column_name!r}: cannot drop index from column that belongs to base ({col.tbl.name}!r)')
885
+ f'Column {col.name!r}: cannot drop index from column that belongs to base ({col.tbl.name}!r)')
796
886
  idx_info = [info for info in self._tbl_version.idxs_by_name.values() if info.col.id == col.id]
797
887
  if _idx_class is not None:
798
888
  idx_info = [info for info in idx_info if isinstance(info.idx, _idx_class)]
799
889
  if len(idx_info) == 0:
800
- raise excs.Error(f'Column {column_name!r} does not have an index')
890
+ raise excs.Error(f'Column {col.name!r} does not have an index')
801
891
  if len(idx_info) > 1:
802
- raise excs.Error(f"Column {column_name!r} has multiple indices; specify 'idx_name' instead")
892
+ raise excs.Error(f"Column {col.name!r} has multiple indices; specify 'idx_name' instead")
803
893
  idx_id = idx_info[0].id
804
894
  self._tbl_version.drop_index(idx_id)
805
895
 
@@ -194,6 +194,9 @@ class View(Table):
194
194
 
195
195
  def _drop(self) -> None:
196
196
  cat = catalog.Catalog.get()
197
+ # verify all dependents are deleted by now
198
+ for dep in cat.tbl_dependents[self._id]:
199
+ assert dep._is_dropped
197
200
  if self._snapshot_only:
198
201
  # there is not TableVersion to drop
199
202
  self._check_is_dropped()
@@ -58,6 +58,9 @@ class SimilarityExpr(Expr):
58
58
  def __str__(self) -> str:
59
59
  return f'{self.components[0]}.similarity({self.components[1]})'
60
60
 
61
+ def default_column_name(self) -> str:
62
+ return 'similarity'
63
+
61
64
  def sql_expr(self, _: SqlElementCache) -> Optional[sql.ColumnElement]:
62
65
  if not isinstance(self.components[1], Literal):
63
66
  raise excs.Error(f'similarity(): requires a string or a PIL.Image.Image object, not an expression')
@@ -3,16 +3,20 @@ from __future__ import annotations
3
3
  import abc
4
4
  import importlib
5
5
  import inspect
6
- from typing import Any, Callable, Optional
6
+ from typing import TYPE_CHECKING, Any, Callable, Optional
7
7
 
8
8
  import sqlalchemy as sql
9
9
 
10
10
  import pixeltable as pxt
11
+ import pixeltable.exceptions as excs
11
12
  import pixeltable.type_system as ts
12
13
 
13
14
  from .globals import resolve_symbol
14
15
  from .signature import Signature
15
16
 
17
+ if TYPE_CHECKING:
18
+ from .expr_template_function import ExprTemplateFunction
19
+
16
20
 
17
21
  class Function(abc.ABC):
18
22
  """Base class for Pixeltable's function interface.
@@ -99,6 +103,38 @@ class Function(abc.ABC):
99
103
  self._conditional_return_type = fn
100
104
  return fn
101
105
 
106
+ def using(self, **kwargs: Any) -> 'ExprTemplateFunction':
107
+ from pixeltable import exprs
108
+
109
+ from .expr_template_function import ExprTemplateFunction
110
+
111
+ # Resolve each kwarg into a parameter binding
112
+ bindings: dict[str, exprs.Expr] = {}
113
+ for k, v in kwargs.items():
114
+ if k not in self.signature.parameters:
115
+ raise excs.Error(f'Unknown parameter: {k}')
116
+ param = self.signature.parameters[k]
117
+ expr = exprs.Expr.from_object(v)
118
+ if not param.col_type.is_supertype_of(expr.col_type):
119
+ raise excs.Error(f'Expected type `{param.col_type}` for parameter `{k}`; got `{expr.col_type}`')
120
+ bindings[k] = v # Use the original value, not the Expr (The Expr is only for validation)
121
+
122
+ residual_params = [
123
+ p for p in self.signature.parameters.values() if p.name not in bindings
124
+ ]
125
+
126
+ # Bind each remaining parameter to a like-named variable
127
+ for param in residual_params:
128
+ bindings[param.name] = exprs.Variable(param.name, param.col_type)
129
+
130
+ call = exprs.FunctionCall(self, bindings)
131
+
132
+ # Construct the (n-k)-ary signature of the new function. We use `call.col_type` for this, rather than
133
+ # `self.signature.return_type`, because the return type of the new function may be specialized via a
134
+ # conditional return type.
135
+ new_signature = Signature(call.col_type, residual_params, self.signature.is_batched)
136
+ return ExprTemplateFunction(call, new_signature)
137
+
102
138
  @abc.abstractmethod
103
139
  def exec(self, *args: Any, **kwargs: Any) -> Any:
104
140
  """Execute the function with the given arguments and return the result."""
@@ -91,6 +91,7 @@ class Signature:
91
91
  self.parameters_by_pos = parameters.copy()
92
92
  self.constant_parameters = [p for p in parameters if not p.is_batched]
93
93
  self.batched_parameters = [p for p in parameters if p.is_batched]
94
+ self.required_parameters = [p for p in parameters if not p.has_default()]
94
95
  self.py_signature = inspect.Signature([p.to_py_param() for p in self.parameters_by_pos])
95
96
 
96
97
  def get_return_type(self) -> ts.ColumnType:
@@ -426,12 +426,10 @@ def speech2text_for_conditional_generation(
426
426
  env.Env.get().require_package('torchaudio')
427
427
  env.Env.get().require_package('sentencepiece')
428
428
  device = resolve_torch_device('auto', allow_mps=False) # Doesn't seem to work on 'mps'; use 'cpu' instead
429
- import librosa
430
429
  import torch
430
+ import torchaudio # type: ignore[import-untyped]
431
431
  from transformers import Speech2TextForConditionalGeneration, Speech2TextProcessor
432
432
 
433
- # facebook/s2t-small-librispeech-asr
434
- # facebook/s2t-small-mustc-en-fr-st
435
433
  model = _lookup_model(model_id, Speech2TextForConditionalGeneration.from_pretrained, device=device)
436
434
  processor = _lookup_processor(model_id, Speech2TextProcessor.from_pretrained)
437
435
  assert isinstance(processor, Speech2TextProcessor)
@@ -445,12 +443,23 @@ def speech2text_for_conditional_generation(
445
443
 
446
444
  # Get the model's sampling rate. Default to 16 kHz (the standard) if not in config
447
445
  model_sampling_rate = getattr(model.config, 'sampling_rate', 16_000)
448
- waveform, sampling_rate = librosa.load(audio, sr=model_sampling_rate, mono=True)
446
+
447
+ waveform, sampling_rate = torchaudio.load(audio)
448
+
449
+ # Resample to the model's sampling rate, if necessary
450
+ if sampling_rate != model_sampling_rate:
451
+ waveform = torchaudio.transforms.Resample(sampling_rate, model_sampling_rate)(waveform)
452
+
453
+ # Average the channels to get a single-channel waveform as a 1D tensor (if the original waveform is already
454
+ # mono, this will simply squeeze the tensor)
455
+ assert waveform.dim() == 2
456
+ waveform = torch.mean(waveform, dim=0)
457
+ assert waveform.dim() == 1
449
458
 
450
459
  with torch.no_grad():
451
460
  inputs = processor(
452
461
  waveform,
453
- sampling_rate=sampling_rate,
462
+ sampling_rate=model_sampling_rate,
454
463
  return_tensors='pt'
455
464
  )
456
465
  generated_ids = model.generate(**inputs.to(device), forced_bos_token_id=forced_bos_token_id).to('cpu')
@@ -36,7 +36,6 @@ def chat_completions(
36
36
  temperature: Optional[float] = 0.7,
37
37
  top_p: Optional[float] = 1.0,
38
38
  max_tokens: Optional[int] = None,
39
- min_tokens: Optional[int] = None,
40
39
  stop: Optional[list[str]] = None,
41
40
  random_seed: Optional[int] = None,
42
41
  response_format: Optional[dict] = None,
@@ -75,7 +74,6 @@ def chat_completions(
75
74
  temperature=temperature,
76
75
  top_p=top_p,
77
76
  max_tokens=_opt(max_tokens),
78
- min_tokens=_opt(min_tokens),
79
77
  stop=stop,
80
78
  random_seed=_opt(random_seed),
81
79
  response_format=response_format, # type: ignore[arg-type]
pixeltable/globals.py CHANGED
@@ -296,31 +296,42 @@ def move(path: str, new_path: str) -> None:
296
296
  obj._move(new_p.name, new_dir._id)
297
297
 
298
298
 
299
- def drop_table(path: str, force: bool = False, ignore_errors: bool = False) -> None:
299
+ def drop_table(table: Union[str, catalog.Table], force: bool = False, ignore_errors: bool = False) -> None:
300
300
  """Drop a table, view, or snapshot.
301
301
 
302
302
  Args:
303
- path: Path to the [`Table`][pixeltable.Table].
303
+ table: Fully qualified name, or handle, of the table to be dropped.
304
304
  force: If `True`, will also drop all views and sub-views of this table.
305
305
  ignore_errors: If `True`, return silently if the table does not exist (without throwing an exception).
306
306
 
307
307
  Raises:
308
- Error: If the path does not exist or does not designate a table object, and `ignore_errors=False`.
308
+ Error: If the name does not exist or does not designate a table object, and `ignore_errors=False`.
309
309
 
310
310
  Examples:
311
- >>> pxt.drop_table('my_table')
311
+ Drop a table by its fully qualified name:
312
+ >>> pxt.drop_table('subdir.my_table')
313
+
314
+ Drop a table by its handle:
315
+ >>> t = pxt.get_table('subdir.my_table')
316
+ ... pxt.drop_table(t)
317
+
312
318
  """
313
319
  cat = Catalog.get()
314
- path_obj = catalog.Path(path)
315
- try:
316
- cat.paths.check_is_valid(path_obj, expected=catalog.Table)
317
- except Exception as e:
318
- if ignore_errors or force:
319
- _logger.info(f'Skipped table `{path}` (does not exist).')
320
- return
321
- else:
322
- raise e
323
- tbl = cat.paths[path_obj]
320
+ if isinstance(table, str):
321
+ tbl_path_obj = catalog.Path(table)
322
+ try:
323
+ cat.paths.check_is_valid(tbl_path_obj, expected=catalog.Table)
324
+ except Exception as e:
325
+ if ignore_errors or force:
326
+ _logger.info(f'Skipped table `{table}` (does not exist).')
327
+ return
328
+ else:
329
+ raise e
330
+ tbl = cat.paths[tbl_path_obj]
331
+ else:
332
+ tbl = table
333
+ tbl_path_obj = catalog.Path(tbl._path)
334
+
324
335
  assert isinstance(tbl, catalog.Table)
325
336
  if len(cat.tbl_dependents[tbl._id]) > 0:
326
337
  dependent_paths = [dep._path for dep in cat.tbl_dependents[tbl._id]]
@@ -328,10 +339,10 @@ def drop_table(path: str, force: bool = False, ignore_errors: bool = False) -> N
328
339
  for dependent_path in dependent_paths:
329
340
  drop_table(dependent_path, force=True)
330
341
  else:
331
- raise excs.Error(f'Table {path} has dependents: {", ".join(dependent_paths)}')
342
+ raise excs.Error(f'Table {tbl._path} has dependents: {", ".join(dependent_paths)}')
332
343
  tbl._drop()
333
- del cat.paths[path_obj]
334
- _logger.info(f'Dropped table `{path}`.')
344
+ del cat.paths[tbl_path_obj]
345
+ _logger.info(f'Dropped table `{tbl._path}`.')
335
346
 
336
347
 
337
348
  def list_tables(dir_path: str = '', recursive: bool = True) -> list[str]:
@@ -136,7 +136,12 @@ class EmbeddingIndex(IndexBase):
136
136
  """Validate the signature"""
137
137
  assert isinstance(embed_fn, func.Function)
138
138
  sig = embed_fn.signature
139
- if len(sig.parameters) != 1 or sig.parameters_by_pos[0].col_type.type_enum != expected_type:
139
+
140
+ # The embedding function must be a 1-ary function of the correct type. But it's ok if the function signature
141
+ # has more than one parameter, as long as it has at most one *required* parameter.
142
+ if (len(sig.parameters) == 0
143
+ or len(sig.required_parameters) > 1
144
+ or sig.parameters_by_pos[0].col_type.type_enum != expected_type):
140
145
  raise excs.Error(
141
146
  f'{name} must take a single {expected_type.name.lower()} parameter, but has signature {sig}')
142
147
 
@@ -1,5 +1,6 @@
1
1
  from .base import ComponentIterator
2
2
  from .document import DocumentSplitter
3
+ from .image import TileIterator
3
4
  from .string import StringSplitter
4
5
  from .video import FrameIterator
5
6
 
@@ -0,0 +1,100 @@
1
+ from typing import Any, Sequence
2
+
3
+ import PIL.Image
4
+
5
+ import pixeltable.exceptions as excs
6
+ import pixeltable.type_system as ts
7
+ from pixeltable.iterators.base import ComponentIterator
8
+
9
+
10
+ class TileIterator(ComponentIterator):
11
+ """
12
+ Iterator over tiles of an image. Each image will be divided into tiles of size `tile_size`, and the tiles will be
13
+ iterated over in row-major order (left-to-right, then top-to-bottom). An optional `overlap` parameter may be
14
+ specified. If the tiles do not exactly cover the image, then the rightmost and bottommost tiles will be padded with
15
+ blackspace, so that the output images all have the exact size `tile_size`.
16
+
17
+ Args:
18
+ image: Image to split into tiles.
19
+ tile_size: Size of each tile, as a pair of integers `[width, height]`.
20
+ overlap: Amount of overlap between adjacent tiles, as a pair of integers `[width, height]`.
21
+ """
22
+
23
+ __image: PIL.Image.Image
24
+ __tile_size: Sequence[int]
25
+ __overlap: Sequence[int]
26
+ __width: int
27
+ __height: int
28
+ __xlen: int
29
+ __ylen: int
30
+ __i: int
31
+ __j: int
32
+
33
+ def __init__(
34
+ self,
35
+ image: PIL.Image.Image,
36
+ *,
37
+ tile_size: tuple[int, int],
38
+ overlap: tuple[int, int] = (0, 0),
39
+ ):
40
+ if overlap[0] >= tile_size[0] or overlap[1] >= tile_size[1]:
41
+ raise excs.Error(f"overlap dimensions {overlap} are not strictly smaller than tile size {tile_size}")
42
+
43
+ self.__image = image
44
+ self.__image.load()
45
+ self.__tile_size = tile_size
46
+ self.__overlap = overlap
47
+ self.__width, self.__height = image.size
48
+ # Justification for this formula: let t = tile_size[0], o = overlap[0]. Then the values of w (= width) that
49
+ # exactly accommodate an integer number of tiles are t, 2t - o, 3t - 2o, 4t - 3o, ...
50
+ # This formula ensures that t, 2t - o, 3t - 2o, ... result in an xlen of 1, 2, 3, ...
51
+ # but t + 1, 2t - o + 1, 3t - 2o + 1, ... result in an xlen of 2, 3, 4, ...
52
+ self.__xlen = (self.__width - overlap[0] - 1) // (tile_size[0] - overlap[0]) + 1
53
+ self.__ylen = (self.__height - overlap[1] - 1) // (tile_size[1] - overlap[1]) + 1
54
+ self.__i = 0
55
+ self.__j = 0
56
+
57
+ def __next__(self) -> dict[str, Any]:
58
+ if self.__j >= self.__ylen:
59
+ raise StopIteration
60
+
61
+ x1 = self.__i * (self.__tile_size[0] - self.__overlap[0])
62
+ y1 = self.__j * (self.__tile_size[1] - self.__overlap[1])
63
+ # If x2 > self.__width, PIL does the right thing and pads the image with blackspace
64
+ x2 = x1 + self.__tile_size[0]
65
+ y2 = y1 + self.__tile_size[1]
66
+ tile = self.__image.crop((x1, y1, x2, y2))
67
+ result = {
68
+ 'tile': tile,
69
+ 'tile_coord': [self.__i, self.__j],
70
+ 'tile_box': [x1, y1, x2, y2]
71
+ }
72
+
73
+ self.__i += 1
74
+ if self.__i >= self.__xlen:
75
+ self.__i = 0
76
+ self.__j += 1
77
+ return result
78
+
79
+ def close(self) -> None:
80
+ pass
81
+
82
+ def set_pos(self, pos: int) -> None:
83
+ self.__j = pos // self.__xlen
84
+ self.__i = pos % self.__xlen
85
+
86
+ @classmethod
87
+ def input_schema(cls, *args: Any, **kwargs: Any) -> dict[str, ts.ColumnType]:
88
+ return {
89
+ 'image': ts.ImageType(),
90
+ 'tile_size': ts.JsonType(),
91
+ 'overlap': ts.JsonType(),
92
+ }
93
+
94
+ @classmethod
95
+ def output_schema(cls, *args: Any, **kwargs: Any) -> tuple[dict[str, ts.ColumnType], list[str]]:
96
+ return {
97
+ 'tile': ts.ImageType(),
98
+ 'tile_coord': ts.JsonType(),
99
+ 'tile_box': ts.JsonType(),
100
+ }, ['tile']
@@ -23,13 +23,13 @@ class FrameIterator(ComponentIterator):
23
23
  exact number of frames will be extracted. If neither is specified, then all frames will be extracted. The first
24
24
  frame of the video will always be extracted, and the remaining frames will be spaced as evenly as possible.
25
25
 
26
- Args:
27
- video: URL or path of the video to use for frame extraction.
28
- fps: Number of frames to extract per second of video. This may be a fractional value, such as 0.5.
29
- If omitted or set to 0.0, then the native framerate of the video will be used (all frames will be
30
- extracted). If `fps` is greater than the frame rate of the video, an error will be raised.
31
- num_frames: Exact number of frames to extract. The frames will be spaced as evenly as possible. If
32
- `num_frames` is greater than the number of frames in the video, all frames will be extracted.
26
+ Args:
27
+ video: URL or path of the video to use for frame extraction.
28
+ fps: Number of frames to extract per second of video. This may be a fractional value, such as 0.5.
29
+ If omitted or set to 0.0, then the native framerate of the video will be used (all frames will be
30
+ extracted). If `fps` is greater than the frame rate of the video, an error will be raised.
31
+ num_frames: Exact number of frames to extract. The frames will be spaced as evenly as possible. If
32
+ `num_frames` is greater than the number of frames in the video, all frames will be extracted.
33
33
  """
34
34
 
35
35
  # Input parameters
@@ -180,7 +180,6 @@ class FrameIterator(ComponentIterator):
180
180
  self.container.close()
181
181
 
182
182
  def set_pos(self, pos: int) -> None:
183
- """Seek to frame idx"""
184
183
  if pos == self.next_pos:
185
184
  return # already there
186
185
 
pixeltable/plan.py CHANGED
@@ -224,7 +224,7 @@ class Planner:
224
224
  """Creates a plan for TableVersion.insert()"""
225
225
  assert not tbl.is_view()
226
226
  # stored_cols: all cols we need to store, incl computed cols (and indices)
227
- stored_cols = [c for c in tbl.cols if c.is_stored]
227
+ stored_cols = [c for c in tbl.cols_by_id.values() if c.is_stored]
228
228
  assert len(stored_cols) > 0 # there needs to be something to store
229
229
  row_builder = exprs.RowBuilder([], stored_cols, [])
230
230
 
@@ -270,7 +270,10 @@ class Dumper:
270
270
  add_column('c6_to_string', t.c6.apply(json.dumps))
271
271
  add_column('c6_back_to_json', t[f'{col_prefix}_c6_to_string'].apply(json.loads))
272
272
 
273
- t.add_embedding_index(f'{col_prefix}_function_call', string_embed=embed_udf.clip_text_embed)
273
+ t.add_embedding_index(
274
+ f'{col_prefix}_function_call',
275
+ string_embed=pxt.functions.huggingface.clip_text.using(model_id='openai/clip-vit-base-patch32')
276
+ )
274
277
 
275
278
  # query()
276
279
  @t.query
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pixeltable
3
- Version: 0.2.23
3
+ Version: 0.2.25
4
4
  Summary: Pixeltable: The Multimodal AI Data Plane
5
5
  Author: Pixeltable, Inc.
6
6
  Author-email: contact@pixeltable.com
@@ -40,7 +40,7 @@ Description-Content-Type: text/markdown
40
40
  alt="Pixeltable" width="50%" />
41
41
  <br></br>
42
42
 
43
- <h2>AI Data Insfrastructure — Declarative, Multimodal, and Incremental</h2>
43
+ <h2>AI Data Infrastructure — Declarative, Multimodal, and Incremental</h2>
44
44
 
45
45
  [![License](https://img.shields.io/badge/License-Apache%202.0-0530AD.svg)](https://opensource.org/licenses/Apache-2.0)
46
46
  ![PyPI - Python Version](https://img.shields.io/pypi/pyversions/pixeltable?logo=python&logoColor=white&)
@@ -48,8 +48,8 @@ Description-Content-Type: text/markdown
48
48
  <br>
49
49
  [![tests status](https://github.com/pixeltable/pixeltable/actions/workflows/pytest.yml/badge.svg)](https://github.com/pixeltable/pixeltable/actions/workflows/pytest.yml)
50
50
  [![tests status](https://github.com/pixeltable/pixeltable/actions/workflows/nightly.yml/badge.svg)](https://github.com/pixeltable/pixeltable/actions/workflows/nightly.yml)
51
- [![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2Fpixeltable%2Fpixeltable.svg?type=shield&issueType=security)](https://app.fossa.com/projects/git%2Bgithub.com%2Fpixeltable%2Fpixeltable?ref=badge_shield&issueType=security)
52
51
  [![PyPI Package](https://img.shields.io/pypi/v/pixeltable?color=4D148C)](https://pypi.org/project/pixeltable/)
52
+ [![My Discord (1306431018890166272)](https://img.shields.io/badge/💬-Discord-%235865F2.svg)](https://discord.gg/QPyqFYx2UN)
53
53
  <a target="_blank" href="https://huggingface.co/Pixeltable">
54
54
  <img src="https://img.shields.io/badge/🤗-HF Space-FF7D04" alt="Visit our Hugging Face space"/>
55
55
  </a>
@@ -1,5 +1,5 @@
1
1
  pixeltable/__init__.py,sha256=gv2jvZ7H5tEjLear10E7hSH9jF5Mw6iSeryvRp88bOE,1391
2
- pixeltable/__version__.py,sha256=7KY-iWvvGLR_f-tQMjxiOTfMCj-z3KM_23EsiPZSTl8,114
2
+ pixeltable/__version__.py,sha256=xh8YRFxtPr-QeDur-WT8B-EmoDvFRdiWcybNMNDRN7E,114
3
3
  pixeltable/catalog/__init__.py,sha256=Ar6_F_6C7tkznIlCPBHVHDop5YssBDjKQr2NPQ21QCI,484
4
4
  pixeltable/catalog/catalog.py,sha256=tyDyI5wQw7vV6_FChrp9qgGCRClcjiSdW3eygYT0p9s,7849
5
5
  pixeltable/catalog/column.py,sha256=ezeKoGl6aBTzSZBihDA6vdETcvyCguAD4OsmrxWs73A,9595
@@ -10,10 +10,10 @@ pixeltable/catalog/named_function.py,sha256=W8vikP_3jMJ9pQQsksO2EfQAlaVxuQHBlo65
10
10
  pixeltable/catalog/path.py,sha256=QgccEi_QOfaKt8YsR2zLtd_z7z7QQkU_1kprJFi2SPQ,1677
11
11
  pixeltable/catalog/path_dict.py,sha256=4b9_Ax7Q8tkmoCYPaKNedpQkU17pE0oGDd2XB53eNZA,5979
12
12
  pixeltable/catalog/schema_object.py,sha256=qhpeeUPOYT5doDbsyUNBcPm5QzAQPCAsikqh1PQ6d1k,2226
13
- pixeltable/catalog/table.py,sha256=7UwLGydwfvzBoQXjqZdn8toPU6-6t0OySPFIiwj6KkA,48477
13
+ pixeltable/catalog/table.py,sha256=Ne9xdNOgLuMEE9xdFVNtVOXNwN-_2nzwxBiTN2ma8lI,52015
14
14
  pixeltable/catalog/table_version.py,sha256=W4yrvn-mF63CQ8cQGmjbjYTCf1xYuV6k6rI__ubJOIc,57898
15
15
  pixeltable/catalog/table_version_path.py,sha256=CczGbcz5ESq663arreri_p4chMZHozgG6k3y-ajkJN4,5787
16
- pixeltable/catalog/view.py,sha256=7KQeHiqKmGrS_hjt6QjIjZhOvl9-XKBAi7A9_9_Worg,10715
16
+ pixeltable/catalog/view.py,sha256=FLtRnHeWfHEiRxaz_MLlBhpJssKYSXIo_sXA6MMMF0M,10850
17
17
  pixeltable/dataframe.py,sha256=SGge2DxcjT0gaKpWsmQJhqlqX6b75nefoKAh1Ld4S8Y,33639
18
18
  pixeltable/env.py,sha256=oIFeLJVDQuQgW8REAoNkY45BF2o02NxtysgAlJNEBc8,30520
19
19
  pixeltable/exceptions.py,sha256=NuFY2WtkQpLfLHT_J70kOw9Tr0kEDkkgo-u7As4Gaq4,410
@@ -51,7 +51,7 @@ pixeltable/exprs/method_ref.py,sha256=x9rQzlRQlVnbTpBQoV0COFsiOPstJcOifXl0lJC-ro
51
51
  pixeltable/exprs/object_ref.py,sha256=GVg6uxZnKwFVTC0kouJq-wMFP-gUPb_ld_syHrsGMdE,1283
52
52
  pixeltable/exprs/row_builder.py,sha256=7f-h4y8xv0ktkk6GYqGrMJvLSwkVYtMPHsBBIskmQLw,18435
53
53
  pixeltable/exprs/rowid_ref.py,sha256=hjGrbk9zHH3H-00uFAopyRvLTeQeB2e44kAJUAxiy3k,4400
54
- pixeltable/exprs/similarity_expr.py,sha256=snOseawC4ySvyHo8TCqbh_bDxIrENfIzO_0lXlzL-BA,4016
54
+ pixeltable/exprs/similarity_expr.py,sha256=VYS_fgDBwFj_UIVi60q-6OOpAglI7J-pCdncJKF45zQ,4087
55
55
  pixeltable/exprs/sql_element_cache.py,sha256=DRW5Aa0WQZ-yMf5anlUg-_Rmq3Oz3G6Us1X_KseMC68,1229
56
56
  pixeltable/exprs/type_cast.py,sha256=BTnhgoll7LVZdOU_282QlzGZ4EEMTzPYcNdDcFOfkTs,1837
57
57
  pixeltable/exprs/variable.py,sha256=VATAmLaPrQipv2AXqg-m6FYMLNGyhPtys8c5Et8Ba0g,1414
@@ -63,22 +63,22 @@ pixeltable/func/__init__.py,sha256=WjftUGyNkd6bF_qSxqZ5Gd7Elf8oExb3dUlpydhdFTo,4
63
63
  pixeltable/func/aggregate_function.py,sha256=LM6DMldmaUJNlQgEqPFQIKVmwjl3IpBxS4Y61G8-zG0,9477
64
64
  pixeltable/func/callable_function.py,sha256=PO5Mn5WL2cd7y5LcKr_K0AaYHf2-1NfuXP2IPOfsiVs,4933
65
65
  pixeltable/func/expr_template_function.py,sha256=2co1I9Epzh2ROyQ2WYJAfEbw8OxTbnc0Wp3GOvBLRBU,4043
66
- pixeltable/func/function.py,sha256=BLJbahyKteGemp0EcG7joTrgrG2hnIfwHkuKoLZm4mo,6941
66
+ pixeltable/func/function.py,sha256=BkY2esrFeRsa5sGZ2InJihV5MTxFoZbEJ8c3pV-mN1w,8622
67
67
  pixeltable/func/function_registry.py,sha256=fBXe7NKyk8_JzZz6fsS0LF-WHTdMnmIP_XzrICuj9fA,12328
68
68
  pixeltable/func/globals.py,sha256=sEwn6lGgHMp6VQORb_P5qRd_-Q2_bUSqvqM9-XPN_ec,1483
69
69
  pixeltable/func/query_template_function.py,sha256=pGqwtWiPsEmo7phVoJJODiuD1Sh0gZoW4BpKnZV9cRE,3537
70
- pixeltable/func/signature.py,sha256=vBKs3igtijTQGK7rEGTxBiOznKo6Tj9Ut6OrSfiVcA0,8609
70
+ pixeltable/func/signature.py,sha256=MzOHayuf2S3EWIl4IRtdNWnaKj5tphnw1z7P6xNU6Xg,8691
71
71
  pixeltable/func/udf.py,sha256=_883xbGujeIhYOz-lWH5SvLn22F5JVpP6O_MRvpU4hU,7362
72
72
  pixeltable/functions/__init__.py,sha256=EtR9M3ewYpmtHeshNULqZVBd87bNeKSFAdpOuWCMl6o,389
73
73
  pixeltable/functions/anthropic.py,sha256=P1E5o4-8QP1LTIUsWVgo_wMJ4WOnxtXUUXuFWUagChU,3032
74
74
  pixeltable/functions/audio.py,sha256=7213nTnqKJ6vM9kalaoJ283OwX5SGEJN10vDhaRNZ6E,644
75
75
  pixeltable/functions/fireworks.py,sha256=qwFC_eIaDs-glxyJ_IVXaNGkpgPzeRsQ_SdpzueBxq0,2605
76
76
  pixeltable/functions/globals.py,sha256=pCFX2a_N87SwG9GxyPjSOC3TVMowMB6XIHSWKfFOuGE,3917
77
- pixeltable/functions/huggingface.py,sha256=hcScloxprIuyf89_bSnQi-N3VZRQosmZfBhmDJF_eTc,20814
77
+ pixeltable/functions/huggingface.py,sha256=s5KmOfi9-TOYyrL1Wv-voKP7ykkUN7LlLAA_uo01UQc,21210
78
78
  pixeltable/functions/image.py,sha256=3Qm4ybAT_o4YUl3bzhEXy8dKOwgZ7RCUV-ky-dbL_jc,13836
79
79
  pixeltable/functions/json.py,sha256=ehCnBA5WiIl-crV9PFVgmxrsWsiO8FpRs9LDwcSpLa4,879
80
80
  pixeltable/functions/llama_cpp.py,sha256=1awALuAXVpQH64l7vQlM8gvxLDix4c1-6DV3nG5RHu4,3881
81
- pixeltable/functions/mistralai.py,sha256=zljm0ZfucnJFaRyMQHkjyD51vEe0OlurACin7kCNmxk,5569
81
+ pixeltable/functions/mistralai.py,sha256=GpxtT-a8ltx1kQC8XTJRflxkh-17VhzzFTkxqIUsba8,5494
82
82
  pixeltable/functions/ollama.py,sha256=eZh461HvChjlr0CvQyd93m7qrv889PAoM-Z1IKierY0,4335
83
83
  pixeltable/functions/openai.py,sha256=e1-NZP5v0h9Axlo1oxUYzWfVRxKZVhOrKnqwcy0jH88,15583
84
84
  pixeltable/functions/replicate.py,sha256=j8ZedScOMInmHWmriQSUOviw6tp8gQr-W6n21PNNL2g,2188
@@ -89,11 +89,11 @@ pixeltable/functions/util.py,sha256=GgKTzCjvzUQNjWtSObTkfxkvJ9GVUOzKimY45WhE25M,
89
89
  pixeltable/functions/video.py,sha256=yW1Lwqu4_jYXp1aAOUThKB5-_Qxy-In_vTgB5cuW7Lg,6809
90
90
  pixeltable/functions/vision.py,sha256=K_E1Q-n2plPuFoOPlbKWRMiJp9dPgftIJ2T_o3TNL3I,15594
91
91
  pixeltable/functions/whisper.py,sha256=f2wqRd0n9jSBqRZN3W93UaetiAHtbsK0j9jXR2j2kkQ,2913
92
- pixeltable/globals.py,sha256=GDA5T6jpXlFHLMKraLZxZ9jTC99IiQfW85jkbTCPKKc,20002
92
+ pixeltable/globals.py,sha256=NcGIFmDUzTeQm_TPnlKUXntiPfDp5pWXTRtiGkoXBAA,20397
93
93
  pixeltable/index/__init__.py,sha256=XBwetNQQwnz0fiKwonOKhyy_U32l_cjt77kNvEIdjWs,102
94
94
  pixeltable/index/base.py,sha256=zo0YvJI3oXiK6hZJztB36ZftKKhLfO75Zq3t-PeQA6M,1556
95
95
  pixeltable/index/btree.py,sha256=JFerLyyLoBaD0FSF_jJ6iJFBVa-z_et--KdNR02xjRg,2264
96
- pixeltable/index/embedding_index.py,sha256=g5bajq8Dn82o8ZlkDwPT5kBUClR4ZCuH6dttwCW6KWI,7793
96
+ pixeltable/index/embedding_index.py,sha256=XeWPgr4zWhcHnQxVMiyEaV_8wOV-gk2Ius1E917ZV84,8064
97
97
  pixeltable/io/__init__.py,sha256=PHqbiEJXFtCzTsia7LmsHLfBIkA41tzII3n9L4UkfJ8,547
98
98
  pixeltable/io/external_store.py,sha256=H1jt7MDn464QRgBvU-PmcPcFlo3EZBCG7fKWEZXOfyc,16676
99
99
  pixeltable/io/fiftyone.py,sha256=hH-FahW6BuMQY8lGa2atnNnJto_pK8kWrP_y_EMsq6g,6965
@@ -102,11 +102,12 @@ pixeltable/io/hf_datasets.py,sha256=o5fqm2CJAjhFd3z-NYGxN0jM1tfrp4szuUX0TGnyNRY,
102
102
  pixeltable/io/label_studio.py,sha256=7KTro1H-AlVbwWuoYwU-mxH3zejZWTpQbz56uX-Wnjs,31078
103
103
  pixeltable/io/pandas.py,sha256=7eHg7wnAfRA9eBk4iC0iSSVTKOM59Ne4pXokKWdt3dY,9793
104
104
  pixeltable/io/parquet.py,sha256=4bAQNCahtLGuHRF669kLGx2MhOFmuwTkUYYHLC-qKcs,7862
105
- pixeltable/iterators/__init__.py,sha256=sjldFckkT8aVRiKgEP6faeAK2NQBdzbmpwAeRhI1FkM,366
105
+ pixeltable/iterators/__init__.py,sha256=qA9cJTwPO3gk-7y8mUXw2anWSbnUAVGwzl52SxiUjNU,398
106
106
  pixeltable/iterators/base.py,sha256=ZC0ZvXL4iw6AmT8cu-Mdx-T2UG9nmJYV1C6LK4efAfw,1669
107
107
  pixeltable/iterators/document.py,sha256=AsvEmZ5RGRi3AFGCrH2_-UNx5rTCFA-0WmMYQBTQI20,19679
108
+ pixeltable/iterators/image.py,sha256=7eJupbyLEl_q4bemEozL8WgTgkxjl-DYnSXyHTQ7Rck,3628
108
109
  pixeltable/iterators/string.py,sha256=NG_fWc_GAITDfzl6MvrDOMrSoMcZdMZf6hPQztCSatE,1305
109
- pixeltable/iterators/video.py,sha256=CKx6jHFW-393r4xN-ulVDDZFETSkhhMwT7bPX2NHVU8,9442
110
+ pixeltable/iterators/video.py,sha256=anedQpSOnjYhHLa8UiEx-6ROagb14rpXh9am8oTriUg,9382
110
111
  pixeltable/metadata/__init__.py,sha256=CI0ZWcxsCbXEWhdbbByNJFdSmvIBrMEbf_vqrVb0b-Q,2209
111
112
  pixeltable/metadata/converters/convert_10.py,sha256=J1_r7LNNAWTdb042AwqFpJ4sEB-i4qhUdk5iOjcZk34,719
112
113
  pixeltable/metadata/converters/convert_12.py,sha256=Ci-qyZW1gqci-8wnjeOB5afdq7KTuN-hVSV9OqSPx8g,162
@@ -122,10 +123,10 @@ pixeltable/metadata/converters/convert_21.py,sha256=YTztkbqOC2zQcTWrXfhrP8diUbfx
122
123
  pixeltable/metadata/converters/util.py,sha256=nycZk_UecJcrVZsIyxQrz5ngbke8-yfY-_UcERuzhPk,5983
123
124
  pixeltable/metadata/notes.py,sha256=HJmeA9fo37iREFIhlbGbxWnwbwIDcvdqvJO-BavIvxE,597
124
125
  pixeltable/metadata/schema.py,sha256=CnEsMqLn4hzLDaAr5lyd-NqiOUFQdvhxdCoXMR4Qcs4,9352
125
- pixeltable/plan.py,sha256=GRR1HvQKxYfpFJzeuPa1Ku3aE3tR3g3bcRu7KlfsAqQ,37436
126
+ pixeltable/plan.py,sha256=K8Cy_fmg7X1nNGxLDlWoP_NjUty1T5bJ2VtBSAiPeHg,37451
126
127
  pixeltable/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
127
128
  pixeltable/store.py,sha256=IG33KEu0eHGD2CGMBVV-HmPj7xGUMCLmLGdJn49_4Cs,21990
128
- pixeltable/tool/create_test_db_dump.py,sha256=Qm9uq-PbcwNhj4Iczw5_uYJXh4TIyOp4RVKXssVFAiU,11972
129
+ pixeltable/tool/create_test_db_dump.py,sha256=LpqPcLbsWUHi_jGsHw9fmWgU3dbcUzlQsCRkWyqoBXE,12063
129
130
  pixeltable/tool/create_test_video.py,sha256=4cQmqoKjn3juy7Ilty75gWBygqBxTZ1E9XPlrsC0Ssk,2931
130
131
  pixeltable/tool/doc_plugins/griffe.py,sha256=J5zxyEUchfR3mkWmhx4Vjl_iSodL_pHiuOyD2eczbNU,2182
131
132
  pixeltable/tool/doc_plugins/mkdocstrings.py,sha256=afq7XOaSC5WRmugkh-FMFMK8PqOgIlDIsJdD8cuPhtE,207
@@ -146,8 +147,8 @@ pixeltable/utils/pytorch.py,sha256=6RvOCjy_QV4gc-aht-3d0zoASkuv-warfpl87vgmuKw,3
146
147
  pixeltable/utils/s3.py,sha256=huA5hxDGkPIu18zWet76o0FsO7Vbtp-iPmnOzCe-MvA,586
147
148
  pixeltable/utils/sql.py,sha256=j_tj0h4ffm-DhUIJbvGphxrVyBKlNTwDKqWGhRQ5_PY,795
148
149
  pixeltable/utils/transactional_directory.py,sha256=UGzCrGtLR3hEEf8sYGuWBzLVFAEQml3vdIavigWeTBM,1349
149
- pixeltable-0.2.23.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
150
- pixeltable-0.2.23.dist-info/METADATA,sha256=WYtMLp2PmlhHkQl6WUyvJFuJ2X-_lSB1VUO3i56dKwc,18112
151
- pixeltable-0.2.23.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
152
- pixeltable-0.2.23.dist-info/entry_points.txt,sha256=TNI1Gb5vPwFrTdw6TimSYjO8FeK8c_HuPr28vcf7o_I,108
153
- pixeltable-0.2.23.dist-info/RECORD,,
150
+ pixeltable-0.2.25.dist-info/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
151
+ pixeltable-0.2.25.dist-info/METADATA,sha256=UsoYpQxhrA3j8UlVlcJrunDDASruXLLmldGRw0Ni6lY,17994
152
+ pixeltable-0.2.25.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
153
+ pixeltable-0.2.25.dist-info/entry_points.txt,sha256=TNI1Gb5vPwFrTdw6TimSYjO8FeK8c_HuPr28vcf7o_I,108
154
+ pixeltable-0.2.25.dist-info/RECORD,,