pixeltable 0.1.2__py3-none-any.whl → 0.2.1__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 (140) hide show
  1. pixeltable/__init__.py +21 -4
  2. pixeltable/catalog/__init__.py +13 -0
  3. pixeltable/catalog/catalog.py +159 -0
  4. pixeltable/catalog/column.py +200 -0
  5. pixeltable/catalog/dir.py +32 -0
  6. pixeltable/catalog/globals.py +33 -0
  7. pixeltable/catalog/insertable_table.py +191 -0
  8. pixeltable/catalog/named_function.py +36 -0
  9. pixeltable/catalog/path.py +58 -0
  10. pixeltable/catalog/path_dict.py +139 -0
  11. pixeltable/catalog/schema_object.py +39 -0
  12. pixeltable/catalog/table.py +581 -0
  13. pixeltable/catalog/table_version.py +749 -0
  14. pixeltable/catalog/table_version_path.py +133 -0
  15. pixeltable/catalog/view.py +203 -0
  16. pixeltable/client.py +520 -31
  17. pixeltable/dataframe.py +540 -349
  18. pixeltable/env.py +373 -48
  19. pixeltable/exceptions.py +12 -21
  20. pixeltable/exec/__init__.py +9 -0
  21. pixeltable/exec/aggregation_node.py +78 -0
  22. pixeltable/exec/cache_prefetch_node.py +113 -0
  23. pixeltable/exec/component_iteration_node.py +79 -0
  24. pixeltable/exec/data_row_batch.py +95 -0
  25. pixeltable/exec/exec_context.py +22 -0
  26. pixeltable/exec/exec_node.py +61 -0
  27. pixeltable/exec/expr_eval_node.py +217 -0
  28. pixeltable/exec/in_memory_data_node.py +69 -0
  29. pixeltable/exec/media_validation_node.py +43 -0
  30. pixeltable/exec/sql_scan_node.py +225 -0
  31. pixeltable/exprs/__init__.py +24 -0
  32. pixeltable/exprs/arithmetic_expr.py +102 -0
  33. pixeltable/exprs/array_slice.py +71 -0
  34. pixeltable/exprs/column_property_ref.py +77 -0
  35. pixeltable/exprs/column_ref.py +105 -0
  36. pixeltable/exprs/comparison.py +77 -0
  37. pixeltable/exprs/compound_predicate.py +98 -0
  38. pixeltable/exprs/data_row.py +187 -0
  39. pixeltable/exprs/expr.py +586 -0
  40. pixeltable/exprs/expr_set.py +39 -0
  41. pixeltable/exprs/function_call.py +380 -0
  42. pixeltable/exprs/globals.py +69 -0
  43. pixeltable/exprs/image_member_access.py +115 -0
  44. pixeltable/exprs/image_similarity_predicate.py +58 -0
  45. pixeltable/exprs/inline_array.py +107 -0
  46. pixeltable/exprs/inline_dict.py +101 -0
  47. pixeltable/exprs/is_null.py +38 -0
  48. pixeltable/exprs/json_mapper.py +121 -0
  49. pixeltable/exprs/json_path.py +159 -0
  50. pixeltable/exprs/literal.py +54 -0
  51. pixeltable/exprs/object_ref.py +41 -0
  52. pixeltable/exprs/predicate.py +44 -0
  53. pixeltable/exprs/row_builder.py +355 -0
  54. pixeltable/exprs/rowid_ref.py +94 -0
  55. pixeltable/exprs/type_cast.py +53 -0
  56. pixeltable/exprs/variable.py +45 -0
  57. pixeltable/func/__init__.py +9 -0
  58. pixeltable/func/aggregate_function.py +194 -0
  59. pixeltable/func/batched_function.py +53 -0
  60. pixeltable/func/callable_function.py +69 -0
  61. pixeltable/func/expr_template_function.py +82 -0
  62. pixeltable/func/function.py +110 -0
  63. pixeltable/func/function_registry.py +227 -0
  64. pixeltable/func/globals.py +36 -0
  65. pixeltable/func/nos_function.py +202 -0
  66. pixeltable/func/signature.py +166 -0
  67. pixeltable/func/udf.py +163 -0
  68. pixeltable/functions/__init__.py +52 -103
  69. pixeltable/functions/eval.py +216 -0
  70. pixeltable/functions/fireworks.py +61 -0
  71. pixeltable/functions/huggingface.py +120 -0
  72. pixeltable/functions/image.py +16 -0
  73. pixeltable/functions/openai.py +88 -0
  74. pixeltable/functions/pil/image.py +148 -7
  75. pixeltable/functions/string.py +13 -0
  76. pixeltable/functions/together.py +27 -0
  77. pixeltable/functions/util.py +41 -0
  78. pixeltable/functions/video.py +62 -0
  79. pixeltable/iterators/__init__.py +3 -0
  80. pixeltable/iterators/base.py +48 -0
  81. pixeltable/iterators/document.py +311 -0
  82. pixeltable/iterators/video.py +89 -0
  83. pixeltable/metadata/__init__.py +54 -0
  84. pixeltable/metadata/converters/convert_10.py +18 -0
  85. pixeltable/metadata/schema.py +211 -0
  86. pixeltable/plan.py +656 -0
  87. pixeltable/store.py +413 -182
  88. pixeltable/tests/conftest.py +143 -86
  89. pixeltable/tests/test_audio.py +65 -0
  90. pixeltable/tests/test_catalog.py +27 -0
  91. pixeltable/tests/test_client.py +14 -14
  92. pixeltable/tests/test_component_view.py +372 -0
  93. pixeltable/tests/test_dataframe.py +433 -0
  94. pixeltable/tests/test_dirs.py +78 -62
  95. pixeltable/tests/test_document.py +117 -0
  96. pixeltable/tests/test_exprs.py +591 -135
  97. pixeltable/tests/test_function.py +297 -67
  98. pixeltable/tests/test_functions.py +283 -1
  99. pixeltable/tests/test_migration.py +43 -0
  100. pixeltable/tests/test_nos.py +54 -0
  101. pixeltable/tests/test_snapshot.py +208 -0
  102. pixeltable/tests/test_table.py +1086 -258
  103. pixeltable/tests/test_transactional_directory.py +42 -0
  104. pixeltable/tests/test_types.py +5 -11
  105. pixeltable/tests/test_video.py +149 -34
  106. pixeltable/tests/test_view.py +530 -0
  107. pixeltable/tests/utils.py +186 -45
  108. pixeltable/tool/create_test_db_dump.py +149 -0
  109. pixeltable/type_system.py +490 -133
  110. pixeltable/utils/__init__.py +17 -46
  111. pixeltable/utils/clip.py +12 -15
  112. pixeltable/utils/coco.py +136 -0
  113. pixeltable/utils/documents.py +39 -0
  114. pixeltable/utils/filecache.py +195 -0
  115. pixeltable/utils/help.py +11 -0
  116. pixeltable/utils/media_store.py +76 -0
  117. pixeltable/utils/parquet.py +126 -0
  118. pixeltable/utils/pytorch.py +172 -0
  119. pixeltable/utils/s3.py +13 -0
  120. pixeltable/utils/sql.py +17 -0
  121. pixeltable/utils/transactional_directory.py +35 -0
  122. pixeltable-0.2.1.dist-info/LICENSE +18 -0
  123. pixeltable-0.2.1.dist-info/METADATA +119 -0
  124. pixeltable-0.2.1.dist-info/RECORD +125 -0
  125. {pixeltable-0.1.2.dist-info → pixeltable-0.2.1.dist-info}/WHEEL +1 -1
  126. pixeltable/catalog.py +0 -1421
  127. pixeltable/exprs.py +0 -1745
  128. pixeltable/function.py +0 -269
  129. pixeltable/functions/clip.py +0 -10
  130. pixeltable/functions/pil/__init__.py +0 -23
  131. pixeltable/functions/tf.py +0 -21
  132. pixeltable/index.py +0 -57
  133. pixeltable/tests/test_dict.py +0 -24
  134. pixeltable/tests/test_tf.py +0 -69
  135. pixeltable/tf.py +0 -33
  136. pixeltable/utils/tf.py +0 -33
  137. pixeltable/utils/video.py +0 -32
  138. pixeltable-0.1.2.dist-info/LICENSE +0 -201
  139. pixeltable-0.1.2.dist-info/METADATA +0 -89
  140. pixeltable-0.1.2.dist-info/RECORD +0 -37
@@ -1,94 +1,324 @@
1
+ from typing import Optional
2
+
1
3
  import numpy as np
2
- import pandas as pd
3
4
  import pytest
4
5
 
5
- from pixeltable.function import Function, FunctionRegistry
6
- from pixeltable.type_system import IntType
6
+ import pixeltable as pxt
7
+ import pixeltable.exceptions as excs
7
8
  from pixeltable import catalog
8
- import pixeltable as pt
9
- from pixeltable import exceptions as exc
9
+ from pixeltable.func import Function, FunctionRegistry, Batch
10
+ from pixeltable.type_system import IntType, FloatType
11
+ from pixeltable.tests.utils import assert_resultset_eq
12
+ import pixeltable.func as func
10
13
 
11
14
 
12
15
  def dummy_fn(i: int) -> int:
13
16
  return i
14
17
 
15
18
  class TestFunction:
16
- eval_fn = lambda x: x + 1
17
- func = Function(IntType(), [IntType()], eval_fn=eval_fn)
19
+ @pxt.udf(return_type=IntType(), param_types=[IntType()])
20
+ def func(x: int) -> int:
21
+ return x + 1
22
+
23
+ @pxt.uda(name='agg', value_type=IntType(), update_types=[IntType()])
24
+ class Aggregator:
25
+ def __init__(self):
26
+ self.sum = 0
27
+ def update(self, val: int) -> None:
28
+ if val is not None:
29
+ self.sum += val
30
+ def value(self) -> int:
31
+ return self.sum
18
32
 
19
- def test_serialize_anonymous(self, init_db: None) -> None:
33
+ def test_serialize_anonymous(self, init_env) -> None:
20
34
  d = self.func.as_dict()
21
35
  FunctionRegistry.get().clear_cache()
22
36
  deserialized = Function.from_dict(d)
23
- assert deserialized.eval_fn(1) == 2
37
+ # TODO: add Function.exec() and then use that
38
+ assert deserialized.py_fn(1) == 2
24
39
 
25
- def test_create(self, test_db: catalog.Db) -> None:
26
- db = test_db
27
- db.create_function('test_fn', self.func)
40
+ @pytest.mark.skip(reason='deprecated')
41
+ def test_create(self, test_client: pxt.Client) -> None:
42
+ cl = test_client
43
+ cl.create_function('test_fn', self.func)
44
+ assert self.func.md.fqn == 'test_fn'
28
45
  FunctionRegistry.get().clear_cache()
29
- cl = pt.Client()
30
- db2 = cl.get_db('test')
31
- fn2 = db2.load_function('test_fn')
32
- assert fn2.eval_fn(1) == 2
33
-
34
- with pytest.raises(exc.DuplicateNameError):
35
- db.create_function('test_fn', self.func)
36
- with pytest.raises(exc.UnknownEntityError):
37
- db.create_function('dir1.test_fn', self.func)
38
- with pytest.raises(exc.Error):
39
- library_fn = Function(IntType(), [IntType()], module_name=__name__, eval_symbol='dummy_fn')
40
- db.create_function('library_fn', library_fn)
41
-
42
- def test_update(self, test_db: catalog.Db) -> None:
43
- db = test_db
44
- db.create_function('test_fn', self.func)
46
+ cl = pxt.Client(reload=True)
47
+ _ = cl.list_functions()
48
+ fn2 = cl.get_function('test_fn')
49
+ assert fn2.md.fqn == 'test_fn'
50
+ assert fn2.py_fn(1) == 2
51
+
52
+ with pytest.raises(excs.Error):
53
+ cl.create_function('test_fn', self.func)
54
+ with pytest.raises(excs.Error):
55
+ cl.create_function('dir1.test_fn', self.func)
56
+ with pytest.raises(excs.Error):
57
+ library_fn = make_library_function(IntType(), [IntType()], __name__, 'dummy_fn')
58
+ cl.create_function('library_fn', library_fn)
59
+
60
+ @pytest.mark.skip(reason='deprecated')
61
+ def test_update(self, test_client: pxt.Client, test_tbl: catalog.Table) -> None:
62
+ cl = test_client
63
+ t = test_tbl
64
+ cl.create_function('test_fn', self.func)
65
+ res1 = t[self.func(t.c2)].show(0).to_pandas()
66
+
67
+ # load function from db and make sure it computes the same thing as before
45
68
  FunctionRegistry.get().clear_cache()
46
- cl = pt.Client()
47
- db2 = cl.get_db('test')
48
- fn = db2.load_function('test_fn')
49
- db2.update_function('test_fn', lambda x: x + 2)
50
- # change visible in previously-loaded Function
51
- assert fn.eval_fn(1) == 3
69
+ cl = pxt.Client(reload=True)
70
+ fn = cl.get_function('test_fn')
71
+ res2 = t[fn(t.c2)].show(0).to_pandas()
72
+ assert res1.col_0.equals(res2.col_0)
73
+ fn.py_fn = lambda x: x + 2
74
+ cl.update_function('test_fn', fn)
75
+ assert self.func.md.fqn == fn.md.fqn # fqn doesn't change
52
76
 
53
- def test_rename(self, test_db: catalog.Db) -> None:
54
- db = test_db
55
- db.create_function('test_fn', self.func)
77
+ FunctionRegistry.get().clear_cache()
78
+ cl = pxt.Client(reload=True)
79
+ fn = cl.get_function('test_fn')
80
+ assert self.func.md.fqn == fn.md.fqn # fqn doesn't change
81
+ res3 = t[fn(t.c2)].show(0).to_pandas()
82
+ assert (res2.col_0 + 1).equals(res3.col_0)
83
+
84
+ # signature changes
85
+ with pytest.raises(excs.Error):
86
+ cl.update_function('test_fn', make_function(FloatType(), [IntType()], fn.py_fn))
87
+ with pytest.raises(excs.Error):
88
+ cl.update_function('test_fn', make_function(IntType(), [FloatType()], fn.py_fn))
89
+ with pytest.raises(excs.Error):
90
+ cl.update_function('test_fn', self.agg)
91
+
92
+ @pytest.mark.skip(reason='deprecated')
93
+ def test_move(self, test_client: pxt.Client) -> None:
94
+ cl = test_client
95
+ cl.create_function('test_fn', self.func)
56
96
 
57
97
  FunctionRegistry.get().clear_cache()
58
- cl = pt.Client()
59
- db2 = cl.get_db('test')
60
- with pytest.raises(exc.UnknownEntityError):
61
- db2.rename_function('test_fn2', 'test_fn')
62
- db2.rename_function('test_fn', 'test_fn2')
63
- func = db2.load_function('test_fn2')
64
- assert func.eval_fn(1) == 2
98
+ cl = pxt.Client(reload=True)
99
+ with pytest.raises(excs.Error):
100
+ cl.move('test_fn2', 'test_fn')
101
+ cl.move('test_fn', 'test_fn2')
102
+ func = cl.get_function('test_fn2')
103
+ assert func.py_fn(1) == 2
104
+ assert func.md.fqn == 'test_fn2'
65
105
 
66
- with pytest.raises(exc.UnknownEntityError):
67
- _ = db2.load_function('test_fn')
106
+ with pytest.raises(excs.Error):
107
+ _ = cl.get_function('test_fn')
68
108
 
69
109
  # move function between directories
70
- db2.create_dir('functions')
71
- db2.create_dir('functions2')
72
- db2.create_function('functions.func1', self.func)
73
- with pytest.raises(exc.UnknownEntityError):
74
- db2.rename_function('functions2.func1', 'functions.func1')
75
- db2.rename_function('functions.func1', 'functions2.func1')
110
+ cl.create_dir('functions')
111
+ cl.create_dir('functions2')
112
+ cl.create_function('functions.func1', self.func)
113
+ with pytest.raises(excs.Error):
114
+ cl.move('functions2.func1', 'functions.func1')
115
+ cl.move('functions.func1', 'functions2.func1')
116
+ func = cl.get_function('functions2.func1')
117
+ assert func.md.fqn == 'functions2.func1'
118
+
76
119
 
77
120
  FunctionRegistry.get().clear_cache()
78
- cl = pt.Client()
79
- db3 = cl.get_db('test')
80
- func = db3.load_function('functions2.func1')
81
- assert func.eval_fn(1) == 2
82
- with pytest.raises(exc.UnknownEntityError):
83
- _ = db3.load_function('functions.func1')
84
-
85
- def test_drop(self, test_db: catalog.Db) -> None:
86
- db = test_db
87
- db.create_function('test_fn', self.func)
121
+ cl = pxt.Client(reload=True)
122
+ func = cl.get_function('functions2.func1')
123
+ assert func.py_fn(1) == 2
124
+ assert func.md.fqn == 'functions2.func1'
125
+ with pytest.raises(excs.Error):
126
+ _ = cl.get_function('functions.func1')
127
+
128
+ @pytest.mark.skip(reason='deprecated')
129
+ def test_drop(self, test_client: pxt.Client) -> None:
130
+ cl = test_client
131
+ cl.create_function('test_fn', self.func)
88
132
  FunctionRegistry.get().clear_cache()
89
- cl = pt.Client()
90
- db2 = cl.get_db('test')
91
- db2.drop_function('test_fn')
133
+ cl = pxt.Client(reload=True)
134
+ cl.drop_function('test_fn')
135
+
136
+ with pytest.raises(excs.Error):
137
+ _ = cl.get_function('test_fn')
138
+
139
+ def test_list(self, test_client: pxt.Client) -> None:
140
+ _ = FunctionRegistry.get().list_functions()
141
+ print(_)
142
+
143
+ def test_stored_udf(self, test_client: pxt.Client) -> None:
144
+ cl = test_client
145
+ t = cl.create_table('test', {'c1': pxt.IntType(), 'c2': pxt.FloatType()})
146
+ rows = [{'c1': i, 'c2': i + 0.5} for i in range(100)]
147
+ status = t.insert(rows)
148
+ assert status.num_rows == len(rows)
149
+ assert status.num_excs == 0
150
+
151
+ @pxt.udf(_force_stored=True)
152
+ def f1(a: int, b: float) -> float:
153
+ return a + b
154
+ t['f1'] = f1(t.c1, t.c2)
155
+
156
+ func.FunctionRegistry.get().clear_cache()
157
+ cl = pxt.Client(reload=True)
158
+ t = cl.get_table('test')
159
+ status = t.insert(rows)
160
+ assert status.num_rows == len(rows)
161
+ assert status.num_excs == 0
162
+
163
+ def test_call(self, test_tbl: catalog.Table) -> None:
164
+ t = test_tbl
165
+
166
+ @pxt.udf(return_type=IntType(), param_types=[IntType(), FloatType(), FloatType(), FloatType()])
167
+ def f1(a: int, b: float, c: float = 0.0, d: float = 1.0) -> float:
168
+ return a + b + c + d
169
+
170
+ r0 = t[t.c2, t.c3].show(0).to_pandas()
171
+ # positional params with default args
172
+ r1 = t[f1(t.c2, t.c3)].show(0).to_pandas()['col_0']
173
+ assert np.all(r1 == r0.c2 + r0.c3 + 1.0)
174
+ # kw args only
175
+ r2 = t[f1(c=0.0, b=t.c3, a=t.c2)].show(0).to_pandas()['col_0']
176
+ assert np.all(r1 == r2)
177
+ # overriding default args
178
+ r3 = t[f1(d=0.0, c=1.0, b=t.c3, a=t.c2)].show(0).to_pandas()['col_0']
179
+ assert np.all(r2 == r3)
180
+ # overriding default with positional arg
181
+ r4 = t[f1(t.c2, t.c3, 0.0)].show(0).to_pandas()['col_0']
182
+ assert np.all(r3 == r4)
183
+ # overriding default with positional arg and kw arg
184
+ r5 = t[f1(t.c2, t.c3, 1.0, d=0.0)].show(0).to_pandas()['col_0']
185
+ assert np.all(r4 == r5)
186
+ # d is kwarg
187
+ r6 = t[f1(t.c2, d=1.0, b=t.c3)].show(0).to_pandas()['col_0']
188
+ assert np.all(r5 == r6)
189
+ # d is Expr kwarg
190
+ r6 = t[f1(1, d=t.c3, b=t.c3)].show(0).to_pandas()['col_0']
191
+ assert np.all(r5 == r6)
192
+
193
+ # test handling of Nones
194
+ @pxt.udf(
195
+ return_type=IntType(),
196
+ param_types=[IntType(nullable=True), FloatType(nullable=False), FloatType(nullable=True)])
197
+ def f2(a: int, b: float = 0.0, c: float = 1.0) -> float:
198
+ return (0.0 if a is None else a) + b + (0.0 if c is None else c)
199
+ r0 = t[f2(1, t.c3)].show(0).to_pandas()['col_0']
200
+ r1 = t[f2(None, t.c3, 2.0)].show(0).to_pandas()['col_0']
201
+ assert np.all(r0 == r1)
202
+ r2 = t[f2(2, t.c3, None)].show(0).to_pandas()['col_0']
203
+ assert np.all(r1 == r2)
204
+ # kwarg with None
205
+ r3 = t[f2(c=None, a=t.c2)].show(0).to_pandas()['col_0']
206
+ # kwarg with Expr
207
+ r4 = t[f2(c=t.c3, a=None)].show(0).to_pandas()['col_0']
208
+ assert np.all(r3 == r4)
209
+
210
+ with pytest.raises(TypeError) as exc_info:
211
+ _ = t[f1(t.c2, c=0.0)].show(0)
212
+ assert "'b'" in str(exc_info.value)
213
+ with pytest.raises(TypeError) as exc_info:
214
+ _ = t[f1(t.c2)].show(0)
215
+ assert "'b'" in str(exc_info.value)
216
+ with pytest.raises(TypeError) as exc_info:
217
+ _ = t[f1(c=1.0, a=t.c2)].show(0)
218
+ assert "'b'" in str(exc_info.value)
219
+
220
+ # bad default value
221
+ with pytest.raises(excs.Error) as exc_info:
222
+ @pxt.udf(return_type=IntType(), param_types=[IntType(), FloatType(), FloatType()])
223
+ def f1(a: int, b: float, c: str = '') -> float:
224
+ return a + b + c
225
+ assert 'default value' in str(exc_info.value).lower()
226
+ # missing param type
227
+ with pytest.raises(excs.Error) as exc_info:
228
+ @pxt.udf(return_type=IntType(), param_types=[IntType(), FloatType()])
229
+ def f1(a: int, b: float, c: str = '') -> float:
230
+ return a + b + c
231
+ assert 'missing type for parameter c' in str(exc_info.value).lower()
232
+ # bad parameter name
233
+ with pytest.raises(excs.Error) as exc_info:
234
+ @pxt.udf(return_type=IntType(), param_types=[IntType()])
235
+ def f1(group_by: int) -> int:
236
+ return group_by
237
+ assert 'reserved' in str(exc_info.value)
238
+ # bad parameter name
239
+ with pytest.raises(excs.Error) as exc_info:
240
+ @pxt.udf(return_type=IntType(), param_types=[IntType()])
241
+ def f1(order_by: int) -> int:
242
+ return order_by
243
+ assert 'reserved' in str(exc_info.value)
244
+
245
+ def test_expr_udf(self, test_tbl: catalog.Table) -> None:
246
+ t = test_tbl
247
+ @pxt.expr_udf
248
+ def times2(x: int) -> int:
249
+ return x + x
250
+ res1 = t.select(out=times2(t.c2)).order_by(t.c2).collect()
251
+ res2 = t.select(t.c2 * 2).order_by(t.c2).collect()
252
+ assert_resultset_eq(res1, res2)
253
+
254
+ with pytest.raises(TypeError) as exc_info:
255
+ _ = t.select(times2(y=t.c2)).collect()
256
+ assert 'missing a required argument' in str(exc_info.value).lower()
257
+
258
+ with pytest.raises(excs.Error) as exc_info:
259
+ # parameter types cannot be inferred
260
+ @pxt.expr_udf
261
+ def add1(x, y) -> int:
262
+ return x + y
263
+ assert 'cannot infer pixeltable type' in str(exc_info.value).lower()
264
+
265
+ with pytest.raises(excs.Error) as exc_info:
266
+ # return type cannot be inferred
267
+ @pxt.expr_udf
268
+ def add1(x: int, y: int):
269
+ return x + y
270
+ assert 'cannot infer pixeltable return type' in str(exc_info.value).lower()
271
+
272
+ with pytest.raises(excs.Error) as exc_info:
273
+ # missing param types
274
+ @pxt.expr_udf(param_types=[IntType()])
275
+ def add1(x, y) -> int:
276
+ return x + y
277
+ assert 'missing type for parameter y' in str(exc_info.value).lower()
278
+
279
+ with pytest.raises(TypeError) as exc_info:
280
+ # signature has correct parameter kind
281
+ @pxt.expr_udf
282
+ def add1(*, x: int) -> int:
283
+ return x + 1
284
+ _ = t.select(add1(t.c2)).collect()
285
+ assert 'takes 0 positional arguments' in str(exc_info.value).lower()
286
+
287
+ @pxt.expr_udf
288
+ def add2(x: int, y: int = 1) -> int:
289
+ return x + y
290
+ res1 = t.select(out=add2(t.c2)).order_by(t.c2).collect()
291
+
292
+ # Test that various invalid udf definitions generate
293
+ # correct error messages.
294
+ def test_invalid_udfs(self):
295
+ with pytest.raises(excs.Error) as exc_info:
296
+ @pxt.udf
297
+ def udf1(name: Batch[str]) -> str:
298
+ return ''
299
+ assert 'batched parameters in udf, but no `batch_size` given' in str(exc_info.value).lower()
300
+
301
+ with pytest.raises(excs.Error) as exc_info:
302
+ @pxt.udf(batch_size=32)
303
+ def udf2(name: Batch[str]) -> str:
304
+ return ''
305
+ assert 'batch_size is specified; Python return type must be a `Batch`' in str(exc_info.value)
306
+
307
+ with pytest.raises(excs.Error) as exc_info:
308
+ @pxt.udf
309
+ def udf3(name: str) -> Optional[np.ndarray]:
310
+ return None
311
+ assert 'cannot infer pixeltable return type' in str(exc_info.value).lower()
312
+
313
+ with pytest.raises(excs.Error) as exc_info:
314
+ @pxt.udf
315
+ def udf4(array: np.ndarray) -> str:
316
+ return ''
317
+ assert 'cannot infer pixeltable type for parameter array' in str(exc_info.value).lower()
318
+
319
+ with pytest.raises(excs.Error) as exc_info:
320
+ @pxt.udf
321
+ def udf5(name: str, untyped) -> str:
322
+ return ''
323
+ assert 'cannot infer pixeltable type for parameter untyped' in str(exc_info.value).lower()
92
324
 
93
- with pytest.raises(exc.UnknownEntityError):
94
- _ = db2.load_function('test_fn')