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.
Files changed (46) hide show
  1. {detquantlib-5.9.2 → detquantlib-5.10.1}/PKG-INFO +1 -1
  2. {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/data/databases/snowflake_client.py +53 -11
  3. {detquantlib-5.9.2 → detquantlib-5.10.1}/pyproject.toml +1 -1
  4. {detquantlib-5.9.2 → detquantlib-5.10.1}/LICENSE.txt +0 -0
  5. {detquantlib-5.9.2 → detquantlib-5.10.1}/README.md +0 -0
  6. {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/__init__.py +0 -0
  7. {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/assets/__init__.py +0 -0
  8. {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/assets/battery.py +0 -0
  9. {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/assets/wind_turbine.py +0 -0
  10. {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/converters/__init__.py +0 -0
  11. {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/converters/definitions.py +0 -0
  12. {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/converters/energy.py +0 -0
  13. {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/converters/helpers.py +0 -0
  14. {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/converters/price.py +0 -0
  15. {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/data/__init__.py +0 -0
  16. {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/data/databases/azure_client.py +0 -0
  17. {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/data/databases/detdatabase.py +0 -0
  18. {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/data/databases/helpers.py +0 -0
  19. {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/data/entsoe/entsoe.py +0 -0
  20. {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/data/sftp/sftp.py +0 -0
  21. {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/data/tennet/tennet_client.py +0 -0
  22. {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/dates/__init__.py +0 -0
  23. {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/dates/dates.py +0 -0
  24. {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/figures/__init__.py +0 -0
  25. {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/figures/plotly_figures.py +0 -0
  26. {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/forecasting/__init__.py +0 -0
  27. {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/forecasting/forecasting.py +0 -0
  28. {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/outputs/__init__.py +0 -0
  29. {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/outputs/outputs_interface.py +0 -0
  30. {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/snapshot_testing/README.md +0 -0
  31. {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/snapshot_testing/__init__.py +0 -0
  32. {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/snapshot_testing/cases.py +0 -0
  33. {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/snapshot_testing/comparison.py +0 -0
  34. {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/snapshot_testing/config.py +0 -0
  35. {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/snapshot_testing/errors.py +0 -0
  36. {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/snapshot_testing/isolation.py +0 -0
  37. {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/snapshot_testing/pytest_plugin.py +0 -0
  38. {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/snapshot_testing/recording.py +0 -0
  39. {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/snapshot_testing/tables.py +0 -0
  40. {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/stats/__init__.py +0 -0
  41. {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/stats/data_analysis.py +0 -0
  42. {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/tradable_products/__init__.py +0 -0
  43. {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/tradable_products/tradable_products.py +0 -0
  44. {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/utils/__init__.py +0 -0
  45. {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/utils/logging.py +0 -0
  46. {detquantlib-5.9.2 → detquantlib-5.10.1}/detquantlib/utils/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: detquantlib
3
- Version: 5.9.2
3
+ Version: 5.10.1
4
4
  Summary: An internal library containing functions and classes that can be used across Quant models.
5
5
  License-File: LICENSE.txt
6
6
  Author: Dynamic Energy Trading
@@ -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``, ``SNOWFLAKE_WAREHOUSE``, ``SNOWFLAKE_DATABASE``,
72
- ``SNOWFLAKE_SCHEMA``, ``SNOWFLAKE_ROLE``, ``SNOWFLAKE_AUTHENTICATOR``,
73
- ``SNOWFLAKE_PRIVATE_KEY_PATH`` and ``SNOWFLAKE_PRIVATE_KEY_PASSPHRASE``. Unset variables
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, authenticator or password, in that order)
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
  """
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "detquantlib"
3
- version = "5.9.2"
3
+ version = "5.10.1"
4
4
  description = "An internal library containing functions and classes that can be used across Quant models."
5
5
  authors = ["Dynamic Energy Trading"]
6
6
  readme = "README.md"
File without changes
File without changes