Hydro-Quebec-API-Wrapper 4.2.2__py3-none-any.whl → 4.2.3__py3-none-any.whl

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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: Hydro-Quebec-API-Wrapper
3
- Version: 4.2.2
3
+ Version: 4.2.3
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>
@@ -1,4 +1,4 @@
1
- hydro_quebec_api_wrapper-4.2.2.dist-info/licenses/LICENSE,sha256=46mU2C5kSwOnkqkw9XQAJlhBL2JAf1_uCD8lVcXyMRg,7652
1
+ hydro_quebec_api_wrapper-4.2.3.dist-info/licenses/LICENSE,sha256=46mU2C5kSwOnkqkw9XQAJlhBL2JAf1_uCD8lVcXyMRg,7652
2
2
  hydroqc/__init__.py,sha256=sFhCh7-LWoISCQuAtdrLQOmj2x5wjHBGbAAPg4SN6Pc,40
3
3
  hydroqc/account.py,sha256=84PO2npB_VG8cEzGIbmXj9sD1whG8qynkiGd-yEYddM,3375
4
4
  hydroqc/consts.py,sha256=a9dWq5i6t5VedYosUbP8zIuG2q5TPw4ruT0jOaC8Yo8,209
@@ -15,7 +15,7 @@ hydroqc/contract/__init__.py,sha256=uqxjOgyhpKFd-n_umxdaW5FB_DkJJaettWnEFC0nRYc,
15
15
  hydroqc/contract/common.py,sha256=xVw9z_Yjqp95SGCPNAfJVABvgp_C-KM51VTBEU9wjeM,15705
16
16
  hydroqc/contract/contract_d.py,sha256=6jPYTCjFq8zonfbXPrEujDVz58n5RUEuq54VCGqph6o,856
17
17
  hydroqc/contract/contract_d_cpc.py,sha256=-Y2S4MkzAfsbf_L2jCKfU7OD-nbzgoEpyT3SWh0U0tQ,1418
18
- hydroqc/contract/contract_dpc.py,sha256=JNWZqpALuw1kNQhZXN4ql8KiEU3OeJteHLU4d489pdA,7579
18
+ hydroqc/contract/contract_dpc.py,sha256=6XMj2gq4oWvfekwqHvbnivWhL5pk7HTP4F2wOfdKi3A,7271
19
19
  hydroqc/contract/contract_dt.py,sha256=TSKf2oh-MlG8A42xVUCCyf7oezlWTFoyATF9xlQn2JE,1687
20
20
  hydroqc/contract/contract_m.py,sha256=2hSSb9Tw2eZu7PV1XicfTFKLCRiffq72NfOYiVz8QMk,2108
21
21
  hydroqc/contract/contract_m_gdp.py,sha256=y8seZHmjkkul0TZ5Epxuxc4g0JVbs9o8KjL_y1gIldU,801
@@ -23,7 +23,7 @@ hydroqc/contract/contract_residential.py,sha256=v88mWvJsJP7FsY5cHmr0ZYYT_S-6xtSk
23
23
  hydroqc/contract/outage.py,sha256=K5XX0enSKMVpL-JCkWsykuKUw-5_GcSJLzcSR1SqQDk,3979
24
24
  hydroqc/hydro_api/__init__.py,sha256=bIIjTkhQcXG7kn4cQr_Q6tFSKGjAx-AXLjMMlRCjclY,32
25
25
  hydroqc/hydro_api/cache.py,sha256=mC3UO246V5zJ1p89nUb5EzQon_4tQGUgFMZKy4YY8I8,912
26
- hydroqc/hydro_api/client.py,sha256=8yKiAFE3-gdiXGZSuw11Cydpa8rX_39LAWr51yvtsvQ,36706
26
+ hydroqc/hydro_api/client.py,sha256=hZLddlsN5_H-jBL7Tt5w1ASofC9Yza9_kWHL116zVXI,36958
27
27
  hydroqc/hydro_api/consts.py,sha256=XDNOjZVpC-Z7rsOL4GDgfexb7m8zc7HiafiKGiFvoUI,4607
28
28
  hydroqc/peak/__init__.py,sha256=ye2KbWXhJEHZnBDaFylWioy3W-mzJw_lDplsJNAapNs,19
29
29
  hydroqc/peak/basepeak.py,sha256=DMXjZXyNRHWnuhB0Vp3ZINGhNQngG7AU15cDLajmC7M,1954
@@ -46,8 +46,8 @@ hydroqc/types/cpc.py,sha256=ghmtBTT1FKuqVodJJ0kCtUbYNTLCOrngxgcrzjJYvok,2116
46
46
  hydroqc/types/dpc.py,sha256=mwA5T9fGcNTA-kb_f25wq2ALJCwa5lKABOS73L6znY0,1981
47
47
  hydroqc/types/dt.py,sha256=rEXDgNewhlOsRbso0Shlq69TvJFoT56EGYOAjTFZU80,693
48
48
  hydroqc/types/outage.py,sha256=XTsNyu8mmxUx44XWvOOUHKSoijp-lgbXwQroiqm9S0o,3932
49
- hydro_quebec_api_wrapper-4.2.2.dist-info/METADATA,sha256=pHB876NvBQYDH2R0wTvKQmTaMeyTTLmR5RLjm4InHRQ,7948
50
- hydro_quebec_api_wrapper-4.2.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
51
- hydro_quebec_api_wrapper-4.2.2.dist-info/entry_points.txt,sha256=G2YKkCH9rFt2dXbFyHINBp4bmqY2HTEIaf7F1CwbC_k,57
52
- hydro_quebec_api_wrapper-4.2.2.dist-info/top_level.txt,sha256=YAcYLnu5YYkbxt1psUoBfo_opl-lAZKq0A7B3IN_P6M,8
53
- hydro_quebec_api_wrapper-4.2.2.dist-info/RECORD,,
49
+ hydro_quebec_api_wrapper-4.2.3.dist-info/METADATA,sha256=LlGJNtk6_0JhAKXOWd7sz8gxaUjNY_vg70EA7mho2g4,7948
50
+ hydro_quebec_api_wrapper-4.2.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
51
+ hydro_quebec_api_wrapper-4.2.3.dist-info/entry_points.txt,sha256=G2YKkCH9rFt2dXbFyHINBp4bmqY2HTEIaf7F1CwbC_k,57
52
+ hydro_quebec_api_wrapper-4.2.3.dist-info/top_level.txt,sha256=YAcYLnu5YYkbxt1psUoBfo_opl-lAZKq0A7B3IN_P6M,8
53
+ hydro_quebec_api_wrapper-4.2.3.dist-info/RECORD,,
@@ -21,7 +21,10 @@ class WinterState(str, Enum):
21
21
  def check_dpc_data_present(
22
22
  method: Callable[..., None | str | bool | float | int | date | WinterState],
23
23
  ) -> Callable[..., None | str | bool | float | int | date | WinterState]:
24
- """Check if contractDPC's data are present."""
24
+ """Check if contractDPC's data are present.
25
+
26
+ Returns None if data is not available or incomplete.
27
+ """
25
28
 
26
29
  def wrapper(
27
30
  contract: "ContractDPC",
@@ -110,19 +113,6 @@ class ContractDPC(ContractResidential):
110
113
  )
111
114
  return self._dpc_data
112
115
 
113
- def _get_dpc_field(self, field_name: str) -> str | float | int:
114
- """Get DPC field from periodesHiver data.
115
-
116
- Raises:
117
- KeyError: If the required field is not present in the API response.
118
- """
119
- winter_period = self._dpc_data["periodesHiver"][0]
120
- if field_name not in winter_period:
121
- raise KeyError(
122
- f"Required field '{field_name}' not found in DPC periodesHiver data"
123
- )
124
- return winter_period[field_name] # type: ignore
125
-
126
116
  @property
127
117
  @check_period_data_present
128
118
  def cp_lower_price_consumption(self) -> float:
@@ -148,7 +138,7 @@ class ContractDPC(ContractResidential):
148
138
  @check_dpc_data_present
149
139
  def last_update_date(self) -> date:
150
140
  """DPC data last update."""
151
- date_str = str(self._get_dpc_field("dateMaj"))
141
+ date_str = str(self._dpc_data["periodesHiver"][0]["dateMaj"])
152
142
  # Handle ISO format with timezone
153
143
  if "T" in date_str:
154
144
  return date.fromisoformat(date_str.split("T", maxsplit=1)[0])
@@ -158,13 +148,13 @@ class ContractDPC(ContractResidential):
158
148
  @check_dpc_data_present
159
149
  def critical_called_hours(self) -> int:
160
150
  """Get number of critical hours."""
161
- return int(self._get_dpc_field("hrsCritiquesAppelees"))
151
+ return int(self._dpc_data["periodesHiver"][0]["hrsCritiquesAppelees"])
162
152
 
163
153
  @property
164
154
  @check_dpc_data_present
165
155
  def max_critical_called_hours(self) -> int:
166
156
  """Get max number of critical hours."""
167
- return int(self._get_dpc_field("hrsCritiquesAppeleesMax"))
157
+ return int(self._dpc_data["periodesHiver"][0]["hrsCritiquesAppeleesMax"])
168
158
 
169
159
  @property
170
160
  @check_dpc_data_present
@@ -173,10 +163,12 @@ class ContractDPC(ContractResidential):
173
163
 
174
164
  Returns None if comparison data is not yet available (e.g., first year on Flex D).
175
165
  """
176
- try:
177
- return float(self._get_dpc_field("montantEconPerteVSTarifBase"))
178
- except KeyError:
166
+ if (
167
+ self._dpc_data["periodesHiver"][0].get("montantEconPerteVSTarifBase")
168
+ is None
169
+ ):
179
170
  return None
171
+ return float(self._dpc_data["periodesHiver"][0]["montantEconPerteVSTarifBase"])
180
172
 
181
173
  @property
182
174
  @check_dpc_data_present
@@ -1,5 +1,6 @@
1
1
  """HydroQc Client Module."""
2
2
 
3
+ import asyncio
3
4
  import base64
4
5
  import csv
5
6
  import json
@@ -7,7 +8,6 @@ import logging
7
8
  import os
8
9
  import platform
9
10
  import re
10
- import ssl
11
11
  import uuid
12
12
  from collections.abc import Iterator
13
13
  from datetime import date, datetime, timedelta
@@ -114,6 +114,8 @@ class HydroClient:
114
114
  self.guid: str = str(uuid.uuid1())
115
115
  self._logger: logging.Logger = get_logger("httpclient", log_level)
116
116
  self._logger.debug("HydroQc initialized")
117
+ # Cache for blocking operations (initialized lazily)
118
+ self._cached_version: str | None = None
117
119
  self.reset()
118
120
 
119
121
  def reset(self) -> None:
@@ -128,20 +130,27 @@ class HydroClient:
128
130
  self._selected_customer = ""
129
131
  self._selected_contract = ""
130
132
 
131
- @property
132
- def user_agent(self) -> str:
133
+ async def _get_user_agent(self) -> str:
133
134
  """Get http client user_agent."""
134
- try:
135
- hydroqc_version = version("Hydro_Quebec_API_Wrapper")
136
- except PackageNotFoundError:
137
- # package is not installed
138
- self._logger.warning(
139
- "Python package `Hydro_Quebec_API_Wrapper` is not installed. "
140
- "Install it using pip"
141
- )
142
- hydroqc_version = "unkwown"
135
+ if self._cached_version is None:
136
+ # Run blocking version lookup in executor to avoid blocking event loop
137
+ loop = asyncio.get_event_loop()
138
+ try:
139
+ self._cached_version = await loop.run_in_executor(
140
+ None, version, "Hydro_Quebec_API_Wrapper"
141
+ )
142
+ except PackageNotFoundError:
143
+ # package is not installed
144
+ self._logger.warning(
145
+ "Python package `Hydro_Quebec_API_Wrapper` is not installed. "
146
+ "Install it using pip"
147
+ )
148
+ self._cached_version = "unkwown"
143
149
  os_name = platform.system()
144
- return f"Hydroqc/{hydroqc_version} (dev@hydroqc.ca; https://gitlab.com/hydroqc) {os_name}"
150
+ return (
151
+ f"Hydroqc/{self._cached_version} "
152
+ f"(dev@hydroqc.ca; https://gitlab.com/hydroqc) {os_name}"
153
+ )
145
154
 
146
155
  async def http_request(
147
156
  self,
@@ -163,15 +172,10 @@ class HydroClient:
163
172
  data = {}
164
173
  if headers is None:
165
174
  headers = {}
166
- headers["User-Agent"] = self.user_agent
175
+ headers["User-Agent"] = await self._get_user_agent()
167
176
  if verify_ssl is None:
168
177
  verify_ssl = self._verify_ssl
169
178
 
170
- ssl_context: ssl.SSLContext | None = None
171
- if verify_ssl:
172
- ssl_context = ssl.create_default_context()
173
- ssl_context.set_ciphers("DEFAULT") # Needed for python3.10
174
-
175
179
  # Query Diagnostic
176
180
  if self._diag_folder is not None:
177
181
  cleaned_url = url.replace("/", "_").replace(":", "").split("#", 1)[0]
@@ -205,7 +209,7 @@ class HydroClient:
205
209
  params=params,
206
210
  data=data,
207
211
  allow_redirects=allow_redirects,
208
- ssl=ssl_context,
212
+ ssl=verify_ssl,
209
213
  headers=headers,
210
214
  )
211
215