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.
- felis/__init__.py +10 -24
- felis/cli.py +437 -341
- felis/config/tap_schema/columns.csv +33 -0
- felis/config/tap_schema/key_columns.csv +8 -0
- felis/config/tap_schema/keys.csv +8 -0
- felis/config/tap_schema/schemas.csv +2 -0
- felis/config/tap_schema/tables.csv +6 -0
- felis/config/tap_schema/tap_schema_std.yaml +273 -0
- felis/datamodel.py +1386 -193
- felis/db/dialects.py +116 -0
- felis/db/schema.py +62 -0
- felis/db/sqltypes.py +275 -48
- felis/db/utils.py +409 -0
- felis/db/variants.py +159 -0
- felis/diff.py +234 -0
- felis/metadata.py +385 -0
- felis/tap_schema.py +767 -0
- felis/tests/__init__.py +0 -0
- felis/tests/postgresql.py +134 -0
- felis/tests/run_cli.py +79 -0
- felis/types.py +57 -9
- lsst_felis-29.2025.4500.dist-info/METADATA +38 -0
- lsst_felis-29.2025.4500.dist-info/RECORD +31 -0
- {lsst_felis-26.2024.900.dist-info → lsst_felis-29.2025.4500.dist-info}/WHEEL +1 -1
- {lsst_felis-26.2024.900.dist-info → lsst_felis-29.2025.4500.dist-info/licenses}/COPYRIGHT +1 -1
- felis/check.py +0 -381
- felis/simple.py +0 -424
- felis/sql.py +0 -275
- felis/tap.py +0 -433
- felis/utils.py +0 -100
- felis/validation.py +0 -103
- felis/version.py +0 -2
- felis/visitor.py +0 -180
- lsst_felis-26.2024.900.dist-info/METADATA +0 -28
- lsst_felis-26.2024.900.dist-info/RECORD +0 -23
- {lsst_felis-26.2024.900.dist-info → lsst_felis-29.2025.4500.dist-info}/entry_points.txt +0 -0
- {lsst_felis-26.2024.900.dist-info → lsst_felis-29.2025.4500.dist-info/licenses}/LICENSE +0 -0
- {lsst_felis-26.2024.900.dist-info → lsst_felis-29.2025.4500.dist-info}/top_level.txt +0 -0
- {lsst_felis-26.2024.900.dist-info → lsst_felis-29.2025.4500.dist-info}/zip-safe +0 -0
felis/db/dialects.py
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
"""Get SQLAlchemy dialects and their type modules."""
|
|
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
|
+
from __future__ import annotations
|
|
25
|
+
|
|
26
|
+
from collections.abc import Mapping
|
|
27
|
+
from types import MappingProxyType, ModuleType
|
|
28
|
+
|
|
29
|
+
from sqlalchemy import dialects
|
|
30
|
+
from sqlalchemy.engine import Dialect
|
|
31
|
+
from sqlalchemy.engine.mock import create_mock_engine
|
|
32
|
+
|
|
33
|
+
from .sqltypes import MYSQL, POSTGRES, SQLITE
|
|
34
|
+
|
|
35
|
+
__all__ = ["get_dialect_module", "get_supported_dialects"]
|
|
36
|
+
|
|
37
|
+
_DIALECT_NAMES = (MYSQL, POSTGRES, SQLITE)
|
|
38
|
+
"""List of supported dialect names.
|
|
39
|
+
|
|
40
|
+
This list is used to create the dialect and module dictionaries.
|
|
41
|
+
"""
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _dialect(dialect_name: str) -> Dialect:
|
|
45
|
+
"""Create the SQLAlchemy dialect for the given name using a mock engine.
|
|
46
|
+
|
|
47
|
+
Parameters
|
|
48
|
+
----------
|
|
49
|
+
dialect_name
|
|
50
|
+
The name of the dialect to create.
|
|
51
|
+
|
|
52
|
+
Returns
|
|
53
|
+
-------
|
|
54
|
+
`~sqlalchemy.engine.Dialect`
|
|
55
|
+
The SQLAlchemy dialect.
|
|
56
|
+
"""
|
|
57
|
+
return create_mock_engine(f"{dialect_name}://", executor=None).dialect
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
_DIALECTS = MappingProxyType({name: _dialect(name) for name in _DIALECT_NAMES})
|
|
61
|
+
"""Dictionary of dialect names to SQLAlchemy dialects."""
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def get_supported_dialects() -> Mapping[str, Dialect]:
|
|
65
|
+
"""Get a dictionary of the supported SQLAlchemy dialects.
|
|
66
|
+
|
|
67
|
+
Returns
|
|
68
|
+
-------
|
|
69
|
+
`dict` [ `str`, `~sqlalchemy.engine.Dialect`]
|
|
70
|
+
A dictionary of the supported SQLAlchemy dialects.
|
|
71
|
+
|
|
72
|
+
Notes
|
|
73
|
+
-----
|
|
74
|
+
The dictionary is keyed by the dialect name and the value is the SQLAlchemy
|
|
75
|
+
dialect object. This function is intended as the primary interface for
|
|
76
|
+
getting the supported dialects.
|
|
77
|
+
"""
|
|
78
|
+
return _DIALECTS
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def _dialect_module(dialect_name: str) -> ModuleType:
|
|
82
|
+
"""Get the SQLAlchemy dialect module for the given name.
|
|
83
|
+
|
|
84
|
+
Parameters
|
|
85
|
+
----------
|
|
86
|
+
dialect_name
|
|
87
|
+
The name of the dialect module to get from the SQLAlchemy package.
|
|
88
|
+
"""
|
|
89
|
+
return getattr(dialects, dialect_name)
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
_DIALECT_MODULES = MappingProxyType({name: _dialect_module(name) for name in _DIALECT_NAMES})
|
|
93
|
+
"""Dictionary of dialect names to SQLAlchemy modules."""
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def get_dialect_module(dialect_name: str) -> ModuleType:
|
|
97
|
+
"""Get the SQLAlchemy dialect module for the given name.
|
|
98
|
+
|
|
99
|
+
Parameters
|
|
100
|
+
----------
|
|
101
|
+
dialect_name
|
|
102
|
+
The name of the dialect module to get from the SQLAlchemy package.
|
|
103
|
+
|
|
104
|
+
Returns
|
|
105
|
+
-------
|
|
106
|
+
`~types.ModuleType`
|
|
107
|
+
The SQLAlchemy dialect module.
|
|
108
|
+
|
|
109
|
+
Raises
|
|
110
|
+
------
|
|
111
|
+
ValueError
|
|
112
|
+
Raised if the dialect name is not supported.
|
|
113
|
+
"""
|
|
114
|
+
if dialect_name not in _DIALECT_MODULES:
|
|
115
|
+
raise ValueError(f"Unsupported dialect: {dialect_name}")
|
|
116
|
+
return _DIALECT_MODULES[dialect_name]
|
felis/db/schema.py
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
"""Database utilities for Felis schemas."""
|
|
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
|
+
from sqlalchemy import Engine, create_engine
|
|
25
|
+
|
|
26
|
+
from ..datamodel import Schema
|
|
27
|
+
from ..metadata import MetaDataBuilder
|
|
28
|
+
from .utils import DatabaseContext
|
|
29
|
+
|
|
30
|
+
__all__ = ["create_database"]
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def create_database(schema: Schema, engine_or_url_str: Engine | str | None = None) -> DatabaseContext:
|
|
34
|
+
"""
|
|
35
|
+
Create a database from the specified `Schema`.
|
|
36
|
+
|
|
37
|
+
Parameters
|
|
38
|
+
----------
|
|
39
|
+
schema
|
|
40
|
+
The schema to create.
|
|
41
|
+
engine_or_url_str
|
|
42
|
+
The SQLAlchemy engine or URL to use for database creation.
|
|
43
|
+
If None, an in-memory SQLite database will be created.
|
|
44
|
+
|
|
45
|
+
Returns
|
|
46
|
+
-------
|
|
47
|
+
`DatabaseContext`
|
|
48
|
+
The database context object.
|
|
49
|
+
"""
|
|
50
|
+
if engine_or_url_str is not None:
|
|
51
|
+
engine = (
|
|
52
|
+
engine_or_url_str if isinstance(engine_or_url_str, Engine) else create_engine(engine_or_url_str)
|
|
53
|
+
)
|
|
54
|
+
else:
|
|
55
|
+
engine = create_engine("sqlite:///:memory:")
|
|
56
|
+
metadata = MetaDataBuilder(
|
|
57
|
+
schema, apply_schema_to_metadata=False if engine.url.drivername == "sqlite" else True
|
|
58
|
+
).build()
|
|
59
|
+
ctx = DatabaseContext(metadata, engine)
|
|
60
|
+
ctx.initialize()
|
|
61
|
+
ctx.create_all()
|
|
62
|
+
return ctx
|
felis/db/sqltypes.py
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
"""Map Felis types to SQLAlchemy types."""
|
|
2
|
+
|
|
1
3
|
# This file is part of felis.
|
|
2
4
|
#
|
|
3
5
|
# Developed for the LSST Data Management System.
|
|
@@ -19,16 +21,34 @@
|
|
|
19
21
|
# You should have received a copy of the GNU General Public License
|
|
20
22
|
# along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
21
23
|
|
|
24
|
+
from __future__ import annotations
|
|
25
|
+
|
|
22
26
|
import builtins
|
|
23
|
-
from collections.abc import Mapping
|
|
27
|
+
from collections.abc import Callable, Mapping
|
|
24
28
|
from typing import Any
|
|
25
29
|
|
|
26
|
-
from sqlalchemy import
|
|
27
|
-
from sqlalchemy.dialects import mysql,
|
|
30
|
+
from sqlalchemy import SmallInteger, types
|
|
31
|
+
from sqlalchemy.dialects import mysql, postgresql
|
|
28
32
|
from sqlalchemy.ext.compiler import compiles
|
|
29
33
|
|
|
34
|
+
__all__ = [
|
|
35
|
+
"binary",
|
|
36
|
+
"boolean",
|
|
37
|
+
"byte",
|
|
38
|
+
"char",
|
|
39
|
+
"double",
|
|
40
|
+
"float",
|
|
41
|
+
"get_type_func",
|
|
42
|
+
"int",
|
|
43
|
+
"long",
|
|
44
|
+
"short",
|
|
45
|
+
"string",
|
|
46
|
+
"text",
|
|
47
|
+
"timestamp",
|
|
48
|
+
"unicode",
|
|
49
|
+
]
|
|
50
|
+
|
|
30
51
|
MYSQL = "mysql"
|
|
31
|
-
ORACLE = "oracle"
|
|
32
52
|
POSTGRES = "postgresql"
|
|
33
53
|
SQLITE = "sqlite"
|
|
34
54
|
|
|
@@ -39,41 +59,46 @@ class TINYINT(SmallInteger):
|
|
|
39
59
|
__visit_name__ = "TINYINT"
|
|
40
60
|
|
|
41
61
|
|
|
42
|
-
class DOUBLE(Float):
|
|
43
|
-
"""The non-standard DOUBLE type."""
|
|
44
|
-
|
|
45
|
-
__visit_name__ = "DOUBLE"
|
|
46
|
-
|
|
47
|
-
|
|
48
62
|
@compiles(TINYINT)
|
|
49
|
-
def compile_tinyint(type_: Any, compiler: Any, **
|
|
50
|
-
"""
|
|
63
|
+
def compile_tinyint(type_: Any, compiler: Any, **kwargs: Any) -> str:
|
|
64
|
+
"""Compile the non-standard ``TINYINT`` type to SQL.
|
|
65
|
+
|
|
66
|
+
Parameters
|
|
67
|
+
----------
|
|
68
|
+
type_
|
|
69
|
+
The type object.
|
|
70
|
+
compiler
|
|
71
|
+
The compiler object.
|
|
72
|
+
**kwargs
|
|
73
|
+
Additional keyword arguments.
|
|
74
|
+
|
|
75
|
+
Returns
|
|
76
|
+
-------
|
|
77
|
+
`str`
|
|
78
|
+
The compiled SQL for TINYINT.
|
|
79
|
+
|
|
80
|
+
Notes
|
|
81
|
+
-----
|
|
82
|
+
This function returns the SQL for the the TINYINT type. The function
|
|
83
|
+
signature and parameters are defined by SQLAlchemy.
|
|
84
|
+
"""
|
|
51
85
|
return "TINYINT"
|
|
52
86
|
|
|
53
87
|
|
|
54
|
-
@compiles(DOUBLE)
|
|
55
|
-
def compile_double(type_: Any, compiler: Any, **kw: Any) -> str:
|
|
56
|
-
"""Return type name for double precision type."""
|
|
57
|
-
return "DOUBLE"
|
|
58
|
-
|
|
59
|
-
|
|
60
88
|
_TypeMap = Mapping[str, types.TypeEngine | type[types.TypeEngine]]
|
|
61
89
|
|
|
62
|
-
boolean_map: _TypeMap = {MYSQL: mysql.
|
|
90
|
+
boolean_map: _TypeMap = {MYSQL: mysql.BOOLEAN, POSTGRES: postgresql.BOOLEAN()}
|
|
63
91
|
|
|
64
92
|
byte_map: _TypeMap = {
|
|
65
93
|
MYSQL: mysql.TINYINT(),
|
|
66
|
-
ORACLE: oracle.NUMBER(3),
|
|
67
94
|
POSTGRES: postgresql.SMALLINT(),
|
|
68
95
|
}
|
|
69
96
|
|
|
70
97
|
short_map: _TypeMap = {
|
|
71
98
|
MYSQL: mysql.SMALLINT(),
|
|
72
|
-
ORACLE: oracle.NUMBER(5),
|
|
73
99
|
POSTGRES: postgresql.SMALLINT(),
|
|
74
100
|
}
|
|
75
101
|
|
|
76
|
-
# Skip Oracle
|
|
77
102
|
int_map: _TypeMap = {
|
|
78
103
|
MYSQL: mysql.INTEGER(),
|
|
79
104
|
POSTGRES: postgresql.INTEGER(),
|
|
@@ -81,116 +106,293 @@ int_map: _TypeMap = {
|
|
|
81
106
|
|
|
82
107
|
long_map: _TypeMap = {
|
|
83
108
|
MYSQL: mysql.BIGINT(),
|
|
84
|
-
ORACLE: oracle.NUMBER(38, 0),
|
|
85
109
|
POSTGRES: postgresql.BIGINT(),
|
|
86
110
|
}
|
|
87
111
|
|
|
88
112
|
float_map: _TypeMap = {
|
|
89
113
|
MYSQL: mysql.FLOAT(),
|
|
90
|
-
ORACLE: oracle.BINARY_FLOAT(),
|
|
91
114
|
POSTGRES: postgresql.FLOAT(),
|
|
92
115
|
}
|
|
93
116
|
|
|
94
117
|
double_map: _TypeMap = {
|
|
95
118
|
MYSQL: mysql.DOUBLE(),
|
|
96
|
-
ORACLE: oracle.BINARY_DOUBLE(),
|
|
97
119
|
POSTGRES: postgresql.DOUBLE_PRECISION(),
|
|
98
120
|
}
|
|
99
121
|
|
|
100
122
|
char_map: _TypeMap = {
|
|
101
123
|
MYSQL: mysql.CHAR,
|
|
102
|
-
ORACLE: oracle.CHAR,
|
|
103
124
|
POSTGRES: postgresql.CHAR,
|
|
104
125
|
}
|
|
105
126
|
|
|
106
127
|
string_map: _TypeMap = {
|
|
107
128
|
MYSQL: mysql.VARCHAR,
|
|
108
|
-
ORACLE: oracle.VARCHAR2,
|
|
109
129
|
POSTGRES: postgresql.VARCHAR,
|
|
110
130
|
}
|
|
111
131
|
|
|
112
132
|
unicode_map: _TypeMap = {
|
|
113
133
|
MYSQL: mysql.NVARCHAR,
|
|
114
|
-
ORACLE: oracle.NVARCHAR2,
|
|
115
134
|
POSTGRES: postgresql.VARCHAR,
|
|
116
135
|
}
|
|
117
136
|
|
|
118
137
|
text_map: _TypeMap = {
|
|
119
138
|
MYSQL: mysql.LONGTEXT,
|
|
120
|
-
ORACLE: oracle.CLOB,
|
|
121
139
|
POSTGRES: postgresql.TEXT,
|
|
122
140
|
}
|
|
123
141
|
|
|
124
142
|
binary_map: _TypeMap = {
|
|
125
143
|
MYSQL: mysql.LONGBLOB,
|
|
126
|
-
ORACLE: oracle.BLOB,
|
|
127
144
|
POSTGRES: postgresql.BYTEA,
|
|
128
145
|
}
|
|
129
146
|
|
|
147
|
+
timestamp_map: _TypeMap = {
|
|
148
|
+
MYSQL: mysql.DATETIME(timezone=False),
|
|
149
|
+
POSTGRES: postgresql.TIMESTAMP(timezone=False),
|
|
150
|
+
}
|
|
151
|
+
|
|
130
152
|
|
|
131
153
|
def boolean(**kwargs: Any) -> types.TypeEngine:
|
|
132
|
-
"""
|
|
154
|
+
"""Get the SQL type for Felis `~felis.types.Boolean` with variants.
|
|
155
|
+
|
|
156
|
+
Parameters
|
|
157
|
+
----------
|
|
158
|
+
**kwargs
|
|
159
|
+
Additional keyword arguments to pass to the type object.
|
|
160
|
+
|
|
161
|
+
Returns
|
|
162
|
+
-------
|
|
163
|
+
`~sqlalchemy.types.TypeEngine`
|
|
164
|
+
The SQL type for a Felis boolean.
|
|
165
|
+
"""
|
|
133
166
|
return _vary(types.BOOLEAN(), boolean_map, kwargs)
|
|
134
167
|
|
|
135
168
|
|
|
136
169
|
def byte(**kwargs: Any) -> types.TypeEngine:
|
|
137
|
-
"""
|
|
170
|
+
"""Get the SQL type for Felis `~felis.types.Byte` with variants.
|
|
171
|
+
|
|
172
|
+
Parameters
|
|
173
|
+
----------
|
|
174
|
+
**kwargs
|
|
175
|
+
Additional keyword arguments to pass to the type object.
|
|
176
|
+
|
|
177
|
+
Returns
|
|
178
|
+
-------
|
|
179
|
+
`~sqlalchemy.types.TypeEngine`
|
|
180
|
+
The SQL type for a Felis byte.
|
|
181
|
+
"""
|
|
138
182
|
return _vary(TINYINT(), byte_map, kwargs)
|
|
139
183
|
|
|
140
184
|
|
|
141
185
|
def short(**kwargs: Any) -> types.TypeEngine:
|
|
142
|
-
"""
|
|
186
|
+
"""Get the SQL type for Felis `~felis.types.Short` with variants.
|
|
187
|
+
|
|
188
|
+
Parameters
|
|
189
|
+
----------
|
|
190
|
+
**kwargs
|
|
191
|
+
Additional keyword arguments to pass to the type object.
|
|
192
|
+
|
|
193
|
+
Returns
|
|
194
|
+
-------
|
|
195
|
+
`~sqlalchemy.types.TypeEngine`
|
|
196
|
+
The SQL type for a Felis short.
|
|
197
|
+
"""
|
|
143
198
|
return _vary(types.SMALLINT(), short_map, kwargs)
|
|
144
199
|
|
|
145
200
|
|
|
146
201
|
def int(**kwargs: Any) -> types.TypeEngine:
|
|
147
|
-
"""
|
|
202
|
+
"""Get the SQL type for Felis `~felis.types.Int` with variants.
|
|
203
|
+
|
|
204
|
+
Parameters
|
|
205
|
+
----------
|
|
206
|
+
**kwargs
|
|
207
|
+
Additional keyword arguments to pass to the type object.
|
|
208
|
+
|
|
209
|
+
Returns
|
|
210
|
+
-------
|
|
211
|
+
`~sqlalchemy.types.TypeEngine`
|
|
212
|
+
The SQL type for a Felis int.
|
|
213
|
+
"""
|
|
148
214
|
return _vary(types.INTEGER(), int_map, kwargs)
|
|
149
215
|
|
|
150
216
|
|
|
151
217
|
def long(**kwargs: Any) -> types.TypeEngine:
|
|
152
|
-
"""
|
|
218
|
+
"""Get the SQL type for Felis `~felis.types.Long` with variants.
|
|
219
|
+
|
|
220
|
+
Parameters
|
|
221
|
+
----------
|
|
222
|
+
**kwargs
|
|
223
|
+
Additional keyword arguments to pass to the type object.
|
|
224
|
+
|
|
225
|
+
Returns
|
|
226
|
+
-------
|
|
227
|
+
`~sqlalchemy.types.TypeEngine`
|
|
228
|
+
The SQL type for a Felis long.
|
|
229
|
+
"""
|
|
153
230
|
return _vary(types.BIGINT(), long_map, kwargs)
|
|
154
231
|
|
|
155
232
|
|
|
156
233
|
def float(**kwargs: Any) -> types.TypeEngine:
|
|
157
|
-
"""
|
|
234
|
+
"""Get the SQL type for Felis `~felis.types.Float` with variants.
|
|
235
|
+
|
|
236
|
+
Parameters
|
|
237
|
+
----------
|
|
238
|
+
**kwargs
|
|
239
|
+
Additional keyword arguments to pass to the type object.
|
|
240
|
+
|
|
241
|
+
Returns
|
|
242
|
+
-------
|
|
243
|
+
`~sqlalchemy.types.TypeEngine`
|
|
244
|
+
The SQL type for a Felis float.
|
|
245
|
+
"""
|
|
158
246
|
return _vary(types.FLOAT(), float_map, kwargs)
|
|
159
247
|
|
|
160
248
|
|
|
161
249
|
def double(**kwargs: Any) -> types.TypeEngine:
|
|
162
|
-
"""
|
|
163
|
-
|
|
250
|
+
"""Get the SQL type for Felis `~felis.types.Double` with variants.
|
|
251
|
+
|
|
252
|
+
Parameters
|
|
253
|
+
----------
|
|
254
|
+
**kwargs
|
|
255
|
+
Additional keyword arguments to pass to the type object.
|
|
256
|
+
|
|
257
|
+
Returns
|
|
258
|
+
-------
|
|
259
|
+
`~sqlalchemy.types.TypeEngine`
|
|
260
|
+
The SQL type for a Felis double.
|
|
261
|
+
"""
|
|
262
|
+
return _vary(types.DOUBLE(), double_map, kwargs)
|
|
164
263
|
|
|
165
264
|
|
|
166
265
|
def char(length: builtins.int, **kwargs: Any) -> types.TypeEngine:
|
|
167
|
-
"""
|
|
266
|
+
"""Get the SQL type for Felis `~felis.types.Char` with variants.
|
|
267
|
+
|
|
268
|
+
Parameters
|
|
269
|
+
----------
|
|
270
|
+
length
|
|
271
|
+
The length of the character field.
|
|
272
|
+
**kwargs
|
|
273
|
+
Additional keyword arguments to pass to the type object.
|
|
274
|
+
|
|
275
|
+
Returns
|
|
276
|
+
-------
|
|
277
|
+
`~sqlalchemy.types.TypeEngine`
|
|
278
|
+
The SQL type for a Felis char.
|
|
279
|
+
"""
|
|
168
280
|
return _vary(types.CHAR(length), char_map, kwargs, length)
|
|
169
281
|
|
|
170
282
|
|
|
171
283
|
def string(length: builtins.int, **kwargs: Any) -> types.TypeEngine:
|
|
172
|
-
"""
|
|
284
|
+
"""Get the SQL type for Felis `~felis.types.String` with variants.
|
|
285
|
+
|
|
286
|
+
Parameters
|
|
287
|
+
----------
|
|
288
|
+
length
|
|
289
|
+
The length of the string field.
|
|
290
|
+
**kwargs
|
|
291
|
+
Additional keyword arguments to pass to the type object.
|
|
292
|
+
|
|
293
|
+
Returns
|
|
294
|
+
-------
|
|
295
|
+
`~sqlalchemy.types.TypeEngine`
|
|
296
|
+
The SQL type for a Felis string.
|
|
297
|
+
"""
|
|
173
298
|
return _vary(types.VARCHAR(length), string_map, kwargs, length)
|
|
174
299
|
|
|
175
300
|
|
|
176
301
|
def unicode(length: builtins.int, **kwargs: Any) -> types.TypeEngine:
|
|
177
|
-
"""
|
|
302
|
+
"""Get the SQL type for Felis `~felis.types.Unicode` with variants.
|
|
303
|
+
|
|
304
|
+
Parameters
|
|
305
|
+
----------
|
|
306
|
+
length
|
|
307
|
+
The length of the unicode string field.
|
|
308
|
+
**kwargs
|
|
309
|
+
Additional keyword arguments to pass to the type object.
|
|
310
|
+
|
|
311
|
+
Returns
|
|
312
|
+
-------
|
|
313
|
+
`~sqlalchemy.types.TypeEngine`
|
|
314
|
+
The SQL type for a Felis unicode string.
|
|
315
|
+
"""
|
|
178
316
|
return _vary(types.NVARCHAR(length), unicode_map, kwargs, length)
|
|
179
317
|
|
|
180
318
|
|
|
181
|
-
def text(
|
|
182
|
-
"""
|
|
183
|
-
|
|
319
|
+
def text(**kwargs: Any) -> types.TypeEngine:
|
|
320
|
+
"""Get the SQL type for Felis `~felis.types.Text` with variants.
|
|
321
|
+
|
|
322
|
+
Parameters
|
|
323
|
+
----------
|
|
324
|
+
**kwargs
|
|
325
|
+
Additional keyword arguments to pass to the type object.
|
|
326
|
+
|
|
327
|
+
Returns
|
|
328
|
+
-------
|
|
329
|
+
`~sqlalchemy.types.TypeEngine`
|
|
330
|
+
The SQL type for Felis text.
|
|
331
|
+
"""
|
|
332
|
+
return _vary(types.TEXT(), text_map, kwargs)
|
|
184
333
|
|
|
185
334
|
|
|
186
335
|
def binary(length: builtins.int, **kwargs: Any) -> types.TypeEngine:
|
|
187
|
-
"""
|
|
336
|
+
"""Get the SQL type for Felis `~felis.types.Binary` with variants.
|
|
337
|
+
|
|
338
|
+
Parameters
|
|
339
|
+
----------
|
|
340
|
+
length
|
|
341
|
+
The length of the binary field.
|
|
342
|
+
**kwargs
|
|
343
|
+
Additional keyword arguments to pass to the type object.
|
|
344
|
+
|
|
345
|
+
Returns
|
|
346
|
+
-------
|
|
347
|
+
`~sqlalchemy.types.TypeEngine`
|
|
348
|
+
The SQL type for Felis binary.
|
|
349
|
+
"""
|
|
188
350
|
return _vary(types.BLOB(length), binary_map, kwargs, length)
|
|
189
351
|
|
|
190
352
|
|
|
191
353
|
def timestamp(**kwargs: Any) -> types.TypeEngine:
|
|
192
|
-
"""
|
|
193
|
-
|
|
354
|
+
"""Get the SQL type for Felis `~felis.types.Timestamp` with variants.
|
|
355
|
+
|
|
356
|
+
Parameters
|
|
357
|
+
----------
|
|
358
|
+
**kwargs
|
|
359
|
+
Additional keyword arguments to pass to the type object.
|
|
360
|
+
|
|
361
|
+
Returns
|
|
362
|
+
-------
|
|
363
|
+
`~sqlalchemy.types.TypeEngine`
|
|
364
|
+
The SQL type for a Felis timestamp.
|
|
365
|
+
"""
|
|
366
|
+
return _vary(types.TIMESTAMP(timezone=False), timestamp_map, kwargs)
|
|
367
|
+
|
|
368
|
+
|
|
369
|
+
def get_type_func(type_name: str) -> Callable:
|
|
370
|
+
"""Find the function which creates a specific SQL type by its Felis type
|
|
371
|
+
name.
|
|
372
|
+
|
|
373
|
+
Parameters
|
|
374
|
+
----------
|
|
375
|
+
type_name
|
|
376
|
+
The name of the type function to get.
|
|
377
|
+
|
|
378
|
+
Returns
|
|
379
|
+
-------
|
|
380
|
+
`Callable`
|
|
381
|
+
The function for the type.
|
|
382
|
+
|
|
383
|
+
Raises
|
|
384
|
+
------
|
|
385
|
+
ValueError
|
|
386
|
+
Raised if the type name is not recognized.
|
|
387
|
+
|
|
388
|
+
Notes
|
|
389
|
+
-----
|
|
390
|
+
This maps the type name to the function that creates the SQL type. This is
|
|
391
|
+
the main way to get the type functions from the type names.
|
|
392
|
+
"""
|
|
393
|
+
if type_name not in globals():
|
|
394
|
+
raise ValueError(f"Unknown type: {type_name}")
|
|
395
|
+
return globals()[type_name]
|
|
194
396
|
|
|
195
397
|
|
|
196
398
|
def _vary(
|
|
@@ -199,11 +401,36 @@ def _vary(
|
|
|
199
401
|
overrides: _TypeMap,
|
|
200
402
|
*args: Any,
|
|
201
403
|
) -> types.TypeEngine:
|
|
404
|
+
"""Add datatype variants and overrides to a SQLAlchemy type.
|
|
405
|
+
|
|
406
|
+
Parameters
|
|
407
|
+
----------
|
|
408
|
+
type_
|
|
409
|
+
The base SQLAlchemy type object. This is essentially a default
|
|
410
|
+
SQLAlchemy ``TypeEngine`` object, which will apply if there is no
|
|
411
|
+
variant or type override from the schema.
|
|
412
|
+
variant_map
|
|
413
|
+
The dictionary of dialects to types. Each key is a string representing
|
|
414
|
+
a dialect name, and each value is either an instance of
|
|
415
|
+
``TypeEngine`` representing the variant type object or a callable
|
|
416
|
+
reference to its class type that will be instantiated later.
|
|
417
|
+
overrides
|
|
418
|
+
The dictionary of dialects to types to override the defaults. Each key
|
|
419
|
+
is a string representing a dialect name and type with a similar
|
|
420
|
+
structure as the `variant_map`.
|
|
421
|
+
args
|
|
422
|
+
The extra arguments to pass to the type object.
|
|
423
|
+
|
|
424
|
+
Notes
|
|
425
|
+
-----
|
|
426
|
+
This function is intended for internal use only. It builds a SQLAlchemy
|
|
427
|
+
``TypeEngine`` that includes variants and overrides defined by Felis.
|
|
428
|
+
"""
|
|
202
429
|
variants: dict[str, types.TypeEngine | type[types.TypeEngine]] = dict(variant_map)
|
|
203
430
|
variants.update(overrides)
|
|
204
431
|
for dialect, variant in variants.items():
|
|
205
432
|
# If this is a class and not an instance, instantiate
|
|
206
|
-
if
|
|
433
|
+
if callable(variant):
|
|
207
434
|
variant = variant(*args)
|
|
208
435
|
type_ = type_.with_variant(variant, dialect)
|
|
209
436
|
return type_
|