detquantlib 5.10.0__tar.gz → 5.10.2__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.10.0 → detquantlib-5.10.2}/PKG-INFO +1 -1
  2. {detquantlib-5.10.0 → detquantlib-5.10.2}/detquantlib/data/databases/azure_client.py +1 -3
  3. {detquantlib-5.10.0 → detquantlib-5.10.2}/detquantlib/data/databases/snowflake_client.py +40 -6
  4. {detquantlib-5.10.0 → detquantlib-5.10.2}/detquantlib/utils/logging.py +2 -1
  5. {detquantlib-5.10.0 → detquantlib-5.10.2}/pyproject.toml +1 -1
  6. {detquantlib-5.10.0 → detquantlib-5.10.2}/LICENSE.txt +0 -0
  7. {detquantlib-5.10.0 → detquantlib-5.10.2}/README.md +0 -0
  8. {detquantlib-5.10.0 → detquantlib-5.10.2}/detquantlib/__init__.py +0 -0
  9. {detquantlib-5.10.0 → detquantlib-5.10.2}/detquantlib/assets/__init__.py +0 -0
  10. {detquantlib-5.10.0 → detquantlib-5.10.2}/detquantlib/assets/battery.py +0 -0
  11. {detquantlib-5.10.0 → detquantlib-5.10.2}/detquantlib/assets/wind_turbine.py +0 -0
  12. {detquantlib-5.10.0 → detquantlib-5.10.2}/detquantlib/converters/__init__.py +0 -0
  13. {detquantlib-5.10.0 → detquantlib-5.10.2}/detquantlib/converters/definitions.py +0 -0
  14. {detquantlib-5.10.0 → detquantlib-5.10.2}/detquantlib/converters/energy.py +0 -0
  15. {detquantlib-5.10.0 → detquantlib-5.10.2}/detquantlib/converters/helpers.py +0 -0
  16. {detquantlib-5.10.0 → detquantlib-5.10.2}/detquantlib/converters/price.py +0 -0
  17. {detquantlib-5.10.0 → detquantlib-5.10.2}/detquantlib/data/__init__.py +0 -0
  18. {detquantlib-5.10.0 → detquantlib-5.10.2}/detquantlib/data/databases/detdatabase.py +0 -0
  19. {detquantlib-5.10.0 → detquantlib-5.10.2}/detquantlib/data/databases/helpers.py +0 -0
  20. {detquantlib-5.10.0 → detquantlib-5.10.2}/detquantlib/data/entsoe/entsoe.py +0 -0
  21. {detquantlib-5.10.0 → detquantlib-5.10.2}/detquantlib/data/sftp/sftp.py +0 -0
  22. {detquantlib-5.10.0 → detquantlib-5.10.2}/detquantlib/data/tennet/tennet_client.py +0 -0
  23. {detquantlib-5.10.0 → detquantlib-5.10.2}/detquantlib/dates/__init__.py +0 -0
  24. {detquantlib-5.10.0 → detquantlib-5.10.2}/detquantlib/dates/dates.py +0 -0
  25. {detquantlib-5.10.0 → detquantlib-5.10.2}/detquantlib/figures/__init__.py +0 -0
  26. {detquantlib-5.10.0 → detquantlib-5.10.2}/detquantlib/figures/plotly_figures.py +0 -0
  27. {detquantlib-5.10.0 → detquantlib-5.10.2}/detquantlib/forecasting/__init__.py +0 -0
  28. {detquantlib-5.10.0 → detquantlib-5.10.2}/detquantlib/forecasting/forecasting.py +0 -0
  29. {detquantlib-5.10.0 → detquantlib-5.10.2}/detquantlib/outputs/__init__.py +0 -0
  30. {detquantlib-5.10.0 → detquantlib-5.10.2}/detquantlib/outputs/outputs_interface.py +0 -0
  31. {detquantlib-5.10.0 → detquantlib-5.10.2}/detquantlib/snapshot_testing/README.md +0 -0
  32. {detquantlib-5.10.0 → detquantlib-5.10.2}/detquantlib/snapshot_testing/__init__.py +0 -0
  33. {detquantlib-5.10.0 → detquantlib-5.10.2}/detquantlib/snapshot_testing/cases.py +0 -0
  34. {detquantlib-5.10.0 → detquantlib-5.10.2}/detquantlib/snapshot_testing/comparison.py +0 -0
  35. {detquantlib-5.10.0 → detquantlib-5.10.2}/detquantlib/snapshot_testing/config.py +0 -0
  36. {detquantlib-5.10.0 → detquantlib-5.10.2}/detquantlib/snapshot_testing/errors.py +0 -0
  37. {detquantlib-5.10.0 → detquantlib-5.10.2}/detquantlib/snapshot_testing/isolation.py +0 -0
  38. {detquantlib-5.10.0 → detquantlib-5.10.2}/detquantlib/snapshot_testing/pytest_plugin.py +0 -0
  39. {detquantlib-5.10.0 → detquantlib-5.10.2}/detquantlib/snapshot_testing/recording.py +0 -0
  40. {detquantlib-5.10.0 → detquantlib-5.10.2}/detquantlib/snapshot_testing/tables.py +0 -0
  41. {detquantlib-5.10.0 → detquantlib-5.10.2}/detquantlib/stats/__init__.py +0 -0
  42. {detquantlib-5.10.0 → detquantlib-5.10.2}/detquantlib/stats/data_analysis.py +0 -0
  43. {detquantlib-5.10.0 → detquantlib-5.10.2}/detquantlib/tradable_products/__init__.py +0 -0
  44. {detquantlib-5.10.0 → detquantlib-5.10.2}/detquantlib/tradable_products/tradable_products.py +0 -0
  45. {detquantlib-5.10.0 → detquantlib-5.10.2}/detquantlib/utils/__init__.py +0 -0
  46. {detquantlib-5.10.0 → detquantlib-5.10.2}/detquantlib/utils/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: detquantlib
3
- Version: 5.10.0
3
+ Version: 5.10.2
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
@@ -254,7 +254,6 @@ class AzureClient:
254
254
  database = self._resolve_database(database)
255
255
  if database not in self._engines:
256
256
  conn_str = self._to_connection_string(self._connection_params(database))
257
- logger.info("Connecting to Azure SQL database %s on %s", database, self.config.server)
258
257
  self._engines[database] = create_engine(
259
258
  "mssql+pyodbc://",
260
259
  creator=lambda: pyodbc.connect(conn_str),
@@ -268,9 +267,8 @@ class AzureClient:
268
267
 
269
268
  Safe to call multiple times, or when no connection was ever opened.
270
269
  """
271
- for database, engine in self._engines.items():
270
+ for engine in self._engines.values():
272
271
  engine.dispose()
273
- logger.info("Azure SQL connection to database %s closed", database)
274
272
  self._engines = {}
275
273
 
276
274
  def __enter__(self) -> "AzureClient":
@@ -22,6 +22,7 @@ from snowflake.connector.errors import Error as SnowflakeError
22
22
  from snowflake.connector.pandas_tools import write_pandas
23
23
 
24
24
  logger = logging.getLogger(__name__)
25
+ logging.getLogger("snowflake.connector").setLevel(logging.WARNING)
25
26
 
26
27
  _IDENTIFIER_RE = re.compile(r"^[A-Za-z_][A-Za-z0-9_$]*$")
27
28
 
@@ -225,7 +226,6 @@ class SnowflakeClient:
225
226
  The active Snowflake connection.
226
227
  """
227
228
  if self._conn is None or self._conn.is_closed():
228
- logger.info("Connecting to Snowflake account %s", self.config.account)
229
229
  self._conn = snowflake.connector.connect(**self._connection_params())
230
230
  return self._conn
231
231
 
@@ -237,7 +237,6 @@ class SnowflakeClient:
237
237
  """
238
238
  if self._conn is not None and not self._conn.is_closed():
239
239
  self._conn.close()
240
- logger.info("Snowflake connection closed")
241
240
  self._conn = None
242
241
 
243
242
  def __enter__(self) -> "SnowflakeClient":
@@ -286,7 +285,9 @@ class SnowflakeClient:
286
285
  Args:
287
286
  sql: The SQL statement to execute.
288
287
  params: Values bound to the placeholders in ``sql``: a sequence for positional ``%s``
289
- placeholders, or a dict for named ``%(name)s`` placeholders.
288
+ placeholders, or a dict for named ``%(name)s`` placeholders. pandas Timestamps
289
+ are converted to Python datetimes (and ``NaT`` to ``None``), since the connector
290
+ cannot bind them.
290
291
  lowercase_columns: If ``True``, column names in the result are converted to lowercase
291
292
  (Snowflake returns them uppercase by default).
292
293
 
@@ -299,7 +300,7 @@ class SnowflakeClient:
299
300
  conn = self.connect()
300
301
  try:
301
302
  with conn.cursor() as cur:
302
- cur.execute(sql, params)
303
+ cur.execute(sql, self._prepare_params(params))
303
304
  df = cur.fetch_pandas_all()
304
305
  except SnowflakeError as e:
305
306
  logger.error("Query failed: %s", e)
@@ -329,7 +330,7 @@ class SnowflakeClient:
329
330
  """
330
331
  conn = self.connect()
331
332
  with conn.cursor() as cur:
332
- cur.execute(sql, params)
333
+ cur.execute(sql, self._prepare_params(params))
333
334
  for batch in cur.fetch_pandas_batches():
334
335
  yield batch
335
336
 
@@ -492,7 +493,7 @@ class SnowflakeClient:
492
493
  conn = self.connect()
493
494
  try:
494
495
  with conn.cursor() as cur:
495
- cur.execute(sql, params)
496
+ cur.execute(sql, self._prepare_params(params))
496
497
  rowcount = cur.rowcount
497
498
  except SnowflakeError as e:
498
499
  logger.error("Statement failed: %s", e)
@@ -500,6 +501,39 @@ class SnowflakeClient:
500
501
  logger.info("Statement affected %s rows", rowcount)
501
502
  return rowcount
502
503
 
504
+ @staticmethod
505
+ def _prepare_params(
506
+ params: Optional[Union[Sequence[Any], dict[str, Any]]],
507
+ ) -> Optional[Union[tuple[Any, ...], dict[str, Any]]]:
508
+ """
509
+ Converts parameter values that the Snowflake connector cannot bind.
510
+
511
+ The connector picks a converter by type name, so it rejects pandas Timestamps even though
512
+ they are datetime subclasses. They are converted to Python datetimes, and ``NaT`` to
513
+ ``None``. Note that Python datetimes only have microsecond precision, so nanoseconds are
514
+ dropped (pandas warns when this happens). Other values are passed through unchanged.
515
+
516
+ Args:
517
+ params: Values bound to the placeholders of a query, as in ``query()``.
518
+
519
+ Returns:
520
+ The converted values, as a dict for named placeholders or a tuple for positional
521
+ ones, or ``None`` if ``params`` is ``None``.
522
+ """
523
+ if params is None:
524
+ return None
525
+
526
+ def convert(value: Any) -> Any:
527
+ if value is pd.NaT:
528
+ return None
529
+ if isinstance(value, pd.Timestamp):
530
+ return value.to_pydatetime()
531
+ return value
532
+
533
+ if isinstance(params, dict):
534
+ return {k: convert(v) for k, v in params.items()}
535
+ return tuple(convert(v) for v in params)
536
+
503
537
  @staticmethod
504
538
  def _check_identifier(name: str) -> str:
505
539
  """
@@ -29,6 +29,7 @@ def setup_logger(log_dir: Path):
29
29
  fmt=(
30
30
  "%(log_color)s%(asctime)-8s%(reset)s "
31
31
  "%(log_color)s[%(levelname)s]%(reset)s "
32
+ "%(log_color)s[%(name)s]%(reset)s "
32
33
  "%(log_color)s%(message)s"
33
34
  ),
34
35
  datefmt="%Y-%m-%d %H:%M:%S",
@@ -40,7 +41,7 @@ def setup_logger(log_dir: Path):
40
41
 
41
42
  # File handler (plain, no colors)
42
43
  file_formatter = logging.Formatter(
43
- fmt="%(asctime)s [%(levelname)s] %(message)s",
44
+ fmt="%(asctime)s [%(levelname)s] [%(name)s] %(message)s",
44
45
  datefmt="%Y-%m-%d %H:%M:%S",
45
46
  )
46
47
  file_handler = logging.FileHandler(log_dir, mode="a", encoding="utf-8")
@@ -1,6 +1,6 @@
1
1
  [tool.poetry]
2
2
  name = "detquantlib"
3
- version = "5.10.0"
3
+ version = "5.10.2"
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