Hydro-Quebec-API-Wrapper 4.2.4__tar.gz → 4.2.6__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 (106) hide show
  1. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/.gitlab-ci.yml +2 -2
  2. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/Hydro_Quebec_API_Wrapper.egg-info/PKG-INFO +7 -7
  3. hydro_quebec_api_wrapper-4.2.6/Hydro_Quebec_API_Wrapper.egg-info/requires.txt +5 -0
  4. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/PKG-INFO +7 -7
  5. hydro_quebec_api_wrapper-4.2.6/hydroqc/hydro_api/cache.py +50 -0
  6. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/hydroqc/hydro_api/client.py +53 -5
  7. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/pyproject.toml +1 -1
  8. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/setup.cfg +5 -5
  9. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/test_requirements.txt +4 -3
  10. hydro_quebec_api_wrapper-4.2.4/Hydro_Quebec_API_Wrapper.egg-info/requires.txt +0 -5
  11. hydro_quebec_api_wrapper-4.2.4/hydroqc/hydro_api/cache.py +0 -30
  12. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/.coveragerc +0 -0
  13. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/.devcontainer/Dockerfile +0 -0
  14. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/.devcontainer/devcontainer.json +0 -0
  15. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/.flake8 +0 -0
  16. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/.gitignore +0 -0
  17. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/.pre-commit-config.yaml +0 -0
  18. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/.pylintrc +0 -0
  19. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/Hydro_Quebec_API_Wrapper.egg-info/SOURCES.txt +0 -0
  20. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/Hydro_Quebec_API_Wrapper.egg-info/dependency_links.txt +0 -0
  21. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/Hydro_Quebec_API_Wrapper.egg-info/entry_points.txt +0 -0
  22. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/Hydro_Quebec_API_Wrapper.egg-info/top_level.txt +0 -0
  23. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/LICENSE +0 -0
  24. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/MANIFEST.in +0 -0
  25. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/README.md +0 -0
  26. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/config.default.yaml +0 -0
  27. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/conflict_test_requirements.txt +0 -0
  28. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/docs/Makefile +0 -0
  29. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/docs/make.bat +0 -0
  30. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/docs/source/_static/custom.css +0 -0
  31. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/docs/source/_static/hydroqc-logo-128.png +0 -0
  32. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/docs/source/_static/hydroqc-logo.svg +0 -0
  33. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/docs/source/conf.py +0 -0
  34. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/docs/source/external/description-des-codes-interruption.pdf +0 -0
  35. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/docs/source/index.rst +0 -0
  36. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/docs/source/reference/hydroqc.contract.rst +0 -0
  37. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/docs/source/reference/hydroqc.hydro_api.rst +0 -0
  38. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/docs/source/reference/hydroqc.peak.cpc.rst +0 -0
  39. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/docs/source/reference/hydroqc.peak.dpc.rst +0 -0
  40. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/docs/source/reference/hydroqc.peak.rst +0 -0
  41. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/docs/source/reference/hydroqc.rst +0 -0
  42. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/docs/source/reference/hydroqc.types.rst +0 -0
  43. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/docs/source/reference/modules.rst +0 -0
  44. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/docs/source/todo.rst +0 -0
  45. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/examples/hydro.py +0 -0
  46. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/hydroqc/__init__.py +0 -0
  47. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/hydroqc/account.py +0 -0
  48. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/hydroqc/consts.py +0 -0
  49. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/hydroqc/contract/__init__.py +0 -0
  50. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/hydroqc/contract/common.py +0 -0
  51. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/hydroqc/contract/contract_d.py +0 -0
  52. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/hydroqc/contract/contract_d_cpc.py +0 -0
  53. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/hydroqc/contract/contract_dpc.py +0 -0
  54. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/hydroqc/contract/contract_dt.py +0 -0
  55. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/hydroqc/contract/contract_m.py +0 -0
  56. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/hydroqc/contract/contract_m_gdp.py +0 -0
  57. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/hydroqc/contract/contract_residential.py +0 -0
  58. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/hydroqc/contract/outage.py +0 -0
  59. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/hydroqc/customer.py +0 -0
  60. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/hydroqc/diagnostic.py +0 -0
  61. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/hydroqc/error.py +0 -0
  62. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/hydroqc/hydro_api/__init__.py +0 -0
  63. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/hydroqc/hydro_api/consts.py +0 -0
  64. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/hydroqc/logger.py +0 -0
  65. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/hydroqc/peak/__init__.py +0 -0
  66. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/hydroqc/peak/basepeak.py +0 -0
  67. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/hydroqc/peak/basepeakhandler.py +0 -0
  68. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/hydroqc/peak/consts.py +0 -0
  69. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/hydroqc/peak/cpc/__init__.py +0 -0
  70. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/hydroqc/peak/cpc/consts.py +0 -0
  71. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/hydroqc/peak/cpc/handler.py +0 -0
  72. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/hydroqc/peak/cpc/peak.py +0 -0
  73. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/hydroqc/peak/dpc/__init__.py +0 -0
  74. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/hydroqc/peak/dpc/consts.py +0 -0
  75. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/hydroqc/peak/dpc/handler.py +0 -0
  76. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/hydroqc/peak/dpc/peak.py +0 -0
  77. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/hydroqc/public_client.py +0 -0
  78. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/hydroqc/py.typed +0 -0
  79. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/hydroqc/timerange.py +0 -0
  80. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/hydroqc/types/__init__.py +0 -0
  81. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/hydroqc/types/account.py +0 -0
  82. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/hydroqc/types/common.py +0 -0
  83. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/hydroqc/types/consump.py +0 -0
  84. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/hydroqc/types/contract.py +0 -0
  85. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/hydroqc/types/cpc.py +0 -0
  86. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/hydroqc/types/dpc.py +0 -0
  87. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/hydroqc/types/dt.py +0 -0
  88. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/hydroqc/types/outage.py +0 -0
  89. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/hydroqc/utils.py +0 -0
  90. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/hydroqc/webuser.py +0 -0
  91. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/logo/hydroqc-logo-128.png +0 -0
  92. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/logo/hydroqc-logo.svg +0 -0
  93. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/renovate.json5 +0 -0
  94. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/tests/__init__.py +0 -0
  95. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/tests/conftest.py +0 -0
  96. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/tests/subtest_common.py +0 -0
  97. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/tests/subtest_contract_d.py +0 -0
  98. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/tests/subtest_contract_d_cpc.py +0 -0
  99. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/tests/subtest_contract_dpc.py +0 -0
  100. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/tests/subtest_contract_dt.py +0 -0
  101. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/tests/subtest_contract_m.py +0 -0
  102. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/tests/subtest_contract_m_gdp.py +0 -0
  103. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/tests/test_basic.py +0 -0
  104. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/tests/test_timerange.py +0 -0
  105. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/tests/tools.py +0 -0
  106. {hydro_quebec_api_wrapper-4.2.4 → hydro_quebec_api_wrapper-4.2.6}/tox.ini +0 -0
@@ -29,8 +29,8 @@ renovate-config-validator:
29
29
 
30
30
  test:
31
31
  stage: test
32
- tags:
33
- - hydroqc
32
+ # tags:
33
+ # - hydroqc
34
34
  image: registry.gitlab.com/hydroqc/hydroqc-base-container/3.13:latest
35
35
  script:
36
36
  - tox -e pylint,flake8,pydocstyle,typing,black,deps-conflict --parallel auto
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Hydro-Quebec-API-Wrapper
3
- Version: 4.2.4
3
+ Version: 4.2.6
4
4
  Summary: A wrapper library to access hydro quebec API and more
5
5
  Home-page: https://hydroqc.ca
6
6
  Author-email: Hydroqc Team <info@hydroqc.ca>
@@ -12,14 +12,14 @@ Keywords: hydroquebec,lib
12
12
  Platform: any
13
13
  Classifier: Programming Language :: Python :: 3
14
14
  Classifier: Operating System :: OS Independent
15
- Requires-Python: >=3.13.0
15
+ Requires-Python: >=3.12.0
16
16
  Description-Content-Type: text/markdown
17
17
  License-File: LICENSE
18
- Requires-Dist: aiohttp==3.13.2
19
- Requires-Dist: python-dateutil==2.9.0.post0
20
- Requires-Dist: pytz==2025.2
21
- Requires-Dist: aiocache==0.12.3
22
- Requires-Dist: pkce==1.0.3
18
+ Requires-Dist: aiohttp<4.0.0,>=3.10.0
19
+ Requires-Dist: python-dateutil>=2.8.0
20
+ Requires-Dist: pytz>=2024.1
21
+ Requires-Dist: aiocache>=0.12.0
22
+ Requires-Dist: pkce>=1.0.3
23
23
  Dynamic: license-file
24
24
 
25
25
  # HydroQC - Hydro Quebec API wrapper
@@ -0,0 +1,5 @@
1
+ aiohttp<4.0.0,>=3.10.0
2
+ python-dateutil>=2.8.0
3
+ pytz>=2024.1
4
+ aiocache>=0.12.0
5
+ pkce>=1.0.3
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Hydro-Quebec-API-Wrapper
3
- Version: 4.2.4
3
+ Version: 4.2.6
4
4
  Summary: A wrapper library to access hydro quebec API and more
5
5
  Home-page: https://hydroqc.ca
6
6
  Author-email: Hydroqc Team <info@hydroqc.ca>
@@ -12,14 +12,14 @@ Keywords: hydroquebec,lib
12
12
  Platform: any
13
13
  Classifier: Programming Language :: Python :: 3
14
14
  Classifier: Operating System :: OS Independent
15
- Requires-Python: >=3.13.0
15
+ Requires-Python: >=3.12.0
16
16
  Description-Content-Type: text/markdown
17
17
  License-File: LICENSE
18
- Requires-Dist: aiohttp==3.13.2
19
- Requires-Dist: python-dateutil==2.9.0.post0
20
- Requires-Dist: pytz==2025.2
21
- Requires-Dist: aiocache==0.12.3
22
- Requires-Dist: pkce==1.0.3
18
+ Requires-Dist: aiohttp<4.0.0,>=3.10.0
19
+ Requires-Dist: python-dateutil>=2.8.0
20
+ Requires-Dist: pytz>=2024.1
21
+ Requires-Dist: aiocache>=0.12.0
22
+ Requires-Dist: pkce>=1.0.3
23
23
  Dynamic: license-file
24
24
 
25
25
  # HydroQC - Hydro Quebec API wrapper
@@ -0,0 +1,50 @@
1
+ """Custon cache module."""
2
+
3
+ import functools
4
+ from collections.abc import Callable
5
+ from typing import ParamSpec, TypeVar, cast
6
+
7
+ from aiocache import Cache, cached # type: ignore
8
+
9
+ T = TypeVar("T")
10
+ P = ParamSpec("P")
11
+
12
+
13
+ class CCached(cached): # type: ignore[misc]
14
+ """Custom cache class based on aiocache.
15
+
16
+ This decorator respects the enable_cache attribute on the instance
17
+ (self) if it exists, allowing caching to be toggled at runtime.
18
+ """
19
+
20
+ def __init__(self, ttl: int) -> None:
21
+ """Initialize custom cache decorator.
22
+
23
+ Args:
24
+ ttl: Time to live for cached values in seconds.
25
+
26
+ """
27
+ cached.__init__(self, ttl=ttl, cache=Cache.MEMORY)
28
+ self.ttl = ttl
29
+
30
+ def __call__(self, func: Callable[P, T]) -> Callable[P, T]:
31
+ """Call custom cache decorator."""
32
+ self.cache = self._cache()
33
+
34
+ @functools.wraps(func)
35
+ async def wrapper(*args: P.args, **kwargs: P.kwargs) -> T:
36
+ # Check if the first argument (self) has enable_cache attribute
37
+ # If caching is disabled, call the function directly
38
+ if args and hasattr(args[0], "enable_cache") and not args[0].enable_cache:
39
+ result = func(*args, **kwargs)
40
+ # func is async, so we need to await it
41
+ if hasattr(result, "__await__"):
42
+ return await result # type: ignore[no-any-return]
43
+ return result # pragma: no cover
44
+
45
+ # Otherwise use the cached decorator
46
+ value = await self.decorator(func, *args, **kwargs)
47
+ return cast(T, value)
48
+
49
+ wrapper.cache = self.cache # type: ignore[attr-defined]
50
+ return cast(Callable[P, T], wrapper)
@@ -91,13 +91,27 @@ class HydroClient:
91
91
  session: aiohttp.ClientSession | None = None,
92
92
  log_level: str | None = "INFO",
93
93
  diag_folder: str | None = None,
94
+ enable_cache: bool = True,
94
95
  ):
95
- """Initialize the client object."""
96
+ """Initialize the client object.
97
+
98
+ Args:
99
+ username: Hydro-Québec account username.
100
+ password: Hydro-Québec account password.
101
+ timeout: HTTP request timeout in seconds. Defaults to REQUESTS_TIMEOUT.
102
+ verify_ssl: Whether to verify SSL certificates. Defaults to True.
103
+ session: Optional aiohttp ClientSession to reuse. Defaults to None.
104
+ log_level: Logging level. Defaults to "INFO".
105
+ diag_folder: Optional folder path for diagnostic output. Defaults to None.
106
+ enable_cache: Whether to enable response caching. Defaults to True.
107
+
108
+ """
96
109
  self.username: str = username
97
110
  self.password: str = password
98
111
  self._timeout: int = timeout
99
112
  self._session: aiohttp.ClientSession | None = session
100
113
  self._verify_ssl: bool = verify_ssl
114
+ self.enable_cache: bool = enable_cache
101
115
 
102
116
  # OAuth handling variables
103
117
  self._id_token: str = ""
@@ -113,7 +127,10 @@ class HydroClient:
113
127
  self._diag_id: int = 0
114
128
  self.guid: str = str(uuid.uuid1())
115
129
  self._logger: logging.Logger = get_logger("httpclient", log_level)
116
- self._logger.debug("HydroQc initialized")
130
+ self._logger.debug(
131
+ "HydroQc initialized (caching: %s)",
132
+ "enabled" if enable_cache else "disabled",
133
+ )
117
134
  # Cache for blocking operations (initialized lazily)
118
135
  self._cached_version: str | None = None
119
136
  self.reset()
@@ -247,8 +264,26 @@ class HydroClient:
247
264
  try:
248
265
  return json.loads(data)
249
266
  except json.decoder.JSONDecodeError as exp:
250
- self._logger.debug("JSON received : ", data)
251
- raise HydroQcHTTPError("Bad JSON format") from exp
267
+ # Log the actual response for debugging
268
+ data_preview = (
269
+ (
270
+ data[:500]
271
+ if isinstance(data, str)
272
+ else data[:500].decode("utf-8", errors="replace")
273
+ )
274
+ if data
275
+ else "(empty response)"
276
+ )
277
+ self._logger.error(
278
+ "Failed to parse JSON response. Position: line %d column %d. "
279
+ "Response preview (first 500 chars): %s",
280
+ exp.lineno,
281
+ exp.colno,
282
+ data_preview,
283
+ )
284
+ raise HydroQcHTTPError(
285
+ f"Bad JSON format at line {exp.lineno} column {exp.colno}: {exp.msg}"
286
+ ) from exp
252
287
 
253
288
  def get_token_data(self) -> IDTokenTyping | None:
254
289
  """Decode id token data."""
@@ -776,7 +811,20 @@ class HydroClient:
776
811
  HOURLY_CONSUMPTION_API_URL, "get", params=params
777
812
  )
778
813
  # We can not use res.json() because the response header are not application/json
779
- data: ConsumpHourlyTyping = self._load_json(await api_call_response.text())
814
+ response_text = await api_call_response.text()
815
+
816
+ # Check if response is empty or whitespace only
817
+ if not response_text or not response_text.strip():
818
+ self._logger.warning(
819
+ "Empty response received for hourly consumption on %s. "
820
+ "Data may not be available yet from Hydro-Québec.",
821
+ date_wanted.isoformat(),
822
+ )
823
+ raise HydroQcHTTPError(
824
+ f"No data available for date {date_wanted.isoformat()}"
825
+ )
826
+
827
+ data: ConsumpHourlyTyping = self._load_json(response_text)
780
828
  return data
781
829
 
782
830
  @CCached(ttl=43200)
@@ -21,7 +21,7 @@ classifiers = [
21
21
  ]
22
22
  keywords = ["hydroquebec", "lib"]
23
23
 
24
- requires-python = ">=3.13.0"
24
+ requires-python = ">=3.12.0"
25
25
 
26
26
  [project.urls]
27
27
  "Source Code" = "https://gitlab.com/hydroqc/hydroqc"
@@ -3,11 +3,11 @@ url = https://hydroqc.ca
3
3
 
4
4
  [options]
5
5
  install_requires =
6
- aiohttp==3.13.2
7
- python-dateutil==2.9.0.post0
8
- pytz==2025.2
9
- aiocache==0.12.3
10
- pkce==1.0.3
6
+ aiohttp>=3.10.0,<4.0.0
7
+ python-dateutil>=2.8.0
8
+ pytz>=2024.1
9
+ aiocache>=0.12.0
10
+ pkce>=1.0.3
11
11
 
12
12
  [egg_info]
13
13
  tag_build =
@@ -1,14 +1,15 @@
1
1
  flake8==7.3.0
2
2
  pydocstyle==6.3.0
3
3
  parameterized==0.9.0
4
- pylint==4.0.3
5
- pytest==9.0.1
4
+ pylint==4.0.4
5
+ pytest==9.0.2
6
6
  pytest-cov==7.0.0
7
7
  pytest-asyncio==1.3.0
8
- black==25.11.0
8
+ black==25.12.0
9
9
  mypy==1.19.0
10
10
  types-pytz==2025.2.0.20251108
11
11
  types-PyYAML==6.0.12.20250915
12
12
  types-python-dateutil==2.9.0.20251115
13
+ homeassistant-stubs==2025.12.2
13
14
  pyright==1.1.407
14
15
  lxml==6.0.2
@@ -1,5 +0,0 @@
1
- aiohttp==3.13.2
2
- python-dateutil==2.9.0.post0
3
- pytz==2025.2
4
- aiocache==0.12.3
5
- pkce==1.0.3
@@ -1,30 +0,0 @@
1
- """Custon cache module."""
2
-
3
- import functools
4
- from collections.abc import Callable
5
- from typing import ParamSpec, TypeVar, cast
6
-
7
- from aiocache import Cache, cached # type: ignore
8
-
9
- T = TypeVar("T")
10
- P = ParamSpec("P")
11
-
12
-
13
- class CCached(cached): # type: ignore[misc]
14
- """Custom cache class based on aiocache."""
15
-
16
- def __init__(self, ttl: int) -> None:
17
- """Initialize custom cache decorator."""
18
- cached.__init__(self, ttl=ttl, cache=Cache.MEMORY)
19
-
20
- def __call__(self, func: Callable[P, T]) -> Callable[P, T]:
21
- """Call custom cache decorator."""
22
- self.cache = self._cache()
23
-
24
- @functools.wraps(func)
25
- async def wrapper(*args: P.args, **kwargs: P.kwargs) -> T:
26
- value = await self.decorator(func, *args, **kwargs)
27
- return cast(T, value)
28
-
29
- wrapper.cache = self.cache # type: ignore[attr-defined]
30
- return cast(Callable[P, T], wrapper)