lsst-felis 27.2024.2400__py3-none-any.whl → 27.2024.2600__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 lsst-felis might be problematic. Click here for more details.
- felis/cli.py +162 -45
- felis/datamodel.py +377 -95
- felis/db/dialects.py +65 -12
- felis/db/sqltypes.py +255 -16
- felis/db/utils.py +108 -52
- felis/db/variants.py +66 -8
- felis/metadata.py +70 -54
- felis/tap.py +180 -18
- felis/types.py +56 -8
- felis/version.py +1 -1
- {lsst_felis-27.2024.2400.dist-info → lsst_felis-27.2024.2600.dist-info}/COPYRIGHT +1 -1
- {lsst_felis-27.2024.2400.dist-info → lsst_felis-27.2024.2600.dist-info}/METADATA +4 -2
- lsst_felis-27.2024.2600.dist-info/RECORD +21 -0
- {lsst_felis-27.2024.2400.dist-info → lsst_felis-27.2024.2600.dist-info}/WHEEL +1 -1
- felis/validation.py +0 -103
- lsst_felis-27.2024.2400.dist-info/RECORD +0 -22
- {lsst_felis-27.2024.2400.dist-info → lsst_felis-27.2024.2600.dist-info}/LICENSE +0 -0
- {lsst_felis-27.2024.2400.dist-info → lsst_felis-27.2024.2600.dist-info}/entry_points.txt +0 -0
- {lsst_felis-27.2024.2400.dist-info → lsst_felis-27.2024.2600.dist-info}/top_level.txt +0 -0
- {lsst_felis-27.2024.2400.dist-info → lsst_felis-27.2024.2600.dist-info}/zip-safe +0 -0
felis/cli.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
"""Click command line interface."""
|
|
2
|
+
|
|
1
3
|
# This file is part of felis.
|
|
2
4
|
#
|
|
3
5
|
# Developed for the LSST Data Management System.
|
|
@@ -37,7 +39,8 @@ from .datamodel import Schema
|
|
|
37
39
|
from .db.utils import DatabaseContext
|
|
38
40
|
from .metadata import MetaDataBuilder
|
|
39
41
|
from .tap import Tap11Base, TapLoadingVisitor, init_tables
|
|
40
|
-
|
|
42
|
+
|
|
43
|
+
__all__ = ["cli"]
|
|
41
44
|
|
|
42
45
|
logger = logging.getLogger("felis")
|
|
43
46
|
|
|
@@ -60,14 +63,14 @@ loglevel_choices = ["CRITICAL", "FATAL", "ERROR", "WARNING", "INFO", "DEBUG"]
|
|
|
60
63
|
help="Felis log file path",
|
|
61
64
|
)
|
|
62
65
|
def cli(log_level: str, log_file: str | None) -> None:
|
|
63
|
-
"""Felis
|
|
66
|
+
"""Felis command line tools"""
|
|
64
67
|
if log_file:
|
|
65
68
|
logging.basicConfig(filename=log_file, level=log_level)
|
|
66
69
|
else:
|
|
67
70
|
logging.basicConfig(level=log_level)
|
|
68
71
|
|
|
69
72
|
|
|
70
|
-
@cli.command("create")
|
|
73
|
+
@cli.command("create", help="Create database objects from the Felis file")
|
|
71
74
|
@click.option("--engine-url", envvar="ENGINE_URL", help="SQLAlchemy Engine URL", default="sqlite://")
|
|
72
75
|
@click.option("--schema-name", help="Alternate schema name to override Felis file")
|
|
73
76
|
@click.option(
|
|
@@ -90,7 +93,34 @@ def create(
|
|
|
90
93
|
output_file: IO[str] | None,
|
|
91
94
|
file: IO,
|
|
92
95
|
) -> None:
|
|
93
|
-
"""Create database objects from the Felis file.
|
|
96
|
+
"""Create database objects from the Felis file.
|
|
97
|
+
|
|
98
|
+
Parameters
|
|
99
|
+
----------
|
|
100
|
+
engine_url
|
|
101
|
+
SQLAlchemy Engine URL.
|
|
102
|
+
schema_name
|
|
103
|
+
Alternate schema name to override Felis file.
|
|
104
|
+
create_if_not_exists
|
|
105
|
+
Create the schema in the database if it does not exist.
|
|
106
|
+
drop_if_exists
|
|
107
|
+
Drop schema if it already exists in the database.
|
|
108
|
+
echo
|
|
109
|
+
Echo database commands as they are executed.
|
|
110
|
+
dry_run
|
|
111
|
+
Dry run only to print out commands instead of executing.
|
|
112
|
+
output_file
|
|
113
|
+
Write SQL commands to a file instead of executing.
|
|
114
|
+
file
|
|
115
|
+
Felis file to read.
|
|
116
|
+
|
|
117
|
+
Notes
|
|
118
|
+
-----
|
|
119
|
+
This command creates database objects from the Felis file. The
|
|
120
|
+
``--create-if-not-exists`` or ``--drop-if-exists`` flags can be used to
|
|
121
|
+
create a new MySQL database or PostgreSQL schema if it does not exist
|
|
122
|
+
already.
|
|
123
|
+
"""
|
|
94
124
|
yaml_data = yaml.safe_load(file)
|
|
95
125
|
schema = Schema.model_validate(yaml_data)
|
|
96
126
|
url = make_url(engine_url)
|
|
@@ -131,13 +161,13 @@ def create(
|
|
|
131
161
|
context.create_all()
|
|
132
162
|
|
|
133
163
|
|
|
134
|
-
@cli.command("init-tap")
|
|
135
|
-
@click.option("--tap-schema-name", help="
|
|
136
|
-
@click.option("--tap-schemas-table", help="
|
|
137
|
-
@click.option("--tap-tables-table", help="
|
|
138
|
-
@click.option("--tap-columns-table", help="
|
|
139
|
-
@click.option("--tap-keys-table", help="
|
|
140
|
-
@click.option("--tap-key-columns-table", help="
|
|
164
|
+
@cli.command("init-tap", help="Initialize TAP_SCHEMA objects in the database")
|
|
165
|
+
@click.option("--tap-schema-name", help="Alternate database schema name for 'TAP_SCHEMA'")
|
|
166
|
+
@click.option("--tap-schemas-table", help="Alternate table name for 'schemas'")
|
|
167
|
+
@click.option("--tap-tables-table", help="Alternate table name for 'tables'")
|
|
168
|
+
@click.option("--tap-columns-table", help="Alternate table name for 'columns'")
|
|
169
|
+
@click.option("--tap-keys-table", help="Alternate table name for 'keys'")
|
|
170
|
+
@click.option("--tap-key-columns-table", help="Alternate table name for 'key_columns'")
|
|
141
171
|
@click.argument("engine-url")
|
|
142
172
|
def init_tap(
|
|
143
173
|
engine_url: str,
|
|
@@ -148,10 +178,31 @@ def init_tap(
|
|
|
148
178
|
tap_keys_table: str,
|
|
149
179
|
tap_key_columns_table: str,
|
|
150
180
|
) -> None:
|
|
151
|
-
"""Initialize
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
181
|
+
"""Initialize TAP_SCHEMA objects in the database.
|
|
182
|
+
|
|
183
|
+
Parameters
|
|
184
|
+
----------
|
|
185
|
+
engine_url
|
|
186
|
+
SQLAlchemy Engine URL. The target PostgreSQL schema or MySQL database
|
|
187
|
+
must already exist and be referenced in the URL.
|
|
188
|
+
tap_schema_name
|
|
189
|
+
Alterate name for the database schema ``TAP_SCHEMA``.
|
|
190
|
+
tap_schemas_table
|
|
191
|
+
Alterate table name for ``schemas``.
|
|
192
|
+
tap_tables_table
|
|
193
|
+
Alterate table name for ``tables``.
|
|
194
|
+
tap_columns_table
|
|
195
|
+
Alterate table name for ``columns``.
|
|
196
|
+
tap_keys_table
|
|
197
|
+
Alterate table name for ``keys``.
|
|
198
|
+
tap_key_columns_table
|
|
199
|
+
Alterate table name for ``key_columns``.
|
|
200
|
+
|
|
201
|
+
Notes
|
|
202
|
+
-----
|
|
203
|
+
The supported version of TAP_SCHEMA in the SQLAlchemy metadata is 1.1. The
|
|
204
|
+
tables are created in the database schema specified by the engine URL,
|
|
205
|
+
which must be a PostgreSQL schema or MySQL database that already exists.
|
|
155
206
|
"""
|
|
156
207
|
engine = create_engine(engine_url, echo=True)
|
|
157
208
|
init_tables(
|
|
@@ -165,19 +216,19 @@ def init_tap(
|
|
|
165
216
|
Tap11Base.metadata.create_all(engine)
|
|
166
217
|
|
|
167
218
|
|
|
168
|
-
@cli.command("load-tap")
|
|
219
|
+
@cli.command("load-tap", help="Load metadata from a Felis file into a TAP_SCHEMA database")
|
|
169
220
|
@click.option("--engine-url", envvar="ENGINE_URL", help="SQLAlchemy Engine URL to catalog")
|
|
170
221
|
@click.option("--schema-name", help="Alternate Schema Name for Felis file")
|
|
171
222
|
@click.option("--catalog-name", help="Catalog Name for Schema")
|
|
172
223
|
@click.option("--dry-run", is_flag=True, help="Dry Run Only. Prints out the DDL that would be executed")
|
|
173
|
-
@click.option("--tap-schema-name", help="
|
|
174
|
-
@click.option("--tap-tables-postfix", help="Postfix for
|
|
175
|
-
@click.option("--tap-schemas-table", help="
|
|
176
|
-
@click.option("--tap-tables-table", help="
|
|
177
|
-
@click.option("--tap-columns-table", help="
|
|
178
|
-
@click.option("--tap-keys-table", help="
|
|
179
|
-
@click.option("--tap-key-columns-table", help="
|
|
180
|
-
@click.option("--tap-schema-index", type=int, help="TAP_SCHEMA index of the schema")
|
|
224
|
+
@click.option("--tap-schema-name", help="Alternate schema name for 'TAP_SCHEMA'")
|
|
225
|
+
@click.option("--tap-tables-postfix", help="Postfix for TAP_SCHEMA table names")
|
|
226
|
+
@click.option("--tap-schemas-table", help="Alternate table name for 'schemas'")
|
|
227
|
+
@click.option("--tap-tables-table", help="Alternate table name for 'tables'")
|
|
228
|
+
@click.option("--tap-columns-table", help="Alternate table name for 'columns'")
|
|
229
|
+
@click.option("--tap-keys-table", help="Alternate table name for 'keys'")
|
|
230
|
+
@click.option("--tap-key-columns-table", help="Alternate table name for 'key_columns'")
|
|
231
|
+
@click.option("--tap-schema-index", type=int, help="TAP_SCHEMA index of the schema in this environment")
|
|
181
232
|
@click.argument("file", type=click.File())
|
|
182
233
|
def load_tap(
|
|
183
234
|
engine_url: str,
|
|
@@ -194,10 +245,46 @@ def load_tap(
|
|
|
194
245
|
tap_schema_index: int,
|
|
195
246
|
file: io.TextIOBase,
|
|
196
247
|
) -> None:
|
|
197
|
-
"""Load TAP metadata from a Felis
|
|
198
|
-
|
|
199
|
-
This command loads the associated TAP metadata from a Felis
|
|
200
|
-
|
|
248
|
+
"""Load TAP metadata from a Felis file.
|
|
249
|
+
|
|
250
|
+
This command loads the associated TAP metadata from a Felis YAML file
|
|
251
|
+
into the TAP_SCHEMA tables.
|
|
252
|
+
|
|
253
|
+
Parameters
|
|
254
|
+
----------
|
|
255
|
+
engine_url
|
|
256
|
+
SQLAlchemy Engine URL to catalog.
|
|
257
|
+
schema_name
|
|
258
|
+
Alternate schema name. This overrides the schema name in the
|
|
259
|
+
``catalog`` field of the Felis file.
|
|
260
|
+
catalog_name
|
|
261
|
+
Catalog name for the schema. This possibly duplicates the
|
|
262
|
+
``tap_schema_name`` argument (DM-44870).
|
|
263
|
+
dry_run
|
|
264
|
+
Dry run only to print out commands instead of executing.
|
|
265
|
+
tap_schema_name
|
|
266
|
+
Alternate name for the schema of TAP_SCHEMA in the database.
|
|
267
|
+
tap_tables_postfix
|
|
268
|
+
Postfix for TAP table names that will be automatically appended.
|
|
269
|
+
tap_schemas_table
|
|
270
|
+
Alternate table name for ``schemas``.
|
|
271
|
+
tap_tables_table
|
|
272
|
+
Alternate table name for ``tables``.
|
|
273
|
+
tap_columns_table
|
|
274
|
+
Alternate table name for ``columns``.
|
|
275
|
+
tap_keys_table
|
|
276
|
+
Alternate table name for ``keys``.
|
|
277
|
+
tap_key_columns_table
|
|
278
|
+
Alternate table name for ``key_columns``.
|
|
279
|
+
tap_schema_index
|
|
280
|
+
TAP_SCHEMA index of the schema in this TAP environment.
|
|
281
|
+
file
|
|
282
|
+
Felis file to read.
|
|
283
|
+
|
|
284
|
+
Notes
|
|
285
|
+
-----
|
|
286
|
+
The data will be loaded into the TAP_SCHEMA from the engine URL. The
|
|
287
|
+
tables must have already been initialized or an error will occur.
|
|
201
288
|
"""
|
|
202
289
|
yaml_data = yaml.load(file, Loader=yaml.SafeLoader)
|
|
203
290
|
schema = Schema.model_validate(yaml_data)
|
|
@@ -240,43 +327,73 @@ def load_tap(
|
|
|
240
327
|
tap_visitor.visit_schema(schema)
|
|
241
328
|
|
|
242
329
|
|
|
243
|
-
@cli.command("validate")
|
|
330
|
+
@cli.command("validate", help="Validate one or more Felis YAML files")
|
|
244
331
|
@click.option(
|
|
245
|
-
"-
|
|
246
|
-
"--schema-name",
|
|
247
|
-
help="Schema name for validation",
|
|
248
|
-
type=click.Choice(["RSP", "default"]),
|
|
249
|
-
default="default",
|
|
332
|
+
"--check-description", is_flag=True, help="Check that all objects have a description", default=False
|
|
250
333
|
)
|
|
251
334
|
@click.option(
|
|
252
|
-
"
|
|
335
|
+
"--check-redundant-datatypes", is_flag=True, help="Check for redundant datatype overrides", default=False
|
|
253
336
|
)
|
|
254
337
|
@click.option(
|
|
255
|
-
"
|
|
338
|
+
"--check-tap-table-indexes",
|
|
339
|
+
is_flag=True,
|
|
340
|
+
help="Check that every table has a unique TAP table index",
|
|
341
|
+
default=False,
|
|
342
|
+
)
|
|
343
|
+
@click.option(
|
|
344
|
+
"--check-tap-principal",
|
|
345
|
+
is_flag=True,
|
|
346
|
+
help="Check that at least one column per table is flagged as TAP principal",
|
|
347
|
+
default=False,
|
|
256
348
|
)
|
|
257
349
|
@click.argument("files", nargs=-1, type=click.File())
|
|
258
350
|
def validate(
|
|
259
|
-
|
|
260
|
-
require_description: bool,
|
|
351
|
+
check_description: bool,
|
|
261
352
|
check_redundant_datatypes: bool,
|
|
353
|
+
check_tap_table_indexes: bool,
|
|
354
|
+
check_tap_principal: bool,
|
|
262
355
|
files: Iterable[io.TextIOBase],
|
|
263
356
|
) -> None:
|
|
264
|
-
"""Validate one or more felis YAML files.
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
357
|
+
"""Validate one or more felis YAML files.
|
|
358
|
+
|
|
359
|
+
Parameters
|
|
360
|
+
----------
|
|
361
|
+
check_description
|
|
362
|
+
Check that all objects have a valid description.
|
|
363
|
+
check_redundant_datatypes
|
|
364
|
+
Check for redundant type overrides.
|
|
365
|
+
check_tap_table_indexes
|
|
366
|
+
Check that every table has a unique TAP table index.
|
|
367
|
+
check_tap_principal
|
|
368
|
+
Check that at least one column per table is flagged as TAP principal.
|
|
369
|
+
files
|
|
370
|
+
The Felis YAML files to validate.
|
|
371
|
+
|
|
372
|
+
Raises
|
|
373
|
+
------
|
|
374
|
+
click.exceptions.Exit
|
|
375
|
+
If any validation errors are found. The ``ValidationError`` which is
|
|
376
|
+
thrown when a schema fails to validate will be logged as an error
|
|
377
|
+
message.
|
|
378
|
+
|
|
379
|
+
Notes
|
|
380
|
+
-----
|
|
381
|
+
All of the ``check`` flags are turned off by default and represent
|
|
382
|
+
optional validations controlled by the Pydantic context.
|
|
383
|
+
"""
|
|
269
384
|
rc = 0
|
|
270
385
|
for file in files:
|
|
271
386
|
file_name = getattr(file, "name", None)
|
|
272
387
|
logger.info(f"Validating {file_name}")
|
|
273
388
|
try:
|
|
274
389
|
data = yaml.load(file, Loader=yaml.SafeLoader)
|
|
275
|
-
|
|
390
|
+
Schema.model_validate(
|
|
276
391
|
data,
|
|
277
392
|
context={
|
|
393
|
+
"check_description": check_description,
|
|
278
394
|
"check_redundant_datatypes": check_redundant_datatypes,
|
|
279
|
-
"
|
|
395
|
+
"check_tap_table_indexes": check_tap_table_indexes,
|
|
396
|
+
"check_tap_principal": check_tap_principal,
|
|
280
397
|
},
|
|
281
398
|
)
|
|
282
399
|
except ValidationError as e:
|