beancount-gocardless 0.1.7__tar.gz → 0.1.8__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: beancount-gocardless
3
- Version: 0.1.7
3
+ Version: 0.1.8
4
4
  Summary:
5
5
  License: MIT
6
6
  Requires-Python: >=3.12
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "beancount-gocardless"
3
- version = "0.1.7"
3
+ version = "0.1.8"
4
4
  description = ""
5
5
  authors = []
6
6
  readme = "README.md"
@@ -7,22 +7,24 @@ import logging
7
7
  logger = logging.getLogger(__name__)
8
8
 
9
9
 
10
- def cleanup_headers(response):
10
+ def strip_headers_hook(response, *args, **kwargs):
11
11
  to_preserve = [
12
12
  "Content-Type",
13
13
  "Date",
14
14
  "Content-Encoding",
15
15
  "Content-Language",
16
- "ETag",
17
16
  "Last-Modified",
18
17
  ]
19
18
  deleted = set()
20
19
  to_preserve_lower = [h.lower() for h in to_preserve]
21
- for header in response.headers.keys():
22
- if header.lower() not in to_preserve_lower:
23
- del response.headers[header]
20
+ header_keys_to_check = response.headers.copy().keys()
21
+ for header in header_keys_to_check:
22
+ if header.lower() in to_preserve_lower:
23
+ continue
24
+ else:
25
+ response.headers.pop(header, None)
24
26
  deleted.add(header)
25
- logger.info("Deleted headers: %s", ", ".join(deleted))
27
+ logger.debug("Deleted headers: %s", ", ".join(deleted))
26
28
  return response
27
29
 
28
30
 
@@ -39,7 +41,7 @@ class CacheOptions(TypedDict, total=False):
39
41
 
40
42
  cache_name: requests_cache.StrOrPath
41
43
  backend: Optional[requests_cache.BackendSpecifier]
42
- expire_after: requests_cache.ExpirationTime
44
+ expire_after: int
43
45
  old_data_on_error: bool
44
46
 
45
47
 
@@ -80,7 +82,6 @@ class BaseService:
80
82
  "old_data_on_error": True,
81
83
  "match_headers": False,
82
84
  "cache_control": False,
83
- "response_hook": cleanup_headers,
84
85
  }
85
86
 
86
87
  def __init__(
@@ -99,21 +100,78 @@ class BaseService:
99
100
  """
100
101
  self.secret_id = secret_id
101
102
  self.secret_key = secret_key
102
- self.token = None
103
+ self._token = None
103
104
  merged_options = {**self.DEFAULT_CACHE_OPTIONS, **(cache_options or {})}
104
105
  self.session = requests_cache.CachedSession(**merged_options)
106
+ self.session.hooks["response"].append(strip_headers_hook)
105
107
 
106
- def _ensure_token_valid(self):
108
+ def check_cache_status(self, method: str, url: str, params=None, data=None) -> dict:
107
109
  """
108
- Ensure a valid token exists. Gets a new token if one doesn't exist.
110
+ Attempts to predict the cache status for a given request.
111
+
112
+ NOTE: This is an approximation and relies on internal mechanisms
113
+ that might change. It also performs I/O to check the cache.
114
+
115
+ Args:
116
+ method (str): HTTP method ("GET", "POST", etc.).
117
+ endpoint (str): API endpoint.
118
+ params (dict, optional): URL parameters.
119
+ data (dict, optional): Request body data.
120
+
121
+ Returns:
122
+ dict: Information about the potential cache state:
123
+ {'key_exists': bool, 'is_expired': Optional[bool], 'cache_key': str}
124
+ 'is_expired' is None if the key doesn't exist or expiration
125
+ cannot be reliably determined without full retrieval.
126
+ """
127
+ headers = {"Authorization": f"Bearer {self.token}"}
128
+
129
+ req = requests.Request(method, url, params=params, data=data, headers=headers)
130
+ prepared_request: requests.PreparedRequest = self.session.prepare_request(req)
131
+ cache = self.session.cache
132
+ cache_key = cache.create_key(prepared_request)
133
+ key_exists = cache.contains(cache_key)
134
+ is_expired = None
135
+
136
+ if key_exists:
137
+ try:
138
+ # Try to get the response object without triggering expiration side effects
139
+ # Note: This still reads from the cache backend (I/O)
140
+ cached_response = cache.get_response(cache_key)
141
+ if cached_response:
142
+ # is_expired is a property calculated on the CachedResponse
143
+ is_expired = cached_response.is_expired
144
+ else:
145
+ # get_response might return None if item expired and configured to delete
146
+ # Or if backend consistency issue. Treat as expired/absent.
147
+ key_exists = False # Correct the state if get_response fails
148
+ is_expired = True # Assume expired if get_response returns None for existing key
149
+ except Exception as e:
150
+ logger.error(
151
+ f"Error checking expiration for cache key {cache_key}: {e}"
152
+ )
153
+ # Cannot determine expiration reliably
154
+ is_expired = None # Mark as unknown
155
+
156
+ return {
157
+ "key_exists": key_exists,
158
+ "is_expired": is_expired,
159
+ "cache_key": cache_key,
160
+ }
161
+
162
+ @property
163
+ def token(self):
164
+ """
165
+ Ensure a token exists. Gets a new token if one doesn't exist.
109
166
  Nordigen tokens don't currently have a refresh mechanism, so this just gets a new one if needed.
110
167
  """
111
- if not self.token:
168
+ if not self._token:
112
169
  self.get_token()
170
+ return self._token
113
171
 
114
172
  def get_token(self):
115
173
  """
116
- Fetch a new API access token using credentials. Sets the `self.token` attribute.
174
+ Fetch a new API access token using credentials. Sets the `self._token` attribute.
117
175
 
118
176
  Raises:
119
177
  HttpServiceException: If the API request fails.
@@ -123,7 +181,7 @@ class BaseService:
123
181
  data={"secret_id": self.secret_id, "secret_key": self.secret_key},
124
182
  )
125
183
  self._handle_response(response)
126
- self.token = response.json()["access"]
184
+ self._token = response.json()["access"]
127
185
 
128
186
  def _handle_response(self, response):
129
187
  """
@@ -157,9 +215,10 @@ class BaseService:
157
215
  HttpServiceException: If the API request fails.
158
216
  """
159
217
  url = f"{self.BASE_URL}{endpoint}"
160
- self._ensure_token_valid()
161
218
  headers = {"Authorization": f"Bearer {self.token}"}
162
219
 
220
+ status = self.check_cache_status(method, url, params, data)
221
+ logger.debug(f"{endpoint}: {'expired' if status["is_expired"] else 'cache ok'}")
163
222
  response = self.session.request(
164
223
  method, url, headers=headers, params=params, data=data
165
224
  )