pixeltable 0.3.10__py3-none-any.whl → 0.3.11__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of pixeltable might be problematic. Click here for more details.
- pixeltable/__init__.py +1 -1
- pixeltable/__version__.py +2 -2
- pixeltable/catalog/__init__.py +2 -1
- pixeltable/catalog/catalog.py +63 -36
- pixeltable/catalog/column.py +6 -4
- pixeltable/catalog/dir.py +5 -5
- pixeltable/catalog/globals.py +12 -14
- pixeltable/catalog/insertable_table.py +4 -7
- pixeltable/catalog/path.py +2 -2
- pixeltable/catalog/table.py +64 -56
- pixeltable/catalog/table_version.py +42 -40
- pixeltable/catalog/table_version_handle.py +3 -0
- pixeltable/catalog/table_version_path.py +1 -1
- pixeltable/catalog/view.py +8 -7
- pixeltable/dataframe.py +5 -3
- pixeltable/env.py +108 -42
- pixeltable/exec/__init__.py +2 -0
- pixeltable/exec/aggregation_node.py +6 -8
- pixeltable/exec/cache_prefetch_node.py +4 -7
- pixeltable/exec/component_iteration_node.py +1 -3
- pixeltable/exec/data_row_batch.py +1 -2
- pixeltable/exec/exec_context.py +1 -1
- pixeltable/exec/exec_node.py +1 -2
- pixeltable/exec/expr_eval/__init__.py +2 -0
- pixeltable/exec/expr_eval/evaluators.py +137 -20
- pixeltable/exec/expr_eval/expr_eval_node.py +43 -64
- pixeltable/exec/expr_eval/globals.py +68 -7
- pixeltable/exec/expr_eval/schedulers.py +25 -23
- pixeltable/exec/in_memory_data_node.py +8 -6
- pixeltable/exec/row_update_node.py +3 -4
- pixeltable/exec/sql_node.py +16 -17
- pixeltable/exprs/__init__.py +1 -1
- pixeltable/exprs/column_property_ref.py +1 -1
- pixeltable/exprs/column_ref.py +3 -3
- pixeltable/exprs/compound_predicate.py +1 -1
- pixeltable/exprs/data_row.py +17 -1
- pixeltable/exprs/expr.py +12 -12
- pixeltable/exprs/function_call.py +34 -2
- pixeltable/exprs/json_mapper.py +95 -48
- pixeltable/exprs/json_path.py +3 -4
- pixeltable/exprs/method_ref.py +2 -2
- pixeltable/exprs/object_ref.py +2 -2
- pixeltable/exprs/row_builder.py +33 -6
- pixeltable/exprs/similarity_expr.py +1 -1
- pixeltable/exprs/sql_element_cache.py +1 -1
- pixeltable/exprs/string_op.py +2 -2
- pixeltable/ext/__init__.py +1 -1
- pixeltable/ext/functions/__init__.py +1 -1
- pixeltable/ext/functions/whisperx.py +1 -1
- pixeltable/ext/functions/yolox.py +1 -1
- pixeltable/func/aggregate_function.py +1 -1
- pixeltable/func/callable_function.py +2 -5
- pixeltable/func/expr_template_function.py +22 -2
- pixeltable/func/function.py +4 -5
- pixeltable/func/function_registry.py +1 -1
- pixeltable/func/signature.py +1 -1
- pixeltable/func/udf.py +2 -2
- pixeltable/functions/__init__.py +1 -1
- pixeltable/functions/anthropic.py +2 -2
- pixeltable/functions/audio.py +1 -1
- pixeltable/functions/deepseek.py +1 -1
- pixeltable/functions/fireworks.py +1 -1
- pixeltable/functions/globals.py +6 -6
- pixeltable/functions/huggingface.py +1 -1
- pixeltable/functions/image.py +1 -1
- pixeltable/functions/json.py +1 -1
- pixeltable/functions/llama_cpp.py +1 -1
- pixeltable/functions/math.py +1 -1
- pixeltable/functions/mistralai.py +1 -1
- pixeltable/functions/ollama.py +1 -1
- pixeltable/functions/openai.py +2 -2
- pixeltable/functions/replicate.py +1 -1
- pixeltable/functions/string.py +1 -1
- pixeltable/functions/timestamp.py +1 -1
- pixeltable/functions/together.py +1 -1
- pixeltable/functions/util.py +1 -1
- pixeltable/functions/video.py +2 -2
- pixeltable/functions/vision.py +2 -2
- pixeltable/index/embedding_index.py +12 -1
- pixeltable/io/__init__.py +5 -3
- pixeltable/io/fiftyone.py +6 -7
- pixeltable/io/label_studio.py +21 -20
- pixeltable/io/pandas.py +6 -5
- pixeltable/iterators/__init__.py +1 -1
- pixeltable/metadata/__init__.py +5 -3
- pixeltable/metadata/converters/convert_24.py +3 -3
- pixeltable/metadata/converters/convert_25.py +1 -1
- pixeltable/metadata/converters/convert_29.py +1 -1
- pixeltable/store.py +2 -2
- pixeltable/type_system.py +19 -7
- pixeltable/utils/console_output.py +3 -2
- pixeltable/utils/coroutine.py +3 -3
- pixeltable/utils/dbms.py +66 -0
- pixeltable/utils/documents.py +61 -67
- pixeltable/utils/filecache.py +1 -1
- pixeltable/utils/http_server.py +3 -2
- pixeltable/utils/pytorch.py +1 -1
- pixeltable/utils/sql.py +1 -1
- pixeltable-0.3.11.dist-info/METADATA +436 -0
- pixeltable-0.3.11.dist-info/RECORD +179 -0
- pixeltable/catalog/path_dict.py +0 -169
- pixeltable-0.3.10.dist-info/METADATA +0 -382
- pixeltable-0.3.10.dist-info/RECORD +0 -179
- {pixeltable-0.3.10.dist-info → pixeltable-0.3.11.dist-info}/LICENSE +0 -0
- {pixeltable-0.3.10.dist-info → pixeltable-0.3.11.dist-info}/WHEEL +0 -0
- {pixeltable-0.3.10.dist-info → pixeltable-0.3.11.dist-info}/entry_points.txt +0 -0
pixeltable/__init__.py
CHANGED
pixeltable/__version__.py
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
# These version placeholders will be replaced during build.
|
|
2
|
-
__version__ = '0.3.
|
|
3
|
-
__version_tuple__ = (0, 3,
|
|
2
|
+
__version__ = '0.3.11'
|
|
3
|
+
__version_tuple__ = (0, 3, 11)
|
pixeltable/catalog/__init__.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
# ruff: noqa: F401
|
|
2
|
+
|
|
1
3
|
from .catalog import Catalog
|
|
2
4
|
from .column import Column
|
|
3
5
|
from .dir import Dir
|
|
@@ -5,7 +7,6 @@ from .globals import IfExistsParam, IfNotExistsParam, MediaValidation, UpdateSta
|
|
|
5
7
|
from .insertable_table import InsertableTable
|
|
6
8
|
from .named_function import NamedFunction
|
|
7
9
|
from .path import Path
|
|
8
|
-
from .path_dict import PathDict
|
|
9
10
|
from .schema_object import SchemaObject
|
|
10
11
|
from .table import Table
|
|
11
12
|
from .table_version import TableVersion
|
pixeltable/catalog/catalog.py
CHANGED
|
@@ -10,10 +10,10 @@ from uuid import UUID
|
|
|
10
10
|
import psycopg
|
|
11
11
|
import sqlalchemy as sql
|
|
12
12
|
|
|
13
|
-
import
|
|
14
|
-
import pixeltable.metadata.schema as schema
|
|
13
|
+
from pixeltable import exceptions as excs
|
|
15
14
|
from pixeltable.env import Env
|
|
16
15
|
from pixeltable.iterators import ComponentIterator
|
|
16
|
+
from pixeltable.metadata import schema
|
|
17
17
|
|
|
18
18
|
from .dir import Dir
|
|
19
19
|
from .globals import IfExistsParam, IfNotExistsParam, MediaValidation
|
|
@@ -79,7 +79,7 @@ def _retry_loop(op: Callable[..., T]) -> Callable[..., T]:
|
|
|
79
79
|
# in order for retry to work, we need to make sure that there aren't any prior db updates
|
|
80
80
|
# that are part of an ongoing transaction
|
|
81
81
|
assert not Env.get().in_xact()
|
|
82
|
-
with Env.get().begin_xact()
|
|
82
|
+
with Env.get().begin_xact():
|
|
83
83
|
return op(*args, **kwargs)
|
|
84
84
|
except sql.exc.DBAPIError as e:
|
|
85
85
|
if isinstance(e.orig, psycopg.errors.SerializationFailure) and num_remaining_retries > 0:
|
|
@@ -222,10 +222,10 @@ class Catalog:
|
|
|
222
222
|
|
|
223
223
|
add_dir: Optional[schema.Dir] = None
|
|
224
224
|
drop_dir: Optional[schema.Dir] = None
|
|
225
|
-
for p in sorted(
|
|
225
|
+
for p in sorted(dir_paths):
|
|
226
226
|
dir = self._get_dir(p, for_update=True)
|
|
227
227
|
if dir is None:
|
|
228
|
-
raise excs.Error(f'Directory {str(p)!r} does not exist')
|
|
228
|
+
raise excs.Error(f'Directory {str(p)!r} does not exist.')
|
|
229
229
|
if p == add_dir_path:
|
|
230
230
|
add_dir = dir
|
|
231
231
|
if p == drop_dir_path:
|
|
@@ -236,14 +236,14 @@ class Catalog:
|
|
|
236
236
|
add_obj = self._get_dir_entry(add_dir.id, add_name, for_update=True)
|
|
237
237
|
if add_obj is not None and raise_if_exists:
|
|
238
238
|
add_path = add_dir_path.append(add_name)
|
|
239
|
-
raise excs.Error(f'Path {str(add_path)!r} already exists')
|
|
239
|
+
raise excs.Error(f'Path {str(add_path)!r} already exists.')
|
|
240
240
|
|
|
241
241
|
drop_obj: Optional[SchemaObject] = None
|
|
242
242
|
if drop_dir is not None:
|
|
243
243
|
drop_path = drop_dir_path.append(drop_name)
|
|
244
244
|
drop_obj = self._get_dir_entry(drop_dir.id, drop_name, for_update=True)
|
|
245
245
|
if drop_obj is None and raise_if_not_exists:
|
|
246
|
-
raise excs.Error(f'Path {str(drop_path)!r} does not exist')
|
|
246
|
+
raise excs.Error(f'Path {str(drop_path)!r} does not exist.')
|
|
247
247
|
if drop_obj is not None and drop_expected is not None and not isinstance(drop_obj, drop_expected):
|
|
248
248
|
raise excs.Error(
|
|
249
249
|
f'{str(drop_path)!r} needs to be a {drop_expected._display_name()} '
|
|
@@ -254,10 +254,13 @@ class Catalog:
|
|
|
254
254
|
return add_obj, add_dir_obj, drop_obj
|
|
255
255
|
|
|
256
256
|
def _get_dir_entry(self, dir_id: UUID, name: str, for_update: bool = False) -> Optional[SchemaObject]:
|
|
257
|
+
user = Env.get().user
|
|
257
258
|
conn = Env.get().conn
|
|
258
259
|
|
|
259
260
|
# check for subdirectory
|
|
260
|
-
q = sql.select(schema.Dir).where(
|
|
261
|
+
q = sql.select(schema.Dir).where(
|
|
262
|
+
schema.Dir.parent_id == dir_id, schema.Dir.md['name'].astext == name, schema.Dir.md['user'].astext == user
|
|
263
|
+
)
|
|
261
264
|
if for_update:
|
|
262
265
|
q = q.with_for_update()
|
|
263
266
|
# _debug_print(for_update, f'dir name={name!r} parent={dir_id}')
|
|
@@ -267,19 +270,23 @@ class Catalog:
|
|
|
267
270
|
# return Dir(dir_record.id, dir_record.parent_id, name)
|
|
268
271
|
rows = conn.execute(q).all()
|
|
269
272
|
if len(rows) > 1:
|
|
270
|
-
|
|
273
|
+
raise AssertionError(rows)
|
|
271
274
|
if len(rows) == 1:
|
|
272
275
|
dir_record = schema.Dir(**rows[0]._mapping)
|
|
273
276
|
return Dir(dir_record.id, dir_record.parent_id, name)
|
|
274
277
|
|
|
275
278
|
# check for table
|
|
276
|
-
q = sql.select(schema.Table.id).where(
|
|
279
|
+
q = sql.select(schema.Table.id).where(
|
|
280
|
+
schema.Table.dir_id == dir_id,
|
|
281
|
+
schema.Table.md['name'].astext == name,
|
|
282
|
+
schema.Table.md['user'].astext == user,
|
|
283
|
+
)
|
|
277
284
|
if for_update:
|
|
278
285
|
q = q.with_for_update()
|
|
279
286
|
# _debug_print(for_update, f'table name={name!r} parent={dir_id}')
|
|
280
287
|
tbl_id = conn.execute(q).scalar_one_or_none()
|
|
281
288
|
if tbl_id is not None:
|
|
282
|
-
if not
|
|
289
|
+
if tbl_id not in self._tbls:
|
|
283
290
|
self._tbls[tbl_id] = self._load_tbl(tbl_id)
|
|
284
291
|
return self._tbls[tbl_id]
|
|
285
292
|
|
|
@@ -304,26 +311,32 @@ class Catalog:
|
|
|
304
311
|
if path.is_root:
|
|
305
312
|
# the root dir
|
|
306
313
|
if expected is not None and expected is not Dir:
|
|
307
|
-
raise excs.Error(
|
|
314
|
+
raise excs.Error(
|
|
315
|
+
f'{str(path)!r} needs to be a {expected._display_name()} but is a {Dir._display_name()}'
|
|
316
|
+
)
|
|
308
317
|
dir = self._get_dir(path, for_update=for_update)
|
|
318
|
+
if dir is None:
|
|
319
|
+
raise excs.Error(f'Unknown user: {Env.get().user}')
|
|
309
320
|
return Dir(dir.id, dir.parent_id, dir.md['name'])
|
|
310
321
|
|
|
311
322
|
parent_path = path.parent
|
|
312
323
|
parent_dir = self._get_dir(parent_path, for_update=False)
|
|
313
324
|
if parent_dir is None:
|
|
314
|
-
raise excs.Error(f'Directory {parent_path!r} does not exist')
|
|
325
|
+
raise excs.Error(f'Directory {str(parent_path)!r} does not exist.')
|
|
315
326
|
obj = self._get_dir_entry(parent_dir.id, path.name, for_update=for_update)
|
|
316
327
|
|
|
317
328
|
if obj is None and raise_if_not_exists:
|
|
318
|
-
raise excs.Error(f'Path {path!r} does not exist')
|
|
329
|
+
raise excs.Error(f'Path {str(path)!r} does not exist.')
|
|
319
330
|
elif obj is not None and raise_if_exists:
|
|
320
|
-
raise excs.Error(f'Path {path!r} is an existing {type(obj)._display_name()}')
|
|
331
|
+
raise excs.Error(f'Path {str(path)!r} is an existing {type(obj)._display_name()}.')
|
|
321
332
|
elif obj is not None and expected is not None and not isinstance(obj, expected):
|
|
322
|
-
raise excs.Error(
|
|
333
|
+
raise excs.Error(
|
|
334
|
+
f'{str(path)!r} needs to be a {expected._display_name()} but is a {type(obj)._display_name()}.'
|
|
335
|
+
)
|
|
323
336
|
return obj
|
|
324
337
|
|
|
325
338
|
def get_table_by_id(self, tbl_id: UUID) -> Optional[Table]:
|
|
326
|
-
if not
|
|
339
|
+
if tbl_id not in self._tbls:
|
|
327
340
|
tbl = self._load_tbl(tbl_id)
|
|
328
341
|
if tbl is None:
|
|
329
342
|
return None
|
|
@@ -475,7 +488,7 @@ class Catalog:
|
|
|
475
488
|
# parent = self._get_schema_object(path.parent)
|
|
476
489
|
# assert parent is not None
|
|
477
490
|
# dir = Dir._create(parent._id, path.name)
|
|
478
|
-
# Env.get().console_logger.info(f'Created directory {path!r}.')
|
|
491
|
+
# Env.get().console_logger.info(f'Created directory {str(path)!r}.')
|
|
479
492
|
# return dir
|
|
480
493
|
|
|
481
494
|
if parents:
|
|
@@ -519,7 +532,7 @@ class Catalog:
|
|
|
519
532
|
q = sql.select(sql.func.count()).select_from(schema.Table).where(schema.Table.dir_id == dir_id)
|
|
520
533
|
num_tbls = conn.execute(q).scalar()
|
|
521
534
|
if num_subdirs + num_tbls > 0:
|
|
522
|
-
raise excs.Error(f'Directory {dir_path!r} is not empty.')
|
|
535
|
+
raise excs.Error(f'Directory {str(dir_path)!r} is not empty.')
|
|
523
536
|
|
|
524
537
|
# drop existing subdirs
|
|
525
538
|
dir_q = sql.select(schema.Dir).where(schema.Dir.parent_id == dir_id).with_for_update()
|
|
@@ -551,12 +564,12 @@ class Catalog:
|
|
|
551
564
|
|
|
552
565
|
def get_tbl_version(self, tbl_id: UUID, effective_version: Optional[int]) -> Optional[TableVersion]:
|
|
553
566
|
if (tbl_id, effective_version) not in self._tbl_versions:
|
|
554
|
-
self._tbl_versions[
|
|
555
|
-
return self._tbl_versions[
|
|
567
|
+
self._tbl_versions[tbl_id, effective_version] = self._load_tbl_version(tbl_id, effective_version)
|
|
568
|
+
return self._tbl_versions[tbl_id, effective_version]
|
|
556
569
|
|
|
557
570
|
def add_tbl_version(self, tbl_version: TableVersion) -> None:
|
|
558
571
|
"""Explicitly add a TableVersion"""
|
|
559
|
-
self._tbl_versions[
|
|
572
|
+
self._tbl_versions[tbl_version.id, tbl_version.effective_version] = tbl_version
|
|
560
573
|
# if this is a mutable view, also record it in the base
|
|
561
574
|
if tbl_version.is_view and tbl_version.effective_version is None:
|
|
562
575
|
base = tbl_version.base.get()
|
|
@@ -564,7 +577,7 @@ class Catalog:
|
|
|
564
577
|
|
|
565
578
|
def remove_tbl_version(self, tbl_version: TableVersion) -> None:
|
|
566
579
|
assert (tbl_version.id, tbl_version.effective_version) in self._tbl_versions
|
|
567
|
-
del self._tbl_versions[
|
|
580
|
+
del self._tbl_versions[tbl_version.id, tbl_version.effective_version]
|
|
568
581
|
|
|
569
582
|
def get_dir(self, dir_id: UUID, for_update: bool = False) -> Optional[Dir]:
|
|
570
583
|
"""Return the Dir with the given id, or None if it doesn't exist"""
|
|
@@ -585,20 +598,23 @@ class Catalog:
|
|
|
585
598
|
- S locks on all ancestors
|
|
586
599
|
- X lock on dir if for_update == True, otherwise also an S lock
|
|
587
600
|
"""
|
|
601
|
+
user = Env.get().user
|
|
588
602
|
conn = Env.get().conn
|
|
589
603
|
if path.is_root:
|
|
590
|
-
q = sql.select(schema.Dir).where(schema.Dir.parent_id.is_(None))
|
|
604
|
+
q = sql.select(schema.Dir).where(schema.Dir.parent_id.is_(None), schema.Dir.md['user'].astext == user)
|
|
591
605
|
if for_update:
|
|
592
606
|
q = q.with_for_update()
|
|
593
607
|
# _debug_print(for_update, 'root dir')
|
|
594
|
-
row = conn.execute(q).
|
|
595
|
-
return schema.Dir(**row._mapping)
|
|
608
|
+
row = conn.execute(q).one_or_none()
|
|
609
|
+
return schema.Dir(**row._mapping) if row is not None else None
|
|
596
610
|
else:
|
|
597
611
|
parent_dir = self._get_dir(path.parent, for_update=False)
|
|
598
612
|
if parent_dir is None:
|
|
599
613
|
return None
|
|
600
614
|
q = sql.select(schema.Dir).where(
|
|
601
|
-
schema.Dir.parent_id == parent_dir.id,
|
|
615
|
+
schema.Dir.parent_id == parent_dir.id,
|
|
616
|
+
schema.Dir.md['name'].astext == path.name,
|
|
617
|
+
schema.Dir.md['user'].astext == user,
|
|
602
618
|
)
|
|
603
619
|
if for_update:
|
|
604
620
|
q = q.with_for_update()
|
|
@@ -636,7 +652,7 @@ class Catalog:
|
|
|
636
652
|
if view_md is None:
|
|
637
653
|
# this is a base table
|
|
638
654
|
if (tbl_id, None) not in self._tbl_versions:
|
|
639
|
-
self._tbl_versions[
|
|
655
|
+
self._tbl_versions[tbl_id, None] = self._load_tbl_version(tbl_id, None)
|
|
640
656
|
tbl = InsertableTable(tbl_record.dir_id, TableVersionHandle(tbl_id, None))
|
|
641
657
|
return tbl
|
|
642
658
|
|
|
@@ -657,7 +673,7 @@ class Catalog:
|
|
|
657
673
|
view_path: Optional[TableVersionPath] = None
|
|
658
674
|
for id, effective_version in tbl_version_path[::-1]:
|
|
659
675
|
if (id, effective_version) not in self._tbl_versions:
|
|
660
|
-
self._tbl_versions[
|
|
676
|
+
self._tbl_versions[id, effective_version] = self._load_tbl_version(id, effective_version)
|
|
661
677
|
view_path = TableVersionPath(TableVersionHandle(id, effective_version), base=base_path)
|
|
662
678
|
base_path = view_path
|
|
663
679
|
view = View(tbl_id, tbl_record.dir_id, tbl_md.name, view_path, snapshot_only=pure_snapshot)
|
|
@@ -758,16 +774,25 @@ class Catalog:
|
|
|
758
774
|
|
|
759
775
|
def _init_store(self) -> None:
|
|
760
776
|
"""One-time initialization of the stored catalog. Idempotent."""
|
|
777
|
+
self.create_user(None)
|
|
778
|
+
_logger.info('Initialized catalog.')
|
|
779
|
+
|
|
780
|
+
def create_user(self, user: Optional[str]) -> None:
|
|
781
|
+
"""
|
|
782
|
+
Creates a catalog record (root directory) for the specified user, if one does not already exist.
|
|
783
|
+
"""
|
|
761
784
|
with Env.get().begin_xact():
|
|
762
785
|
session = Env.get().session
|
|
763
|
-
if
|
|
786
|
+
# See if there are any directories in the catalog matching the specified user.
|
|
787
|
+
if session.query(schema.Dir).where(schema.Dir.md['user'].astext == user).count() > 0:
|
|
788
|
+
# At least one such directory exists; no need to create a new one.
|
|
764
789
|
return
|
|
765
|
-
|
|
766
|
-
dir_md = schema.DirMd(name='', user=
|
|
790
|
+
|
|
791
|
+
dir_md = schema.DirMd(name='', user=user, additional_md={})
|
|
767
792
|
dir_record = schema.Dir(parent_id=None, md=dataclasses.asdict(dir_md))
|
|
768
793
|
session.add(dir_record)
|
|
769
794
|
session.flush()
|
|
770
|
-
_logger.info(f'
|
|
795
|
+
_logger.info(f'Added root directory record for user: {user!r}')
|
|
771
796
|
|
|
772
797
|
def _handle_path_collision(
|
|
773
798
|
self, path: Path, expected_obj_type: type[SchemaObject], expected_snapshot: bool, if_exists: IfExistsParam
|
|
@@ -775,13 +800,14 @@ class Catalog:
|
|
|
775
800
|
obj, _, _ = self._prepare_dir_op(add_dir_path=path.parent, add_name=path.name)
|
|
776
801
|
|
|
777
802
|
if if_exists == IfExistsParam.ERROR and obj is not None:
|
|
778
|
-
raise excs.Error(f'Path {path!r} is an existing {type(obj)._display_name()}')
|
|
803
|
+
raise excs.Error(f'Path {str(path)!r} is an existing {type(obj)._display_name()}')
|
|
779
804
|
else:
|
|
780
805
|
is_snapshot = isinstance(obj, View) and obj._tbl_version_path.is_snapshot()
|
|
781
806
|
if obj is not None and (not isinstance(obj, expected_obj_type) or (expected_snapshot and not is_snapshot)):
|
|
782
807
|
obj_type_str = 'snapshot' if expected_snapshot else expected_obj_type._display_name()
|
|
783
808
|
raise excs.Error(
|
|
784
|
-
f'Path {path!r} already exists but is not a {obj_type_str}.
|
|
809
|
+
f'Path {str(path)!r} already exists but is not a {obj_type_str}. '
|
|
810
|
+
f'Cannot {if_exists.name.lower()} it.'
|
|
785
811
|
)
|
|
786
812
|
|
|
787
813
|
if obj is None:
|
|
@@ -794,7 +820,8 @@ class Catalog:
|
|
|
794
820
|
dir_contents = self._get_dir_contents(obj._id)
|
|
795
821
|
if len(dir_contents) > 0 and if_exists == IfExistsParam.REPLACE:
|
|
796
822
|
raise excs.Error(
|
|
797
|
-
f'Directory {path!r} already exists and is not empty.
|
|
823
|
+
f'Directory {str(path)!r} already exists and is not empty. '
|
|
824
|
+
'Use `if_exists="replace_force"` to replace it.'
|
|
798
825
|
)
|
|
799
826
|
self._drop_dir(obj._id, path, force=True)
|
|
800
827
|
else:
|
pixeltable/catalog/column.py
CHANGED
|
@@ -140,12 +140,12 @@ class Column:
|
|
|
140
140
|
The computed column {self.name!r} in table {self.tbl.get().name!r} is no longer valid.
|
|
141
141
|
{{validation_error}}
|
|
142
142
|
You can continue to query existing data from this column, but evaluating it on new data will raise an error.
|
|
143
|
-
"""
|
|
143
|
+
""" # noqa: E501
|
|
144
144
|
)
|
|
145
145
|
.strip()
|
|
146
146
|
.format(validation_error=self._value_expr.validation_error)
|
|
147
147
|
)
|
|
148
|
-
warnings.warn(message, category=excs.PixeltableWarning)
|
|
148
|
+
warnings.warn(message, category=excs.PixeltableWarning, stacklevel=2)
|
|
149
149
|
return self._value_expr
|
|
150
150
|
|
|
151
151
|
def set_value_expr(self, value_expr: exprs.Expr) -> None:
|
|
@@ -165,8 +165,10 @@ class Column:
|
|
|
165
165
|
return False
|
|
166
166
|
from pixeltable import exprs
|
|
167
167
|
|
|
168
|
-
|
|
169
|
-
|
|
168
|
+
window_fn_calls = list(
|
|
169
|
+
self.value_expr.subexprs(filter=lambda e: isinstance(e, exprs.FunctionCall) and e.is_window_fn_call)
|
|
170
|
+
)
|
|
171
|
+
return len(window_fn_calls) > 0
|
|
170
172
|
|
|
171
173
|
def get_idx_info(self) -> dict[str, 'TableVersion.IndexInfo']:
|
|
172
174
|
assert self.tbl is not None
|
pixeltable/catalog/dir.py
CHANGED
|
@@ -1,13 +1,11 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
3
|
import dataclasses
|
|
4
|
-
import datetime
|
|
5
4
|
import json
|
|
6
5
|
import logging
|
|
7
6
|
from uuid import UUID
|
|
8
7
|
|
|
9
8
|
import sqlalchemy as sql
|
|
10
|
-
from sqlalchemy.dialects.postgresql import JSONB
|
|
11
9
|
|
|
12
10
|
from pixeltable.env import Env
|
|
13
11
|
from pixeltable.metadata import schema
|
|
@@ -24,8 +22,9 @@ class Dir(SchemaObject):
|
|
|
24
22
|
@classmethod
|
|
25
23
|
def _create(cls, parent_id: UUID, name: str) -> Dir:
|
|
26
24
|
session = Env.get().session
|
|
25
|
+
user = Env.get().user
|
|
27
26
|
assert session is not None
|
|
28
|
-
dir_md = schema.DirMd(name=name, user=
|
|
27
|
+
dir_md = schema.DirMd(name=name, user=user, additional_md={})
|
|
29
28
|
dir_record = schema.Dir(parent_id=parent_id, md=dataclasses.asdict(dir_md))
|
|
30
29
|
session.add(dir_record)
|
|
31
30
|
session.flush()
|
|
@@ -48,14 +47,15 @@ class Dir(SchemaObject):
|
|
|
48
47
|
|
|
49
48
|
def _move(self, new_name: str, new_dir_id: UUID) -> None:
|
|
50
49
|
# print(
|
|
51
|
-
# f'{datetime.datetime.now()} move dir name={self._name} parent={self._dir_id}
|
|
50
|
+
# f'{datetime.datetime.now()} move dir name={self._name} parent={self._dir_id} '
|
|
51
|
+
# f'new_name={new_name} new_dir_id={new_dir_id}'
|
|
52
52
|
# )
|
|
53
53
|
super()._move(new_name, new_dir_id)
|
|
54
54
|
stmt = sql.text(
|
|
55
55
|
(
|
|
56
56
|
f'UPDATE {schema.Dir.__table__} '
|
|
57
57
|
f'SET {schema.Dir.parent_id.name} = :new_dir_id, '
|
|
58
|
-
f" {schema.Dir.md.name}
|
|
58
|
+
f" {schema.Dir.md.name} = jsonb_set({schema.Dir.md.name}, '{{name}}', (:new_name)::jsonb) "
|
|
59
59
|
f'WHERE {schema.Dir.id.name} = :id'
|
|
60
60
|
)
|
|
61
61
|
)
|
pixeltable/catalog/globals.py
CHANGED
|
@@ -6,6 +6,8 @@ import itertools
|
|
|
6
6
|
import logging
|
|
7
7
|
from typing import Optional
|
|
8
8
|
|
|
9
|
+
from typing_extensions import Self
|
|
10
|
+
|
|
9
11
|
import pixeltable.exceptions as excs
|
|
10
12
|
|
|
11
13
|
_logger = logging.getLogger('pixeltable')
|
|
@@ -32,7 +34,7 @@ class UpdateStatus:
|
|
|
32
34
|
updated_cols: list[str] = dataclasses.field(default_factory=list)
|
|
33
35
|
cols_with_excs: list[str] = dataclasses.field(default_factory=list)
|
|
34
36
|
|
|
35
|
-
def __iadd__(self, other: 'UpdateStatus') ->
|
|
37
|
+
def __iadd__(self, other: 'UpdateStatus') -> Self:
|
|
36
38
|
self.num_rows += other.num_rows
|
|
37
39
|
self.num_computed_values += other.num_computed_values
|
|
38
40
|
self.num_excs += other.num_excs
|
|
@@ -66,8 +68,8 @@ class MediaValidation(enum.Enum):
|
|
|
66
68
|
try:
|
|
67
69
|
return cls[name.upper()]
|
|
68
70
|
except KeyError:
|
|
69
|
-
val_strs = ', '.join(f'{s.lower()!r}' for s in cls.__members__
|
|
70
|
-
raise excs.Error(f'{error_prefix} must be one of: [{val_strs}]')
|
|
71
|
+
val_strs = ', '.join(f'{s.lower()!r}' for s in cls.__members__)
|
|
72
|
+
raise excs.Error(f'{error_prefix} must be one of: [{val_strs}]') from None
|
|
71
73
|
|
|
72
74
|
|
|
73
75
|
class IfExistsParam(enum.Enum):
|
|
@@ -81,8 +83,8 @@ class IfExistsParam(enum.Enum):
|
|
|
81
83
|
try:
|
|
82
84
|
return cls[param_val.upper()]
|
|
83
85
|
except KeyError:
|
|
84
|
-
val_strs = ', '.join(f'{s.lower()!r}' for s in cls.__members__
|
|
85
|
-
raise excs.Error(f'{param_name} must be one of: [{val_strs}]')
|
|
86
|
+
val_strs = ', '.join(f'{s.lower()!r}' for s in cls.__members__)
|
|
87
|
+
raise excs.Error(f'{param_name} must be one of: [{val_strs}]') from None
|
|
86
88
|
|
|
87
89
|
|
|
88
90
|
class IfNotExistsParam(enum.Enum):
|
|
@@ -94,8 +96,8 @@ class IfNotExistsParam(enum.Enum):
|
|
|
94
96
|
try:
|
|
95
97
|
return cls[param_val.upper()]
|
|
96
98
|
except KeyError:
|
|
97
|
-
val_strs = ', '.join(f'{s.lower()!r}' for s in cls.__members__
|
|
98
|
-
raise excs.Error(f'{param_name} must be one of: [{val_strs}]')
|
|
99
|
+
val_strs = ', '.join(f'{s.lower()!r}' for s in cls.__members__)
|
|
100
|
+
raise excs.Error(f'{param_name} must be one of: [{val_strs}]') from None
|
|
99
101
|
|
|
100
102
|
|
|
101
103
|
def is_valid_identifier(name: str) -> bool:
|
|
@@ -103,19 +105,15 @@ def is_valid_identifier(name: str) -> bool:
|
|
|
103
105
|
|
|
104
106
|
|
|
105
107
|
def is_valid_path(path: str, empty_is_valid: bool) -> bool:
|
|
106
|
-
if path
|
|
108
|
+
if not path:
|
|
107
109
|
return empty_is_valid
|
|
108
|
-
|
|
109
|
-
for part in path.split('.'):
|
|
110
|
-
if not is_valid_identifier(part):
|
|
111
|
-
return False
|
|
112
|
-
return True
|
|
110
|
+
return all(is_valid_identifier(part) for part in path.split('.'))
|
|
113
111
|
|
|
114
112
|
|
|
115
113
|
def is_system_column_name(name: str) -> bool:
|
|
116
114
|
from pixeltable.catalog import InsertableTable, View
|
|
117
115
|
|
|
118
|
-
global _PREDEF_SYMBOLS
|
|
116
|
+
global _PREDEF_SYMBOLS # noqa: PLW0603
|
|
119
117
|
if _PREDEF_SYMBOLS is None:
|
|
120
118
|
_PREDEF_SYMBOLS = set(itertools.chain(dir(InsertableTable), dir(View)))
|
|
121
119
|
return name == _POS_COLUMN_NAME or name in _PREDEF_SYMBOLS
|
|
@@ -2,12 +2,11 @@ from __future__ import annotations
|
|
|
2
2
|
|
|
3
3
|
import enum
|
|
4
4
|
import logging
|
|
5
|
-
from typing import TYPE_CHECKING, Any,
|
|
5
|
+
from typing import TYPE_CHECKING, Any, Literal, Optional, overload
|
|
6
6
|
from uuid import UUID
|
|
7
7
|
|
|
8
8
|
import pixeltable as pxt
|
|
9
|
-
import
|
|
10
|
-
from pixeltable import exceptions as excs
|
|
9
|
+
from pixeltable import exceptions as excs, type_system as ts
|
|
11
10
|
from pixeltable.env import Env
|
|
12
11
|
from pixeltable.utils.filecache import FileCache
|
|
13
12
|
|
|
@@ -18,9 +17,7 @@ from .table_version_handle import TableVersionHandle
|
|
|
18
17
|
from .table_version_path import TableVersionPath
|
|
19
18
|
|
|
20
19
|
if TYPE_CHECKING:
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
from pixeltable.globals import RowData, TableDataSource
|
|
20
|
+
from pixeltable.globals import TableDataSource
|
|
24
21
|
from pixeltable.io.table_data_conduit import TableDataConduit
|
|
25
22
|
|
|
26
23
|
_logger = logging.getLogger('pixeltable')
|
|
@@ -167,7 +164,7 @@ class InsertableTable(Table):
|
|
|
167
164
|
self, data_source: TableDataConduit, fail_on_exception: bool, print_stats: bool = False
|
|
168
165
|
) -> pxt.UpdateStatus:
|
|
169
166
|
"""Insert row batches into this table from a `TableDataConduit`."""
|
|
170
|
-
from pixeltable.io.table_data_conduit import DFTableDataConduit
|
|
167
|
+
from pixeltable.io.table_data_conduit import DFTableDataConduit
|
|
171
168
|
|
|
172
169
|
status = pxt.UpdateStatus()
|
|
173
170
|
with Env.get().begin_xact():
|
pixeltable/catalog/path.py
CHANGED
|
@@ -27,7 +27,7 @@ class Path:
|
|
|
27
27
|
|
|
28
28
|
@property
|
|
29
29
|
def is_root(self) -> bool:
|
|
30
|
-
return self.components[0]
|
|
30
|
+
return not self.components[0]
|
|
31
31
|
|
|
32
32
|
@property
|
|
33
33
|
def parent(self) -> Path:
|
|
@@ -43,7 +43,7 @@ class Path:
|
|
|
43
43
|
if self.is_root:
|
|
44
44
|
return Path(name)
|
|
45
45
|
else:
|
|
46
|
-
return Path(f'{
|
|
46
|
+
return Path(f'{self!s}.{name}')
|
|
47
47
|
|
|
48
48
|
def is_ancestor(self, other: Path, is_parent: bool = False) -> bool:
|
|
49
49
|
"""
|