ee-client 2.6.3__tar.gz → 3.0.0__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. {ee_client-2.6.3 → ee_client-3.0.0}/PKG-INFO +28 -30
  2. {ee_client-2.6.3 → ee_client-3.0.0}/README.rst +25 -29
  3. {ee_client-2.6.3 → ee_client-3.0.0}/ee_client.egg-info/PKG-INFO +28 -30
  4. {ee_client-2.6.3 → ee_client-3.0.0}/ee_client.egg-info/SOURCES.txt +6 -0
  5. {ee_client-2.6.3 → ee_client-3.0.0}/ee_client.egg-info/requires.txt +2 -0
  6. {ee_client-2.6.3 → ee_client-3.0.0}/eeclient/__init__.py +1 -1
  7. {ee_client-2.6.3 → ee_client-3.0.0}/eeclient/client.py +127 -21
  8. ee_client-3.0.0/eeclient/credential_mixin.py +97 -0
  9. {ee_client-2.6.3 → ee_client-3.0.0}/eeclient/exceptions.py +7 -0
  10. {ee_client-2.6.3 → ee_client-3.0.0}/eeclient/interfaces/export.py +4 -8
  11. {ee_client-2.6.3 → ee_client-3.0.0}/eeclient/interfaces/operations.py +7 -14
  12. {ee_client-2.6.3 → ee_client-3.0.0}/eeclient/interfaces/tasks.py +3 -6
  13. ee_client-3.0.0/eeclient/providers.py +419 -0
  14. ee_client-3.0.0/eeclient/sepal_credential_mixin.py +11 -0
  15. {ee_client-2.6.3 → ee_client-3.0.0}/pyproject.toml +4 -2
  16. ee_client-3.0.0/tests/test_agnostic_auth_integration.py +38 -0
  17. {ee_client-2.6.3 → ee_client-3.0.0}/tests/test_cache.py +0 -1
  18. {ee_client-2.6.3 → ee_client-3.0.0}/tests/test_export_table.py +4 -3
  19. ee_client-3.0.0/tests/test_factories.py +147 -0
  20. ee_client-3.0.0/tests/test_providers.py +184 -0
  21. ee_client-3.0.0/tests/test_resolution.py +94 -0
  22. ee_client-2.6.3/eeclient/sepal_credential_mixin.py +0 -382
  23. {ee_client-2.6.3 → ee_client-3.0.0}/LICENSE +0 -0
  24. {ee_client-2.6.3 → ee_client-3.0.0}/ee_client.egg-info/dependency_links.txt +0 -0
  25. {ee_client-2.6.3 → ee_client-3.0.0}/ee_client.egg-info/top_level.txt +0 -0
  26. {ee_client-2.6.3 → ee_client-3.0.0}/eeclient/cache.py +0 -0
  27. {ee_client-2.6.3 → ee_client-3.0.0}/eeclient/data.py +0 -0
  28. {ee_client-2.6.3 → ee_client-3.0.0}/eeclient/export/__init__.py +0 -0
  29. {ee_client-2.6.3 → ee_client-3.0.0}/eeclient/export/image.py +0 -0
  30. {ee_client-2.6.3 → ee_client-3.0.0}/eeclient/export/table.py +0 -0
  31. {ee_client-2.6.3 → ee_client-3.0.0}/eeclient/helpers.py +0 -0
  32. {ee_client-2.6.3 → ee_client-3.0.0}/eeclient/interfaces/__init__.py +0 -0
  33. {ee_client-2.6.3 → ee_client-3.0.0}/eeclient/models.py +0 -0
  34. {ee_client-2.6.3 → ee_client-3.0.0}/eeclient/oauth_app.py +0 -0
  35. {ee_client-2.6.3 → ee_client-3.0.0}/eeclient/tasks.py +0 -0
  36. {ee_client-2.6.3 → ee_client-3.0.0}/setup.cfg +0 -0
  37. {ee_client-2.6.3 → ee_client-3.0.0}/tests/test_client.py +0 -0
  38. {ee_client-2.6.3 → ee_client-3.0.0}/tests/test_data.py +0 -0
  39. {ee_client-2.6.3 → ee_client-3.0.0}/tests/test_export_image.py +0 -0
  40. {ee_client-2.6.3 → ee_client-3.0.0}/tests/test_integration_get_assets.py +0 -0
  41. {ee_client-2.6.3 → ee_client-3.0.0}/tests/test_models.py +0 -0
  42. {ee_client-2.6.3 → ee_client-3.0.0}/tests/test_tasks.py +0 -0
  43. {ee_client-2.6.3 → ee_client-3.0.0}/tests/test_tls_verify.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ee-client
3
- Version: 2.6.3
3
+ Version: 3.0.0
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
@@ -18,6 +18,8 @@ Requires-Dist: earthengine-api
18
18
  Requires-Dist: httpx[http2]
19
19
  Requires-Dist: tenacity
20
20
  Requires-Dist: pydantic
21
+ Requires-Dist: google-auth
22
+ Requires-Dist: requests
21
23
  Provides-Extra: dev
22
24
  Requires-Dist: pre-commit>=2.18.0; extra == "dev"
23
25
  Requires-Dist: commitizen; extra == "dev"
@@ -60,13 +62,12 @@ While Google Earth Engine applications can be created using a global service acc
60
62
 
61
63
  Unlike the standard GEE API—which relies on a global session object and does not support multi-user environments—this client ensures that each session is authenticated and managed independently with user-specific credentials.
62
64
 
63
- Each session is instantiated via the ``EESession`` class, currently only accepts SEPAL headers as its only parameter. **A valid ``sepal-session-id`` cookie must be present in these headers**, as it is used to retrieve the corresponding GEE credentials. Once authenticated, the session exposes an ``operations`` property that provides easy access to key API methods.
65
+ Each session is instantiated via the ``EESession`` class, which supports several credential sources a service-account key, an ``EARTHENGINE_TOKEN`` environment variable, ``earthengine authenticate`` OAuth credentials, or Application Default Credentials. Once authenticated, the session exposes an ``operations`` property that provides easy access to key API methods.
64
66
 
65
67
  Key Features
66
68
  ------------
67
69
 
68
70
  - **Custom User Authentication**: Enable users to access their private GEE assets without requiring them to be public, solving the limitation of global service account approaches.
69
- - **SEPAL-based Initialization**: Create sessions using SEPAL headers. The required ``sepal-session-id`` cookie is automatically used to retrieve GEE credentials.
70
71
  - **Multi-User Session Management**: Encapsulate user-specific credentials and project data in independent ``EESession`` objects.
71
72
  - **Enhanced API Operations**: Access GEE functionalities via the ``operations`` property, which includes methods such as:
72
73
  - ``get_info``: Retrieve detailed information about an Earth Engine object.
@@ -89,38 +90,35 @@ Usage
89
90
  Initialization and Authentication
90
91
  +++++++++++++++++++++++++++++++++
91
92
 
92
- The Earth Engine Session Client must be initialized using SEPAL headers. **Ensure that the headers include the ``sepal-session-id`` cookie**, which is essential for retrieving the GEE credentials.
93
+ A session can be built from any standard Google/Earth Engine credential via factory constructors, each holding a live, refreshable credential:
93
94
 
94
95
  .. code-block:: python
95
96
 
96
97
  from eeclient import EESession
97
98
 
98
- # Example SEPAL headers with the mandatory sepal-session-id cookie.
99
- sepal_headers = {
100
- "cookie": [
101
- "sepal-session-id=your_session_id",
102
- "other_cookie=other_value"
103
- ],
104
- "sepal_user": [{
105
- "id": 123,
106
- "username": "your_username",
107
- "googleTokens": {
108
- "accessToken": "your_access_token",
109
- "refreshToken": "your_refresh_token",
110
- "accessTokenExpiryDate": 1234567890,
111
- "REFRESH_IF_EXPIRES_IN_MINUTES": 10,
112
- "projectId": "your_project_id",
113
- "legacyProject": "your_legacy_project"
114
- },
115
- "status": "active",
116
- "roles": ["role1", "role2"],
117
- "systemUser": False,
118
- "admin": False
119
- }]
120
- }
121
-
122
- # Create and validate the session with SEPAL headers
123
- session = EESession(sepal_headers)
99
+ # Service-account key (dict or path)
100
+ session = EESession.from_service_account("sa-key.json")
101
+
102
+ # EARTHENGINE_TOKEN env var, else ~/.config/earthengine/credentials
103
+ session = EESession.from_earthengine_token()
104
+
105
+ # An existing google.auth Credentials object
106
+ session = EESession.from_google_credentials(creds, project="my-project")
107
+
108
+ # Application Default Credentials (explicit opt-in)
109
+ session = EESession.from_application_default()
110
+
111
+ # Resolve from the environment (opt-in): EARTHENGINE_TOKEN / EE OAuth file
112
+ session = EESession.from_default()
113
+
114
+ Credentials are **never resolved implicitly** — a bare ``EESession()`` constructor call is not supported; always use a ``from_*`` factory to build a session. To resolve from the environment explicitly, call ``EESession.from_default()``, which walks local sources only, in this order:
115
+
116
+ 1. ``EARTHENGINE_TOKEN`` environment variable
117
+ 2. Earth Engine OAuth file (``~/.config/earthengine/credentials``)
118
+
119
+ Application Default Credentials are **not** included — call ``EESession.from_application_default()`` to use them.
120
+
121
+ To see which source a session ended up using, inspect ``session.auth_mode`` (the credential *kind*: ``file``/``oauth``/``service_account``/``adc``) and ``session.auth_source`` (the precise origin: ``earthengine_token``/``ee_oauth_file``/``service_account``/``application_default``/``google_credentials``).
124
122
 
125
123
  Making API Calls
126
124
  ++++++++++++++++
@@ -29,13 +29,12 @@ While Google Earth Engine applications can be created using a global service acc
29
29
 
30
30
  Unlike the standard GEE API—which relies on a global session object and does not support multi-user environments—this client ensures that each session is authenticated and managed independently with user-specific credentials.
31
31
 
32
- Each session is instantiated via the ``EESession`` class, currently only accepts SEPAL headers as its only parameter. **A valid ``sepal-session-id`` cookie must be present in these headers**, as it is used to retrieve the corresponding GEE credentials. Once authenticated, the session exposes an ``operations`` property that provides easy access to key API methods.
32
+ Each session is instantiated via the ``EESession`` class, which supports several credential sources a service-account key, an ``EARTHENGINE_TOKEN`` environment variable, ``earthengine authenticate`` OAuth credentials, or Application Default Credentials. Once authenticated, the session exposes an ``operations`` property that provides easy access to key API methods.
33
33
 
34
34
  Key Features
35
35
  ------------
36
36
 
37
37
  - **Custom User Authentication**: Enable users to access their private GEE assets without requiring them to be public, solving the limitation of global service account approaches.
38
- - **SEPAL-based Initialization**: Create sessions using SEPAL headers. The required ``sepal-session-id`` cookie is automatically used to retrieve GEE credentials.
39
38
  - **Multi-User Session Management**: Encapsulate user-specific credentials and project data in independent ``EESession`` objects.
40
39
  - **Enhanced API Operations**: Access GEE functionalities via the ``operations`` property, which includes methods such as:
41
40
  - ``get_info``: Retrieve detailed information about an Earth Engine object.
@@ -58,38 +57,35 @@ Usage
58
57
  Initialization and Authentication
59
58
  +++++++++++++++++++++++++++++++++
60
59
 
61
- The Earth Engine Session Client must be initialized using SEPAL headers. **Ensure that the headers include the ``sepal-session-id`` cookie**, which is essential for retrieving the GEE credentials.
60
+ A session can be built from any standard Google/Earth Engine credential via factory constructors, each holding a live, refreshable credential:
62
61
 
63
62
  .. code-block:: python
64
63
 
65
64
  from eeclient import EESession
66
65
 
67
- # Example SEPAL headers with the mandatory sepal-session-id cookie.
68
- sepal_headers = {
69
- "cookie": [
70
- "sepal-session-id=your_session_id",
71
- "other_cookie=other_value"
72
- ],
73
- "sepal_user": [{
74
- "id": 123,
75
- "username": "your_username",
76
- "googleTokens": {
77
- "accessToken": "your_access_token",
78
- "refreshToken": "your_refresh_token",
79
- "accessTokenExpiryDate": 1234567890,
80
- "REFRESH_IF_EXPIRES_IN_MINUTES": 10,
81
- "projectId": "your_project_id",
82
- "legacyProject": "your_legacy_project"
83
- },
84
- "status": "active",
85
- "roles": ["role1", "role2"],
86
- "systemUser": False,
87
- "admin": False
88
- }]
89
- }
90
-
91
- # Create and validate the session with SEPAL headers
92
- session = EESession(sepal_headers)
66
+ # Service-account key (dict or path)
67
+ session = EESession.from_service_account("sa-key.json")
68
+
69
+ # EARTHENGINE_TOKEN env var, else ~/.config/earthengine/credentials
70
+ session = EESession.from_earthengine_token()
71
+
72
+ # An existing google.auth Credentials object
73
+ session = EESession.from_google_credentials(creds, project="my-project")
74
+
75
+ # Application Default Credentials (explicit opt-in)
76
+ session = EESession.from_application_default()
77
+
78
+ # Resolve from the environment (opt-in): EARTHENGINE_TOKEN / EE OAuth file
79
+ session = EESession.from_default()
80
+
81
+ Credentials are **never resolved implicitly** — a bare ``EESession()`` constructor call is not supported; always use a ``from_*`` factory to build a session. To resolve from the environment explicitly, call ``EESession.from_default()``, which walks local sources only, in this order:
82
+
83
+ 1. ``EARTHENGINE_TOKEN`` environment variable
84
+ 2. Earth Engine OAuth file (``~/.config/earthengine/credentials``)
85
+
86
+ Application Default Credentials are **not** included — call ``EESession.from_application_default()`` to use them.
87
+
88
+ To see which source a session ended up using, inspect ``session.auth_mode`` (the credential *kind*: ``file``/``oauth``/``service_account``/``adc``) and ``session.auth_source`` (the precise origin: ``earthengine_token``/``ee_oauth_file``/``service_account``/``application_default``/``google_credentials``).
93
89
 
94
90
  Making API Calls
95
91
  ++++++++++++++++
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: ee-client
3
- Version: 2.6.3
3
+ Version: 3.0.0
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
@@ -18,6 +18,8 @@ Requires-Dist: earthengine-api
18
18
  Requires-Dist: httpx[http2]
19
19
  Requires-Dist: tenacity
20
20
  Requires-Dist: pydantic
21
+ Requires-Dist: google-auth
22
+ Requires-Dist: requests
21
23
  Provides-Extra: dev
22
24
  Requires-Dist: pre-commit>=2.18.0; extra == "dev"
23
25
  Requires-Dist: commitizen; extra == "dev"
@@ -60,13 +62,12 @@ While Google Earth Engine applications can be created using a global service acc
60
62
 
61
63
  Unlike the standard GEE API—which relies on a global session object and does not support multi-user environments—this client ensures that each session is authenticated and managed independently with user-specific credentials.
62
64
 
63
- Each session is instantiated via the ``EESession`` class, currently only accepts SEPAL headers as its only parameter. **A valid ``sepal-session-id`` cookie must be present in these headers**, as it is used to retrieve the corresponding GEE credentials. Once authenticated, the session exposes an ``operations`` property that provides easy access to key API methods.
65
+ Each session is instantiated via the ``EESession`` class, which supports several credential sources a service-account key, an ``EARTHENGINE_TOKEN`` environment variable, ``earthengine authenticate`` OAuth credentials, or Application Default Credentials. Once authenticated, the session exposes an ``operations`` property that provides easy access to key API methods.
64
66
 
65
67
  Key Features
66
68
  ------------
67
69
 
68
70
  - **Custom User Authentication**: Enable users to access their private GEE assets without requiring them to be public, solving the limitation of global service account approaches.
69
- - **SEPAL-based Initialization**: Create sessions using SEPAL headers. The required ``sepal-session-id`` cookie is automatically used to retrieve GEE credentials.
70
71
  - **Multi-User Session Management**: Encapsulate user-specific credentials and project data in independent ``EESession`` objects.
71
72
  - **Enhanced API Operations**: Access GEE functionalities via the ``operations`` property, which includes methods such as:
72
73
  - ``get_info``: Retrieve detailed information about an Earth Engine object.
@@ -89,38 +90,35 @@ Usage
89
90
  Initialization and Authentication
90
91
  +++++++++++++++++++++++++++++++++
91
92
 
92
- The Earth Engine Session Client must be initialized using SEPAL headers. **Ensure that the headers include the ``sepal-session-id`` cookie**, which is essential for retrieving the GEE credentials.
93
+ A session can be built from any standard Google/Earth Engine credential via factory constructors, each holding a live, refreshable credential:
93
94
 
94
95
  .. code-block:: python
95
96
 
96
97
  from eeclient import EESession
97
98
 
98
- # Example SEPAL headers with the mandatory sepal-session-id cookie.
99
- sepal_headers = {
100
- "cookie": [
101
- "sepal-session-id=your_session_id",
102
- "other_cookie=other_value"
103
- ],
104
- "sepal_user": [{
105
- "id": 123,
106
- "username": "your_username",
107
- "googleTokens": {
108
- "accessToken": "your_access_token",
109
- "refreshToken": "your_refresh_token",
110
- "accessTokenExpiryDate": 1234567890,
111
- "REFRESH_IF_EXPIRES_IN_MINUTES": 10,
112
- "projectId": "your_project_id",
113
- "legacyProject": "your_legacy_project"
114
- },
115
- "status": "active",
116
- "roles": ["role1", "role2"],
117
- "systemUser": False,
118
- "admin": False
119
- }]
120
- }
121
-
122
- # Create and validate the session with SEPAL headers
123
- session = EESession(sepal_headers)
99
+ # Service-account key (dict or path)
100
+ session = EESession.from_service_account("sa-key.json")
101
+
102
+ # EARTHENGINE_TOKEN env var, else ~/.config/earthengine/credentials
103
+ session = EESession.from_earthengine_token()
104
+
105
+ # An existing google.auth Credentials object
106
+ session = EESession.from_google_credentials(creds, project="my-project")
107
+
108
+ # Application Default Credentials (explicit opt-in)
109
+ session = EESession.from_application_default()
110
+
111
+ # Resolve from the environment (opt-in): EARTHENGINE_TOKEN / EE OAuth file
112
+ session = EESession.from_default()
113
+
114
+ Credentials are **never resolved implicitly** — a bare ``EESession()`` constructor call is not supported; always use a ``from_*`` factory to build a session. To resolve from the environment explicitly, call ``EESession.from_default()``, which walks local sources only, in this order:
115
+
116
+ 1. ``EARTHENGINE_TOKEN`` environment variable
117
+ 2. Earth Engine OAuth file (``~/.config/earthengine/credentials``)
118
+
119
+ Application Default Credentials are **not** included — call ``EESession.from_application_default()`` to use them.
120
+
121
+ To see which source a session ended up using, inspect ``session.auth_mode`` (the credential *kind*: ``file``/``oauth``/``service_account``/``adc``) and ``session.auth_source`` (the precise origin: ``earthengine_token``/``ee_oauth_file``/``service_account``/``application_default``/``google_credentials``).
124
122
 
125
123
  Making API Calls
126
124
  ++++++++++++++++
@@ -9,11 +9,13 @@ ee_client.egg-info/top_level.txt
9
9
  eeclient/__init__.py
10
10
  eeclient/cache.py
11
11
  eeclient/client.py
12
+ eeclient/credential_mixin.py
12
13
  eeclient/data.py
13
14
  eeclient/exceptions.py
14
15
  eeclient/helpers.py
15
16
  eeclient/models.py
16
17
  eeclient/oauth_app.py
18
+ eeclient/providers.py
17
19
  eeclient/sepal_credential_mixin.py
18
20
  eeclient/tasks.py
19
21
  eeclient/export/__init__.py
@@ -23,12 +25,16 @@ eeclient/interfaces/__init__.py
23
25
  eeclient/interfaces/export.py
24
26
  eeclient/interfaces/operations.py
25
27
  eeclient/interfaces/tasks.py
28
+ tests/test_agnostic_auth_integration.py
26
29
  tests/test_cache.py
27
30
  tests/test_client.py
28
31
  tests/test_data.py
29
32
  tests/test_export_image.py
30
33
  tests/test_export_table.py
34
+ tests/test_factories.py
31
35
  tests/test_integration_get_assets.py
32
36
  tests/test_models.py
37
+ tests/test_providers.py
38
+ tests/test_resolution.py
33
39
  tests/test_tasks.py
34
40
  tests/test_tls_verify.py
@@ -2,6 +2,8 @@ earthengine-api
2
2
  httpx[http2]
3
3
  tenacity
4
4
  pydantic
5
+ google-auth
6
+ requests
5
7
 
6
8
  [dev]
7
9
  pre-commit>=2.18.0
@@ -1,6 +1,6 @@
1
1
  __title__ = "eeclient"
2
2
  __summary__ = "A client for Google Earth Engine"
3
- __version__ = "2.6.3"
3
+ __version__ = "3.0.0"
4
4
 
5
5
  __author__ = "Daniel Guerrero"
6
6
  __email__ = "dfgm2006@gmail.com"
@@ -10,7 +10,7 @@ from contextlib import asynccontextmanager
10
10
 
11
11
  from eeclient.exceptions import EEClientError, EERestException
12
12
  from eeclient.models import GEEHeaders, SepalHeaders
13
- from eeclient.sepal_credential_mixin import SepalCredentialMixin
13
+ from eeclient.credential_mixin import CredentialMixin
14
14
  from eeclient.cache import ResponseCache
15
15
 
16
16
  import eeclient.export as _export_module
@@ -30,11 +30,6 @@ logger = logging.getLogger("eeclient")
30
30
  # Default values that won't raise exceptions during import
31
31
  EARTH_ENGINE_API_URL = "https://earthengine.googleapis.com/v1alpha"
32
32
 
33
- # These will be set properly when EESession is initialized
34
- SEPAL_HOST = os.getenv("SEPAL_HOST")
35
- SEPAL_API_DOWNLOAD_URL = None
36
- VERIFY_SSL = True
37
-
38
33
 
39
34
  class SimpleRateLimiter:
40
35
  def __init__(self, qps: float | None):
@@ -53,24 +48,29 @@ class SimpleRateLimiter:
53
48
  self._next = max(now, self._next) + 1.0 / self.qps
54
49
 
55
50
 
56
- class EESession(SepalCredentialMixin):
51
+ class EESession(CredentialMixin):
57
52
  def __init__(
58
53
  self,
59
54
  sepal_headers: Optional[SepalHeaders] = None,
60
55
  enforce_project_id: bool = True,
56
+ *,
57
+ _provider=None,
61
58
  ):
62
- """Session that handles two scenarios to set the headers for Earth Engine API
59
+ """Session for the Earth Engine REST API with pluggable authentication.
63
60
 
64
- It can be initialized with the headers sent by SEPAL or with the
65
- credentials and project
61
+ Provide a credential source: ``sepal_headers`` for the SEPAL session
62
+ path, or use an ``EESession.from_*()`` factory / ``from_default()`` for
63
+ service-account, ``EARTHENGINE_TOKEN``, OAuth or ADC sources. A bare
64
+ ``EESession()`` with no source raises ``EEClientError``.
66
65
 
67
66
  Args:
68
- sepal_headers (SepalHeaders): The headers sent by SEPAL
69
- enforce_project_id (bool, optional): If set, it cannot be changed.
70
- Defaults to True.
67
+ sepal_headers (Optional[SepalHeaders]): SEPAL session headers; omit
68
+ and use a factory / ``from_default()`` for other sources.
69
+ enforce_project_id (bool, optional): If set, the project id is not
70
+ overwritten on refresh. Defaults to True.
71
71
 
72
72
  Raises:
73
- ValueError: If SEPAL_HOST environment variable is not set
73
+ EEClientError: If no credential source is provided.
74
74
  """
75
75
  self._inflight = asyncio.BoundedSemaphore(30)
76
76
  self._rate = SimpleRateLimiter(60)
@@ -83,7 +83,7 @@ class EESession(SepalCredentialMixin):
83
83
  self._assets_cache = ResponseCache(ttl=10.0, max_size=100)
84
84
 
85
85
  self.enforce_project_id = enforce_project_id
86
- super().__init__(sepal_headers)
86
+ super().__init__(sepal_headers, provider=_provider)
87
87
 
88
88
  self.logger = logging.getLogger(f"eeclient.{self.user}")
89
89
  self.logger.debug(
@@ -117,8 +117,9 @@ class EESession(SepalCredentialMixin):
117
117
  """Asynchronously create an EESession instance.
118
118
 
119
119
  Args:
120
- sepal_headers (Optional[SepalHeaders]): The headers sent by SEPAL.
121
- If None, will use file-based authentication.
120
+ sepal_headers (Optional[SepalHeaders]): SEPAL headers for SEPAL
121
+ session mode (required here). For other sources use an
122
+ EESession.from_*() factory, or EESession.from_default().
122
123
  enforce_project_id (bool, optional): If set, it cannot be changed.
123
124
  Defaults to True.
124
125
 
@@ -128,6 +129,113 @@ class EESession(SepalCredentialMixin):
128
129
  session = cls(sepal_headers, enforce_project_id)
129
130
  return await session.initialize()
130
131
 
132
+ # -- Agnostic credential factories -------------------------------------
133
+ @classmethod
134
+ def from_google_credentials(
135
+ cls,
136
+ creds,
137
+ *,
138
+ project=None,
139
+ enforce_project_id=True,
140
+ _auth_mode="oauth",
141
+ _auth_source="google_credentials",
142
+ ) -> "EESession":
143
+ """Build a session from a live ``google.auth`` Credentials object."""
144
+ from eeclient.providers import GoogleAuthProvider
145
+
146
+ provider = GoogleAuthProvider(
147
+ creds, project, auth_mode=_auth_mode, auth_source=_auth_source
148
+ )
149
+ session = cls(enforce_project_id=enforce_project_id, _provider=provider)
150
+ session.set_credentials_sync() # eager load (factories are the sync path, D6)
151
+ return session
152
+
153
+ @classmethod
154
+ def from_service_account(
155
+ cls, info_or_path, *, project=None, scopes=None, enforce_project_id=True
156
+ ) -> "EESession":
157
+ """Build a session from a service-account key (dict or file path)."""
158
+ from eeclient.providers import DEFAULT_SCOPES, _service_account_credentials
159
+
160
+ creds, sa_project = _service_account_credentials(
161
+ info_or_path, scopes or DEFAULT_SCOPES
162
+ )
163
+ return cls.from_google_credentials(
164
+ creds,
165
+ project=project or sa_project,
166
+ enforce_project_id=enforce_project_id,
167
+ _auth_mode="service_account",
168
+ _auth_source="service_account",
169
+ )
170
+
171
+ @classmethod
172
+ def from_earthengine_token(
173
+ cls, *, project=None, enforce_project_id=True
174
+ ) -> "EESession":
175
+ """Build a session from ``EARTHENGINE_TOKEN``, else the EE OAuth file."""
176
+ from eeclient.providers import (
177
+ DEFAULT_SCOPES,
178
+ _credentials_from_earthengine_token,
179
+ _google_auth_mode,
180
+ _oauth_credentials_from_ee_file,
181
+ )
182
+
183
+ raw = os.getenv("EARTHENGINE_TOKEN")
184
+ if raw:
185
+ creds, tok_project = _credentials_from_earthengine_token(
186
+ raw, DEFAULT_SCOPES
187
+ )
188
+ mode, source = _google_auth_mode(creds), "earthengine_token"
189
+ else:
190
+ creds, tok_project = _oauth_credentials_from_ee_file(DEFAULT_SCOPES)
191
+ mode, source = "oauth", "ee_oauth_file"
192
+ return cls.from_google_credentials(
193
+ creds,
194
+ project=project or tok_project,
195
+ enforce_project_id=enforce_project_id,
196
+ _auth_mode=mode,
197
+ _auth_source=source,
198
+ )
199
+
200
+ @classmethod
201
+ def from_application_default(
202
+ cls, *, project=None, scopes=None, enforce_project_id=True
203
+ ) -> "EESession":
204
+ """Build a session from Application Default Credentials (opt-in ADC)."""
205
+ from eeclient.providers import DEFAULT_SCOPES, _application_default_credentials
206
+
207
+ creds, adc_project = _application_default_credentials(scopes or DEFAULT_SCOPES)
208
+ return cls.from_google_credentials(
209
+ creds,
210
+ project=project or adc_project,
211
+ enforce_project_id=enforce_project_id,
212
+ _auth_mode="adc",
213
+ _auth_source="application_default",
214
+ )
215
+
216
+ @classmethod
217
+ def from_sepal_headers(cls, headers, *, enforce_project_id=True) -> "EESession":
218
+ """Build a session from SEPAL headers (names the existing path)."""
219
+ return cls(headers, enforce_project_id=enforce_project_id)
220
+
221
+ @classmethod
222
+ def from_default(cls, *, enforce_project_id=True) -> "EESession":
223
+ """Resolve credentials from the environment — explicit opt-in.
224
+
225
+ Walks local sources only (SEPAL file, ``EARTHENGINE_TOKEN``, Earth Engine
226
+ OAuth file) and fails closed in a SEPAL context. ADC is not included —
227
+ use ``from_application_default()``. This is opt-in: a bare ``EESession()``
228
+ does not auto-resolve; the caller must pick a source.
229
+
230
+ Construction does no network I/O — the token refresh is deferred to
231
+ ``await initialize()`` / the first ``get_headers()`` so async callers are
232
+ not blocked (use ``set_credentials_sync()`` for a synchronous refresh).
233
+ """
234
+ from eeclient.providers import resolve_default_provider
235
+
236
+ provider = resolve_default_provider()
237
+ return cls(enforce_project_id=enforce_project_id, _provider=provider)
238
+
131
239
  async def get_assets_folder(self) -> str:
132
240
  if self.needs_credentials_refresh():
133
241
  await self.set_credentials()
@@ -149,7 +257,7 @@ class EESession(SepalCredentialMixin):
149
257
 
150
258
  data = {
151
259
  "x-goog-user-project": self.project_id,
152
- "Authorization": f"Bearer {self._credentials.access_token}",
260
+ "Authorization": f"Bearer {self.access_token}",
153
261
  "Username": username,
154
262
  }
155
263
 
@@ -268,9 +376,7 @@ class EESession(SepalCredentialMixin):
268
376
  error_type = (
269
377
  "Rate limit exceeded"
270
378
  if e.code == 429
271
- else "Unauthorized"
272
- if e.code == 401
273
- else "Service unavailable"
379
+ else "Unauthorized" if e.code == 401 else "Service unavailable"
274
380
  )
275
381
  attempt += 1
276
382
  wait_time = min(initial_wait * (2**attempt), max_wait)
@@ -0,0 +1,97 @@
1
+ import logging
2
+ import time
3
+ from typing import Optional
4
+
5
+ from eeclient.exceptions import EEClientError
6
+ from eeclient.models import SepalHeaders
7
+ from eeclient.providers import CredentialSnapshot, SepalSessionProvider
8
+
9
+ log = logging.getLogger("eeclient")
10
+
11
+ # SEPAL-specific attributes forwarded from a provider onto the session for
12
+ # backward compatibility (present on SepalSessionProvider / SepalFileProvider,
13
+ # absent -> None for google-auth providers).
14
+ _FORWARDED = (
15
+ "sepal_headers",
16
+ "sepal_user_data",
17
+ "sepal_host",
18
+ "sepal_session_id",
19
+ "sepal_api_download_url",
20
+ "credentials_path",
21
+ )
22
+
23
+
24
+ class CredentialMixin:
25
+ """Holds a :class:`CredentialProvider` and applies its snapshots.
26
+
27
+ The provider (SEPAL session, SEPAL file, or google-auth) owns the actual
28
+ credential logic; this mixin normalizes the result onto the session's
29
+ ``access_token`` / ``project_id`` / ``expiry_date`` / ``_credentials``
30
+ surface and delegates refresh.
31
+ """
32
+
33
+ def __init__(self, sepal_headers: Optional[SepalHeaders] = None, *, provider=None):
34
+ self.max_retries = 3
35
+ self._credentials = None
36
+ self._service = None # backward compatibility
37
+
38
+ if provider is not None:
39
+ self._provider = provider
40
+ elif sepal_headers is not None:
41
+ self._provider = SepalSessionProvider(sepal_headers)
42
+ else:
43
+ raise EEClientError(
44
+ "EESession requires a credential source. Call "
45
+ "EESession.from_default() to resolve credentials from the "
46
+ "environment, or use an explicit EESession.from_*() factory."
47
+ )
48
+
49
+ prov = self._provider
50
+ self.auth_mode = prov.auth_mode
51
+ self.auth_source = getattr(prov, "auth_source", None)
52
+ self.user = prov.user
53
+ self.verify_ssl = getattr(prov, "verify_ssl", True)
54
+ for name in _FORWARDED:
55
+ setattr(self, name, getattr(prov, name, None))
56
+
57
+ self.access_token = None
58
+ self.project_id = None
59
+ self.expiry_date = 0
60
+
61
+ snap = prov.initial_snapshot()
62
+ if snap is not None:
63
+ self._apply_snapshot(snap, initial=True)
64
+
65
+ self.logger = logging.getLogger(f"eeclient.{self.user}")
66
+
67
+ def _apply_snapshot(
68
+ self, snap: CredentialSnapshot, *, initial: bool = False
69
+ ) -> None:
70
+ self.access_token = snap.access_token
71
+ self.expiry_date = snap.expiry_date
72
+ self._credentials = snap.native
73
+ # Preserve enforce_project_id: on refresh, don't overwrite an enforced
74
+ # project; on initial population always set it.
75
+ enforce = getattr(self, "enforce_project_id", False)
76
+ if initial or not (enforce and self.project_id):
77
+ self.project_id = snap.project_id
78
+
79
+ def is_expired(self) -> bool:
80
+ """Returns if a token is about to expire."""
81
+ return (self.expiry_date / 1000) - time.time() < 60
82
+
83
+ def needs_credentials_refresh(self) -> bool:
84
+ """Returns if credentials need to be refreshed (missing or expired)."""
85
+ return not self._credentials or self.is_expired()
86
+
87
+ async def set_credentials(self) -> None:
88
+ """Refresh credentials via the active provider (async)."""
89
+ self._apply_snapshot(await self._provider.refresh())
90
+
91
+ def set_credentials_sync(self) -> None:
92
+ """Refresh credentials via the active provider (sync)."""
93
+ self._apply_snapshot(self._provider.refresh_sync())
94
+
95
+
96
+ # Backward-compat alias: SEPAL is now one provider among several.
97
+ SepalCredentialMixin = CredentialMixin
@@ -92,6 +92,13 @@ class SepalCredentialsUnavailableError(EEClientError):
92
92
  super().__init__(error)
93
93
 
94
94
 
95
+ class CredentialsResolutionError(EEClientError):
96
+ """Raised when no credential source resolves for a default EESession()."""
97
+
98
+ def __init__(self, message: str):
99
+ super().__init__(message)
100
+
101
+
95
102
  # {'code': 401, 'message': 'Request had invalid authentication credentials.
96
103
  # Expected OAuth 2 access token, login cookie or other valid authentication
97
104
  # credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.',