pixeltable 0.1.1__py3-none-any.whl → 0.2.0__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 (139) hide show
  1. pixeltable/__init__.py +34 -6
  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 -30
  17. pixeltable/dataframe.py +540 -349
  18. pixeltable/env.py +373 -45
  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 -87
  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 +1085 -262
  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 -126
  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.0.dist-info/LICENSE +18 -0
  123. pixeltable-0.2.0.dist-info/METADATA +117 -0
  124. pixeltable-0.2.0.dist-info/RECORD +125 -0
  125. {pixeltable-0.1.1.dist-info → pixeltable-0.2.0.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.1.dist-info/METADATA +0 -31
  139. pixeltable-0.1.1.dist-info/RECORD +0 -36
@@ -1,119 +1,175 @@
1
- import numpy as np
2
- import pandas as pd
3
- import datetime
1
+ import json
2
+ import logging
3
+ import os
4
+ import pathlib
5
+ from typing import List
4
6
 
7
+ import numpy as np
5
8
  import pytest
6
9
 
7
- import pixeltable as pt
10
+ import pixeltable as pxt
8
11
  import pixeltable.catalog as catalog
9
- from pixeltable.type_system import \
10
- StringType, IntType, FloatType, BoolType, TimestampType, ImageType, JsonType
11
- from pixeltable.tests.utils import read_data_file, make_tbl, create_table_data
12
+ from pixeltable import exprs
13
+ from pixeltable import functions as ptf
14
+ from pixeltable.exprs import RELATIVE_PATH_ROOT as R
15
+ from pixeltable.metadata import SystemInfo, create_system_info
16
+ from pixeltable.metadata.schema import TableSchemaVersion, TableVersion, Table, Function, Dir
17
+ from pixeltable.tests.utils import read_data_file, create_test_tbl, create_all_datatypes_tbl, skip_test_if_not_installed
18
+ from pixeltable.type_system import StringType, ImageType, FloatType
12
19
 
13
20
 
14
21
  @pytest.fixture(scope='session')
15
- def init_db(tmp_path_factory) -> None:
22
+ def init_env(tmp_path_factory) -> None:
16
23
  from pixeltable.env import Env
24
+ # set the relevant env vars for Client() to connect to the test db
25
+
26
+ shared_home = pathlib.Path(os.environ.get('PIXELTABLE_HOME', '~/.pixeltable')).expanduser()
27
+ home_dir = str(tmp_path_factory.mktemp('base') / '.pixeltable')
28
+ os.environ['PIXELTABLE_HOME'] = home_dir
29
+ os.environ['PIXELTABLE_CONFIG'] = str(shared_home / 'config.yaml')
30
+ test_db = 'test'
31
+ os.environ['PIXELTABLE_DB'] = test_db
32
+ os.environ['PIXELTABLE_PGDATA'] = str(shared_home / 'pgdata')
33
+
17
34
  # this also runs create_all()
18
- db_name = 'test'
19
- Env.get().set_up(tmp_path_factory.mktemp('base'), db_name=db_name, echo=True)
35
+ Env.get().set_up(echo=True)
20
36
  yield
21
37
  # leave db in place for debugging purposes
22
38
 
39
+ @pytest.fixture(scope='function')
40
+ def test_client(init_env) -> pxt.Client:
41
+ # Clean the DB *before* instantiating a client object. This is because some tests
42
+ # (such as test_migration.py) may leave the DB in a broken state, from which the
43
+ # client is uninstantiable.
44
+ clean_db()
45
+ cl = pxt.Client(reload=True)
46
+ cl.logging(level=logging.DEBUG, to_stdout=True)
47
+ yield cl
48
+
49
+
50
+ def clean_db(restore_tables: bool = True) -> None:
51
+ from pixeltable.env import Env
52
+ # The logic from Client.reset_catalog() has been moved here, so that it
53
+ # does not rely on instantiating a Client object. As before, UUID-named data tables will
54
+ # not be cleaned. If in the future it is desirable to clean out data tables as well,
55
+ # the commented lines may be used to drop ALL tables from the test db.
56
+ # sql_md = declarative_base().metadata
57
+ # sql_md.reflect(Env.get().engine)
58
+ # sql_md.drop_all(bind=Env.get().engine)
59
+ engine = Env.get().engine
60
+ SystemInfo.__table__.drop(engine, checkfirst=True)
61
+ TableSchemaVersion.__table__.drop(engine, checkfirst=True)
62
+ TableVersion.__table__.drop(engine, checkfirst=True)
63
+ Table.__table__.drop(engine, checkfirst=True)
64
+ Function.__table__.drop(engine, checkfirst=True)
65
+ Dir.__table__.drop(engine, checkfirst=True)
66
+ if restore_tables:
67
+ Dir.__table__.create(engine)
68
+ Function.__table__.create(engine)
69
+ Table.__table__.create(engine)
70
+ TableVersion.__table__.create(engine)
71
+ TableSchemaVersion.__table__.create(engine)
72
+ SystemInfo.__table__.create(engine)
73
+ create_system_info(engine)
74
+
23
75
 
24
76
  @pytest.fixture(scope='function')
25
- def test_db(init_db: None) -> pt.Db:
26
- cl = pt.Client()
27
- db = cl.create_db(f'test')
28
- yield db
29
- cl.drop_db(db.name, force=True)
77
+ def test_tbl(test_client: pxt.Client) -> catalog.Table:
78
+ return create_test_tbl(test_client)
30
79
 
80
+ # @pytest.fixture(scope='function')
81
+ # def test_stored_fn(test_client: pt.Client) -> pt.Function:
82
+ # @pt.udf(return_type=pt.IntType(), param_types=[pt.IntType()])
83
+ # def test_fn(x):
84
+ # return x + 1
85
+ # test_client.create_function('test_fn', test_fn)
86
+ # return test_fn
31
87
 
32
88
  @pytest.fixture(scope='function')
33
- def test_tbl(test_db: pt.Db) -> catalog.Table:
34
- cols = [
35
- catalog.Column('c1', StringType(), nullable=False),
36
- catalog.Column('c2', IntType(), nullable=False),
37
- catalog.Column('c3', FloatType(), nullable=False),
38
- catalog.Column('c4', BoolType(), nullable=False),
39
- catalog.Column('c5', TimestampType(), nullable=False),
40
- catalog.Column('c6', JsonType(), nullable=False),
41
- catalog.Column('c7', JsonType(), nullable=False),
89
+ def test_tbl_exprs(test_tbl: catalog.Table) -> List[exprs.Expr]:
90
+ #def test_tbl_exprs(test_tbl: catalog.Table, test_stored_fn: pt.Function) -> List[exprs.Expr]:
91
+
92
+ t = test_tbl
93
+ return [
94
+ t.c1,
95
+ t.c7['*'].f1,
96
+ exprs.Literal('test'),
97
+ exprs.InlineDict({
98
+ 'a': t.c1, 'b': t.c6.f1, 'c': 17,
99
+ 'd': exprs.InlineDict({'e': t.c2}),
100
+ 'f': exprs.InlineArray((t.c3, t.c3))
101
+ }),
102
+ exprs.InlineArray([[t.c2, t.c2], [t.c2, t.c2]]),
103
+ t.c2 > 5,
104
+ t.c2 == None,
105
+ ~(t.c2 > 5),
106
+ (t.c2 > 5) & (t.c1 == 'test'),
107
+ (t.c2 > 5) | (t.c1 == 'test'),
108
+ t.c7['*'].f5 >> [R[3], R[2], R[1], R[0]],
109
+ t.c8[0, 1:],
110
+ t.c2.astype(FloatType()),
111
+ (t.c2 + 1).astype(FloatType()),
112
+ t.c2.apply(str),
113
+ (t.c2 + 1).apply(str),
114
+ t.c3.apply(str),
115
+ t.c4.apply(str),
116
+ t.c5.apply(str),
117
+ t.c6.apply(str),
118
+ t.c1.apply(json.loads),
119
+ t.c8.errortype,
120
+ t.c8.errormsg,
121
+ ptf.sum(t.c2, group_by=t.c4, order_by=t.c3),
122
+ #test_stored_fn(t.c2),
42
123
  ]
43
- t = test_db.create_table('test__tbl', cols)
44
-
45
- num_rows = 100
46
- d1 = {
47
- 'f1': 'test string 1',
48
- 'f2': 1,
49
- 'f3': 1.0,
50
- 'f4': True,
51
- 'f5': [1.0, 2.0, 3.0, 4.0],
52
- 'f6': {
53
- 'f7': 'test string 2',
54
- 'f8': [1.0, 2.0, 3.0, 4.0],
55
- },
56
- }
57
- d2 = [d1, d1]
58
-
59
- c1_data = [f'test string {i}' for i in range(num_rows)]
60
- c2_data = [i for i in range(num_rows)]
61
- c3_data = [float(i) for i in range(num_rows)]
62
- c4_data = [bool(i % 2) for i in range(num_rows)]
63
- c5_data = [datetime.datetime.now()] * num_rows
64
- c6_data = []
65
- for i in range(num_rows):
66
- d = {
67
- 'f1': f'test string {i}',
68
- 'f2': i,
69
- 'f3': float(i),
70
- 'f4': bool(i % 2),
71
- 'f5': [1.0, 2.0, 3.0, 4.0],
72
- 'f6': {
73
- 'f7': 'test string 2',
74
- 'f8': [1.0, 2.0, 3.0, 4.0],
75
- },
76
- }
77
- c6_data.append(d)
78
-
79
- c7_data = [d2] * num_rows
80
- data = {'c1': c1_data, 'c2': c2_data, 'c3': c3_data, 'c4': c4_data, 'c5': c5_data, 'c6': c6_data, 'c7': c7_data}
81
- pd_df = pd.DataFrame(data=data)
82
- t.insert_pandas(pd_df)
83
- return t
84
124
 
125
+ @pytest.fixture(scope='function')
126
+ def all_datatypes_tbl(test_client: pxt.Client) -> catalog.Table:
127
+ return create_all_datatypes_tbl(test_client)
85
128
 
86
129
  @pytest.fixture(scope='function')
87
- def img_tbl(test_db: pt.Db) -> catalog.Table:
88
- cols = [
89
- catalog.Column('img', ImageType(), nullable=False, indexed=False),
90
- catalog.Column('category', StringType(), nullable=False),
91
- catalog.Column('split', StringType(), nullable=False),
92
- ]
130
+ def img_tbl(test_client: pxt.Client) -> catalog.Table:
131
+ schema = {
132
+ 'img': ImageType(nullable=False),
133
+ 'category': StringType(nullable=False),
134
+ 'split': StringType(nullable=False),
135
+ }
93
136
  # this table is not indexed in order to avoid the cost of computing embeddings
94
- tbl = test_db.create_table('test_img_tbl', cols)
95
- df = read_data_file('imagenette2-160', 'manifest.csv', ['img'])
96
- tbl.insert_pandas(df)
137
+ tbl = test_client.create_table('test_img_tbl', schema)
138
+ rows = read_data_file('imagenette2-160', 'manifest.csv', ['img'])
139
+ tbl.insert(rows)
97
140
  return tbl
98
141
 
142
+ @pytest.fixture(scope='function')
143
+ def img_tbl_exprs(img_tbl: catalog.Table) -> List[exprs.Expr]:
144
+ img_t = img_tbl
145
+ return [
146
+ img_t.img.width,
147
+ img_t.img.rotate(90),
148
+ # we're using a list here, not a tuple; the latter turns into a list during the back/forth conversion
149
+ img_t.img.rotate(90).resize([224, 224]),
150
+ img_t.img.fileurl,
151
+ img_t.img.localpath,
152
+ ]
99
153
 
100
154
  # TODO: why does this not work with a session scope? (some user tables don't get created with create_all())
101
155
  #@pytest.fixture(scope='session')
102
- #def indexed_img_tbl(init_db: None) -> catalog.Table:
156
+ #def indexed_img_tbl(init_env: None) -> catalog.Table:
103
157
  # cl = pt.Client()
104
158
  # db = cl.create_db('test_indexed')
105
159
  @pytest.fixture(scope='function')
106
- def indexed_img_tbl(test_db: pt.Db) -> catalog.Table:
107
- db = test_db
108
- cols = [
109
- catalog.Column('img', ImageType(), nullable=False, indexed=True),
110
- catalog.Column('category', StringType(), nullable=False),
111
- catalog.Column('split', StringType(), nullable=False),
112
- ]
113
- tbl = db.create_table('test_indexed_img_tbl', cols)
114
- df = read_data_file('imagenette2-160', 'manifest.csv', ['img'])
115
- # select rows randomly in the hope of getting a good sample of the available categories
160
+ def indexed_img_tbl(test_client: pxt.Client) -> catalog.Table:
161
+ skip_test_if_not_installed('nos')
162
+ cl = test_client
163
+ schema = {
164
+ 'img': { 'type': ImageType(nullable=False), 'indexed': True },
165
+ 'category': StringType(nullable=False),
166
+ 'split': StringType(nullable=False),
167
+ }
168
+ tbl = cl.create_table('test_indexed_img_tbl', schema)
169
+ rows = read_data_file('imagenette2-160', 'manifest.csv', ['img'])
170
+ # select output_rows randomly in the hope of getting a good sample of the available categories
116
171
  rng = np.random.default_rng(17)
117
- idxs = rng.choice(np.arange(len(df)), size=40, replace=False)
118
- tbl.insert_pandas(df.iloc[idxs])
172
+ idxs = rng.choice(np.arange(len(rows)), size=40, replace=False)
173
+ rows = [rows[i] for i in idxs]
174
+ tbl.insert(rows)
119
175
  return tbl
@@ -0,0 +1,65 @@
1
+ from typing import Optional
2
+
3
+ import av
4
+
5
+ import pixeltable as pxt
6
+ import pixeltable.env as env
7
+ from pixeltable.tests.utils import get_video_files, get_audio_files
8
+ from pixeltable.type_system import VideoType, AudioType
9
+ from pixeltable.utils.media_store import MediaStore
10
+
11
+
12
+ class TestAudio:
13
+ def check_audio_params(self, path: str, format: Optional[str] = None, codec: Optional[str] = None):
14
+ with av.open(path) as container:
15
+ audio_stream = container.streams.audio[0]
16
+ if format is not None:
17
+ assert format == container.format.name
18
+ if codec is not None:
19
+ assert codec == audio_stream.codec_context.codec.name
20
+
21
+ def test_basic(self, test_client: pxt.Client) -> None:
22
+ audio_filepaths = get_audio_files()
23
+ cl = test_client
24
+ audio_t = cl.create_table('audio', {'audio_file': AudioType()})
25
+ status = audio_t.insert({'audio_file': p} for p in audio_filepaths)
26
+ assert status.num_rows == len(audio_filepaths)
27
+ assert status.num_excs == 0
28
+ paths = audio_t.select(output=audio_t.audio_file.localpath).collect()['output']
29
+ assert set(paths) == set(audio_filepaths)
30
+
31
+ def test_extract(self, test_client: pxt.Client) -> None:
32
+ video_filepaths = get_video_files()
33
+ cl = test_client
34
+ video_t = cl.create_table('videos', {'video': VideoType()})
35
+ from pixeltable.functions.video import extract_audio
36
+ video_t.add_column(audio=extract_audio(video_t.video))
37
+
38
+ # one of the 3 videos doesn't have audio
39
+ status = video_t.insert({'video': p} for p in video_filepaths)
40
+ assert status.num_rows == len(video_filepaths)
41
+ assert status.num_excs == 0
42
+ assert MediaStore.count(video_t.get_id()) == len(video_filepaths) - 1
43
+ assert video_t.where(video_t.audio != None).count() == len(video_filepaths) - 1
44
+ assert env.Env.get().num_tmp_files() == 0
45
+
46
+ # make sure everything works with a fresh client
47
+ cl = pxt.Client()
48
+ video_t = cl.get_table('videos')
49
+ assert video_t.where(video_t.audio != None).count() == len(video_filepaths) - 1
50
+
51
+ # test generating different formats and codecs
52
+ paths = video_t.select(output=extract_audio(video_t.video, format='wav', codec='pcm_s16le')).collect()['output']
53
+ # media files that are created as a part of a query end up in the tmp dir
54
+ assert env.Env.get().num_tmp_files() == video_t.where(video_t.audio != None).count()
55
+ for path in [p for p in paths if p is not None]:
56
+ self.check_audio_params(path, format='wav', codec='pcm_s16le')
57
+ # higher resolution
58
+ paths = video_t.select(output=extract_audio(video_t.video, format='wav', codec='pcm_s32le')).collect()['output']
59
+ for path in [p for p in paths if p is not None]:
60
+ self.check_audio_params(path, format='wav', codec='pcm_s32le')
61
+
62
+ for format in ['mp3', 'flac']:
63
+ paths = video_t.select(output=extract_audio(video_t.video, format=format)).collect()['output']
64
+ for path in [p for p in paths if p is not None]:
65
+ self.check_audio_params(path, format=format)
@@ -0,0 +1,27 @@
1
+ from pixeltable.catalog import is_valid_identifier, is_valid_path
2
+
3
+ class TestCatalog:
4
+ """Tests for miscellanous catalog functions."""
5
+ def test_valid_identifier(self) -> None:
6
+ valid_ids = ['a', 'a1', 'a_1', 'a_']
7
+ invalid_ids = ['', '_', '__', '_a', '1a', 'a.b', '.a', 'a-b']
8
+ for valid_id in valid_ids:
9
+ assert is_valid_identifier(valid_id), valid_ids
10
+
11
+ for invalid_id in invalid_ids:
12
+ assert not is_valid_identifier(invalid_id), invalid_ids
13
+
14
+ def test_valid_path(self) -> None:
15
+ assert is_valid_path('', empty_is_valid=True)
16
+ assert not is_valid_path('', empty_is_valid=False)
17
+
18
+ valid_paths = ['a', 'a_.b_', 'a.b.c', 'a.b.c.d']
19
+ invalid_paths = ['.', '..', 'a.', '.a', 'a..b']
20
+
21
+ for valid_path in valid_paths:
22
+ assert is_valid_path(valid_path, empty_is_valid=False), valid_path
23
+ assert is_valid_path(valid_path, empty_is_valid=True), valid_path
24
+
25
+ for invalid_path in invalid_paths:
26
+ assert not is_valid_path(invalid_path, empty_is_valid=False), invalid_path
27
+ assert not is_valid_path(invalid_path, empty_is_valid=True), invalid_path
@@ -1,21 +1,21 @@
1
1
  import pytest
2
2
 
3
- import pixeltable as pt
4
- from pixeltable import exceptions as exc
3
+ import pixeltable as pxt
4
+ import pixeltable.exceptions as excs
5
5
 
6
6
 
7
7
  class TestClient:
8
- def test_create_db(self, init_db: None) -> None:
9
- cl = pt.Client()
10
- _ = cl.create_db('test')
11
- with pytest.raises(exc.DuplicateNameError):
12
- _ = cl.create_db('test')
8
+ def test_list_functions(self, init_env) -> None:
9
+ cl = pxt.Client()
10
+ _ = cl.list_functions()
11
+ print(_)
13
12
 
14
- _ = cl.get_db('test')
15
- with pytest.raises(exc.UnknownEntityError):
16
- _ = cl.get_db('xyz')
17
-
18
- cl.drop_db('test', force=True)
19
- with pytest.raises(exc.UnknownEntityError):
20
- cl.drop_db('test', force=True)
13
+ def test_drop_table(self, test_tbl: pxt.Table) -> None:
14
+ cl = pxt.Client()
15
+ t = cl.get_table('test_tbl')
16
+ cl.drop_table('test_tbl')
17
+ with pytest.raises(excs.Error):
18
+ _ = cl.get_table('test_tbl')
19
+ with pytest.raises(excs.Error):
20
+ _ = t.show(1)
21
21