ee-client 2.2.0__tar.gz → 2.2.1__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 (30) hide show
  1. {ee_client-2.2.0 → ee_client-2.2.1}/PKG-INFO +1 -1
  2. {ee_client-2.2.0 → ee_client-2.2.1}/ee_client.egg-info/PKG-INFO +1 -1
  3. {ee_client-2.2.0 → ee_client-2.2.1}/eeclient/__init__.py +1 -1
  4. ee_client-2.2.1/eeclient/exceptions.py +100 -0
  5. {ee_client-2.2.0 → ee_client-2.2.1}/eeclient/sepal_credential_mixin.py +36 -7
  6. {ee_client-2.2.0 → ee_client-2.2.1}/pyproject.toml +2 -2
  7. ee_client-2.2.0/eeclient/exceptions.py +0 -26
  8. {ee_client-2.2.0 → ee_client-2.2.1}/LICENSE +0 -0
  9. {ee_client-2.2.0 → ee_client-2.2.1}/README.rst +0 -0
  10. {ee_client-2.2.0 → ee_client-2.2.1}/ee_client.egg-info/SOURCES.txt +0 -0
  11. {ee_client-2.2.0 → ee_client-2.2.1}/ee_client.egg-info/dependency_links.txt +0 -0
  12. {ee_client-2.2.0 → ee_client-2.2.1}/ee_client.egg-info/requires.txt +0 -0
  13. {ee_client-2.2.0 → ee_client-2.2.1}/ee_client.egg-info/top_level.txt +0 -0
  14. {ee_client-2.2.0 → ee_client-2.2.1}/eeclient/client.py +0 -0
  15. {ee_client-2.2.0 → ee_client-2.2.1}/eeclient/data.py +0 -0
  16. {ee_client-2.2.0 → ee_client-2.2.1}/eeclient/export/__init__.py +0 -0
  17. {ee_client-2.2.0 → ee_client-2.2.1}/eeclient/export/image.py +0 -0
  18. {ee_client-2.2.0 → ee_client-2.2.1}/eeclient/export/table.py +0 -0
  19. {ee_client-2.2.0 → ee_client-2.2.1}/eeclient/helpers.py +0 -0
  20. {ee_client-2.2.0 → ee_client-2.2.1}/eeclient/interfaces/__init__.py +0 -0
  21. {ee_client-2.2.0 → ee_client-2.2.1}/eeclient/interfaces/export.py +0 -0
  22. {ee_client-2.2.0 → ee_client-2.2.1}/eeclient/interfaces/operations.py +0 -0
  23. {ee_client-2.2.0 → ee_client-2.2.1}/eeclient/interfaces/tasks.py +0 -0
  24. {ee_client-2.2.0 → ee_client-2.2.1}/eeclient/models.py +0 -0
  25. {ee_client-2.2.0 → ee_client-2.2.1}/eeclient/oauth_app.py +0 -0
  26. {ee_client-2.2.0 → ee_client-2.2.1}/eeclient/tasks.py +0 -0
  27. {ee_client-2.2.0 → ee_client-2.2.1}/setup.cfg +0 -0
  28. {ee_client-2.2.0 → ee_client-2.2.1}/tests/test_client.py +0 -0
  29. {ee_client-2.2.0 → ee_client-2.2.1}/tests/test_data.py +0 -0
  30. {ee_client-2.2.0 → ee_client-2.2.1}/tests/test_models.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ee-client
3
- Version: 2.2.0
3
+ Version: 2.2.1
4
4
  Summary: extends the capabilities of the earthengine-api by providing custom session management and client interactions
5
5
  Author-email: Daniel Guerrero <dfgm2006@gmail.com>
6
6
  Project-URL: Homepage, https://github.com/dfguerrerom/ee-client
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ee-client
3
- Version: 2.2.0
3
+ Version: 2.2.1
4
4
  Summary: extends the capabilities of the earthengine-api by providing custom session management and client interactions
5
5
  Author-email: Daniel Guerrero <dfgm2006@gmail.com>
6
6
  Project-URL: Homepage, https://github.com/dfguerrerom/ee-client
@@ -1,6 +1,6 @@
1
1
  __title__ = "eeclient"
2
2
  __summary__ = "A client for Google Earth Engine"
3
- __version__ = "2.2.0"
3
+ __version__ = "2.2.1"
4
4
 
5
5
  __author__ = "Daniel Guerrero"
6
6
  __email__ = "dfgm2006@gmail.com"
@@ -0,0 +1,100 @@
1
+ from ee.ee_exception import EEException
2
+
3
+
4
+ class EERestException(EEException):
5
+ def __init__(self, error):
6
+ self.message = error.get("message", "EE responded with an error")
7
+ super().__init__(self.message)
8
+ self.code = error.get("code", -1)
9
+ self.status = error.get("status", "UNDEFINED")
10
+ self.details = error.get("details")
11
+
12
+
13
+ class EEClientError(Exception):
14
+ """Custom exception class for EEClient errors."""
15
+
16
+ def __init__(self, error):
17
+ if isinstance(error, str):
18
+ self.message = error
19
+ self.code = -1
20
+ self.status = "UNDEFINED"
21
+ self.details = None
22
+ else:
23
+ self.message = error.get("message", "EEClient responded with an error")
24
+ self.code = error.get("code", -1)
25
+ self.status = error.get("status", "UNDEFINED")
26
+ self.details = error.get("details")
27
+ super().__init__(self.message)
28
+
29
+
30
+ class CredentialsFileNotFoundError(EEClientError):
31
+ """Raised when local credentials file is not found."""
32
+
33
+ def __init__(self, file_path: str):
34
+ self.file_path = file_path
35
+ error = {
36
+ "code": 404,
37
+ "message": (
38
+ "There was an error when trying to authenticate with Google "
39
+ "Earth Engine. Make sure your GEE account is properly "
40
+ "connected to SEPAL. For more information, please visit "
41
+ "<a href='https://docs.sepal.io/en/latest/setup/gee.html' "
42
+ "target='_blank'> the documentation </a>."
43
+ ),
44
+ "status": "CREDENTIALS_NOT_FOUND",
45
+ "details": f"Credentials file not found at {file_path}",
46
+ }
47
+ super().__init__(error)
48
+
49
+
50
+ class CredentialsFileUnknownError(EEClientError):
51
+ """Raised when unknown error occurred while accessing credentials file."""
52
+
53
+ def __init__(self):
54
+ error = {
55
+ "code": 500,
56
+ "message": (
57
+ "There was an error when trying to authenticate with Google "
58
+ "Earth Engine. Please re-authenticate your GEE account with "
59
+ "SEPAL. For more information, visit "
60
+ "<a href='https://docs.sepal.io/en/latest/setup/gee.html' "
61
+ "target='_blank'> the documentation </a>."
62
+ ),
63
+ "status": "CREDENTIALS_ERROR",
64
+ "details": "Unknown error occurred while accessing credentials file",
65
+ }
66
+ super().__init__(error)
67
+
68
+
69
+ class SepalCredentialsUnavailableError(EEClientError):
70
+ """Raised when SEPAL API cannot provide credentials (e.g., 500 error)."""
71
+
72
+ def __init__(self, status_code: int | None = None):
73
+ self.status_code = status_code
74
+ error = {
75
+ "code": status_code or 500,
76
+ "message": (
77
+ "There was an error when trying to authenticate with Google "
78
+ "Earth Engine. Make sure your GEE account is properly "
79
+ "connected to SEPAL. Please visit "
80
+ "<a href='https://docs.sepal.io/en/latest/setup/gee.html' "
81
+ "target='_blank'> the documentation </a> for more information."
82
+ ),
83
+ "status": "SEPAL_CREDENTIALS_UNAVAILABLE",
84
+ "details": (
85
+ f"SEPAL API returned error {status_code}"
86
+ if status_code
87
+ else "SEPAL API is unable to provide credentials"
88
+ ),
89
+ }
90
+ super().__init__(error)
91
+
92
+
93
+ # {'code': 401, 'message': 'Request had invalid authentication credentials.
94
+ # Expected OAuth 2 access token, login cookie or other valid authentication
95
+ # credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.',
96
+ # 'status': 'UNAUTHENTICATED'}
97
+ # Exception in _run_task: Request had invalid authentication credentials.
98
+ # Expected OAuth 2 access token, login cookie or other valid authentication
99
+ # credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.
100
+ # when that error happens, I need to re-set the credentials
@@ -1,4 +1,9 @@
1
1
  from eeclient.models import SepalHeaders, GoogleTokens
2
+ from eeclient.exceptions import (
3
+ CredentialsFileNotFoundError,
4
+ CredentialsFileUnknownError,
5
+ SepalCredentialsUnavailableError,
6
+ )
2
7
  import os
3
8
  import logging
4
9
  import json
@@ -84,10 +89,14 @@ class SepalCredentialMixin:
84
89
  def _load_credentials_from_file(self):
85
90
  """Load credentials from file and update internal state"""
86
91
  if not self.credentials_path.exists():
87
- raise ValueError(f"Credentials file not found at {self.credentials_path}")
92
+ raise CredentialsFileNotFoundError(str(self.credentials_path))
88
93
 
89
94
  try:
90
- credentials_data = json.loads(self.credentials_path.read_text())
95
+ file_content = self.credentials_path.read_text().strip()
96
+ if not file_content:
97
+ raise CredentialsFileNotFoundError(str(self.credentials_path))
98
+
99
+ credentials_data = json.loads(file_content)
91
100
  self._credentials = GoogleTokens.model_validate(credentials_data)
92
101
  self.access_token = self._credentials.access_token
93
102
  self.project_id = self._credentials.project_id
@@ -166,6 +175,12 @@ class SepalCredentialMixin:
166
175
  f"Project: {self.project_id}"
167
176
  )
168
177
  return
178
+ elif response.status_code == 500:
179
+ self.logger.error(
180
+ "SEPAL API returned 500 error - "
181
+ "credentials not available on server"
182
+ )
183
+ raise SepalCredentialsUnavailableError(500)
169
184
  else:
170
185
  self.logger.debug(
171
186
  f"Attempt {attempt}/{self.max_retries} failed with "
@@ -191,6 +206,8 @@ class SepalCredentialMixin:
191
206
  )
192
207
  attempt = 0
193
208
 
209
+ log.debug(f"Credentials path: {self.credentials_path}")
210
+
194
211
  while attempt < self.max_retries:
195
212
  attempt += 1
196
213
  try:
@@ -214,11 +231,17 @@ class SepalCredentialMixin:
214
231
  f"Credentials from file are still expired."
215
232
  )
216
233
 
234
+ except CredentialsFileNotFoundError:
235
+ self.logger.error(
236
+ f"Credentials file not found: {self.credentials_path}"
237
+ )
238
+ raise
217
239
  except Exception as e:
218
240
  self.logger.error(
219
241
  f"Attempt {attempt}/{self.max_retries} "
220
242
  f"encountered an exception while reading file: {e}"
221
243
  )
244
+ raise
222
245
 
223
246
  # Wait before retrying (the file might be updated externally)
224
247
  if attempt < self.max_retries:
@@ -226,11 +249,7 @@ class SepalCredentialMixin:
226
249
  self.logger.debug(f"Waiting {wait_time} seconds before retry...")
227
250
  await asyncio.sleep(wait_time)
228
251
 
229
- raise ValueError(
230
- f"Failed to retrieve valid credentials from file after "
231
- f"{self.max_retries} attempts. File may not be automatically "
232
- f"updated or credentials are permanently expired."
233
- )
252
+ raise CredentialsFileUnknownError()
234
253
 
235
254
  def set_credentials_sync(self) -> None:
236
255
  """
@@ -282,6 +301,13 @@ class SepalCredentialMixin:
282
301
  f"Project: {self.project_id}"
283
302
  )
284
303
  return
304
+ elif response.status_code == 500:
305
+ self.logger.error(
306
+ "SEPAL API returned 500 error - "
307
+ "credentials not available on server"
308
+ )
309
+ session.close()
310
+ raise SepalCredentialsUnavailableError(500)
285
311
  else:
286
312
  self.logger.debug(
287
313
  f"Attempt {attempt}/{self.max_retries} failed with "
@@ -336,6 +362,9 @@ class SepalCredentialMixin:
336
362
  f"Credentials from file are still expired."
337
363
  )
338
364
 
365
+ except CredentialsFileNotFoundError:
366
+ # Re-raise immediately - no point in retrying if file doesn't exist
367
+ raise
339
368
  except Exception as e:
340
369
  self.logger.error(
341
370
  f"Attempt {attempt}/{self.max_retries} "
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "ee-client"
7
- version = "2.2.0"
7
+ version = "2.2.1"
8
8
  description = "extends the capabilities of the earthengine-api by providing custom session management and client interactions"
9
9
  readme = { file = "README.rst", content-type = "text/x-rst" }
10
10
  authors = [
@@ -66,7 +66,7 @@ branch = true
66
66
  [tool.commitizen]
67
67
  tag_format = "v$major.$minor.$patch$prerelease"
68
68
  update_changelog_on_bump = false
69
- version = "2.2.0"
69
+ version = "2.2.1"
70
70
  version_files = [
71
71
  "pyproject.toml:version",
72
72
  "eeclient/__init__.py:__version__",
@@ -1,26 +0,0 @@
1
- from ee.ee_exception import EEException
2
-
3
-
4
- class EERestException(EEException):
5
- def __init__(self, error):
6
- self.message = error.get("message", "EE responded with an error")
7
- super().__init__(self.message)
8
- self.code = error.get("code", -1)
9
- self.status = error.get("status", "UNDEFINED")
10
- self.details = error.get("details")
11
-
12
-
13
- class EEClientError(Exception):
14
- """Custom exception class for EEClient errors."""
15
-
16
- pass
17
-
18
-
19
- # {'code': 401, 'message': 'Request had invalid authentication credentials.
20
- # Expected OAuth 2 access token, login cookie or other valid authentication
21
- # credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.',
22
- # 'status': 'UNAUTHENTICATED'}
23
- # Exception in _run_task: Request had invalid authentication credentials.
24
- # Expected OAuth 2 access token, login cookie or other valid authentication
25
- # credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.
26
- # when that error happens, I need to re-set the credentials
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes