pixeltable 0.2.11__py3-none-any.whl → 0.2.12__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/__init__.py CHANGED
@@ -3,8 +3,8 @@ from .dataframe import DataFrame
3
3
  from .exceptions import Error
4
4
  from .exprs import RELATIVE_PATH_ROOT
5
5
  from .func import Function, udf, Aggregator, uda, expr_udf
6
- from .globals import init, create_table, create_view, get_table, move, drop_table, list_tables, create_dir, rm_dir, \
7
- list_dirs, list_functions, get_path, configure_logging
6
+ from .globals import init, create_table, create_view, get_table, move, drop_table, list_tables, create_dir, drop_dir, \
7
+ list_dirs, list_functions, configure_logging
8
8
  from .type_system import (
9
9
  ColumnType,
10
10
  StringType,
pixeltable/__version__.py CHANGED
@@ -1,3 +1,3 @@
1
1
  # These version placeholders will be replaced during build.
2
- __version__ = "0.2.11"
3
- __version_tuple__ = (0, 2, 11)
2
+ __version__ = "0.2.12"
3
+ __version_tuple__ = (0, 2, 12)
@@ -1,7 +1,10 @@
1
1
  from abc import abstractmethod
2
- from typing import Optional
2
+ from typing import TYPE_CHECKING, Optional
3
3
  from uuid import UUID
4
4
 
5
+ if TYPE_CHECKING:
6
+ from pixeltable import catalog
7
+
5
8
 
6
9
  class SchemaObject:
7
10
  """
@@ -17,9 +20,32 @@ class SchemaObject:
17
20
  def _get_id(self) -> UUID:
18
21
  return self._id
19
22
 
20
- def get_name(self) -> str:
23
+ @property
24
+ def name(self) -> str:
25
+ """Returns the name of this schema object."""
21
26
  return self._name
22
27
 
28
+ @property
29
+ def parent(self) -> Optional['catalog.Dir']:
30
+ """Returns the parent directory of this schema object."""
31
+ from pixeltable import catalog
32
+ if self._dir_id is None:
33
+ return None
34
+ dir = catalog.Catalog.get().paths.get_schema_obj(self._dir_id)
35
+ assert isinstance(dir, catalog.Dir)
36
+ return dir
37
+
38
+ @property
39
+ def path(self) -> str:
40
+ """Returns the path to this schema object."""
41
+ parent = self.parent
42
+ if parent is None or parent.parent is None:
43
+ # Either this is the root directory, with empty path, or its parent is the
44
+ # root directory. Either way, we return just the name.
45
+ return self.name
46
+ else:
47
+ return f'{parent.path}.{self.name}'
48
+
23
49
  @classmethod
24
50
  @abstractmethod
25
51
  def display_name(cls) -> str:
@@ -82,14 +82,22 @@ class Table(SchemaObject):
82
82
  return self._queries[index]
83
83
  return self._tbl_version_path.__getitem__(index)
84
84
 
85
- def get_views(self, *, recursive: bool = False) -> list['Table']:
85
+ def list_views(self, *, recursive: bool = True) -> list[str]:
86
86
  """
87
- All views and snapshots of this `Table`.
87
+ Returns a list of all views and snapshots of this `Table`.
88
+
89
+ Args:
90
+ recursive: If `False`, returns only the immediate successor views of this `Table`. If `True`, returns
91
+ all sub-views (including views of views, etc.)
88
92
  """
93
+ return [t.path for t in self._get_views(recursive=recursive)]
94
+
95
+ def _get_views(self, *, recursive: bool = True) -> list['Table']:
96
+ dependents = catalog.Catalog.get().tbl_dependents[self._get_id()]
89
97
  if recursive:
90
- return [self] + [t for view in self.get_views(recursive=False) for t in view.get_views(recursive=True)]
98
+ return dependents + [t for view in dependents for t in view._get_views(recursive=True)]
91
99
  else:
92
- return catalog.Catalog.get().tbl_dependents[self._get_id()]
100
+ return dependents
93
101
 
94
102
  def _df(self) -> 'pixeltable.dataframe.DataFrame':
95
103
  """Return a DataFrame for this table.
@@ -500,7 +508,37 @@ class Table(SchemaObject):
500
508
  >>> tbl.drop_column('factorial')
501
509
  """
502
510
  self._check_is_dropped()
503
- self._tbl_version.drop_column(name)
511
+
512
+ if name not in self._tbl_version.cols_by_name:
513
+ raise excs.Error(f'Unknown column: {name}')
514
+ col = self._tbl_version.cols_by_name[name]
515
+
516
+ dependent_user_cols = [c for c in col.dependent_cols if c.name is not None]
517
+ if len(dependent_user_cols) > 0:
518
+ raise excs.Error(
519
+ f'Cannot drop column `{name}` because the following columns depend on it:\n'
520
+ f'{", ".join(c.name for c in dependent_user_cols)}'
521
+ )
522
+
523
+ # See if this column has a dependent store. We need to look through all stores in all
524
+ # (transitive) views of this table.
525
+ dependent_stores = [
526
+ (view, store)
527
+ for view in [self] + self._get_views(recursive=True)
528
+ for store in view._tbl_version.external_stores.values()
529
+ if col in store.get_local_columns()
530
+ ]
531
+ if len(dependent_stores) > 0:
532
+ dependent_store_names = [
533
+ store.name if view._get_id() == self._get_id() else f'{store.name} (in view `{view.name}`)'
534
+ for view, store in dependent_stores
535
+ ]
536
+ raise excs.Error(
537
+ f'Cannot drop column `{name}` because the following external stores depend on it:\n'
538
+ f'{", ".join(dependent_store_names)}'
539
+ )
540
+
541
+ self._tbl_version.drop_column(col)
504
542
 
505
543
  def rename_column(self, old_name: str, new_name: str) -> None:
506
544
  """Rename a column.
@@ -522,15 +560,15 @@ class Table(SchemaObject):
522
560
 
523
561
  def add_embedding_index(
524
562
  self, col_name: str, *, idx_name: Optional[str] = None,
525
- text_embed: Optional[pixeltable.Function] = None, img_embed: Optional[pixeltable.Function] = None,
563
+ string_embed: Optional[pixeltable.Function] = None, image_embed: Optional[pixeltable.Function] = None,
526
564
  metric: str = 'cosine'
527
565
  ) -> None:
528
566
  """Add an index to the table.
529
567
  Args:
530
568
  col_name: name of column to index
531
569
  idx_name: name of index, which needs to be unique for the table; if not provided, a name will be generated
532
- text_embed: function to embed text; required if the column is a text column
533
- img_embed: function to embed images; required if the column is an image column
570
+ string_embed: function to embed text; required if the column is a text column
571
+ image_embed: function to embed images; required if the column is an image column
534
572
  metric: distance metric to use for the index; one of 'cosine', 'ip', 'l2'; default is 'cosine'
535
573
 
536
574
  Raises:
@@ -539,13 +577,13 @@ class Table(SchemaObject):
539
577
  Examples:
540
578
  Add an index to the ``img`` column:
541
579
 
542
- >>> tbl.add_embedding_index('img', img_embed=...)
580
+ >>> tbl.add_embedding_index('img', image_embed=...)
543
581
 
544
582
  Add another index to the ``img`` column, using the inner product as the distance metric,
545
- and with a specific name; ``text_embed`` is also specified in order to search with text:
583
+ and with a specific name; ``string_embed`` is also specified in order to search with text:
546
584
 
547
585
  >>> tbl.add_embedding_index(
548
- 'img', idx_name='clip_idx', img_embed=..., text_embed=...text_embed..., metric='ip')
586
+ 'img', idx_name='clip_idx', image_embed=..., string_embed=..., metric='ip')
549
587
  """
550
588
  if self._tbl_version_path.is_snapshot():
551
589
  raise excs.Error('Cannot add an index to a snapshot')
@@ -557,7 +595,7 @@ class Table(SchemaObject):
557
595
  raise excs.Error(f'Duplicate index name: {idx_name}')
558
596
  from pixeltable.index import EmbeddingIndex
559
597
  # create the EmbeddingIndex instance to verify args
560
- idx = EmbeddingIndex(col, metric=metric, text_embed=text_embed, img_embed=img_embed)
598
+ idx = EmbeddingIndex(col, metric=metric, string_embed=string_embed, image_embed=image_embed)
561
599
  status = self._tbl_version.add_index(col, idx_name=idx_name, idx=idx)
562
600
  # TODO: how to deal with exceptions here? drop the index and raise?
563
601
 
@@ -604,26 +642,26 @@ class Table(SchemaObject):
604
642
  raise excs.Error('Cannot drop an index from a snapshot')
605
643
  self._check_is_dropped()
606
644
  if (column_name is None) == (idx_name is None):
607
- raise excs.Error('Exactly one of column_name or idx_name must be provided')
645
+ raise excs.Error("Exactly one of 'column_name' or 'idx_name' must be provided")
608
646
 
609
647
  if idx_name is not None:
610
648
  if idx_name not in self._tbl_version.idxs_by_name:
611
- raise excs.Error(f'Index {idx_name} does not exist')
649
+ raise excs.Error(f'Index {idx_name!r} does not exist')
612
650
  idx_id = self._tbl_version.idxs_by_name[idx_name].id
613
651
  else:
614
652
  col = self._tbl_version_path.get_column(column_name, include_bases=True)
615
653
  if col is None:
616
- raise excs.Error(f'Column {column_name} unknown')
654
+ raise excs.Error(f'Column {column_name!r} unknown')
617
655
  if col.tbl.id != self._tbl_version.id:
618
656
  raise excs.Error(
619
- f'Column {column_name}: cannot drop index from column that belongs to base ({col.tbl.name})')
657
+ f'Column {column_name!r}: cannot drop index from column that belongs to base ({col.tbl.name}!r)')
620
658
  idx_info = [info for info in self._tbl_version.idxs_by_name.values() if info.col.id == col.id]
621
659
  if _idx_class is not None:
622
660
  idx_info = [info for info in idx_info if isinstance(info.idx, _idx_class)]
623
661
  if len(idx_info) == 0:
624
- raise excs.Error(f'Column {column_name} does not have an index')
662
+ raise excs.Error(f'Column {column_name!r} does not have an index')
625
663
  if len(idx_info) > 1:
626
- raise excs.Error(f'Column {column_name} has multiple indices; specify idx_name instead')
664
+ raise excs.Error(f"Column {column_name!r} has multiple indices; specify 'idx_name' instead")
627
665
  idx_id = idx_info[0].id
628
666
  self._tbl_version.drop_index(idx_id)
629
667
 
@@ -823,13 +861,13 @@ class Table(SchemaObject):
823
861
  Links the specified `ExternalStore` to this table.
824
862
  """
825
863
  if self._tbl_version.is_snapshot:
826
- raise excs.Error(f'Table `{self.get_name()}` is a snapshot, so it cannot be linked to an external store.')
864
+ raise excs.Error(f'Table `{self.name}` is a snapshot, so it cannot be linked to an external store.')
827
865
  self._check_is_dropped()
828
866
  if store.name in self.external_stores:
829
- raise excs.Error(f'Table `{self.get_name()}` already has an external store with that name: {store.name}')
830
- _logger.info(f'Linking external store `{store.name}` to table `{self.get_name()}`')
867
+ raise excs.Error(f'Table `{self.name}` already has an external store with that name: {store.name}')
868
+ _logger.info(f'Linking external store `{store.name}` to table `{self.name}`')
831
869
  self._tbl_version.link_external_store(store)
832
- print(f'Linked external store `{store.name}` to table `{self.get_name()}`.')
870
+ print(f'Linked external store `{store.name}` to table `{self.name}`.')
833
871
 
834
872
  def unlink_external_stores(
835
873
  self,
@@ -861,11 +899,11 @@ class Table(SchemaObject):
861
899
  if not ignore_errors:
862
900
  for store in stores:
863
901
  if store not in all_stores:
864
- raise excs.Error(f'Table `{self.get_name()}` has no external store with that name: {store}')
902
+ raise excs.Error(f'Table `{self.name}` has no external store with that name: {store}')
865
903
 
866
904
  for store in stores:
867
905
  self._tbl_version.unlink_external_store(store, delete_external_data=delete_external_data)
868
- print(f'Unlinked external store from table `{self.get_name()}`: {store}')
906
+ print(f'Unlinked external store from table `{self.name}`: {store}')
869
907
 
870
908
  def sync(
871
909
  self,
@@ -893,7 +931,7 @@ class Table(SchemaObject):
893
931
 
894
932
  for store in stores:
895
933
  if store not in all_stores:
896
- raise excs.Error(f'Table `{self.get_name()}` has no external store with that name: {store}')
934
+ raise excs.Error(f'Table `{self.name}` has no external store with that name: {store}')
897
935
 
898
936
  from pixeltable.io import SyncStatus
899
937
 
@@ -540,39 +540,12 @@ class TableVersion:
540
540
  num_rows=row_count, num_computed_values=row_count, num_excs=num_excs,
541
541
  cols_with_excs=[f'{col.tbl.name}.{col.name}'for col in cols_with_excs if col.name is not None])
542
542
 
543
- def drop_column(self, name: str) -> None:
543
+ def drop_column(self, col: Column) -> None:
544
544
  """Drop a column from the table.
545
545
  """
546
546
  from pixeltable.catalog import Catalog
547
547
 
548
548
  assert not self.is_snapshot
549
- if name not in self.cols_by_name:
550
- raise excs.Error(f'Unknown column: {name}')
551
- col = self.cols_by_name[name]
552
- dependent_user_cols = [c for c in col.dependent_cols if c.name is not None]
553
- if len(dependent_user_cols) > 0:
554
- raise excs.Error(
555
- f'Cannot drop column `{name}` because the following columns depend on it:\n'
556
- f'{", ".join(c.name for c in dependent_user_cols)}'
557
- )
558
- # See if this column has a dependent store. We need to look through all stores in all
559
- # (transitive) views of this table.
560
- transitive_views = Catalog.get().tbls[self.id].get_views(recursive=True)
561
- dependent_stores = [
562
- (view, store)
563
- for view in transitive_views
564
- for store in view._tbl_version.external_stores.values()
565
- if col in store.get_local_columns()
566
- ]
567
- if len(dependent_stores) > 0:
568
- dependent_store_names = [
569
- store.name if view._get_id() == self.id else f'{store.name} (in view `{view.get_name()}`)'
570
- for view, store in dependent_stores
571
- ]
572
- raise excs.Error(
573
- f'Cannot drop column `{name}` because the following external stores depend on it:\n'
574
- f'{", ".join(dependent_store_names)}'
575
- )
576
549
 
577
550
  # we're creating a new schema version
578
551
  self.version += 1
@@ -596,7 +569,7 @@ class TableVersion:
596
569
  del self.idxs_by_name[idx_name]
597
570
  self._drop_columns(dropped_cols)
598
571
  self._update_md(time.time(), conn, preceding_schema_version=preceding_schema_version)
599
- _logger.info(f'Dropped column {name} from table {self.name}, new version: {self.version}')
572
+ _logger.info(f'Dropped column {col.name} from table {self.name}, new version: {self.version}')
600
573
 
601
574
  def _drop_columns(self, cols: Iterable[Column]) -> None:
602
575
  """Mark columns as dropped"""
pixeltable/dataframe.py CHANGED
@@ -558,7 +558,7 @@ class DataFrame:
558
558
  # we need to make sure that the grouping table is a base of self.tbl
559
559
  base = self.tbl.find_tbl_version(item._tbl_version_path.tbl_id())
560
560
  if base is None or base.id == self.tbl.tbl_id():
561
- raise excs.Error(f'group_by(): {item.get_name()} is not a base table of {self.tbl.tbl_name()}')
561
+ raise excs.Error(f'group_by(): {item.name} is not a base table of {self.tbl.tbl_name()}')
562
562
  grouping_tbl = item._tbl_version_path.tbl_version
563
563
  break
564
564
  if not isinstance(item, exprs.Expr):
@@ -63,14 +63,9 @@ class ColumnRef(Expr):
63
63
 
64
64
  return super().__getattr__(name)
65
65
 
66
- def similarity(self, other: Any) -> Expr:
67
- # if isinstance(other, Expr):
68
- # raise excs.Error(f'similarity(): requires a string or a PIL.Image.Image object, not an expression')
69
- item = Expr.from_object(other)
70
- if item is None or not(item.col_type.is_string_type() or item.col_type.is_image_type()):
71
- raise excs.Error(f'similarity(): requires a string or a PIL.Image.Image object, not a {type(other)}')
66
+ def similarity(self, item: Any, *, idx: Optional[str] = None) -> Expr:
72
67
  from .similarity_expr import SimilarityExpr
73
- return SimilarityExpr(self, item)
68
+ return SimilarityExpr(self, item, idx_name=idx)
74
69
 
75
70
  def default_column_name(self) -> Optional[str]:
76
71
  return str(self)
@@ -1,4 +1,4 @@
1
- from typing import Optional, List
1
+ from typing import Optional, List, Any
2
2
 
3
3
  import sqlalchemy as sql
4
4
  import PIL.Image
@@ -14,33 +14,44 @@ from .row_builder import RowBuilder
14
14
 
15
15
  class SimilarityExpr(Expr):
16
16
 
17
- def __init__(self, col_ref: ColumnRef, item: Expr):
17
+ def __init__(self, col_ref: ColumnRef, item: Any, idx_name: Optional[str] = None):
18
18
  super().__init__(ts.FloatType())
19
- self.components = [col_ref, item]
19
+ item_expr = Expr.from_object(item)
20
+ if item_expr is None or not(item_expr.col_type.is_string_type() or item_expr.col_type.is_image_type()):
21
+ raise excs.Error(f'similarity(): requires a string or a PIL.Image.Image object, not a {type(item)}')
22
+ assert item_expr.col_type.is_string_type() or item_expr.col_type.is_image_type()
23
+
24
+ self.components = [col_ref, item_expr]
20
25
  self.id = self._create_id()
21
- assert item.col_type.is_string_type() or item.col_type.is_image_type()
22
26
 
23
27
  # determine index to use
24
28
  idx_info = col_ref.col.get_idx_info()
25
29
  import pixeltable.index as index
26
- embedding_idx_info = [info for info in idx_info.values() if isinstance(info.idx, index.EmbeddingIndex)]
30
+ embedding_idx_info = {
31
+ info.name: info for info in idx_info.values() if isinstance(info.idx, index.EmbeddingIndex)
32
+ }
27
33
  if len(embedding_idx_info) == 0:
28
- raise excs.Error(f'No index found for column {col_ref.col}')
34
+ raise excs.Error(f'No index found for column {col_ref.col!r}')
35
+ if idx_name is not None and idx_name not in embedding_idx_info:
36
+ raise excs.Error(f'Index {idx_name!r} not found for column {col_ref.col.name!r}')
29
37
  if len(embedding_idx_info) > 1:
30
- raise excs.Error(
31
- f'Column {col_ref.col.name} has multiple indices; use the index name to disambiguate, '
32
- f'e.g., `{col_ref.col.name}.<index-name>.similarity(...)`')
33
- self.idx_info = embedding_idx_info[0]
38
+ if idx_name is None:
39
+ raise excs.Error(
40
+ f'Column {col_ref.col.name!r} has multiple indices; use the index name to disambiguate: '
41
+ f'`{col_ref.col.name}.similarity(..., idx=<name>)`')
42
+ self.idx_info = embedding_idx_info[idx_name]
43
+ else:
44
+ self.idx_info = next(iter(embedding_idx_info.values()))
34
45
  idx = self.idx_info.idx
35
46
 
36
- if item.col_type.is_string_type() and idx.txt_embed is None:
47
+ if item_expr.col_type.is_string_type() and idx.string_embed is None:
37
48
  raise excs.Error(
38
- f'Embedding index {self.idx_info.name} on column {self.idx_info.col.name} was created without the '
39
- f'text_embed parameter and does not support text queries')
40
- if item.col_type.is_image_type() and idx.img_embed is None:
49
+ f'Embedding index {self.idx_info.name!r} on column {self.idx_info.col.name!r} was created without the '
50
+ f"'string_embed' parameter and does not support string queries")
51
+ if item_expr.col_type.is_image_type() and idx.image_embed is None:
41
52
  raise excs.Error(
42
- f'Embedding index {self.idx_info.name} on column {self.idx_info.col.name} was created without the '
43
- f'img_embed parameter and does not support image queries')
53
+ f'Embedding index {self.idx_info.name!r} on column {self.idx_info.col.name!r} was created without the '
54
+ f"'image_embed' parameter and does not support image queries")
44
55
 
45
56
  def __str__(self) -> str:
46
57
  return f'{self.components[0]}.similarity({self.components[1]})'
@@ -141,7 +141,7 @@ def chat_completions(
141
141
 
142
142
 
143
143
  @pxt.udf
144
- def vision(prompt: str, image: PIL.Image.Image, *, model: str = 'gpt-4-vision-preview') -> str:
144
+ def vision(prompt: str, image: PIL.Image.Image, *, model: str) -> str:
145
145
  # TODO(aaron-siegel): Decompose CPU/GPU ops into separate functions
146
146
  bytes_arr = io.BytesIO()
147
147
  image.save(bytes_arr, format='png')
pixeltable/globals.py CHANGED
@@ -234,7 +234,7 @@ def drop_table(path: str, force: bool = False, ignore_errors: bool = False) -> N
234
234
 
235
235
  Args:
236
236
  path: Path to the table.
237
- force: Whether to drop the table even if it has unsaved changes.
237
+ force: If `True`, will also drop all views or sub-views of this table.
238
238
  ignore_errors: Whether to ignore errors if the table does not exist.
239
239
 
240
240
  Raises:
@@ -243,21 +243,27 @@ def drop_table(path: str, force: bool = False, ignore_errors: bool = False) -> N
243
243
  Examples:
244
244
  >>> cl.drop_table('my_table')
245
245
  """
246
+ cat = Catalog.get()
246
247
  path_obj = catalog.Path(path)
247
248
  try:
248
- Catalog.get().paths.check_is_valid(path_obj, expected=catalog.Table)
249
+ cat.paths.check_is_valid(path_obj, expected=catalog.Table)
249
250
  except Exception as e:
250
- if ignore_errors:
251
+ if ignore_errors or force:
251
252
  _logger.info(f'Skipped table `{path}` (does not exist).')
252
253
  return
253
254
  else:
254
255
  raise e
255
- tbl = Catalog.get().paths[path_obj]
256
- if len(Catalog.get().tbl_dependents[tbl._id]) > 0:
257
- dependent_paths = [get_path(dep) for dep in Catalog.get().tbl_dependents[tbl._id]]
258
- raise excs.Error(f'Table {path} has dependents: {", ".join(dependent_paths)}')
256
+ tbl = cat.paths[path_obj]
257
+ assert isinstance(tbl, catalog.Table)
258
+ if len(cat.tbl_dependents[tbl._id]) > 0:
259
+ dependent_paths = [dep.path for dep in cat.tbl_dependents[tbl._id]]
260
+ if force:
261
+ for dependent_path in dependent_paths:
262
+ drop_table(dependent_path, force=True)
263
+ else:
264
+ raise excs.Error(f'Table {path} has dependents: {", ".join(dependent_paths)}')
259
265
  tbl._drop()
260
- del Catalog.get().paths[path_obj]
266
+ del cat.paths[path_obj]
261
267
  _logger.info(f'Dropped table `{path}`.')
262
268
 
263
269
 
@@ -291,7 +297,7 @@ def list_tables(dir_path: str = '', recursive: bool = True) -> list[str]:
291
297
  return [str(p) for p in Catalog.get().paths.get_children(path, child_type=catalog.Table, recursive=recursive)]
292
298
 
293
299
 
294
- def create_dir(path_str: str, ignore_errors: bool = False) -> None:
300
+ def create_dir(path_str: str, ignore_errors: bool = False) -> catalog.Dir:
295
301
  """Create a directory.
296
302
 
297
303
  Args:
@@ -319,10 +325,12 @@ def create_dir(path_str: str, ignore_errors: bool = False) -> None:
319
325
  session.add(dir_record)
320
326
  session.flush()
321
327
  assert dir_record.id is not None
322
- Catalog.get().paths[path] = catalog.Dir(dir_record.id, parent._id, path.name)
328
+ dir = catalog.Dir(dir_record.id, parent._id, path.name)
329
+ Catalog.get().paths[path] = dir
323
330
  session.commit()
324
331
  _logger.info(f'Created directory `{path_str}`.')
325
332
  print(f'Created directory `{path_str}`.')
333
+ return dir
326
334
  except excs.Error as e:
327
335
  if ignore_errors:
328
336
  return
@@ -330,7 +338,7 @@ def create_dir(path_str: str, ignore_errors: bool = False) -> None:
330
338
  raise e
331
339
 
332
340
 
333
- def rm_dir(path_str: str) -> None:
341
+ def drop_dir(path_str: str, force: bool = False, ignore_errors: bool = False) -> None:
334
342
  """Remove a directory.
335
343
 
336
344
  Args:
@@ -340,31 +348,49 @@ def rm_dir(path_str: str) -> None:
340
348
  Error: If the path does not exist or does not designate a directory or if the directory is not empty.
341
349
 
342
350
  Examples:
343
- >>> cl.rm_dir('my_dir')
351
+ >>> cl.drop_dir('my_dir')
344
352
 
345
353
  Remove a subdirectory:
346
354
 
347
- >>> cl.rm_dir('my_dir.sub_dir')
355
+ >>> cl.drop_dir('my_dir.sub_dir')
348
356
  """
357
+ cat = Catalog.get()
349
358
  path = catalog.Path(path_str)
350
- Catalog.get().paths.check_is_valid(path, expected=catalog.Dir)
351
359
 
352
- # make sure it's empty
353
- if len(Catalog.get().paths.get_children(path, child_type=None, recursive=True)) > 0:
354
- raise excs.Error(f'Directory {path_str} is not empty')
355
- # TODO: figure out how to make force=True work in the presence of snapshots
356
- # # delete tables
357
- # for tbl_path in self.paths.get_children(path, child_type=MutableTable, recursive=True):
358
- # self.drop_table(str(tbl_path), force=True)
359
- # # rm subdirs
360
- # for dir_path in self.paths.get_children(path, child_type=Dir, recursive=False):
361
- # self.rm_dir(str(dir_path), force=True)
360
+ try:
361
+ cat.paths.check_is_valid(path, expected=catalog.Dir)
362
+ except Exception as e:
363
+ if ignore_errors or force:
364
+ _logger.info(f'Skipped directory `{path}` (does not exist).')
365
+ return
366
+ else:
367
+ raise e
368
+
369
+ children = cat.paths.get_children(path, child_type=None, recursive=True)
370
+
371
+ if len(children) > 0 and not force:
372
+ raise excs.Error(f'Directory `{path_str}` is not empty.')
373
+
374
+ for child in children:
375
+ assert isinstance(child, catalog.Path)
376
+ # We need to check that the child is still in `cat.paths`, since it is possible it was
377
+ # already deleted as a dependent of a preceding child in the iteration.
378
+ try:
379
+ obj = cat.paths[child]
380
+ except excs.Error:
381
+ continue
382
+ if isinstance(obj, catalog.Dir):
383
+ drop_dir(str(child), force=True)
384
+ else:
385
+ assert isinstance(obj, catalog.Table)
386
+ assert not obj._is_dropped # else it should have been removed from `cat.paths` already
387
+ drop_table(str(child), force=True)
362
388
 
363
389
  with Env.get().engine.begin() as conn:
364
390
  dir = Catalog.get().paths[path]
365
391
  conn.execute(sql.delete(schema.Dir.__table__).where(schema.Dir.id == dir._id))
366
392
  del Catalog.get().paths[path]
367
- _logger.info(f'Removed directory {path_str}')
393
+ _logger.info(f'Removed directory `{path_str}`.')
368
394
 
369
395
 
370
396
  def list_dirs(path_str: str = '', recursive: bool = True) -> list[str]:
@@ -416,28 +442,6 @@ def list_functions() -> pd.DataFrame:
416
442
  return pd_df.hide(axis='index')
417
443
 
418
444
 
419
- def get_path(schema_obj: catalog.SchemaObject) -> str:
420
- """Returns the path to a SchemaObject.
421
-
422
- Args:
423
- schema_obj: SchemaObject to get the path for.
424
-
425
- Returns:
426
- Path to the SchemaObject.
427
- """
428
- path_elements: list[str] = []
429
- dir_id = schema_obj._dir_id
430
- while dir_id is not None:
431
- dir = Catalog.get().paths.get_schema_obj(dir_id)
432
- if dir._dir_id is None:
433
- # this is the root dir with name '', which we don't want to include in the path
434
- break
435
- path_elements.insert(0, dir._name)
436
- dir_id = dir._dir_id
437
- path_elements.append(schema_obj._name)
438
- return '.'.join(path_elements)
439
-
440
-
441
445
  def configure_logging(
442
446
  *,
443
447
  to_stdout: Optional[bool] = None,
@@ -24,6 +24,7 @@ class EmbeddingIndex(IndexBase):
24
24
  - similarity_clause() converts those metrics back to their original form; it is used in expressions outside
25
25
  the Order By clause
26
26
  - order_by_clause() is used exclusively in the ORDER BY clause
27
+ - embedding function parameters are named '<type-name>_embed', where type-name is ColumnType.Type.name
27
28
  """
28
29
 
29
30
  class Metric(enum.Enum):
@@ -38,30 +39,30 @@ class EmbeddingIndex(IndexBase):
38
39
  }
39
40
 
40
41
  def __init__(
41
- self, c: catalog.Column, metric: str, text_embed: Optional[func.Function] = None,
42
- img_embed: Optional[func.Function] = None):
42
+ self, c: catalog.Column, metric: str, string_embed: Optional[func.Function] = None,
43
+ image_embed: Optional[func.Function] = None):
43
44
  metric_names = [m.name.lower() for m in self.Metric]
44
45
  if metric.lower() not in metric_names:
45
46
  raise excs.Error(f'Invalid metric {metric}, must be one of {metric_names}')
46
47
  if not c.col_type.is_string_type() and not c.col_type.is_image_type():
47
48
  raise excs.Error(f'Embedding index requires string or image column')
48
- if c.col_type.is_string_type() and text_embed is None:
49
- raise excs.Error(f'Text embedding function is required for column {c.name} (parameter `txt_embed`)')
50
- if c.col_type.is_image_type() and img_embed is None:
51
- raise excs.Error(f'Image embedding function is required for column {c.name} (parameter `img_embed`)')
52
- if text_embed is not None:
49
+ if c.col_type.is_string_type() and string_embed is None:
50
+ raise excs.Error(f"Text embedding function is required for column {c.name} (parameter 'string_embed')")
51
+ if c.col_type.is_image_type() and image_embed is None:
52
+ raise excs.Error(f"Image embedding function is required for column {c.name} (parameter 'image_embed')")
53
+ if string_embed is not None:
53
54
  # verify signature
54
- self._validate_embedding_fn(text_embed, 'txt_embed', ts.ColumnType.Type.STRING)
55
- if img_embed is not None:
55
+ self._validate_embedding_fn(string_embed, 'string_embed', ts.ColumnType.Type.STRING)
56
+ if image_embed is not None:
56
57
  # verify signature
57
- self._validate_embedding_fn(img_embed, 'img_embed', ts.ColumnType.Type.IMAGE)
58
+ self._validate_embedding_fn(image_embed, 'image_embed', ts.ColumnType.Type.IMAGE)
58
59
 
59
60
  self.metric = self.Metric[metric.upper()]
60
61
  from pixeltable.exprs import ColumnRef
61
- self.value_expr = text_embed(ColumnRef(c)) if c.col_type.is_string_type() else img_embed(ColumnRef(c))
62
+ self.value_expr = string_embed(ColumnRef(c)) if c.col_type.is_string_type() else image_embed(ColumnRef(c))
62
63
  assert self.value_expr.col_type.is_array_type()
63
- self.txt_embed = text_embed
64
- self.img_embed = img_embed
64
+ self.string_embed = string_embed
65
+ self.image_embed = image_embed
65
66
  vector_size = self.value_expr.col_type.shape[0]
66
67
  assert vector_size is not None
67
68
  self.index_col_type = pgvector.sqlalchemy.Vector(vector_size)
@@ -88,14 +89,14 @@ class EmbeddingIndex(IndexBase):
88
89
  idx.create(bind=conn)
89
90
 
90
91
  def similarity_clause(self, val_column: catalog.Column, item: Any) -> sql.ClauseElement:
91
- """Create a ClauseElement to that represents '<val_column> <op> <item>'"""
92
+ """Create a ClauseElement that represents '<val_column> <op> <item>'"""
92
93
  assert isinstance(item, (str, PIL.Image.Image))
93
94
  if isinstance(item, str):
94
- assert self.txt_embed is not None
95
- embedding = self.txt_embed.exec(item)
95
+ assert self.string_embed is not None
96
+ embedding = self.string_embed.exec(item)
96
97
  if isinstance(item, PIL.Image.Image):
97
- assert self.img_embed is not None
98
- embedding = self.img_embed.exec(item)
98
+ assert self.image_embed is not None
99
+ embedding = self.image_embed.exec(item)
99
100
 
100
101
  if self.metric == self.Metric.COSINE:
101
102
  return val_column.sa_col.cosine_distance(embedding) * -1 + 1
@@ -110,11 +111,11 @@ class EmbeddingIndex(IndexBase):
110
111
  assert isinstance(item, (str, PIL.Image.Image))
111
112
  embedding: Optional[np.ndarray] = None
112
113
  if isinstance(item, str):
113
- assert self.txt_embed is not None
114
- embedding = self.txt_embed.exec(item)
114
+ assert self.string_embed is not None
115
+ embedding = self.string_embed.exec(item)
115
116
  if isinstance(item, PIL.Image.Image):
116
- assert self.img_embed is not None
117
- embedding = self.img_embed.exec(item)
117
+ assert self.image_embed is not None
118
+ embedding = self.image_embed.exec(item)
118
119
  assert embedding is not None
119
120
 
120
121
  if self.metric == self.Metric.COSINE:
@@ -160,12 +161,12 @@ class EmbeddingIndex(IndexBase):
160
161
  def as_dict(self) -> dict:
161
162
  return {
162
163
  'metric': self.metric.name.lower(),
163
- 'txt_embed': None if self.txt_embed is None else self.txt_embed.as_dict(),
164
- 'img_embed': None if self.img_embed is None else self.img_embed.as_dict()
164
+ 'string_embed': None if self.string_embed is None else self.string_embed.as_dict(),
165
+ 'image_embed': None if self.image_embed is None else self.image_embed.as_dict()
165
166
  }
166
167
 
167
168
  @classmethod
168
169
  def from_dict(cls, c: catalog.Column, d: dict) -> EmbeddingIndex:
169
- txt_embed = func.Function.from_dict(d['txt_embed']) if d['txt_embed'] is not None else None
170
- img_embed = func.Function.from_dict(d['img_embed']) if d['img_embed'] is not None else None
171
- return cls(c, metric=d['metric'], text_embed=txt_embed, img_embed=img_embed)
170
+ string_embed = func.Function.from_dict(d['string_embed']) if d['string_embed'] is not None else None
171
+ image_embed = func.Function.from_dict(d['image_embed']) if d['image_embed'] is not None else None
172
+ return cls(c, metric=d['metric'], string_embed=string_embed, image_embed=image_embed)
@@ -222,12 +222,12 @@ class Project(ExternalStore, abc.ABC):
222
222
  if t_col not in t_cols:
223
223
  if is_user_specified_col_mapping:
224
224
  raise excs.Error(
225
- f'Column name `{t_col}` appears as a key in `col_mapping`, but Table `{table.get_name()}` '
225
+ f'Column name `{t_col}` appears as a key in `col_mapping`, but Table `{table.name}` '
226
226
  'contains no such column.'
227
227
  )
228
228
  else:
229
229
  raise excs.Error(
230
- f'Column `{t_col}` does not exist in Table `{table.get_name()}`. Either add a column `{t_col}`, '
230
+ f'Column `{t_col}` does not exist in Table `{table.name}`. Either add a column `{t_col}`, '
231
231
  f'or specify a `col_mapping` to associate a different column with the external field `{ext_col}`.'
232
232
  )
233
233
  if ext_col not in export_cols and ext_col not in import_cols:
pixeltable/io/globals.py CHANGED
@@ -50,7 +50,7 @@ def create_label_studio_project(
50
50
  `ls_project_0`, `ls_project_1`, etc.
51
51
  title: An optional title for the Label Studio project. This is the title that annotators
52
52
  will see inside Label Studio. Unlike `name`, it does not need to be an identifier and
53
- does not need to be unique. If not specified, the table name `t.get_name()` will be used.
53
+ does not need to be unique. If not specified, the table name `t.name` will be used.
54
54
  media_import_method: The method to use when transferring media files to Label Studio:
55
55
  - `post`: Media will be sent to Label Studio via HTTP post. This should generally only be used for
56
56
  prototyping; due to restrictions in Label Studio, it can only be used with projects that have
@@ -95,7 +95,7 @@ class LabelStudioProject(Project):
95
95
  return {ANNOTATIONS_COLUMN: pxt.JsonType(nullable=True)}
96
96
 
97
97
  def sync(self, t: Table, export_data: bool, import_data: bool) -> SyncStatus:
98
- _logger.info(f'Syncing Label Studio project "{self.project_title}" with table `{t.get_name()}`'
98
+ _logger.info(f'Syncing Label Studio project "{self.project_title}" with table `{t.name}`'
99
99
  f' (export: {export_data}, import: {import_data}).')
100
100
  # Collect all existing tasks into a dict with entries `rowid: task`
101
101
  tasks = {tuple(task['meta']['rowid']): task for task in self.__fetch_all_tasks()}
@@ -386,7 +386,7 @@ class LabelStudioProject(Project):
386
386
  updates = [{'_rowid': rowid, local_annotations_col.name: ann} for rowid, ann in annotations.items()]
387
387
  if len(updates) > 0:
388
388
  _logger.info(
389
- f'Updating table `{t.get_name()}`, column `{local_annotations_col.name}` with {len(updates)} total annotations.'
389
+ f'Updating table `{t.name}`, column `{local_annotations_col.name}` with {len(updates)} total annotations.'
390
390
  )
391
391
  # batch_update currently doesn't propagate from views to base tables. As a workaround, we call
392
392
  # batch_update on the actual ancestor table that holds the annotations column.
@@ -554,7 +554,7 @@ class LabelStudioProject(Project):
554
554
 
555
555
  if title is None:
556
556
  # `title` defaults to table name
557
- title = t.get_name()
557
+ title = t.name
558
558
 
559
559
  # Create a column to hold the annotations, if one does not yet exist
560
560
  if col_mapping is None or ANNOTATIONS_COLUMN in col_mapping.values():
@@ -10,7 +10,7 @@ import sqlalchemy.orm as orm
10
10
  from .schema import SystemInfo, SystemInfoMd
11
11
 
12
12
  # current version of the metadata; this is incremented whenever the metadata schema changes
13
- VERSION = 17
13
+ VERSION = 18
14
14
 
15
15
 
16
16
  def create_system_info(engine: sql.engine.Engine) -> None:
@@ -0,0 +1,26 @@
1
+ import sqlalchemy as sql
2
+
3
+ from pixeltable.metadata import register_converter
4
+ from pixeltable.metadata.converters.util import convert_table_md
5
+
6
+
7
+ @register_converter(version=17)
8
+ def _(engine: sql.engine.Engine) -> None:
9
+ convert_table_md(
10
+ engine,
11
+ table_md_updater=__update_table_md
12
+ )
13
+
14
+
15
+ def __update_table_md(table_md: dict) -> None:
16
+ # key changes in IndexMd.init_args: img_embed -> image_embed, txt_embed -> string_embed
17
+ if len(table_md['index_md']) == 0:
18
+ return
19
+ for idx_md in table_md['index_md'].values():
20
+ if not idx_md['class_fqn'].endswith('.EmbeddingIndex'):
21
+ continue
22
+ init_dict = idx_md['init_args']
23
+ init_dict['image_embed'] = init_dict['img_embed']
24
+ del init_dict['img_embed']
25
+ init_dict['string_embed'] = init_dict['txt_embed']
26
+ del init_dict['txt_embed']
@@ -253,7 +253,7 @@ class Dumper:
253
253
  add_column('c6_to_string', t.c6.apply(json.dumps))
254
254
  add_column('c6_back_to_json', t[f'{col_prefix}_c6_to_string'].apply(json.loads))
255
255
 
256
- t.add_embedding_index(f'{col_prefix}_function_call', text_embed=embed_udf.clip_text_embed)
256
+ t.add_embedding_index(f'{col_prefix}_function_call', string_embed=embed_udf.clip_text_embed)
257
257
 
258
258
  # query()
259
259
  @t.query
@@ -1,9 +1,9 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pixeltable
3
- Version: 0.2.11
3
+ Version: 0.2.12
4
4
  Summary: Pixeltable: The Multimodal AI Data Plane
5
- Author: Marcel Kornacker
6
- Author-email: marcelk@gmail.com
5
+ Author: Pixeltable, Inc.
6
+ Author-email: contact@pixeltable.com
7
7
  Requires-Python: >=3.9,<4.0
8
8
  Classifier: Programming Language :: Python :: 3
9
9
  Classifier: Programming Language :: Python :: 3.9
@@ -1,5 +1,5 @@
1
- pixeltable/__init__.py,sha256=GNlV3jQKDQKTrOq8BVvPsnd9W9ROneP9-1C-zgNSWsg,1275
2
- pixeltable/__version__.py,sha256=EOLZYNHwHWPLWPMk2nm8H1fHHCn1KsQtw2ovnB_eAnY,114
1
+ pixeltable/__init__.py,sha256=SkhQ6olglhj3NdcyVYzsTwpTZs8uVmtJa9INfsQV9gk,1267
2
+ pixeltable/__version__.py,sha256=VEuOm2X4I15fkc_I-vzJLLY4TEd1E8QfUbdSN8vRrVA,114
3
3
  pixeltable/catalog/__init__.py,sha256=E41bxaPeQIcgRYzTWc2vkDOboQhRymrJf4IcHQO7o_8,453
4
4
  pixeltable/catalog/catalog.py,sha256=8gsFWm6o9Qg4_BEO6oejdpmP4MAOlhmuKRaJP0o2UPU,7906
5
5
  pixeltable/catalog/column.py,sha256=Dmc6CgFLExJy3tdvuX0Emjc8SgqZvmCbAHozibO1-G0,9417
@@ -9,12 +9,12 @@ pixeltable/catalog/insertable_table.py,sha256=u2yMCw1lWSIeRAknIKQmSMWS_3oLTn1kCR
9
9
  pixeltable/catalog/named_function.py,sha256=UhHaimM_uJHS-0RQcqGOgvWeZtMfKsIgSeKSRwT2moU,1149
10
10
  pixeltable/catalog/path.py,sha256=QgccEi_QOfaKt8YsR2zLtd_z7z7QQkU_1kprJFi2SPQ,1677
11
11
  pixeltable/catalog/path_dict.py,sha256=xfvxg1Ze5jZCARUGASF2DRbQPh7pRVTYhuJ_u82gYUo,5941
12
- pixeltable/catalog/schema_object.py,sha256=Sf8aGARpsPeRUz1NjsZ97oSBMyElsctP39uU9x9Ic80,964
13
- pixeltable/catalog/table.py,sha256=5CSEASIJqWJ0l1mDERYMqj_5rD--y4F8yhqq3Uu0lAI,39473
14
- pixeltable/catalog/table_version.py,sha256=JrwwvG1vizCDJ-11GgTwnCg2XSOqyrJ6w2wbqmWYR60,58582
12
+ pixeltable/catalog/schema_object.py,sha256=1GuxN68P3c7g0gmY8doB6qoUGJsalyECWaO-KEi96D0,1879
13
+ pixeltable/catalog/table.py,sha256=J4MRWMly9uz-utLnXIKh7OTUbY0t4_ogHeEuDcApEPk,41151
14
+ pixeltable/catalog/table_version.py,sha256=qnPIIxwIOQbukV84ydeksVNURb37kAnGOXI-ZO2kkCo,57233
15
15
  pixeltable/catalog/table_version_path.py,sha256=6JZlgKMYa3Xf8p_2Z-iDIFIcfuYRyjbpc3_CC9l1HME,6396
16
16
  pixeltable/catalog/view.py,sha256=3OAk-NBPlSagMCsdVtcx35jHD80SIYCuOy5dc1kM7Fs,10403
17
- pixeltable/dataframe.py,sha256=O8tAgOqFa_twjak-hDIBoXB5MZx-ZyzYOCeQr8jCO-0,34339
17
+ pixeltable/dataframe.py,sha256=7w-1-RGIGEJFJydFazHOzHLFSjZ4Ejvv_JjP4fKfRjo,34333
18
18
  pixeltable/env.py,sha256=WO_WLfRj9Fft6QyW89S9cw47RTg1ALviStu9pNygJEQ,21635
19
19
  pixeltable/exceptions.py,sha256=MSP9zeL0AmXT93XqjdvgGN4rzno1_KRrGriq6hpemnw,376
20
20
  pixeltable/exec/__init__.py,sha256=RK7SKvrQ7Ky3G_LXDP4Bf7lHmMM_uYZl8dJaZYs0FjY,454
@@ -32,7 +32,7 @@ pixeltable/exprs/__init__.py,sha256=7dwrdk-NpF66OT-m5yNtFEhq-o1T476dnXHjluw2K1s,
32
32
  pixeltable/exprs/arithmetic_expr.py,sha256=sWBYCBKI6IHj9ASwDcm2BlkQ5gleVtKtmpiPvzFNBJM,4386
33
33
  pixeltable/exprs/array_slice.py,sha256=VmWc6iFusrM85MjyEBBCfXG1Jnt8-Gr6-J88BXxNoOE,2131
34
34
  pixeltable/exprs/column_property_ref.py,sha256=0PHiBys0fxe2LgjaMId5UHob4E-ZggyPLnnW41RgA0E,2706
35
- pixeltable/exprs/column_ref.py,sha256=jmKUHuRev3PI-58BqYQVcBPLQ3E7bM1mPlFSVlwa5b0,5554
35
+ pixeltable/exprs/column_ref.py,sha256=rlw6Ic_atAfPZpEBNAqbRZauMeEUci2rDyVoHd1pA1I,5195
36
36
  pixeltable/exprs/comparison.py,sha256=hP3M_lMWcFgENBICFosZPw2lRm1R6_qM_O9bKPmWJGI,4789
37
37
  pixeltable/exprs/compound_predicate.py,sha256=Gh22MKi625m5A_RunVRd-a1XFi-fitikqBVz2VNXKrs,3830
38
38
  pixeltable/exprs/data_row.py,sha256=RTBw1cBt29g_9g_hgdEYZ5aiHl7WZMBaBC2fOMOfwOc,8668
@@ -52,7 +52,7 @@ pixeltable/exprs/object_ref.py,sha256=eTcx84aWRI59fIiGvbdv3_cfL0XW4xEFQ4lwpLpJkM
52
52
  pixeltable/exprs/predicate.py,sha256=OSDgjfSqiK7J_5GZMUXMvjfyomKEGi0JNxeB073SGXw,1859
53
53
  pixeltable/exprs/row_builder.py,sha256=0OYd51J2ECPHkk2iN3MfYpS7LqnHTV5l5ubsVcy0dJA,15926
54
54
  pixeltable/exprs/rowid_ref.py,sha256=74w4rEy21YysTVbyKNc3op-pYFqDAx8VJdtl7ZPpxHs,4268
55
- pixeltable/exprs/similarity_expr.py,sha256=i5urJiSD43lJIi0AnMZhNvl7q5I8P3BPRuMx4IaAym0,3015
55
+ pixeltable/exprs/similarity_expr.py,sha256=DqLOWtwPn9LxBRPm-d5Qz0yZ7w9YTOP8w0rgbr-6Lyg,3702
56
56
  pixeltable/exprs/type_cast.py,sha256=JMg8p1qYoFfiAXfJPSbTEnfrK7lRO_JMaqlPHOrhNQU,1793
57
57
  pixeltable/exprs/variable.py,sha256=Kg_O4ytcHYZFijIyMHYBJn063cTKU1-YE583FAz8Qaw,1361
58
58
  pixeltable/ext/__init__.py,sha256=0uugfuME1FybVo-MdxaVNGagRjhcvNTnv5MZUem6Cyo,269
@@ -74,22 +74,22 @@ pixeltable/functions/fireworks.py,sha256=sbZy3HRn8o01nT4k1lOJJ_jGrjhBNkYmj1_Trao
74
74
  pixeltable/functions/globals.py,sha256=MVKcwvfRaqBMV58KBxp5ACMsTbj29WD97AOg1N5ZheY,1596
75
75
  pixeltable/functions/huggingface.py,sha256=qFdFpBX4R8Iz6fB0PKOwicdfvnEV7DQhtdcvsIQz55I,7372
76
76
  pixeltable/functions/image.py,sha256=lC5PVvx0hXlINGcURLrLeTT7xUMXla6QUC1T9t4-A0Y,5440
77
- pixeltable/functions/openai.py,sha256=Q6imhdlyZRuWNgBq9msqXMT8up_0e9jSsZPfSJ5DgUY,7839
77
+ pixeltable/functions/openai.py,sha256=D0cDXe_zy1kUdKqM9T3qs7OBAUCrAINUy5P0DO0OSXc,7814
78
78
  pixeltable/functions/string.py,sha256=Ae_weygd9Aj98buLC4tPLRYGg3LGSJEpXaqr93TF4nw,645
79
79
  pixeltable/functions/together.py,sha256=2vHOoXMUIpeYwTYGTr3hDHePzy8zepvoeyORgV_9n34,4412
80
80
  pixeltable/functions/util.py,sha256=F2iiIL7UfhYdCVzdCa3efYqWbaeLKFrbycKnuPkG57M,650
81
81
  pixeltable/functions/video.py,sha256=yn52MimAVrSzUyAUtyxbd1RWveX_TyjwsomBuhK9V60,6516
82
82
  pixeltable/functions/whisper.py,sha256=s7C4eV5tCJed-4Joob5LojGFEHPgapmT8awFPVxBKgQ,2199
83
- pixeltable/globals.py,sha256=xSj0pmgmRavmD_-TOFhNXd8slskOC2R95el9L7pyzxM,14795
83
+ pixeltable/globals.py,sha256=L_40LFK-wW5wJ8DtuLoeS2gCgm9rmwUjx5FLa5hMOmw,14828
84
84
  pixeltable/index/__init__.py,sha256=XBwetNQQwnz0fiKwonOKhyy_U32l_cjt77kNvEIdjWs,102
85
85
  pixeltable/index/base.py,sha256=YAQ5Dz1mfI0dfu9rxWHWroE8TjB90yKfPtXAzoADq38,1568
86
86
  pixeltable/index/btree.py,sha256=NE4GYhcJWYJhdKyeHI0sQBlFvUaIgGOF9KLyCZOfFjE,1822
87
- pixeltable/index/embedding_index.py,sha256=AYphEggN-0B4GNrm4nMmi46CEtrQw5tguyk67BK2sWo,7627
87
+ pixeltable/index/embedding_index.py,sha256=U1wAjcTYvw3uJf3QHIOzBV8FLOUn8IeaFsLzUb_QTmc,7829
88
88
  pixeltable/io/__init__.py,sha256=DdqOteR1Y-yRvFS0VojXHryBtIGzH8nAN-1MBj3LGRk,493
89
- pixeltable/io/external_store.py,sha256=ZtDkfHMBo73JMKJ-q3J5FMWztYog38APn8kg81nCg34,16434
90
- pixeltable/io/globals.py,sha256=lfaXosEu1e2xkWhwUCUJNhpnZxXlBdf6jPlf8cf4F50,4546
89
+ pixeltable/io/external_store.py,sha256=owha7bEmA1ZvpZvBLBS6LHi9uLq1rUIkrMTGarsbjOU,16422
90
+ pixeltable/io/globals.py,sha256=v-L3ZQrjyutP_DHGXanymGjSJugWJ3STNUkzgylihOU,4540
91
91
  pixeltable/io/hf_datasets.py,sha256=h5M1NkXOvEU8kaeT3AON1A18Vmhnc1lVo5a3TZ5AAic,8004
92
- pixeltable/io/label_studio.py,sha256=ZWXTuIFk4CheM2ON0VDyb_34L9BJUY7iUJLcPvrll7c,28782
92
+ pixeltable/io/label_studio.py,sha256=yKCXPDZUev04O5r3tP5vrPpXe5KP4deQfbLHewNfVXQ,28764
93
93
  pixeltable/io/pandas.py,sha256=cDHUDW2CGiBbsEJB9zE5vkXopTKxDdI-CZxNcp0OnIk,6478
94
94
  pixeltable/io/parquet.py,sha256=i4hvYHsARe2GnZHxNmI66Vf3tr1sIFLN6KGCJYvH3o8,8149
95
95
  pixeltable/iterators/__init__.py,sha256=sjldFckkT8aVRiKgEP6faeAK2NQBdzbmpwAeRhI1FkM,366
@@ -97,18 +97,19 @@ pixeltable/iterators/base.py,sha256=cnEh1tNN2JAxRzrLTg3dhun3N1oNQ8vifCm6ts3_UiE,
97
97
  pixeltable/iterators/document.py,sha256=netSCJatG8NcgbHZ69BvQVICdAorQlYi8OlcpqwLQD4,19436
98
98
  pixeltable/iterators/string.py,sha256=NG_fWc_GAITDfzl6MvrDOMrSoMcZdMZf6hPQztCSatE,1305
99
99
  pixeltable/iterators/video.py,sha256=xtxODL1AfZwTfHVzWekhTCLA8gwTJIvJFdxC0KecD9Q,3836
100
- pixeltable/metadata/__init__.py,sha256=uxufhyUkogiMhztO3OoS59Rqs5pWOHqFJqPO5mZfrVA,2172
100
+ pixeltable/metadata/__init__.py,sha256=R4K9KSnefmvSCxXbBKTLAHD3QuHXgQ3a0wTs-eg94Os,2172
101
101
  pixeltable/metadata/converters/convert_10.py,sha256=J1_r7LNNAWTdb042AwqFpJ4sEB-i4qhUdk5iOjcZk34,719
102
102
  pixeltable/metadata/converters/convert_12.py,sha256=Ci-qyZW1gqci-8wnjeOB5afdq7KTuN-hVSV9OqSPx8g,162
103
103
  pixeltable/metadata/converters/convert_13.py,sha256=yFR6lD3pOrZ46ZQBFKYvxiIYa7rRxh46Bsq7yiCBNak,1356
104
104
  pixeltable/metadata/converters/convert_14.py,sha256=o4Dwu5wujJYILN-2chg3xCSUsh4cnn0sImv6rc75rSM,388
105
105
  pixeltable/metadata/converters/convert_15.py,sha256=N-Lt3OdOrUprN-z1gFcxniZgAtZ7jzup_YUZzXX6EtY,1709
106
106
  pixeltable/metadata/converters/convert_16.py,sha256=SvcWOYgLwRw_gLTnLbCSI9f2cpdkXazYOmmtJUOOzv4,476
107
+ pixeltable/metadata/converters/convert_17.py,sha256=vJg4y2lg53WSj9OSntWsdUiCr6yRgMQm0eFbs_Geqjg,861
107
108
  pixeltable/metadata/converters/util.py,sha256=AcYs3yUICl93y8whf0pkeWZoCzE4JuUMafmcYMyJUCY,2618
108
109
  pixeltable/metadata/schema.py,sha256=WJZ1YPgS88rFElXbjYgDhcrI4VReR1I9VPOnTkoHvoI,8418
109
110
  pixeltable/plan.py,sha256=MXWgwQXD40GB57xQiq_wjXF3OL0XTEjjhQslMfFTt3w,32831
110
111
  pixeltable/store.py,sha256=UDn-UMYuL6dTUym3yFsVhv9hUtnP_QtzhDJzsFInApc,18853
111
- pixeltable/tool/create_test_db_dump.py,sha256=AYYHJKSDx7CMjNH6WUoOlUCICFZKRqQn7sSL3jKkDEw,10727
112
+ pixeltable/tool/create_test_db_dump.py,sha256=iaMfBEyGHSMhhOJDyR0M9Idj9HkNgeTjmYLVhScjMZA,10729
112
113
  pixeltable/tool/create_test_video.py,sha256=OLfccymYReIpzE8osZn4rQvLXxxiPC_l0vc06U74hVM,2899
113
114
  pixeltable/tool/embed_udf.py,sha256=llHUhjGnCMp7Wyz7eHgKZV2v6o2ZWSgLQKscESuHK_o,269
114
115
  pixeltable/type_system.py,sha256=oXnDVoP90ic6WSTF_DcgWDLx0MYKEU0ggGTesAKahic,29505
@@ -126,7 +127,7 @@ pixeltable/utils/pytorch.py,sha256=BR4tgfUWw-2rwWTOgzXj5qdMBpe1Arpp5SK4ax6jjpk,3
126
127
  pixeltable/utils/s3.py,sha256=rkanuhk9DWvSfmbOLQW1j1Iov4sl2KhxGGKN-AJ8LSE,432
127
128
  pixeltable/utils/sql.py,sha256=5n5_OmXAGtqFdL6z5XvgnU-vlx6Ba6f1WJrO1ZwUle8,765
128
129
  pixeltable/utils/transactional_directory.py,sha256=UGzCrGtLR3hEEf8sYGuWBzLVFAEQml3vdIavigWeTBM,1349
129
- pixeltable-0.2.11.dist-info/LICENSE,sha256=0UNMmwuqWPC0xDY1NWMm4uNJ2_MyA1pnTNRgQTvuBiQ,746
130
- pixeltable-0.2.11.dist-info/METADATA,sha256=c4rIZLuAKseDSgl84ndgoMupaq9XUThLxwFmaBeyjtA,9815
131
- pixeltable-0.2.11.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
132
- pixeltable-0.2.11.dist-info/RECORD,,
130
+ pixeltable-0.2.12.dist-info/LICENSE,sha256=0UNMmwuqWPC0xDY1NWMm4uNJ2_MyA1pnTNRgQTvuBiQ,746
131
+ pixeltable-0.2.12.dist-info/METADATA,sha256=oVeTMu4HQQFxqj4AWPpiZiNPs9O5IUzxlBBHazAInJ0,9820
132
+ pixeltable-0.2.12.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
133
+ pixeltable-0.2.12.dist-info/RECORD,,