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.

Files changed (106) hide show
  1. pixeltable/__init__.py +1 -1
  2. pixeltable/__version__.py +2 -2
  3. pixeltable/catalog/__init__.py +2 -1
  4. pixeltable/catalog/catalog.py +63 -36
  5. pixeltable/catalog/column.py +6 -4
  6. pixeltable/catalog/dir.py +5 -5
  7. pixeltable/catalog/globals.py +12 -14
  8. pixeltable/catalog/insertable_table.py +4 -7
  9. pixeltable/catalog/path.py +2 -2
  10. pixeltable/catalog/table.py +64 -56
  11. pixeltable/catalog/table_version.py +42 -40
  12. pixeltable/catalog/table_version_handle.py +3 -0
  13. pixeltable/catalog/table_version_path.py +1 -1
  14. pixeltable/catalog/view.py +8 -7
  15. pixeltable/dataframe.py +5 -3
  16. pixeltable/env.py +108 -42
  17. pixeltable/exec/__init__.py +2 -0
  18. pixeltable/exec/aggregation_node.py +6 -8
  19. pixeltable/exec/cache_prefetch_node.py +4 -7
  20. pixeltable/exec/component_iteration_node.py +1 -3
  21. pixeltable/exec/data_row_batch.py +1 -2
  22. pixeltable/exec/exec_context.py +1 -1
  23. pixeltable/exec/exec_node.py +1 -2
  24. pixeltable/exec/expr_eval/__init__.py +2 -0
  25. pixeltable/exec/expr_eval/evaluators.py +137 -20
  26. pixeltable/exec/expr_eval/expr_eval_node.py +43 -64
  27. pixeltable/exec/expr_eval/globals.py +68 -7
  28. pixeltable/exec/expr_eval/schedulers.py +25 -23
  29. pixeltable/exec/in_memory_data_node.py +8 -6
  30. pixeltable/exec/row_update_node.py +3 -4
  31. pixeltable/exec/sql_node.py +16 -17
  32. pixeltable/exprs/__init__.py +1 -1
  33. pixeltable/exprs/column_property_ref.py +1 -1
  34. pixeltable/exprs/column_ref.py +3 -3
  35. pixeltable/exprs/compound_predicate.py +1 -1
  36. pixeltable/exprs/data_row.py +17 -1
  37. pixeltable/exprs/expr.py +12 -12
  38. pixeltable/exprs/function_call.py +34 -2
  39. pixeltable/exprs/json_mapper.py +95 -48
  40. pixeltable/exprs/json_path.py +3 -4
  41. pixeltable/exprs/method_ref.py +2 -2
  42. pixeltable/exprs/object_ref.py +2 -2
  43. pixeltable/exprs/row_builder.py +33 -6
  44. pixeltable/exprs/similarity_expr.py +1 -1
  45. pixeltable/exprs/sql_element_cache.py +1 -1
  46. pixeltable/exprs/string_op.py +2 -2
  47. pixeltable/ext/__init__.py +1 -1
  48. pixeltable/ext/functions/__init__.py +1 -1
  49. pixeltable/ext/functions/whisperx.py +1 -1
  50. pixeltable/ext/functions/yolox.py +1 -1
  51. pixeltable/func/aggregate_function.py +1 -1
  52. pixeltable/func/callable_function.py +2 -5
  53. pixeltable/func/expr_template_function.py +22 -2
  54. pixeltable/func/function.py +4 -5
  55. pixeltable/func/function_registry.py +1 -1
  56. pixeltable/func/signature.py +1 -1
  57. pixeltable/func/udf.py +2 -2
  58. pixeltable/functions/__init__.py +1 -1
  59. pixeltable/functions/anthropic.py +2 -2
  60. pixeltable/functions/audio.py +1 -1
  61. pixeltable/functions/deepseek.py +1 -1
  62. pixeltable/functions/fireworks.py +1 -1
  63. pixeltable/functions/globals.py +6 -6
  64. pixeltable/functions/huggingface.py +1 -1
  65. pixeltable/functions/image.py +1 -1
  66. pixeltable/functions/json.py +1 -1
  67. pixeltable/functions/llama_cpp.py +1 -1
  68. pixeltable/functions/math.py +1 -1
  69. pixeltable/functions/mistralai.py +1 -1
  70. pixeltable/functions/ollama.py +1 -1
  71. pixeltable/functions/openai.py +2 -2
  72. pixeltable/functions/replicate.py +1 -1
  73. pixeltable/functions/string.py +1 -1
  74. pixeltable/functions/timestamp.py +1 -1
  75. pixeltable/functions/together.py +1 -1
  76. pixeltable/functions/util.py +1 -1
  77. pixeltable/functions/video.py +2 -2
  78. pixeltable/functions/vision.py +2 -2
  79. pixeltable/index/embedding_index.py +12 -1
  80. pixeltable/io/__init__.py +5 -3
  81. pixeltable/io/fiftyone.py +6 -7
  82. pixeltable/io/label_studio.py +21 -20
  83. pixeltable/io/pandas.py +6 -5
  84. pixeltable/iterators/__init__.py +1 -1
  85. pixeltable/metadata/__init__.py +5 -3
  86. pixeltable/metadata/converters/convert_24.py +3 -3
  87. pixeltable/metadata/converters/convert_25.py +1 -1
  88. pixeltable/metadata/converters/convert_29.py +1 -1
  89. pixeltable/store.py +2 -2
  90. pixeltable/type_system.py +19 -7
  91. pixeltable/utils/console_output.py +3 -2
  92. pixeltable/utils/coroutine.py +3 -3
  93. pixeltable/utils/dbms.py +66 -0
  94. pixeltable/utils/documents.py +61 -67
  95. pixeltable/utils/filecache.py +1 -1
  96. pixeltable/utils/http_server.py +3 -2
  97. pixeltable/utils/pytorch.py +1 -1
  98. pixeltable/utils/sql.py +1 -1
  99. pixeltable-0.3.11.dist-info/METADATA +436 -0
  100. pixeltable-0.3.11.dist-info/RECORD +179 -0
  101. pixeltable/catalog/path_dict.py +0 -169
  102. pixeltable-0.3.10.dist-info/METADATA +0 -382
  103. pixeltable-0.3.10.dist-info/RECORD +0 -179
  104. {pixeltable-0.3.10.dist-info → pixeltable-0.3.11.dist-info}/LICENSE +0 -0
  105. {pixeltable-0.3.10.dist-info → pixeltable-0.3.11.dist-info}/WHEEL +0 -0
  106. {pixeltable-0.3.10.dist-info → pixeltable-0.3.11.dist-info}/entry_points.txt +0 -0
pixeltable/__init__.py CHANGED
@@ -76,5 +76,5 @@ __removed_symbols = {
76
76
  __all__ = sorted(__default_dir - __removed_symbols)
77
77
 
78
78
 
79
- def __dir__():
79
+ def __dir__() -> list[str]:
80
80
  return __all__
pixeltable/__version__.py CHANGED
@@ -1,3 +1,3 @@
1
1
  # These version placeholders will be replaced during build.
2
- __version__ = '0.3.10'
3
- __version_tuple__ = (0, 3, 10)
2
+ __version__ = '0.3.11'
3
+ __version_tuple__ = (0, 3, 11)
@@ -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
@@ -10,10 +10,10 @@ from uuid import UUID
10
10
  import psycopg
11
11
  import sqlalchemy as sql
12
12
 
13
- import pixeltable.exceptions as excs
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() as conn:
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(list(dir_paths)):
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(schema.Dir.parent_id == dir_id, schema.Dir.md['name'].astext == name)
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
- assert False, rows
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(schema.Table.dir_id == dir_id, schema.Table.md['name'].astext == name)
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 tbl_id in self._tbls:
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(f'{path!r} needs to be a {expected._display_name()} but is a {Dir._display_name()}')
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(f'{path!r} needs to be a {expected._display_name()} but is a {type(obj)._display_name()}')
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 tbl_id in self._tbls:
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[(tbl_id, effective_version)] = self._load_tbl_version(tbl_id, effective_version)
555
- return self._tbl_versions[(tbl_id, effective_version)]
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[(tbl_version.id, tbl_version.effective_version)] = tbl_version
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[(tbl_version.id, tbl_version.effective_version)]
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).one()
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, schema.Dir.md['name'].astext == path.name
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[(tbl_id, None)] = self._load_tbl_version(tbl_id, None)
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[(id, effective_version)] = self._load_tbl_version(id, effective_version)
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 session.query(sql.func.count(schema.Dir.id)).scalar() > 0:
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
- # create a top-level directory, so that every schema object has a directory
766
- dir_md = schema.DirMd(name='', user=None, additional_md={})
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'Initialized catalog')
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}. Cannot {if_exists.name.lower()} it.'
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. Use `if_exists="replace_force"` to replace it.'
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:
@@ -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
- l = list(self.value_expr.subexprs(filter=lambda e: isinstance(e, exprs.FunctionCall) and e.is_window_fn_call))
169
- return len(l) > 0
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=None, additional_md={})
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} new_name={new_name} new_dir_id={new_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}['name'] = :new_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
  )
@@ -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') -> '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__.keys())
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__.keys())
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__.keys())
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, Iterable, Literal, Optional, overload
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 pixeltable.type_system as ts
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
- import datasets # type: ignore[import-untyped]
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, TableDataConduit
167
+ from pixeltable.io.table_data_conduit import DFTableDataConduit
171
168
 
172
169
  status = pxt.UpdateStatus()
173
170
  with Env.get().begin_xact():
@@ -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'{str(self)}.{name}')
46
+ return Path(f'{self!s}.{name}')
47
47
 
48
48
  def is_ancestor(self, other: Path, is_parent: bool = False) -> bool:
49
49
  """