lsst-felis 28.2024.4500__py3-none-any.whl → 30.0.0rc3__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 (33) hide show
  1. felis/__init__.py +9 -1
  2. felis/cli.py +308 -209
  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_extensions.yaml +73 -0
  9. felis/datamodel.py +599 -59
  10. felis/db/{dialects.py → _dialects.py} +69 -4
  11. felis/db/{variants.py → _variants.py} +1 -1
  12. felis/db/database_context.py +917 -0
  13. felis/diff.py +234 -0
  14. felis/metadata.py +89 -19
  15. felis/tap_schema.py +271 -166
  16. felis/tests/postgresql.py +1 -1
  17. felis/tests/run_cli.py +79 -0
  18. felis/types.py +7 -7
  19. {lsst_felis-28.2024.4500.dist-info → lsst_felis-30.0.0rc3.dist-info}/METADATA +20 -16
  20. lsst_felis-30.0.0rc3.dist-info/RECORD +31 -0
  21. {lsst_felis-28.2024.4500.dist-info → lsst_felis-30.0.0rc3.dist-info}/WHEEL +1 -1
  22. felis/db/utils.py +0 -409
  23. felis/tap.py +0 -597
  24. felis/tests/utils.py +0 -122
  25. felis/version.py +0 -2
  26. lsst_felis-28.2024.4500.dist-info/RECORD +0 -26
  27. felis/{schemas → config/tap_schema}/tap_schema_std.yaml +0 -0
  28. felis/db/{sqltypes.py → _sqltypes.py} +7 -7
  29. {lsst_felis-28.2024.4500.dist-info → lsst_felis-30.0.0rc3.dist-info}/entry_points.txt +0 -0
  30. {lsst_felis-28.2024.4500.dist-info → lsst_felis-30.0.0rc3.dist-info/licenses}/COPYRIGHT +0 -0
  31. {lsst_felis-28.2024.4500.dist-info → lsst_felis-30.0.0rc3.dist-info/licenses}/LICENSE +0 -0
  32. {lsst_felis-28.2024.4500.dist-info → lsst_felis-30.0.0rc3.dist-info}/top_level.txt +0 -0
  33. {lsst_felis-28.2024.4500.dist-info → lsst_felis-30.0.0rc3.dist-info}/zip-safe +0 -0
@@ -1,4 +1,4 @@
1
- """Get SQLAlchemy dialects and their type modules."""
1
+ """Utilities for accessing SQLAlchemy dialects and their type modules."""
2
2
 
3
3
  # This file is part of felis.
4
4
  #
@@ -23,16 +23,18 @@
23
23
 
24
24
  from __future__ import annotations
25
25
 
26
+ import re
26
27
  from collections.abc import Mapping
27
28
  from types import MappingProxyType, ModuleType
28
29
 
29
- from sqlalchemy import dialects
30
+ from sqlalchemy import dialects, types
30
31
  from sqlalchemy.engine import Dialect
31
32
  from sqlalchemy.engine.mock import create_mock_engine
33
+ from sqlalchemy.types import TypeEngine
32
34
 
33
- from .sqltypes import MYSQL, POSTGRES, SQLITE
35
+ from ._sqltypes import MYSQL, POSTGRES, SQLITE
34
36
 
35
- __all__ = ["get_supported_dialects", "get_dialect_module"]
37
+ __all__ = ["get_dialect_module", "get_supported_dialects", "string_to_typeengine"]
36
38
 
37
39
  _DIALECT_NAMES = (MYSQL, POSTGRES, SQLITE)
38
40
  """List of supported dialect names.
@@ -40,6 +42,9 @@ _DIALECT_NAMES = (MYSQL, POSTGRES, SQLITE)
40
42
  This list is used to create the dialect and module dictionaries.
41
43
  """
42
44
 
45
+ _DATATYPE_REGEXP = re.compile(r"(\w+)(\((.*)\))?")
46
+ """Regular expression to match data types with parameters in parentheses."""
47
+
43
48
 
44
49
  def _dialect(dialect_name: str) -> Dialect:
45
50
  """Create the SQLAlchemy dialect for the given name using a mock engine.
@@ -114,3 +119,63 @@ def get_dialect_module(dialect_name: str) -> ModuleType:
114
119
  if dialect_name not in _DIALECT_MODULES:
115
120
  raise ValueError(f"Unsupported dialect: {dialect_name}")
116
121
  return _DIALECT_MODULES[dialect_name]
122
+
123
+
124
+ def string_to_typeengine(
125
+ type_string: str, dialect: Dialect | None = None, length: int | None = None
126
+ ) -> TypeEngine:
127
+ """Convert a string representation of a datatype to a SQLAlchemy type.
128
+
129
+ Parameters
130
+ ----------
131
+ type_string
132
+ The string representation of the data type.
133
+ dialect
134
+ The SQLAlchemy dialect to use. If None, the default dialect will be
135
+ used.
136
+ length
137
+ The length of the data type. If the data type does not have a length
138
+ attribute, this parameter will be ignored.
139
+
140
+ Returns
141
+ -------
142
+ `sqlalchemy.types.TypeEngine`
143
+ The SQLAlchemy type engine object.
144
+
145
+ Raises
146
+ ------
147
+ ValueError
148
+ Raised if the type string is invalid or the type is not supported.
149
+
150
+ Notes
151
+ -----
152
+ This function is used when converting type override strings defined in
153
+ fields such as ``mysql:datatype`` in the schema data.
154
+ """
155
+ match = _DATATYPE_REGEXP.search(type_string)
156
+ if not match:
157
+ raise ValueError(f"Invalid type string: {type_string}")
158
+
159
+ type_name, _, params = match.groups()
160
+ if dialect is None:
161
+ type_class = getattr(types, type_name.upper(), None)
162
+ else:
163
+ try:
164
+ dialect_module = get_dialect_module(dialect.name)
165
+ except KeyError:
166
+ raise ValueError(f"Unsupported dialect: {dialect}")
167
+ type_class = getattr(dialect_module, type_name.upper(), None)
168
+
169
+ if not type_class:
170
+ raise ValueError(f"Unsupported type: {type_name.upper()}")
171
+
172
+ if params:
173
+ params = [int(param) if param.isdigit() else param for param in params.split(",")]
174
+ type_obj = type_class(*params)
175
+ else:
176
+ type_obj = type_class()
177
+
178
+ if hasattr(type_obj, "length") and getattr(type_obj, "length") is None and length is not None:
179
+ type_obj.length = length
180
+
181
+ return type_obj
@@ -32,7 +32,7 @@ from sqlalchemy import types
32
32
  from sqlalchemy.types import TypeEngine
33
33
 
34
34
  from ..datamodel import Column
35
- from .dialects import get_dialect_module, get_supported_dialects
35
+ from ._dialects import get_dialect_module, get_supported_dialects
36
36
 
37
37
  __all__ = ["make_variant_dict"]
38
38