detquantlib 5.9.2__tar.gz → 5.10.1__tar.gz
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.
- {detquantlib-5.9.2 → detquantlib-5.10.1}/PKG-INFO +1 -1
- {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/data/databases/snowflake_client.py +53 -11
- {detquantlib-5.9.2 → detquantlib-5.10.1}/pyproject.toml +1 -1
- {detquantlib-5.9.2 → detquantlib-5.10.1}/LICENSE.txt +0 -0
- {detquantlib-5.9.2 → detquantlib-5.10.1}/README.md +0 -0
- {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/__init__.py +0 -0
- {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/assets/__init__.py +0 -0
- {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/assets/battery.py +0 -0
- {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/assets/wind_turbine.py +0 -0
- {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/converters/__init__.py +0 -0
- {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/converters/definitions.py +0 -0
- {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/converters/energy.py +0 -0
- {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/converters/helpers.py +0 -0
- {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/converters/price.py +0 -0
- {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/data/__init__.py +0 -0
- {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/data/databases/azure_client.py +0 -0
- {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/data/databases/detdatabase.py +0 -0
- {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/data/databases/helpers.py +0 -0
- {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/data/entsoe/entsoe.py +0 -0
- {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/data/sftp/sftp.py +0 -0
- {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/data/tennet/tennet_client.py +0 -0
- {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/dates/__init__.py +0 -0
- {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/dates/dates.py +0 -0
- {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/figures/__init__.py +0 -0
- {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/figures/plotly_figures.py +0 -0
- {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/forecasting/__init__.py +0 -0
- {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/forecasting/forecasting.py +0 -0
- {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/outputs/__init__.py +0 -0
- {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/outputs/outputs_interface.py +0 -0
- {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/snapshot_testing/README.md +0 -0
- {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/snapshot_testing/__init__.py +0 -0
- {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/snapshot_testing/cases.py +0 -0
- {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/snapshot_testing/comparison.py +0 -0
- {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/snapshot_testing/config.py +0 -0
- {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/snapshot_testing/errors.py +0 -0
- {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/snapshot_testing/isolation.py +0 -0
- {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/snapshot_testing/pytest_plugin.py +0 -0
- {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/snapshot_testing/recording.py +0 -0
- {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/snapshot_testing/tables.py +0 -0
- {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/stats/__init__.py +0 -0
- {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/stats/data_analysis.py +0 -0
- {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/tradable_products/__init__.py +0 -0
- {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/tradable_products/tradable_products.py +0 -0
- {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/utils/__init__.py +0 -0
- {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/utils/logging.py +0 -0
- {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/utils/utils.py +0 -0
|
@@ -33,6 +33,7 @@ class SnowflakeConfig:
|
|
|
33
33
|
|
|
34
34
|
Exactly one authentication method is used, chosen in this order of precedence:
|
|
35
35
|
- ``private_key_path`` (i.e. key-pair)
|
|
36
|
+
- ``token`` (i.e. programmatic access token)
|
|
36
37
|
- ``authenticator`` (e.g. SSO)
|
|
37
38
|
- ``password``
|
|
38
39
|
|
|
@@ -40,6 +41,7 @@ class SnowflakeConfig:
|
|
|
40
41
|
account: Snowflake account identifier, e.g. ``"xy12345.eu-west-1"``.
|
|
41
42
|
user: Login name of the Snowflake user.
|
|
42
43
|
password: Password for password authentication.
|
|
44
|
+
token: Programmatic access token (PAT) for token authentication.
|
|
43
45
|
warehouse: Default virtual warehouse for the session.
|
|
44
46
|
database: Default database for the session.
|
|
45
47
|
schema: Default schema for the session.
|
|
@@ -53,6 +55,7 @@ class SnowflakeConfig:
|
|
|
53
55
|
account: Optional[str] = None
|
|
54
56
|
user: Optional[str] = None
|
|
55
57
|
password: Optional[str] = None
|
|
58
|
+
token: Optional[str] = None
|
|
56
59
|
warehouse: Optional[str] = None
|
|
57
60
|
database: Optional[str] = None
|
|
58
61
|
schema: Optional[str] = None
|
|
@@ -68,10 +71,10 @@ class SnowflakeConfig:
|
|
|
68
71
|
Builds a config from environment variables.
|
|
69
72
|
|
|
70
73
|
Reads variables named ``<prefix><FIELD>``, e.g. ``SNOWFLAKE_ACCOUNT``, ``SNOWFLAKE_USER``,
|
|
71
|
-
``SNOWFLAKE_PASSWORD``, ``
|
|
72
|
-
``
|
|
73
|
-
``SNOWFLAKE_PRIVATE_KEY_PATH`` and
|
|
74
|
-
become ``None``.
|
|
74
|
+
``SNOWFLAKE_PASSWORD``, ``SNOWFLAKE_TOKEN``, ``SNOWFLAKE_WAREHOUSE``,
|
|
75
|
+
``SNOWFLAKE_DATABASE``, ``SNOWFLAKE_SCHEMA``, ``SNOWFLAKE_ROLE``,
|
|
76
|
+
``SNOWFLAKE_AUTHENTICATOR``, ``SNOWFLAKE_PRIVATE_KEY_PATH`` and
|
|
77
|
+
``SNOWFLAKE_PRIVATE_KEY_PASSPHRASE``. Unset variables become ``None``.
|
|
75
78
|
|
|
76
79
|
Args:
|
|
77
80
|
prefix: Prefix prepended to each variable name.
|
|
@@ -84,6 +87,7 @@ class SnowflakeConfig:
|
|
|
84
87
|
account=g("ACCOUNT"),
|
|
85
88
|
user=g("USER"),
|
|
86
89
|
password=g("PASSWORD"),
|
|
90
|
+
token=g("TOKEN"),
|
|
87
91
|
warehouse=g("WAREHOUSE"),
|
|
88
92
|
database=g("DATABASE"),
|
|
89
93
|
schema=g("SCHEMA"),
|
|
@@ -147,8 +151,8 @@ class SnowflakeClient:
|
|
|
147
151
|
"""
|
|
148
152
|
Builds the keyword arguments for ``snowflake.connector.connect``.
|
|
149
153
|
|
|
150
|
-
Selects a single authentication method (key-pair,
|
|
151
|
-
and drops any parameters that are ``None``.
|
|
154
|
+
Selects a single authentication method (key-pair, programmatic access token, authenticator
|
|
155
|
+
or password, in that order) and drops any parameters that are ``None``.
|
|
152
156
|
|
|
153
157
|
Returns:
|
|
154
158
|
A dict of connection parameters.
|
|
@@ -174,13 +178,16 @@ class SnowflakeClient:
|
|
|
174
178
|
params["private_key"] = self._load_private_key(
|
|
175
179
|
c.private_key_path, c.private_key_passphrase
|
|
176
180
|
)
|
|
181
|
+
elif c.token:
|
|
182
|
+
params["authenticator"] = "PROGRAMMATIC_ACCESS_TOKEN"
|
|
183
|
+
params["token"] = c.token
|
|
177
184
|
elif c.authenticator:
|
|
178
185
|
params["authenticator"] = c.authenticator
|
|
179
186
|
elif c.password:
|
|
180
187
|
params["password"] = c.password
|
|
181
188
|
else:
|
|
182
189
|
raise ValueError(
|
|
183
|
-
"No credentials provided: set password, private_key_path, or authenticator."
|
|
190
|
+
"No credentials provided: set password, token, private_key_path, or authenticator."
|
|
184
191
|
)
|
|
185
192
|
|
|
186
193
|
return {k: v for k, v in params.items() if v is not None}
|
|
@@ -279,7 +286,9 @@ class SnowflakeClient:
|
|
|
279
286
|
Args:
|
|
280
287
|
sql: The SQL statement to execute.
|
|
281
288
|
params: Values bound to the placeholders in ``sql``: a sequence for positional ``%s``
|
|
282
|
-
placeholders, or a dict for named ``%(name)s`` placeholders.
|
|
289
|
+
placeholders, or a dict for named ``%(name)s`` placeholders. pandas Timestamps
|
|
290
|
+
are converted to Python datetimes (and ``NaT`` to ``None``), since the connector
|
|
291
|
+
cannot bind them.
|
|
283
292
|
lowercase_columns: If ``True``, column names in the result are converted to lowercase
|
|
284
293
|
(Snowflake returns them uppercase by default).
|
|
285
294
|
|
|
@@ -292,7 +301,7 @@ class SnowflakeClient:
|
|
|
292
301
|
conn = self.connect()
|
|
293
302
|
try:
|
|
294
303
|
with conn.cursor() as cur:
|
|
295
|
-
cur.execute(sql, params)
|
|
304
|
+
cur.execute(sql, self._prepare_params(params))
|
|
296
305
|
df = cur.fetch_pandas_all()
|
|
297
306
|
except SnowflakeError as e:
|
|
298
307
|
logger.error("Query failed: %s", e)
|
|
@@ -322,7 +331,7 @@ class SnowflakeClient:
|
|
|
322
331
|
"""
|
|
323
332
|
conn = self.connect()
|
|
324
333
|
with conn.cursor() as cur:
|
|
325
|
-
cur.execute(sql, params)
|
|
334
|
+
cur.execute(sql, self._prepare_params(params))
|
|
326
335
|
for batch in cur.fetch_pandas_batches():
|
|
327
336
|
yield batch
|
|
328
337
|
|
|
@@ -485,7 +494,7 @@ class SnowflakeClient:
|
|
|
485
494
|
conn = self.connect()
|
|
486
495
|
try:
|
|
487
496
|
with conn.cursor() as cur:
|
|
488
|
-
cur.execute(sql, params)
|
|
497
|
+
cur.execute(sql, self._prepare_params(params))
|
|
489
498
|
rowcount = cur.rowcount
|
|
490
499
|
except SnowflakeError as e:
|
|
491
500
|
logger.error("Statement failed: %s", e)
|
|
@@ -493,6 +502,39 @@ class SnowflakeClient:
|
|
|
493
502
|
logger.info("Statement affected %s rows", rowcount)
|
|
494
503
|
return rowcount
|
|
495
504
|
|
|
505
|
+
@staticmethod
|
|
506
|
+
def _prepare_params(
|
|
507
|
+
params: Optional[Union[Sequence[Any], dict[str, Any]]],
|
|
508
|
+
) -> Optional[Union[tuple[Any, ...], dict[str, Any]]]:
|
|
509
|
+
"""
|
|
510
|
+
Converts parameter values that the Snowflake connector cannot bind.
|
|
511
|
+
|
|
512
|
+
The connector picks a converter by type name, so it rejects pandas Timestamps even though
|
|
513
|
+
they are datetime subclasses. They are converted to Python datetimes, and ``NaT`` to
|
|
514
|
+
``None``. Note that Python datetimes only have microsecond precision, so nanoseconds are
|
|
515
|
+
dropped (pandas warns when this happens). Other values are passed through unchanged.
|
|
516
|
+
|
|
517
|
+
Args:
|
|
518
|
+
params: Values bound to the placeholders of a query, as in ``query()``.
|
|
519
|
+
|
|
520
|
+
Returns:
|
|
521
|
+
The converted values, as a dict for named placeholders or a tuple for positional
|
|
522
|
+
ones, or ``None`` if ``params`` is ``None``.
|
|
523
|
+
"""
|
|
524
|
+
if params is None:
|
|
525
|
+
return None
|
|
526
|
+
|
|
527
|
+
def convert(value: Any) -> Any:
|
|
528
|
+
if value is pd.NaT:
|
|
529
|
+
return None
|
|
530
|
+
if isinstance(value, pd.Timestamp):
|
|
531
|
+
return value.to_pydatetime()
|
|
532
|
+
return value
|
|
533
|
+
|
|
534
|
+
if isinstance(params, dict):
|
|
535
|
+
return {k: convert(v) for k, v in params.items()}
|
|
536
|
+
return tuple(convert(v) for v in params)
|
|
537
|
+
|
|
496
538
|
@staticmethod
|
|
497
539
|
def _check_identifier(name: str) -> str:
|
|
498
540
|
"""
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|