e-data 2.0.2.dev135__tar.gz → 2.0.2.dev136__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 (43) hide show
  1. {e_data-2.0.2.dev135/e_data.egg-info → e_data-2.0.2.dev136}/PKG-INFO +1 -1
  2. {e_data-2.0.2.dev135 → e_data-2.0.2.dev136/e_data.egg-info}/PKG-INFO +1 -1
  3. {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/edata/database/controller.py +8 -1
  4. {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/edata/tests/test_controller.py +18 -1
  5. {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/pyproject.toml +1 -1
  6. {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/LICENSE +0 -0
  7. {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/MANIFEST.in +0 -0
  8. {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/README.md +0 -0
  9. {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/e_data.egg-info/SOURCES.txt +0 -0
  10. {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/e_data.egg-info/dependency_links.txt +0 -0
  11. {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/e_data.egg-info/requires.txt +0 -0
  12. {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/e_data.egg-info/top_level.txt +0 -0
  13. {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/edata/__init__.py +0 -0
  14. {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/edata/cli.py +0 -0
  15. {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/edata/core/__init__.py +0 -0
  16. {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/edata/core/completion.py +0 -0
  17. {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/edata/core/const.py +0 -0
  18. {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/edata/core/utils.py +0 -0
  19. {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/edata/database/__init__.py +0 -0
  20. {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/edata/database/migrations/__init__.py +0 -0
  21. {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/edata/database/migrations/base.py +0 -0
  22. {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/edata/database/migrations/legacy_json_1_3_3.py +0 -0
  23. {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/edata/database/models.py +0 -0
  24. {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/edata/database/queries.py +0 -0
  25. {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/edata/database/utils.py +0 -0
  26. {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/edata/models/__init__.py +0 -0
  27. {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/edata/models/bill.py +0 -0
  28. {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/edata/models/data.py +0 -0
  29. {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/edata/models/supply.py +0 -0
  30. {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/edata/providers/__init__.py +0 -0
  31. {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/edata/providers/datadis.py +0 -0
  32. {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/edata/providers/redata.py +0 -0
  33. {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/edata/services/bill_service.py +0 -0
  34. {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/edata/services/data_service.py +0 -0
  35. {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/edata/tests/__init__.py +0 -0
  36. {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/edata/tests/test_completion.py +0 -0
  37. {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/edata/tests/test_datadis_connector.py +0 -0
  38. {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/edata/tests/test_incremental.py +0 -0
  39. {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/edata/tests/test_migrations.py +0 -0
  40. {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/edata/tests/test_redata_connector.py +0 -0
  41. {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/edata/tests/test_services.py +0 -0
  42. {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/edata/tests/test_utils.py +0 -0
  43. {e_data-2.0.2.dev135 → e_data-2.0.2.dev136}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: e-data
3
- Version: 2.0.2.dev135
3
+ Version: 2.0.2.dev136
4
4
  Summary: Python library for managing spanish energy data from various web providers
5
5
  Author-email: VMG <vmayorg@outlook.es>
6
6
  License: GNU GENERAL PUBLIC LICENSE
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: e-data
3
- Version: 2.0.2.dev135
3
+ Version: 2.0.2.dev136
4
4
  Summary: Python library for managing spanish energy data from various web providers
5
5
  Author-email: VMG <vmayorg@outlook.es>
6
6
  License: GNU GENERAL PUBLIC LICENSE
@@ -1,3 +1,4 @@
1
+ import asyncio
1
2
  import logging
2
3
  import os
3
4
  import typing
@@ -93,6 +94,7 @@ class EdataDB:
93
94
  cls._engine = create_async_engine(db_url, future=True)
94
95
  event.listen(cls._engine.sync_engine, "connect", _set_sqlite_pragmas)
95
96
  cls._instance._tables_initialized = False
97
+ cls._instance._tables_lock = asyncio.Lock()
96
98
  elif db_url != cls._db_url:
97
99
  raise ValueError("EdataDB already initialized with a different db_url")
98
100
  return cls._instance
@@ -108,7 +110,12 @@ class EdataDB:
108
110
 
109
111
  if self._tables_initialized:
110
112
  return
111
- if self.engine:
113
+ # concurrent first calls (e.g. several websocket requests right after
114
+ # startup) would otherwise all see the index missing and race to create
115
+ # it, failing with "index ... already exists"
116
+ async with self._tables_lock:
117
+ if self._tables_initialized or not self.engine:
118
+ return
112
119
  async with self.engine.begin() as conn:
113
120
  await conn.run_sync(SQLModel.metadata.create_all)
114
121
  await conn.run_sync(_create_missing_indexes)
@@ -1,5 +1,6 @@
1
- """Bulk upsert tests for the database controller."""
1
+ """Tests for the database controller."""
2
2
 
3
+ import asyncio
3
4
  from collections.abc import AsyncIterator
4
5
  from datetime import datetime, timedelta
5
6
 
@@ -89,3 +90,19 @@ async def test_add_bill_list_applies_overrides(db: EdataDB) -> None:
89
90
  stored = await db.list_bill(CUPS, "hour")
90
91
  assert len(stored) == 3
91
92
  assert all(x.complete and x.confhash == "hash-b" for x in stored)
93
+
94
+
95
+ @pytest.mark.asyncio
96
+ async def test_concurrent_first_calls_do_not_race_index_creation(db: EdataDB) -> None:
97
+ # simulate a database created before the index existed, opened fresh
98
+ async with db.engine.begin() as conn:
99
+ await conn.exec_driver_sql("DROP INDEX ix_energy_cups_datetime")
100
+ db._tables_initialized = False
101
+
102
+ await asyncio.gather(*(db.get_last_energy(CUPS) for _ in range(10)))
103
+
104
+ async with db.engine.connect() as conn:
105
+ result = await conn.exec_driver_sql(
106
+ "SELECT name FROM sqlite_master WHERE name='ix_energy_cups_datetime'"
107
+ )
108
+ assert result.first() is not None
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "e-data"
7
- version = "2.0.2.dev135"
7
+ version = "2.0.2.dev136"
8
8
  description = "Python library for managing spanish energy data from various web providers"
9
9
  readme = "README.md"
10
10
  authors = [
File without changes
File without changes
File without changes
File without changes