pixeltable 0.2.28__tar.gz → 0.2.30__tar.gz

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 (164) hide show
  1. {pixeltable-0.2.28 → pixeltable-0.2.30}/PKG-INFO +4 -2
  2. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/__init__.py +1 -1
  3. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/__version__.py +2 -2
  4. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/catalog/__init__.py +1 -1
  5. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/catalog/dir.py +6 -0
  6. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/catalog/globals.py +25 -0
  7. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/catalog/named_function.py +4 -0
  8. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/catalog/path_dict.py +37 -11
  9. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/catalog/schema_object.py +6 -0
  10. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/catalog/table.py +96 -19
  11. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/catalog/table_version.py +22 -8
  12. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/dataframe.py +201 -3
  13. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/env.py +9 -3
  14. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/exec/expr_eval_node.py +1 -1
  15. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/exec/sql_node.py +2 -2
  16. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/exprs/function_call.py +134 -29
  17. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/exprs/inline_expr.py +22 -2
  18. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/exprs/row_builder.py +1 -1
  19. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/exprs/similarity_expr.py +9 -2
  20. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/func/__init__.py +1 -0
  21. pixeltable-0.2.30/pixeltable/func/aggregate_function.py +289 -0
  22. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/func/callable_function.py +50 -16
  23. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/func/expr_template_function.py +62 -24
  24. pixeltable-0.2.30/pixeltable/func/function.py +377 -0
  25. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/func/function_registry.py +2 -1
  26. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/func/query_template_function.py +11 -6
  27. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/func/signature.py +64 -7
  28. pixeltable-0.2.30/pixeltable/func/tools.py +116 -0
  29. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/func/udf.py +57 -35
  30. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/functions/__init__.py +2 -2
  31. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/functions/anthropic.py +36 -2
  32. pixeltable-0.2.30/pixeltable/functions/globals.py +164 -0
  33. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/functions/json.py +3 -8
  34. pixeltable-0.2.30/pixeltable/functions/math.py +67 -0
  35. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/functions/ollama.py +4 -4
  36. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/functions/openai.py +31 -2
  37. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/functions/timestamp.py +1 -1
  38. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/functions/video.py +2 -8
  39. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/functions/vision.py +1 -1
  40. pixeltable-0.2.30/pixeltable/globals.py +817 -0
  41. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/index/embedding_index.py +44 -24
  42. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/metadata/__init__.py +1 -1
  43. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/metadata/converters/convert_16.py +2 -1
  44. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/metadata/converters/convert_17.py +2 -1
  45. pixeltable-0.2.30/pixeltable/metadata/converters/convert_23.py +35 -0
  46. pixeltable-0.2.30/pixeltable/metadata/converters/convert_24.py +47 -0
  47. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/metadata/converters/util.py +4 -2
  48. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/metadata/notes.py +2 -0
  49. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/metadata/schema.py +1 -0
  50. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/type_system.py +192 -48
  51. {pixeltable-0.2.28 → pixeltable-0.2.30}/pyproject.toml +11 -6
  52. pixeltable-0.2.28/pixeltable/func/aggregate_function.py +0 -206
  53. pixeltable-0.2.28/pixeltable/func/function.py +0 -209
  54. pixeltable-0.2.28/pixeltable/functions/globals.py +0 -144
  55. pixeltable-0.2.28/pixeltable/globals.py +0 -549
  56. pixeltable-0.2.28/pixeltable/tool/create_test_db_dump.py +0 -311
  57. pixeltable-0.2.28/pixeltable/tool/create_test_video.py +0 -81
  58. pixeltable-0.2.28/pixeltable/tool/doc_plugins/griffe.py +0 -50
  59. pixeltable-0.2.28/pixeltable/tool/doc_plugins/mkdocstrings.py +0 -6
  60. pixeltable-0.2.28/pixeltable/tool/doc_plugins/templates/material/udf.html.jinja +0 -135
  61. pixeltable-0.2.28/pixeltable/tool/embed_udf.py +0 -9
  62. pixeltable-0.2.28/pixeltable/tool/mypy_plugin.py +0 -55
  63. {pixeltable-0.2.28 → pixeltable-0.2.30}/LICENSE +0 -0
  64. {pixeltable-0.2.28 → pixeltable-0.2.30}/README.md +0 -0
  65. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/catalog/catalog.py +0 -0
  66. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/catalog/column.py +0 -0
  67. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/catalog/insertable_table.py +0 -0
  68. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/catalog/path.py +0 -0
  69. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/catalog/table_version_path.py +0 -0
  70. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/catalog/view.py +0 -0
  71. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/exceptions.py +0 -0
  72. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/exec/__init__.py +0 -0
  73. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/exec/aggregation_node.py +0 -0
  74. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/exec/cache_prefetch_node.py +0 -0
  75. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/exec/component_iteration_node.py +0 -0
  76. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/exec/data_row_batch.py +0 -0
  77. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/exec/exec_context.py +0 -0
  78. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/exec/exec_node.py +0 -0
  79. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/exec/in_memory_data_node.py +0 -0
  80. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/exec/row_update_node.py +0 -0
  81. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/exprs/__init__.py +0 -0
  82. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/exprs/arithmetic_expr.py +0 -0
  83. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/exprs/array_slice.py +0 -0
  84. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/exprs/column_property_ref.py +0 -0
  85. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/exprs/column_ref.py +0 -0
  86. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/exprs/comparison.py +0 -0
  87. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/exprs/compound_predicate.py +0 -0
  88. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/exprs/data_row.py +0 -0
  89. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/exprs/expr.py +0 -0
  90. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/exprs/expr_dict.py +0 -0
  91. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/exprs/expr_set.py +0 -0
  92. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/exprs/globals.py +0 -0
  93. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/exprs/in_predicate.py +0 -0
  94. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/exprs/is_null.py +0 -0
  95. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/exprs/json_mapper.py +0 -0
  96. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/exprs/json_path.py +0 -0
  97. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/exprs/literal.py +0 -0
  98. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/exprs/method_ref.py +0 -0
  99. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/exprs/object_ref.py +0 -0
  100. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/exprs/rowid_ref.py +0 -0
  101. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/exprs/sql_element_cache.py +0 -0
  102. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/exprs/type_cast.py +0 -0
  103. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/exprs/variable.py +0 -0
  104. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/ext/__init__.py +0 -0
  105. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/ext/functions/__init__.py +0 -0
  106. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/ext/functions/whisperx.py +0 -0
  107. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/ext/functions/yolox.py +0 -0
  108. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/func/globals.py +0 -0
  109. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/functions/audio.py +0 -0
  110. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/functions/fireworks.py +0 -0
  111. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/functions/gemini.py +0 -0
  112. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/functions/huggingface.py +0 -0
  113. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/functions/image.py +0 -0
  114. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/functions/llama_cpp.py +0 -0
  115. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/functions/mistralai.py +0 -0
  116. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/functions/replicate.py +0 -0
  117. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/functions/string.py +0 -0
  118. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/functions/together.py +0 -0
  119. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/functions/util.py +0 -0
  120. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/functions/whisper.py +0 -0
  121. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/index/__init__.py +0 -0
  122. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/index/base.py +0 -0
  123. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/index/btree.py +0 -0
  124. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/io/__init__.py +0 -0
  125. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/io/external_store.py +0 -0
  126. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/io/fiftyone.py +0 -0
  127. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/io/globals.py +0 -0
  128. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/io/hf_datasets.py +0 -0
  129. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/io/label_studio.py +0 -0
  130. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/io/pandas.py +0 -0
  131. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/io/parquet.py +0 -0
  132. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/iterators/__init__.py +0 -0
  133. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/iterators/base.py +0 -0
  134. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/iterators/document.py +0 -0
  135. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/iterators/image.py +0 -0
  136. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/iterators/string.py +0 -0
  137. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/iterators/video.py +0 -0
  138. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/metadata/converters/convert_10.py +0 -0
  139. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/metadata/converters/convert_12.py +0 -0
  140. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/metadata/converters/convert_13.py +0 -0
  141. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/metadata/converters/convert_14.py +0 -0
  142. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/metadata/converters/convert_15.py +0 -0
  143. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/metadata/converters/convert_18.py +0 -0
  144. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/metadata/converters/convert_19.py +0 -0
  145. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/metadata/converters/convert_20.py +0 -0
  146. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/metadata/converters/convert_21.py +0 -0
  147. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/metadata/converters/convert_22.py +0 -0
  148. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/plan.py +0 -0
  149. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/py.typed +0 -0
  150. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/store.py +0 -0
  151. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/utils/__init__.py +0 -0
  152. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/utils/arrow.py +0 -0
  153. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/utils/coco.py +0 -0
  154. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/utils/code.py +0 -0
  155. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/utils/description_helper.py +0 -0
  156. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/utils/documents.py +0 -0
  157. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/utils/filecache.py +0 -0
  158. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/utils/formatter.py +0 -0
  159. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/utils/http_server.py +0 -0
  160. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/utils/media_store.py +0 -0
  161. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/utils/pytorch.py +0 -0
  162. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/utils/s3.py +0 -0
  163. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/utils/sql.py +0 -0
  164. {pixeltable-0.2.28 → pixeltable-0.2.30}/pixeltable/utils/transactional_directory.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pixeltable
3
- Version: 0.2.28
3
+ Version: 0.2.30
4
4
  Summary: AI Data Infrastructure: Declarative, Multimodal, and Incremental
5
5
  Home-page: https://pixeltable.com/
6
6
  License: Apache-2.0
@@ -29,16 +29,18 @@ Requires-Dist: cloudpickle (>=2.2.1,<3.0.0)
29
29
  Requires-Dist: ftfy (>=6.2.0,<7.0.0)
30
30
  Requires-Dist: jinja2 (>=3.1.3,<4.0.0)
31
31
  Requires-Dist: jmespath (>=1.0.1,<2.0.0)
32
+ Requires-Dist: jsonschema (>=4.1.0)
32
33
  Requires-Dist: lxml (>=5.0)
33
34
  Requires-Dist: more-itertools (>=10.2,<11.0)
34
35
  Requires-Dist: numpy (>=1.25,<2.0)
35
36
  Requires-Dist: pandas (>=2.0,<3.0)
36
37
  Requires-Dist: pgvector (>=0.2.1,<0.3.0)
37
38
  Requires-Dist: pillow (>=9.3.0)
38
- Requires-Dist: pixeltable-pgserver (==0.2.8)
39
+ Requires-Dist: pixeltable-pgserver (==0.2.9)
39
40
  Requires-Dist: psutil (>=5.9.5,<6.0.0)
40
41
  Requires-Dist: psycopg[binary] (>=3.1.18)
41
42
  Requires-Dist: puremagic (>=1.20)
43
+ Requires-Dist: pydantic (>=2.7.4)
42
44
  Requires-Dist: pymupdf (>=1.24.1,<2.0.0)
43
45
  Requires-Dist: pyyaml (>=6.0.1,<7.0.0)
44
46
  Requires-Dist: requests (>=2.31.0,<3.0.0)
@@ -4,7 +4,7 @@ from .exceptions import Error
4
4
  from .exprs import RELATIVE_PATH_ROOT
5
5
  from .func import Aggregator, Function, expr_udf, uda, udf
6
6
  from .globals import (array, configure_logging, create_dir, create_snapshot, create_table, create_view, drop_dir,
7
- drop_table, get_table, init, list_dirs, list_functions, list_tables, move)
7
+ drop_table, get_table, init, list_dirs, list_functions, list_tables, move, tool, tools)
8
8
  from .type_system import (Array, ArrayType, Audio, AudioType, Bool, BoolType, ColumnType, Document, DocumentType, Float,
9
9
  FloatType, Image, ImageType, Int, IntType, Json, JsonType, Required, String, StringType,
10
10
  Timestamp, TimestampType, Video, VideoType)
@@ -1,3 +1,3 @@
1
1
  # These version placeholders will be replaced during build.
2
- __version__ = "0.2.28"
3
- __version_tuple__ = (0, 2, 28)
2
+ __version__ = "0.2.30"
3
+ __version_tuple__ = (0, 2, 30)
@@ -1,7 +1,7 @@
1
1
  from .catalog import Catalog
2
2
  from .column import Column
3
3
  from .dir import Dir
4
- from .globals import UpdateStatus, is_valid_identifier, is_valid_path, MediaValidation
4
+ from .globals import UpdateStatus, is_valid_identifier, is_valid_path, MediaValidation, IfExistsParam, IfNotExistsParam
5
5
  from .insertable_table import InsertableTable
6
6
  from .named_function import NamedFunction
7
7
  from .path import Path
@@ -21,6 +21,12 @@ class Dir(SchemaObject):
21
21
  def _display_name(cls) -> str:
22
22
  return 'directory'
23
23
 
24
+ @property
25
+ def _has_dependents(self) -> bool:
26
+ """ Returns True if this directory has any children. """
27
+ from pixeltable.catalog import Catalog, Path
28
+ return len(Catalog.get().paths.get_children(Path(self._path), child_type=None, recursive=False)) > 0
29
+
24
30
  def _move(self, new_name: str, new_dir_id: UUID) -> None:
25
31
  super()._move(new_name, new_dir_id)
26
32
  with Env.get().engine.begin() as conn:
@@ -51,6 +51,31 @@ class MediaValidation(enum.Enum):
51
51
  val_strs = ', '.join(f'{s.lower()!r}' for s in cls.__members__.keys())
52
52
  raise excs.Error(f'{error_prefix} must be one of: [{val_strs}]')
53
53
 
54
+ class IfExistsParam(enum.Enum):
55
+ ERROR = 0
56
+ IGNORE = 1
57
+ REPLACE = 2
58
+ REPLACE_FORCE = 3
59
+
60
+ @classmethod
61
+ def validated(cls, param_val: str, param_name: str) -> IfExistsParam:
62
+ try:
63
+ return cls[param_val.upper()]
64
+ except KeyError:
65
+ val_strs = ', '.join(f'{s.lower()!r}' for s in cls.__members__.keys())
66
+ raise excs.Error(f'{param_name} must be one of: [{val_strs}]')
67
+
68
+ class IfNotExistsParam(enum.Enum):
69
+ ERROR = 0
70
+ IGNORE = 1
71
+
72
+ @classmethod
73
+ def validated(cls, param_val: str, param_name: str) -> IfNotExistsParam:
74
+ try:
75
+ return cls[param_val.upper()]
76
+ except KeyError:
77
+ val_strs = ', '.join(f'{s.lower()!r}' for s in cls.__members__.keys())
78
+ raise excs.Error(f'{param_name} must be one of: [{val_strs}]')
54
79
 
55
80
  def is_valid_identifier(name: str) -> bool:
56
81
  return name.isidentifier() and not name.startswith('_')
@@ -25,6 +25,10 @@ class NamedFunction(SchemaObject):
25
25
  def _display_name(cls) -> str:
26
26
  return 'function'
27
27
 
28
+ @property
29
+ def _has_dependents(self) -> bool:
30
+ return False
31
+
28
32
  def _move(self, new_name: str, new_dir_id: UUID) -> None:
29
33
  super()._move(new_name, new_dir_id)
30
34
  with Env.get().engine.begin() as conn:
@@ -51,11 +51,41 @@ class PathDict:
51
51
  record_dir(dir)
52
52
 
53
53
  def _resolve_path(self, path: Path) -> SchemaObject:
54
+ """Resolve the path to a SchemaObject.
55
+
56
+ Args:
57
+ path: path to resolve
58
+
59
+ Returns:
60
+ SchemaObject at the path.
61
+
62
+ Raises:
63
+ Error if path is invalid or does not exist.
64
+ """
65
+ schema_obj = self.get_object(path)
66
+ if schema_obj is None:
67
+ raise excs.Error(f"No such path: {str(path)}")
68
+ return schema_obj
69
+
70
+ def get_object(self, path: Path) -> Optional[SchemaObject]:
71
+ """Get the object at the given path, if any.
72
+
73
+ Args:
74
+ path: path to object
75
+
76
+ Returns:
77
+ SchemaObject at the path if it exists, None otherwise.
78
+
79
+ Raises:
80
+ Error if path is invalid.
81
+ """
54
82
  if path.is_root:
55
83
  return self.root_dir
56
84
  dir = self.root_dir
57
85
  for i, component in enumerate(path.components):
58
86
  if component not in self.dir_contents[dir._id]:
87
+ if i == len(path.components) - 1:
88
+ return None
59
89
  raise excs.Error(f'No such path: {".".join(path.components[:i + 1])}')
60
90
  schema_obj = self.dir_contents[dir._id][component]
61
91
  if i < len(path.components) - 1:
@@ -112,19 +142,15 @@ class PathDict:
112
142
  Error if path is invalid or object at path has wrong type
113
143
  """
114
144
  # check for existence
145
+ obj = self.get_object(path)
115
146
  if expected is not None:
116
- schema_obj = self._resolve_path(path)
117
- if not isinstance(schema_obj, expected):
118
- raise excs.Error(
119
- f'{str(path)} needs to be a {expected._display_name()} but is a {type(schema_obj)._display_name()}')
120
- if expected is None:
121
- parent_obj = self._resolve_path(path.parent)
122
- if not isinstance(parent_obj, Dir):
147
+ if obj is None:
148
+ raise excs.Error(f"No such path: {str(path)}")
149
+ if not isinstance(obj, expected):
123
150
  raise excs.Error(
124
- f'{str(path.parent)} is a {type(parent_obj)._display_name()}, not a {Dir._display_name()}')
125
- if path.name in self.dir_contents[parent_obj._id]:
126
- obj = self.dir_contents[parent_obj._id][path.name]
127
- raise excs.Error(f"{type(obj)._display_name()} '{str(path)}' already exists")
151
+ f'{str(path)} needs to be a {expected._display_name()} but is a {type(obj)._display_name()}')
152
+ if expected is None and obj is not None:
153
+ raise excs.Error(f"{type(obj)._display_name()} '{str(path)}' already exists")
128
154
 
129
155
  def get_children(self, parent: Path, child_type: Optional[type[SchemaObject]], recursive: bool) -> list[Path]:
130
156
  dir = self._resolve_path(parent)
@@ -66,7 +66,13 @@ class SchemaObject:
66
66
  """
67
67
  pass
68
68
 
69
+ @property
70
+ @abstractmethod
71
+ def _has_dependents(self) -> bool:
72
+ """Returns True if this object has dependents (e.g., children, views)"""
73
+
69
74
  def _move(self, new_name: str, new_dir_id: UUID) -> None:
70
75
  """Subclasses need to override this to make the change persistent"""
71
76
  self.__name = new_name
72
77
  self.__dir_id = new_dir_id
78
+
@@ -25,7 +25,7 @@ from ..exprs import ColumnRef
25
25
  from ..utils.description_helper import DescriptionHelper
26
26
  from ..utils.filecache import FileCache
27
27
  from .column import Column
28
- from .globals import _ROWID_COLUMN_NAME, MediaValidation, UpdateStatus, is_system_column_name, is_valid_identifier
28
+ from .globals import _ROWID_COLUMN_NAME, MediaValidation, UpdateStatus, is_system_column_name, is_valid_identifier, IfNotExistsParam
29
29
  from .schema_object import SchemaObject
30
30
  from .table_version import TableVersion
31
31
  from .table_version_path import TableVersionPath
@@ -63,6 +63,11 @@ class Table(SchemaObject):
63
63
  return self._queries[name]
64
64
  raise AttributeError(f'Table {self.__table._name!r} has no query with that name: {name!r}')
65
65
 
66
+ @property
67
+ def _has_dependents(self) -> bool:
68
+ """Returns True if this table has any dependent views, or snapshots."""
69
+ return len(self._get_views(recursive=False)) > 0
70
+
66
71
  def _move(self, new_name: str, new_dir_id: UUID) -> None:
67
72
  self._check_is_dropped()
68
73
  super()._move(new_name, new_dir_id)
@@ -184,26 +189,38 @@ class Table(SchemaObject):
184
189
  return self.__query_scope
185
190
 
186
191
  def select(self, *items: Any, **named_items: Any) -> 'pxt.DataFrame':
187
- """Return a [`DataFrame`][pixeltable.DataFrame] for this table."""
192
+ """ Select columns or expressions from this table.
193
+
194
+ See [`DataFrame.select`][pixeltable.DataFrame.select] for more details.
195
+ """
188
196
  return self._df().select(*items, **named_items)
189
197
 
190
198
  def where(self, pred: 'exprs.Expr') -> 'pxt.DataFrame':
191
- """Return a [`DataFrame`][pixeltable.DataFrame] for this table."""
199
+ """Filter rows from this table based on the expression.
200
+
201
+ See [`DataFrame.where`][pixeltable.DataFrame.where] for more details.
202
+ """
192
203
  return self._df().where(pred)
193
204
 
194
205
  def join(
195
206
  self, other: 'Table', *, on: Optional['exprs.Expr'] = None,
196
207
  how: 'pixeltable.plan.JoinType.LiteralType' = 'inner'
197
208
  ) -> 'pxt.DataFrame':
198
- """Return a [`DataFrame`][pixeltable.DataFrame] for this table."""
209
+ """Join this table with another table."""
199
210
  return self._df().join(other, on=on, how=how)
200
211
 
201
212
  def order_by(self, *items: 'exprs.Expr', asc: bool = True) -> 'pxt.DataFrame':
202
- """Return a [`DataFrame`][pixeltable.DataFrame] for this table."""
213
+ """Order the rows of this table based on the expression.
214
+
215
+ See [`DataFrame.order_by`][pixeltable.DataFrame.order_by] for more details.
216
+ """
203
217
  return self._df().order_by(*items, asc=asc)
204
218
 
205
219
  def group_by(self, *items: 'exprs.Expr') -> 'pxt.DataFrame':
206
- """Return a [`DataFrame`][pixeltable.DataFrame] for this table."""
220
+ """Group the rows of this table based on the expression.
221
+
222
+ See [`DataFrame.group_by`][pixeltable.DataFrame.group_by] for more details.
223
+ """
207
224
  return self._df().group_by(*items)
208
225
 
209
226
  def limit(self, n: int) -> 'pxt.DataFrame':
@@ -695,14 +712,19 @@ class Table(SchemaObject):
695
712
  if not exists:
696
713
  raise excs.Error(f'Unknown column: {col_ref.col.qualified_name}')
697
714
 
698
- def drop_column(self, column: Union[str, ColumnRef]) -> None:
715
+ def drop_column(self, column: Union[str, ColumnRef], if_not_exists: Literal['error', 'ignore'] = 'error') -> None:
699
716
  """Drop a column from the table.
700
717
 
701
718
  Args:
702
719
  column: The name or reference of the column to drop.
720
+ if_not_exists: Directive for handling a non-existent column. Must be one of the following:
721
+
722
+ - `'error'`: raise an error if the column does not exist.
723
+ - `'ignore'`: do nothing if the column does not exist.
703
724
 
704
725
  Raises:
705
- Error: If the column does not exist or if it is referenced by a dependent computed column.
726
+ Error: If the column does not exist and `if_exists='error'`,
727
+ or if it is referenced by a dependent computed column.
706
728
 
707
729
  Examples:
708
730
  Drop the column `col` from the table `my_table` by column name:
@@ -714,14 +736,32 @@ class Table(SchemaObject):
714
736
 
715
737
  >>> tbl = pxt.get_table('my_table')
716
738
  ... tbl.drop_column(tbl.col)
739
+
740
+ Drop the column `col` from the table `my_table` if it exists, otherwise do nothing:
741
+
742
+ >>> tbl = pxt.get_table('my_table')
743
+ ... tbl.drop_col(tbl.col, if_not_exists='ignore')
717
744
  """
718
745
  self._check_is_dropped()
746
+ if self._tbl_version_path.is_snapshot():
747
+ raise excs.Error('Cannot drop column from a snapshot.')
719
748
  col: Column = None
749
+ _if_not_exists = IfNotExistsParam.validated(if_not_exists, 'if_not_exists')
720
750
  if isinstance(column, str):
721
- self.__check_column_name_exists(column)
751
+ col = self._tbl_version_path.get_column(column, include_bases=False)
752
+ if col is None:
753
+ if _if_not_exists == IfNotExistsParam.ERROR:
754
+ raise excs.Error(f'Column {column!r} unknown')
755
+ assert _if_not_exists == IfNotExistsParam.IGNORE
756
+ return
722
757
  col = self._tbl_version.cols_by_name[column]
723
758
  else:
724
- self.__check_column_ref_exists(column)
759
+ exists = self._tbl_version_path.has_column(column.col, include_bases=False)
760
+ if not exists:
761
+ if _if_not_exists == IfNotExistsParam.ERROR:
762
+ raise excs.Error(f'Unknown column: {column.col.qualified_name}')
763
+ assert _if_not_exists == IfNotExistsParam.IGNORE
764
+ return
725
765
  col = column.col
726
766
 
727
767
  dependent_user_cols = [c for c in col.dependent_cols if c.name is not None]
@@ -849,7 +889,9 @@ class Table(SchemaObject):
849
889
  def drop_embedding_index(
850
890
  self, *,
851
891
  column: Union[str, ColumnRef, None] = None,
852
- idx_name: Optional[str] = None) -> None:
892
+ idx_name: Optional[str] = None,
893
+ if_not_exists: Literal['error', 'ignore'] = 'error'
894
+ ) -> None:
853
895
  """
854
896
  Drop an embedding index from the table. Either a column name or an index name (but not both) must be
855
897
  specified. If a column name or reference is specified, it must be a column containing exactly one
@@ -859,11 +901,20 @@ class Table(SchemaObject):
859
901
  column: The name of, or reference to, the column from which to drop the index.
860
902
  The column must have only one embedding index.
861
903
  idx_name: The name of the index to drop.
904
+ if_not_exists: Directive for handling a non-existent index. Must be one of the following:
905
+
906
+ - `'error'`: raise an error if the index does not exist.
907
+ - `'ignore'`: do nothing if the index does not exist.
908
+
909
+ Note that `if_not_exists` parameter is only applicable when an `idx_name` is specified
910
+ and it does not exist, or when `column` is specified and it has no index.
911
+ `if_not_exists` does not apply to non-exisitng column.
862
912
 
863
913
  Raises:
864
914
  Error: If `column` is specified, but the column does not exist, or it contains no embedding
865
- indices or multiple embedding indices.
866
- Error: If `idx_name` is specified, but the index does not exist or is not an embedding index.
915
+ indices and `if_not_exists='error'`, or the column has multiple embedding indices.
916
+ Error: If `idx_name` is specified, but the index is not an embedding index, or
917
+ the index does not exist and `if_not_exists='error'`.
867
918
 
868
919
  Examples:
869
920
  Drop the embedding index on the `img` column of the table `my_table` by column name:
@@ -880,6 +931,9 @@ class Table(SchemaObject):
880
931
  >>> tbl = pxt.get_table('my_table')
881
932
  ... tbl.drop_embedding_index(idx_name='idx1')
882
933
 
934
+ Drop the embedding index `idx1` of the table `my_table` by index name, if it exists, otherwise do nothing:
935
+ >>> tbl = pxt.get_table('my_table')
936
+ ... tbl.drop_embedding_index(idx_name='idx1', if_not_exists='ignore')
883
937
  """
884
938
  if (column is None) == (idx_name is None):
885
939
  raise excs.Error("Exactly one of 'column' or 'idx_name' must be provided")
@@ -893,12 +947,14 @@ class Table(SchemaObject):
893
947
  self.__check_column_ref_exists(column, include_bases=True)
894
948
  col = column.col
895
949
  assert col is not None
896
- self._drop_index(col=col, idx_name=idx_name, _idx_class=index.EmbeddingIndex)
950
+ self._drop_index(col=col, idx_name=idx_name, _idx_class=index.EmbeddingIndex, if_not_exists=if_not_exists)
897
951
 
898
952
  def drop_index(
899
953
  self, *,
900
954
  column: Union[str, ColumnRef, None] = None,
901
- idx_name: Optional[str] = None) -> None:
955
+ idx_name: Optional[str] = None,
956
+ if_not_exists: Literal['error', 'ignore'] = 'error'
957
+ ) -> None:
902
958
  """
903
959
  Drop an index from the table. Either a column name or an index name (but not both) must be
904
960
  specified. If a column name or reference is specified, it must be a column containing exactly one index;
@@ -908,6 +964,14 @@ class Table(SchemaObject):
908
964
  column: The name of, or reference to, the column from which to drop the index.
909
965
  The column must have only one embedding index.
910
966
  idx_name: The name of the index to drop.
967
+ if_not_exists: Directive for handling a non-existent index. Must be one of the following:
968
+
969
+ - `'error'`: raise an error if the index does not exist.
970
+ - `'ignore'`: do nothing if the index does not exist.
971
+
972
+ Note that `if_not_exists` parameter is only applicable when an `idx_name` is specified
973
+ and it does not exist, or when `column` is specified and it has no index.
974
+ `if_not_exists` does not apply to non-exisitng column.
911
975
 
912
976
  Raises:
913
977
  Error: If `column` is specified, but the column does not exist, or it contains no
@@ -929,6 +993,10 @@ class Table(SchemaObject):
929
993
  >>> tbl = pxt.get_table('my_table')
930
994
  ... tbl.drop_index(idx_name='idx1')
931
995
 
996
+ Drop the index `idx1` of the table `my_table` by index name, if it exists, otherwise do nothing:
997
+ >>> tbl = pxt.get_table('my_table')
998
+ ... tbl.drop_index(idx_name='idx1', if_not_exists='ignore')
999
+
932
1000
  """
933
1001
  if (column is None) == (idx_name is None):
934
1002
  raise excs.Error("Exactly one of 'column' or 'idx_name' must be provided")
@@ -942,20 +1010,25 @@ class Table(SchemaObject):
942
1010
  self.__check_column_ref_exists(column, include_bases=True)
943
1011
  col = column.col
944
1012
  assert col is not None
945
- self._drop_index(col=col, idx_name=idx_name)
1013
+ self._drop_index(col=col, idx_name=idx_name, if_not_exists=if_not_exists)
946
1014
 
947
1015
  def _drop_index(
948
1016
  self, *, col: Optional[Column] = None,
949
1017
  idx_name: Optional[str] = None,
950
- _idx_class: Optional[type[index.IndexBase]] = None
1018
+ _idx_class: Optional[type[index.IndexBase]] = None,
1019
+ if_not_exists: Literal['error', 'ignore'] = 'error'
951
1020
  ) -> None:
952
1021
  if self._tbl_version_path.is_snapshot():
953
1022
  raise excs.Error('Cannot drop an index from a snapshot')
954
1023
  assert (col is None) != (idx_name is None)
955
1024
 
956
1025
  if idx_name is not None:
1026
+ _if_not_exists = IfNotExistsParam.validated(if_not_exists, 'if_not_exists')
957
1027
  if idx_name not in self._tbl_version.idxs_by_name:
958
- raise excs.Error(f'Index {idx_name!r} does not exist')
1028
+ if _if_not_exists == IfNotExistsParam.ERROR:
1029
+ raise excs.Error(f'Index {idx_name!r} does not exist')
1030
+ assert _if_not_exists == IfNotExistsParam.IGNORE
1031
+ return
959
1032
  idx_id = self._tbl_version.idxs_by_name[idx_name].id
960
1033
  else:
961
1034
  if col.tbl.id != self._tbl_version.id:
@@ -965,7 +1038,11 @@ class Table(SchemaObject):
965
1038
  if _idx_class is not None:
966
1039
  idx_info = [info for info in idx_info if isinstance(info.idx, _idx_class)]
967
1040
  if len(idx_info) == 0:
968
- raise excs.Error(f'Column {col.name!r} does not have an index')
1041
+ _if_not_exists = IfNotExistsParam.validated(if_not_exists, 'if_not_exists')
1042
+ if _if_not_exists == IfNotExistsParam.ERROR:
1043
+ raise excs.Error(f'Column {col.name!r} does not have an index')
1044
+ assert _if_not_exists == IfNotExistsParam.IGNORE
1045
+ return
969
1046
  if len(idx_info) > 1:
970
1047
  raise excs.Error(f"Column {col.name!r} has multiple indices; specify 'idx_name' instead")
971
1048
  idx_id = idx_info[0].id
@@ -9,6 +9,7 @@ import uuid
9
9
  from typing import TYPE_CHECKING, Any, Iterable, Iterator, Literal, Optional
10
10
  from uuid import UUID
11
11
 
12
+ import jsonschema.exceptions
12
13
  import sqlalchemy as sql
13
14
  import sqlalchemy.orm as orm
14
15
 
@@ -173,6 +174,14 @@ class TableVersion:
173
174
  def __hash__(self) -> int:
174
175
  return hash(self.id)
175
176
 
177
+ def _get_column(self, tbl_id: UUID, col_id: int) -> Column:
178
+ if self.id == tbl_id:
179
+ return self.cols_by_id[col_id]
180
+ else:
181
+ if self.base is None:
182
+ raise excs.Error(f'Unknown table id: {tbl_id}')
183
+ return self.base._get_column(tbl_id, col_id)
184
+
176
185
  def create_snapshot_copy(self) -> TableVersion:
177
186
  """Create a snapshot copy of this TableVersion"""
178
187
  assert not self.is_snapshot
@@ -335,7 +344,7 @@ class TableVersion:
335
344
  # instantiate index object
336
345
  cls_name = md.class_fqn.rsplit('.', 1)[-1]
337
346
  cls = getattr(index_module, cls_name)
338
- idx_col = self.cols_by_id[md.indexed_col_id]
347
+ idx_col = self._get_column(UUID(md.indexed_col_tbl_id), md.indexed_col_id)
339
348
  idx = cls.from_dict(idx_col, md.init_args)
340
349
 
341
350
  # fix up the sa column type of the index value and undo columns
@@ -457,7 +466,8 @@ class TableVersion:
457
466
  idx_cls = type(idx)
458
467
  idx_md = schema.IndexMd(
459
468
  id=idx_id, name=idx_name,
460
- indexed_col_id=col.id, index_val_col_id=val_col.id, index_val_undo_col_id=undo_col.id,
469
+ indexed_col_id=col.id, indexed_col_tbl_id=str(col.tbl.id),
470
+ index_val_col_id=val_col.id, index_val_undo_col_id=undo_col.id,
461
471
  schema_version_add=self.schema_version, schema_version_drop=None,
462
472
  class_fqn=idx_cls.__module__ + '.' + idx_cls.__name__, init_args=idx.as_dict())
463
473
  idx_info = self.IndexInfo(id=idx_id, name=idx_name, idx=idx, col=col, val_col=val_col, undo_col=undo_col)
@@ -485,7 +495,10 @@ class TableVersion:
485
495
  idx_md.schema_version_drop = self.schema_version
486
496
  assert idx_md.name in self.idxs_by_name
487
497
  idx_info = self.idxs_by_name[idx_md.name]
498
+ # remove this index entry from the active indexes (in memory)
499
+ # and the index metadata (in persistent table metadata)
488
500
  del self.idxs_by_name[idx_md.name]
501
+ del self.idx_md[idx_id]
489
502
 
490
503
  with Env.get().engine.begin() as conn:
491
504
  self._drop_columns([idx_info.val_col, idx_info.undo_col])
@@ -819,7 +832,7 @@ class TableVersion:
819
832
  if error_if_not_exists:
820
833
  raise excs.Error(f'batch_update(): {len(unmatched_rows)} row(s) not found')
821
834
  if insert_if_not_exists:
822
- insert_status = self.insert(unmatched_rows, None, print_stats=False, fail_on_exception=False)
835
+ insert_status = self.insert(unmatched_rows, None, conn=conn, print_stats=False, fail_on_exception=False)
823
836
  result += insert_status
824
837
  return result
825
838
 
@@ -846,10 +859,11 @@ class TableVersion:
846
859
  raise excs.Error(f'Column {col_name} is a primary key column and cannot be updated')
847
860
 
848
861
  # make sure that the value is compatible with the column type
862
+ value_expr: exprs.Expr
849
863
  try:
850
864
  # check if this is a literal
851
- value_expr: exprs.Expr = exprs.Literal(val, col_type=col.col_type)
852
- except TypeError:
865
+ value_expr = exprs.Literal(val, col_type=col.col_type)
866
+ except (TypeError, jsonschema.exceptions.ValidationError):
853
867
  if not allow_exprs:
854
868
  raise excs.Error(
855
869
  f'Column {col_name}: value {val!r} is not a valid literal for this column '
@@ -858,11 +872,11 @@ class TableVersion:
858
872
  value_expr = exprs.Expr.from_object(val)
859
873
  if value_expr is None:
860
874
  raise excs.Error(f'Column {col_name}: value {val!r} is not a recognized literal or expression')
861
- if not col.col_type.matches(value_expr.col_type):
862
- raise excs.Error((
875
+ if not col.col_type.is_supertype_of(value_expr.col_type, ignore_nullable=True):
876
+ raise excs.Error(
863
877
  f'Type of value {val!r} ({value_expr.col_type}) is not compatible with the type of column '
864
878
  f'{col_name} ({col.col_type})'
865
- ))
879
+ )
866
880
  update_targets[col] = value_expr
867
881
 
868
882
  return update_targets