lsst-felis 26.2024.900__py3-none-any.whl → 29.2025.4500__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.
Files changed (39) hide show
  1. felis/__init__.py +10 -24
  2. felis/cli.py +437 -341
  3. felis/config/tap_schema/columns.csv +33 -0
  4. felis/config/tap_schema/key_columns.csv +8 -0
  5. felis/config/tap_schema/keys.csv +8 -0
  6. felis/config/tap_schema/schemas.csv +2 -0
  7. felis/config/tap_schema/tables.csv +6 -0
  8. felis/config/tap_schema/tap_schema_std.yaml +273 -0
  9. felis/datamodel.py +1386 -193
  10. felis/db/dialects.py +116 -0
  11. felis/db/schema.py +62 -0
  12. felis/db/sqltypes.py +275 -48
  13. felis/db/utils.py +409 -0
  14. felis/db/variants.py +159 -0
  15. felis/diff.py +234 -0
  16. felis/metadata.py +385 -0
  17. felis/tap_schema.py +767 -0
  18. felis/tests/__init__.py +0 -0
  19. felis/tests/postgresql.py +134 -0
  20. felis/tests/run_cli.py +79 -0
  21. felis/types.py +57 -9
  22. lsst_felis-29.2025.4500.dist-info/METADATA +38 -0
  23. lsst_felis-29.2025.4500.dist-info/RECORD +31 -0
  24. {lsst_felis-26.2024.900.dist-info → lsst_felis-29.2025.4500.dist-info}/WHEEL +1 -1
  25. {lsst_felis-26.2024.900.dist-info → lsst_felis-29.2025.4500.dist-info/licenses}/COPYRIGHT +1 -1
  26. felis/check.py +0 -381
  27. felis/simple.py +0 -424
  28. felis/sql.py +0 -275
  29. felis/tap.py +0 -433
  30. felis/utils.py +0 -100
  31. felis/validation.py +0 -103
  32. felis/version.py +0 -2
  33. felis/visitor.py +0 -180
  34. lsst_felis-26.2024.900.dist-info/METADATA +0 -28
  35. lsst_felis-26.2024.900.dist-info/RECORD +0 -23
  36. {lsst_felis-26.2024.900.dist-info → lsst_felis-29.2025.4500.dist-info}/entry_points.txt +0 -0
  37. {lsst_felis-26.2024.900.dist-info → lsst_felis-29.2025.4500.dist-info/licenses}/LICENSE +0 -0
  38. {lsst_felis-26.2024.900.dist-info → lsst_felis-29.2025.4500.dist-info}/top_level.txt +0 -0
  39. {lsst_felis-26.2024.900.dist-info → lsst_felis-29.2025.4500.dist-info}/zip-safe +0 -0
File without changes
@@ -0,0 +1,134 @@
1
+ """Provides a temporary Postgresql instance for testing."""
2
+
3
+ # This file is part of felis.
4
+ #
5
+ # Developed for the LSST Data Management System.
6
+ # This product includes software developed by the LSST Project
7
+ # (https://www.lsst.org).
8
+ # See the COPYRIGHT file at the top-level directory of this distribution
9
+ # for details of code ownership.
10
+ #
11
+ # This program is free software: you can redistribute it and/or modify
12
+ # it under the terms of the GNU General Public License as published by
13
+ # the Free Software Foundation, either version 3 of the License, or
14
+ # (at your option) any later version.
15
+ #
16
+ # This program is distributed in the hope that it will be useful,
17
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
18
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19
+ # GNU General Public License for more details.
20
+ #
21
+ # You should have received a copy of the GNU General Public License
22
+ # along with this program. If not, see <https://www.gnu.org/licenses/>.
23
+
24
+ import gc
25
+ import unittest
26
+ from collections.abc import Iterator
27
+ from contextlib import contextmanager
28
+
29
+ from sqlalchemy import text
30
+ from sqlalchemy.engine import Connection, Engine, create_engine
31
+
32
+ try:
33
+ from testing.postgresql import Postgresql # type: ignore
34
+ except ImportError:
35
+ Postgresql = None
36
+
37
+ __all__ = ["TemporaryPostgresInstance", "setup_postgres_test_db"]
38
+
39
+
40
+ class TemporaryPostgresInstance:
41
+ """Wrapper for a temporary Postgres database.
42
+
43
+ Parameters
44
+ ----------
45
+ server
46
+ The ``testing.postgresql.Postgresql`` instance.
47
+ engine
48
+ The SQLAlchemy engine for the temporary database server.
49
+
50
+ Notes
51
+ -----
52
+ This class was copied and modified from
53
+ ``lsst.daf.butler.tests.postgresql``.
54
+ """
55
+
56
+ def __init__(self, server: Postgresql, engine: Engine) -> None:
57
+ """Initialize the temporary Postgres database instance."""
58
+ self._server = server
59
+ self._engine = engine
60
+
61
+ @property
62
+ def url(self) -> str:
63
+ """Return connection URL for the temporary database server.
64
+
65
+ Returns
66
+ -------
67
+ str
68
+ The connection URL.
69
+ """
70
+ return self._server.url()
71
+
72
+ @property
73
+ def engine(self) -> Engine:
74
+ """Return the SQLAlchemy engine for the temporary database server.
75
+
76
+ Returns
77
+ -------
78
+ `~sqlalchemy.engine.Engine`
79
+ The SQLAlchemy engine.
80
+ """
81
+ return self._engine
82
+
83
+ @contextmanager
84
+ def begin(self) -> Iterator[Connection]:
85
+ """Return a SQLAlchemy connection to the test database.
86
+
87
+ Returns
88
+ -------
89
+ `~sqlalchemy.engine.Connection`
90
+ The SQLAlchemy connection.
91
+ """
92
+ with self._engine.begin() as connection:
93
+ yield connection
94
+
95
+ def print_info(self) -> None:
96
+ """Print information about the temporary database server."""
97
+ print("\n\n---- PostgreSQL URL ----")
98
+ print(self.url)
99
+ self._engine = create_engine(self.url)
100
+ with self.begin() as conn:
101
+ print("\n---- PostgreSQL Version ----")
102
+ res = conn.execute(text("SELECT version()")).fetchone()
103
+ if res:
104
+ print(res[0])
105
+ print("\n")
106
+
107
+
108
+ @contextmanager
109
+ def setup_postgres_test_db() -> Iterator[TemporaryPostgresInstance]:
110
+ """Set up a temporary Postgres database instance that can be used for
111
+ testing.
112
+
113
+ Returns
114
+ -------
115
+ TemporaryPostgresInstance
116
+ The temporary Postgres database instance.
117
+
118
+ Raises
119
+ ------
120
+ unittest.SkipTest
121
+ Raised if the ``testing.postgresql`` module is not available.
122
+ """
123
+ if Postgresql is None:
124
+ raise unittest.SkipTest("testing.postgresql module not available.")
125
+
126
+ with Postgresql() as server:
127
+ engine = create_engine(server.url())
128
+ instance = TemporaryPostgresInstance(server, engine)
129
+ yield instance
130
+
131
+ # Clean up any lingering SQLAlchemy engines/connections
132
+ # so they're closed before we shut down the server.
133
+ gc.collect()
134
+ engine.dispose()
felis/tests/run_cli.py ADDED
@@ -0,0 +1,79 @@
1
+ """Test utility for running cli commands."""
2
+
3
+ # This file is part of felis.
4
+ #
5
+ # Developed for the LSST Data Management System.
6
+ # This product includes software developed by the LSST Project
7
+ # (https://www.lsst.org).
8
+ # See the COPYRIGHT file at the top-level directory of this distribution
9
+ # for details of code ownership.
10
+ #
11
+ # This program is free software: you can redistribute it and/or modify
12
+ # it under the terms of the GNU General Public License as published by
13
+ # the Free Software Foundation, either version 3 of the License, or
14
+ # (at your option) any later version.
15
+ #
16
+ # This program is distributed in the hope that it will be useful,
17
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
18
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19
+ # GNU General Public License for more details.
20
+ #
21
+ # You should have received a copy of the GNU General Public License
22
+ # along with this program. If not, see <https://www.gnu.org/licenses/>.
23
+
24
+ import logging
25
+
26
+ from click.testing import CliRunner
27
+
28
+ from felis.cli import cli
29
+
30
+ __all__ = ["run_cli"]
31
+
32
+
33
+ def run_cli(
34
+ cmd: list[str],
35
+ log_level: int = logging.WARNING,
36
+ catch_exceptions: bool = False,
37
+ expect_error: bool = False,
38
+ print_cmd: bool = False,
39
+ print_output: bool = False,
40
+ id_generation: bool = False,
41
+ ) -> None:
42
+ """Run a CLI command and check the exit code.
43
+
44
+ Parameters
45
+ ----------
46
+ cmd : list[str]
47
+ The command to run.
48
+ log_level : int
49
+ The logging level to use, by default logging.WARNING.
50
+ catch_exceptions : bool
51
+ Whether to catch exceptions, by default False.
52
+ expect_error : bool
53
+ Whether to expect an error, by default False.
54
+ print_cmd : bool
55
+ Whether to print the command, by default False.
56
+ print_output : bool
57
+ Whether to print the output, by default False.
58
+ id_generation : bool
59
+ Whether to enable id generation, by default False.
60
+ """
61
+ if not cmd:
62
+ raise ValueError("No command provided.")
63
+ full_cmd = ["--log-level", logging.getLevelName(log_level)] + cmd
64
+ if id_generation:
65
+ full_cmd = ["--id-generation"] + full_cmd
66
+ if print_cmd:
67
+ print(f"Running command: felis {' '.join(full_cmd)}")
68
+ runner = CliRunner()
69
+ result = runner.invoke(
70
+ cli,
71
+ full_cmd,
72
+ catch_exceptions=catch_exceptions,
73
+ )
74
+ if print_output:
75
+ print(result.output)
76
+ if expect_error:
77
+ assert result.exit_code != 0
78
+ else:
79
+ assert result.exit_code == 0
felis/types.py CHANGED
@@ -1,3 +1,5 @@
1
+ """Define the supported Felis datatypes."""
2
+
1
3
  # This file is part of felis.
2
4
  #
3
5
  # Developed for the LSST Data Management System.
@@ -23,10 +25,29 @@ from __future__ import annotations
23
25
 
24
26
  from typing import Any
25
27
 
28
+ __all__ = [
29
+ "Binary",
30
+ "Boolean",
31
+ "Byte",
32
+ "Char",
33
+ "Double",
34
+ "FelisType",
35
+ "Float",
36
+ "Int",
37
+ "Long",
38
+ "Short",
39
+ "String",
40
+ "Text",
41
+ "Timestamp",
42
+ "Unicode",
43
+ ]
44
+
26
45
 
27
46
  class FelisType:
28
- """Base class for types that represent Felis column types.
47
+ """Base class for a representation of Felis column types.
29
48
 
49
+ Notes
50
+ -----
30
51
  This class plays a role of a metaclass without being an actual metaclass.
31
52
  It provides a method to retrieve a class (type) given Felis type name.
32
53
  There should be no instances of this class (or sub-classes), the utility
@@ -34,12 +55,22 @@ class FelisType:
34
55
  """
35
56
 
36
57
  felis_name: str
58
+ """Name of the type as defined in the Felis schema."""
59
+
37
60
  votable_name: str
61
+ """Name of the type as defined in VOTable."""
62
+
38
63
  is_numeric: bool
64
+ """Flag indicating if the type is numeric."""
65
+
39
66
  is_sized: bool
67
+ """Flag indicating if the type is sized, meaning it requires a length."""
68
+
40
69
  is_timestamp: bool
70
+ """Flag indicating if the type is a timestamp."""
41
71
 
42
72
  _types: dict[str, type[FelisType]] = {}
73
+ """Dictionary of all known Felis types."""
43
74
 
44
75
  @classmethod
45
76
  def __init_subclass__(
@@ -52,6 +83,23 @@ class FelisType:
52
83
  is_timestamp: bool = False,
53
84
  **kwargs: Any,
54
85
  ):
86
+ """Register a new Felis type.
87
+
88
+ Parameters
89
+ ----------
90
+ felis_name
91
+ Name of the type.
92
+ votable_name
93
+ Name of the type as defined in VOTable.
94
+ is_numeric
95
+ Flag indicating if the type is numeric.
96
+ is_sized
97
+ Flag indicating if the type is sized.
98
+ is_timestamp
99
+ Flag indicating if the type is a timestamp.
100
+ kwargs
101
+ Additional keyword arguments.
102
+ """
55
103
  super().__init_subclass__(**kwargs)
56
104
  cls.felis_name = felis_name
57
105
  cls.votable_name = votable_name
@@ -62,17 +110,17 @@ class FelisType:
62
110
 
63
111
  @classmethod
64
112
  def felis_type(cls, felis_name: str) -> type[FelisType]:
65
- """Return specific Felis type for a given type name.
113
+ """Return specific Felis type for a given name.
66
114
 
67
115
  Parameters
68
116
  ----------
69
- felis_name : `str`
70
- name of the felis type as defined in felis schema.
117
+ felis_name
118
+ Name of the felis type as defined in felis schema.
71
119
 
72
120
  Returns
73
121
  -------
74
- felis_type : `type`
75
- One of subclasses of `FelisType`.
122
+ `type` [ `FelisType` ]
123
+ A specific Felis type class.
76
124
 
77
125
  Raises
78
126
  ------
@@ -106,11 +154,11 @@ class Long(FelisType, felis_name="long", votable_name="long", is_numeric=True):
106
154
 
107
155
 
108
156
  class Float(FelisType, felis_name="float", votable_name="float", is_numeric=True):
109
- """Felis definition of single precision floating type."""
157
+ """Felis definition of single precision floating point type."""
110
158
 
111
159
 
112
160
  class Double(FelisType, felis_name="double", votable_name="double", is_numeric=True):
113
- """Felis definition of double precision floating type."""
161
+ """Felis definition of double precision floating point type."""
114
162
 
115
163
 
116
164
  class Char(FelisType, felis_name="char", votable_name="char", is_sized=True):
@@ -125,7 +173,7 @@ class Unicode(FelisType, felis_name="unicode", votable_name="unicodeChar", is_si
125
173
  """Felis definition of unicode string type."""
126
174
 
127
175
 
128
- class Text(FelisType, felis_name="text", votable_name="unicodeChar", is_sized=True):
176
+ class Text(FelisType, felis_name="text", votable_name="char"):
129
177
  """Felis definition of text type."""
130
178
 
131
179
 
@@ -0,0 +1,38 @@
1
+ Metadata-Version: 2.4
2
+ Name: lsst-felis
3
+ Version: 29.2025.4500
4
+ Summary: A vocabulary for describing catalogs and acting on those descriptions
5
+ Author-email: Rubin Observatory Data Management <dm-admin@lists.lsst.org>
6
+ License-Expression: GPL-3.0-or-later
7
+ Project-URL: Homepage, https://felis.lsst.io
8
+ Project-URL: Source, https://github.com/lsst/felis
9
+ Keywords: lsst
10
+ Classifier: Intended Audience :: Science/Research
11
+ Classifier: Operating System :: OS Independent
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Classifier: Programming Language :: Python :: 3.13
16
+ Classifier: Programming Language :: Python :: 3.14
17
+ Classifier: Topic :: Scientific/Engineering :: Astronomy
18
+ Requires-Python: >=3.11.0
19
+ Description-Content-Type: text/markdown
20
+ License-File: COPYRIGHT
21
+ License-File: LICENSE
22
+ Requires-Dist: alembic
23
+ Requires-Dist: astropy
24
+ Requires-Dist: click
25
+ Requires-Dist: deepdiff
26
+ Requires-Dist: lsst-resources
27
+ Requires-Dist: lsst-utils
28
+ Requires-Dist: numpy
29
+ Requires-Dist: pydantic<3,>=2
30
+ Requires-Dist: pyyaml
31
+ Requires-Dist: sqlalchemy
32
+ Provides-Extra: test
33
+ Requires-Dist: pytest>=3.2; extra == "test"
34
+ Provides-Extra: dev
35
+ Requires-Dist: documenteer[guide]<2; extra == "dev"
36
+ Requires-Dist: autodoc_pydantic; extra == "dev"
37
+ Requires-Dist: sphinx-click; extra == "dev"
38
+ Dynamic: license-file
@@ -0,0 +1,31 @@
1
+ felis/__init__.py,sha256=HnwWzLaPOSnPzAoppSIHzTrGfixEgvkzJdBxa8-03cw,1294
2
+ felis/cli.py,sha256=g6OrBrIylNLiflSvrLlef86BjoiehV3L5eAvVPrxPog,16911
3
+ felis/datamodel.py,sha256=VAJ9DqOBtfu3fWtpDOcpg4Ca1jfq8NMG6MHH8GbHpl0,52135
4
+ felis/diff.py,sha256=ZzjOJ57p5ZwFn6eem7CYoPjSnxti5OZY33B6Ds5Q-Rg,7797
5
+ felis/metadata.py,sha256=79YcaIqeFP-pj9zhWpqXlvw_piUTUwuLrV5_8eVYalQ,13763
6
+ felis/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
+ felis/tap_schema.py,sha256=Kfpnv6AO_ni_B-DEgi3PRSkGKkTEybuYfWlA9gOMM_I,27071
8
+ felis/types.py,sha256=ifZQjc-Uw5CM3L7hmFUb7wcHY1O_HgJCw6HPqyUkHvk,5510
9
+ felis/config/tap_schema/columns.csv,sha256=9RsyuPObUQ_6myux9vKtlQ-aJgs7rvvxoLf6yYSRWqc,3272
10
+ felis/config/tap_schema/key_columns.csv,sha256=dRezco5ltcM1mG--2DvPsbOxB6cwVaBwczwi3It2vag,210
11
+ felis/config/tap_schema/keys.csv,sha256=6zTXyo-1GNfu5sBWpX-7ZJFAtHrxOys78AViCcdPgu8,377
12
+ felis/config/tap_schema/schemas.csv,sha256=z5g1bW1Y9H8nKLZyH4e5xiBBoK9JezR2Xf8L79K2TZk,138
13
+ felis/config/tap_schema/tables.csv,sha256=o0KioOiL7hw9ntCyKWili-iFMjAaGRMUOE-nM30LBD0,510
14
+ felis/config/tap_schema/tap_schema_std.yaml,sha256=sPW-Vk72nY0PFpCvP5d8L8fWvhkif-x32sGtcfDZ8bU,7131
15
+ felis/db/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
16
+ felis/db/dialects.py,sha256=XTZEbTnfy96GJDNRLCQMbAV6irerC87vhO_HyTIXLbs,3517
17
+ felis/db/schema.py,sha256=NOFXzBoBQcgpoRlgT3LoC70FKp7pCSmFEJ7rU8FIT-c,2101
18
+ felis/db/sqltypes.py,sha256=Q2p3Af3O5-B1ZxQ4M2j_w8SH1o_kp6ezg8h7LmSlfww,11060
19
+ felis/db/utils.py,sha256=jiKQ_SirKRdQITHe8gSiT_i3ckRHZbkAnwUlEHk2u4Y,14116
20
+ felis/db/variants.py,sha256=eahthrbVeV8ZdGamWQccNmWgx6CCscGrU0vQRs5HZK8,5260
21
+ felis/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
22
+ felis/tests/postgresql.py,sha256=B_xk4fLual5-viGDqP20r94okuc0pbSvytRH_L0fvMs,4035
23
+ felis/tests/run_cli.py,sha256=Gg8loUIGj9t6KlkRKrEc9Z9b5dtlkpJy94ORuj4BrxU,2503
24
+ lsst_felis-29.2025.4500.dist-info/licenses/COPYRIGHT,sha256=vJAFLFTSF1mhy9eIuA3P6R-3yxTWKQgpig88P-1IzRw,129
25
+ lsst_felis-29.2025.4500.dist-info/licenses/LICENSE,sha256=jOtLnuWt7d5Hsx6XXB2QxzrSe2sWWh3NgMfFRetluQM,35147
26
+ lsst_felis-29.2025.4500.dist-info/METADATA,sha256=_brJSKPh9-Izo-XR_3FTFQb_zfsnECQyL2t6gjLW6p8,1377
27
+ lsst_felis-29.2025.4500.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
28
+ lsst_felis-29.2025.4500.dist-info/entry_points.txt,sha256=Gk2XFujA_Gp52VBk45g5kim8TDoMDJFPctsMqiq72EM,40
29
+ lsst_felis-29.2025.4500.dist-info/top_level.txt,sha256=F4SvPip3iZRVyISi50CHhwTIAokAhSxjWiVcn4IVWRI,6
30
+ lsst_felis-29.2025.4500.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
31
+ lsst_felis-29.2025.4500.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.42.0)
2
+ Generator: setuptools (80.9.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1 +1 @@
1
- Copyright 2020-2022 The Board of Trustees of the Leland Stanford Junior University, through SLAC National Accelerator Laboratory
1
+ Copyright 2018-2024 The Board of Trustees of the Leland Stanford Junior University, through SLAC National Accelerator Laboratory