earthengine-api 1.5.13rc0__py3-none-any.whl → 1.7.4__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.

Potentially problematic release.


This version of earthengine-api might be problematic. Click here for more details.

Files changed (102) hide show
  1. {earthengine_api-1.5.13rc0.dist-info → earthengine_api-1.7.4.dist-info}/METADATA +3 -3
  2. earthengine_api-1.7.4.dist-info/RECORD +109 -0
  3. {earthengine_api-1.5.13rc0.dist-info → earthengine_api-1.7.4.dist-info}/WHEEL +1 -1
  4. ee/__init__.py +29 -28
  5. ee/_arg_types.py +7 -6
  6. ee/_cloud_api_utils.py +95 -78
  7. ee/_helpers.py +17 -13
  8. ee/_state.py +105 -0
  9. ee/_utils.py +2 -1
  10. ee/apifunction.py +21 -19
  11. ee/apitestcase.py +33 -38
  12. ee/batch.py +87 -77
  13. ee/blob.py +10 -12
  14. ee/classifier.py +57 -59
  15. ee/cli/commands.py +178 -114
  16. ee/cli/eecli.py +1 -1
  17. ee/cli/utils.py +61 -42
  18. ee/clusterer.py +39 -41
  19. ee/collection.py +64 -54
  20. ee/computedobject.py +19 -16
  21. ee/confusionmatrix.py +9 -9
  22. ee/customfunction.py +13 -12
  23. ee/data.py +220 -322
  24. ee/daterange.py +10 -10
  25. ee/deprecation.py +21 -13
  26. ee/deserializer.py +25 -20
  27. ee/dictionary.py +11 -11
  28. ee/ee_array.py +22 -20
  29. ee/ee_date.py +23 -23
  30. ee/ee_list.py +15 -16
  31. ee/ee_number.py +11 -21
  32. ee/ee_string.py +24 -32
  33. ee/ee_types.py +4 -4
  34. ee/element.py +15 -15
  35. ee/encodable.py +7 -4
  36. ee/errormargin.py +4 -4
  37. ee/feature.py +68 -71
  38. ee/featurecollection.py +41 -40
  39. ee/filter.py +90 -92
  40. ee/function.py +8 -8
  41. ee/geometry.py +95 -93
  42. ee/image.py +238 -236
  43. ee/image_converter.py +4 -4
  44. ee/imagecollection.py +30 -27
  45. ee/join.py +13 -15
  46. ee/kernel.py +55 -57
  47. ee/mapclient.py +9 -9
  48. ee/model.py +29 -31
  49. ee/oauth.py +76 -63
  50. ee/pixeltype.py +6 -6
  51. ee/projection.py +5 -4
  52. ee/reducer.py +41 -41
  53. ee/serializer.py +14 -14
  54. ee/table_converter.py +7 -6
  55. ee/terrain.py +7 -9
  56. ee/tests/_cloud_api_utils_test.py +21 -6
  57. ee/tests/_helpers_test.py +57 -4
  58. ee/tests/_state_test.py +49 -0
  59. ee/tests/algorithms.json +85 -2
  60. ee/tests/apifunction_test.py +5 -5
  61. ee/tests/batch_test.py +135 -57
  62. ee/tests/blob_test.py +5 -5
  63. ee/tests/classifier_test.py +3 -3
  64. ee/tests/clusterer_test.py +3 -3
  65. ee/tests/collection_test.py +48 -13
  66. ee/tests/confusionmatrix_test.py +3 -3
  67. ee/tests/data_test.py +484 -55
  68. ee/tests/daterange_test.py +4 -4
  69. ee/tests/deprecation_test.py +6 -4
  70. ee/tests/deserializer_test.py +64 -5
  71. ee/tests/dictionary_test.py +12 -12
  72. ee/tests/ee_array_test.py +3 -3
  73. ee/tests/ee_date_test.py +4 -4
  74. ee/tests/ee_list_test.py +3 -3
  75. ee/tests/ee_number_test.py +75 -30
  76. ee/tests/ee_string_test.py +11 -3
  77. ee/tests/ee_test.py +40 -22
  78. ee/tests/element_test.py +2 -2
  79. ee/tests/errormargin_test.py +1 -1
  80. ee/tests/feature_test.py +10 -10
  81. ee/tests/featurecollection_test.py +3 -3
  82. ee/tests/filter_test.py +4 -4
  83. ee/tests/function_test.py +5 -5
  84. ee/tests/geometry_point_test.py +3 -3
  85. ee/tests/geometry_test.py +93 -52
  86. ee/tests/image_converter_test.py +1 -3
  87. ee/tests/image_test.py +3 -3
  88. ee/tests/imagecollection_test.py +3 -3
  89. ee/tests/join_test.py +3 -3
  90. ee/tests/kernel_test.py +7 -3
  91. ee/tests/model_test.py +17 -5
  92. ee/tests/oauth_test.py +189 -7
  93. ee/tests/pixeltype_test.py +6 -7
  94. ee/tests/projection_test.py +5 -6
  95. ee/tests/reducer_test.py +16 -3
  96. ee/tests/serializer_test.py +39 -12
  97. ee/tests/table_converter_test.py +51 -7
  98. ee/tests/terrain_test.py +11 -3
  99. earthengine_api-1.5.13rc0.dist-info/RECORD +0 -107
  100. {earthengine_api-1.5.13rc0.dist-info → earthengine_api-1.7.4.dist-info}/entry_points.txt +0 -0
  101. {earthengine_api-1.5.13rc0.dist-info → earthengine_api-1.7.4.dist-info}/licenses/LICENSE +0 -0
  102. {earthengine_api-1.5.13rc0.dist-info → earthengine_api-1.7.4.dist-info}/top_level.txt +0 -0
ee/data.py CHANGED
@@ -3,13 +3,16 @@
3
3
  # Using lowercase function naming to match the JavaScript names.
4
4
  # pylint: disable=g-bad-name
5
5
 
6
+ from __future__ import annotations
7
+
8
+ from collections.abc import Callable, Iterator, Sequence
6
9
  import contextlib
7
10
  import json
8
11
  import platform
9
12
  import re
10
13
  import sys
11
14
  import threading
12
- from typing import Any, Callable, Dict, Iterator, List, Optional, Sequence, Union
15
+ from typing import Any
13
16
  import uuid
14
17
  import warnings
15
18
 
@@ -22,6 +25,7 @@ import httplib2
22
25
  import requests
23
26
 
24
27
  from ee import _cloud_api_utils
28
+ from ee import _state
25
29
  from ee import _utils
26
30
  from ee import computedobject
27
31
  from ee import deprecation
@@ -34,73 +38,6 @@ from ee import table_converter
34
38
 
35
39
  from ee import __version__
36
40
 
37
- # OAuth2 credentials object. This may be set by ee.Initialize().
38
- _credentials: Optional[credentials_lib.Credentials] = None
39
-
40
- # The base URL for all data calls. This is set by ee.Initialize().
41
- _api_base_url: Optional[str] = None
42
-
43
- # The base URL for map tiles. This is set by ee.Initialize().
44
- _tile_base_url: Optional[str] = None
45
-
46
- # The base URL for all Cloud API calls. This is set by ee.Initialize().
47
- _cloud_api_base_url: Optional[str] = None
48
-
49
- # Google Cloud API key. This may be set by ee.Initialize().
50
- _cloud_api_key: Optional[str] = None
51
-
52
- # A Requests session. This is set by ee.Initialize()
53
- _requests_session: Optional[requests.Session] = None
54
-
55
- # A resource object for making Cloud API calls.
56
- _cloud_api_resource = None
57
-
58
- # A resource object for making Cloud API calls and receiving raw return types.
59
- _cloud_api_resource_raw = None
60
-
61
- # The default user project to use when making Cloud API calls.
62
- _cloud_api_user_project: Optional[str] = None
63
-
64
- # The API client version number to send when making requests.
65
- _cloud_api_client_version: Optional[str] = None
66
-
67
- # The http_transport to use.
68
- _http_transport = None
69
-
70
- # Whether the module has been initialized.
71
- _initialized: bool = False
72
-
73
- # Sets the number of milliseconds to wait for a request before considering
74
- # it timed out. 0 means no limit.
75
- _deadline_ms: int = 0
76
-
77
- # Maximum number of times to retry a rate-limited request.
78
- _max_retries: int = 5
79
-
80
- # User agent to indicate which application is calling Earth Engine
81
- _user_agent: Optional[str] = None
82
-
83
-
84
- class _ThreadLocals(threading.local):
85
- """Storage for thread local variables."""
86
-
87
- def __init__(self):
88
- # pylint: disable=super-init-not-called
89
-
90
- # A function called when profile results are received from the server. Takes
91
- # the profile ID as an argument. None if profiling is disabled.
92
- #
93
- # This is a thread-local variable because the alternative is to add a
94
- # parameter to ee.data.send_, which would then have to be propagated from
95
- # the assorted API call functions (ee.data.getInfo, ee.data.getMapId, etc.),
96
- # and the user would have to modify each call to profile, rather than
97
- # enabling profiling as a wrapper around the entire program (with
98
- # ee.data.profiling, defined below).
99
- self.profile_hook: Optional[Callable[[str], None]] = None
100
-
101
-
102
- _thread_locals = _ThreadLocals()
103
-
104
41
  # The HTTP header through which profile results are returned.
105
42
  # Lowercase because that's how httplib2 does things.
106
43
  _PROFILE_RESPONSE_HEADER_LOWERCASE = 'x-earth-engine-computation-profile'
@@ -160,13 +97,39 @@ _NOT_INITIALIZED_MESSAGE = (
160
97
  )
161
98
 
162
99
 
100
+ class _ThreadLocals(threading.local):
101
+ """Storage for thread local variables."""
102
+
103
+ def __init__(self):
104
+ # pylint: disable=super-init-not-called
105
+
106
+ # A function called when profile results are received from the server. Takes
107
+ # the profile ID as an argument. None if profiling is disabled.
108
+ #
109
+ # This is a thread-local variable because the alternative is to add a
110
+ # parameter to ee.data.send_, which would then have to be propagated from
111
+ # the assorted API call functions (ee.data.getInfo, ee.data.getMapId, etc.),
112
+ # and the user would have to modify each call to profile, rather than
113
+ # enabling profiling as a wrapper around the entire program (with
114
+ # ee.data.profiling, defined below).
115
+ self.profile_hook: Callable[[str], None] | None = None
116
+
117
+
118
+ _thread_locals = _ThreadLocals()
119
+
120
+
121
+ def _get_state() -> _state.EEState:
122
+ """Returns the current state, or a default state if not initialized."""
123
+ return _state.get_state()
124
+
125
+
163
126
  def initialize(
164
127
  credentials: Any = None,
165
- api_base_url: Optional[str] = None,
166
- tile_base_url: Optional[str] = None,
167
- cloud_api_base_url: Optional[str] = None,
168
- cloud_api_key: Optional[str] = None,
169
- project: Optional[str] = None,
128
+ api_base_url: str | None = None,
129
+ tile_base_url: str | None = None,
130
+ cloud_api_base_url: str | None = None,
131
+ cloud_api_key: str | None = None,
132
+ project: str | None = None,
170
133
  http_transport: Any = None,
171
134
  ) -> None:
172
135
  """Initializes the data module, setting credentials and base URLs.
@@ -187,58 +150,51 @@ def initialize(
187
150
  project: The client project ID or number to use when making API calls.
188
151
  http_transport: The http transport to use
189
152
  """
190
- global _api_base_url, _tile_base_url, _credentials, _initialized
191
- global _requests_session
192
- global _cloud_api_base_url
193
- global _cloud_api_key
194
- global _cloud_api_user_project, _http_transport
195
- global _cloud_api_client_version
196
-
153
+ state = _get_state()
197
154
  # If already initialized, only replace the explicitly specified parts.
198
155
 
199
156
  if credentials is not None:
200
- _credentials = credentials
157
+ state.credentials = credentials
201
158
 
202
159
  if api_base_url is not None:
203
- _api_base_url = api_base_url
204
- elif not _initialized:
205
- _api_base_url = DEFAULT_API_BASE_URL
160
+ state.api_base_url = api_base_url
161
+ elif not state.initialized:
162
+ state.api_base_url = DEFAULT_API_BASE_URL
206
163
 
207
164
  if tile_base_url is not None:
208
- _tile_base_url = tile_base_url
209
- elif not _initialized:
210
- _tile_base_url = DEFAULT_TILE_BASE_URL
165
+ state.tile_base_url = tile_base_url
166
+ elif not state.initialized:
167
+ state.tile_base_url = DEFAULT_TILE_BASE_URL
211
168
 
212
169
  if cloud_api_key is not None:
213
- _cloud_api_key = cloud_api_key
170
+ state.cloud_api_key = cloud_api_key
214
171
 
215
172
  if cloud_api_base_url is not None:
216
- _cloud_api_base_url = cloud_api_base_url
217
- elif not _initialized:
218
- _cloud_api_base_url = DEFAULT_CLOUD_API_BASE_URL
173
+ state.cloud_api_base_url = cloud_api_base_url
174
+ elif not state.initialized:
175
+ state.cloud_api_base_url = DEFAULT_CLOUD_API_BASE_URL
219
176
 
220
177
  if __version__ is not None:
221
178
  version = __version__
222
- _cloud_api_client_version = version
179
+ state.cloud_api_client_version = version
223
180
 
224
- _http_transport = http_transport
181
+ state.http_transport = http_transport
225
182
 
226
- if _requests_session is None:
227
- _requests_session = requests.Session()
183
+ if state.requests_session is None:
184
+ state.requests_session = requests.Session()
228
185
 
229
186
  _install_cloud_api_resource()
230
187
 
231
188
  if project is not None:
232
- _cloud_api_user_project = project
233
- _cloud_api_utils.set_cloud_api_user_project(project)
189
+ state.cloud_api_user_project = project
234
190
  else:
235
- _cloud_api_utils.set_cloud_api_user_project(DEFAULT_CLOUD_API_USER_PROJECT)
191
+ state.cloud_api_user_project = DEFAULT_CLOUD_API_USER_PROJECT
236
192
 
237
- _initialized = True
193
+ state.initialized = True
238
194
 
239
195
 
240
196
  def is_initialized() -> bool:
241
- return _initialized
197
+ return _get_state().initialized
242
198
 
243
199
 
244
200
  def get_persistent_credentials() -> credentials_lib.Credentials:
@@ -254,7 +210,7 @@ def get_persistent_credentials() -> credentials_lib.Credentials:
254
210
  args = {}
255
211
  try:
256
212
  args = oauth.get_credentials_arguments()
257
- except IOError:
213
+ except OSError:
258
214
  pass
259
215
 
260
216
  if args.get('refresh_token'):
@@ -286,93 +242,78 @@ def get_persistent_credentials() -> credentials_lib.Credentials:
286
242
 
287
243
 
288
244
  def reset() -> None:
289
- """Resets the data module, clearing credentials and custom base URLs."""
290
- global _api_base_url, _tile_base_url, _credentials, _initialized
291
- global _requests_session, _cloud_api_resource, _cloud_api_resource_raw
292
- global _cloud_api_base_url, _cloud_api_user_project
293
- global _cloud_api_key, _http_transport
294
- _credentials = None
295
- _api_base_url = None
296
- _tile_base_url = None
297
- if _requests_session is not None:
298
- _requests_session.close()
299
- _requests_session = None
300
- _cloud_api_base_url = None
301
- _cloud_api_key = None
302
- _cloud_api_resource = None
303
- _cloud_api_resource_raw = None
304
- _cloud_api_user_project = None
305
- _cloud_api_utils.set_cloud_api_user_project(DEFAULT_CLOUD_API_USER_PROJECT)
306
- _http_transport = None
307
- _initialized = False
245
+ """Resets the EE state, clearing credentials and custom base URLs."""
246
+ _state.reset_state()
308
247
 
309
248
 
310
249
  def _get_projects_path() -> str:
311
250
  """Returns the projects path to use for constructing a request."""
312
- if _cloud_api_user_project is not None:
313
- return 'projects/' + _cloud_api_user_project
314
- else:
315
- return 'projects/' + DEFAULT_CLOUD_API_USER_PROJECT
251
+ return f'projects/{_get_state().cloud_api_user_project}'
316
252
 
317
253
 
318
254
  def _install_cloud_api_resource() -> None:
319
255
  """Builds or rebuilds the Cloud API resource object, if needed."""
320
- global _cloud_api_resource, _cloud_api_resource_raw
321
-
322
- timeout = (_deadline_ms / 1000.0) or None
323
- assert _requests_session is not None
324
- _cloud_api_resource = _cloud_api_utils.build_cloud_resource(
325
- _cloud_api_base_url,
326
- _requests_session,
327
- credentials=_credentials,
328
- api_key=_cloud_api_key,
256
+ state = _get_state()
257
+
258
+ timeout = (state.deadline_ms / 1000.0) or None
259
+ assert state.requests_session is not None
260
+ state.cloud_api_resource = _cloud_api_utils.build_cloud_resource(
261
+ state.cloud_api_base_url,
262
+ state.requests_session,
263
+ credentials=state.credentials,
264
+ api_key=state.cloud_api_key,
329
265
  timeout=timeout,
330
- num_retries=_max_retries,
266
+ num_retries=state.max_retries,
331
267
  headers_supplier=_make_request_headers,
332
268
  response_inspector=_handle_profiling_response,
333
- http_transport=_http_transport,
269
+ http_transport=state.http_transport,
334
270
  )
335
271
 
336
- _cloud_api_resource_raw = _cloud_api_utils.build_cloud_resource(
337
- _cloud_api_base_url,
338
- _requests_session,
339
- credentials=_credentials,
340
- api_key=_cloud_api_key,
272
+ state.cloud_api_resource_raw = _cloud_api_utils.build_cloud_resource(
273
+ state.cloud_api_base_url,
274
+ state.requests_session,
275
+ credentials=state.credentials,
276
+ api_key=state.cloud_api_key,
341
277
  timeout=timeout,
342
- num_retries=_max_retries,
278
+ num_retries=state.max_retries,
343
279
  headers_supplier=_make_request_headers,
344
280
  response_inspector=_handle_profiling_response,
345
- http_transport=_http_transport,
281
+ http_transport=state.http_transport,
346
282
  raw=True,
347
283
  )
348
284
 
349
285
 
350
286
  def _get_cloud_projects() -> Any:
351
- if _cloud_api_resource is None:
287
+ state = _get_state()
288
+ if state.cloud_api_resource is None:
352
289
  raise ee_exception.EEException(_NOT_INITIALIZED_MESSAGE)
353
- return _cloud_api_resource.projects()
290
+ return state.cloud_api_resource.projects()
354
291
 
355
292
 
356
293
  def _get_cloud_projects_raw() -> Any:
357
- if _cloud_api_resource_raw is None:
294
+ state = _get_state()
295
+ if state.cloud_api_resource_raw is None:
358
296
  raise ee_exception.EEException(_NOT_INITIALIZED_MESSAGE)
359
- return _cloud_api_resource_raw.projects()
297
+ return state.cloud_api_resource_raw.projects()
360
298
 
361
299
 
362
- def _make_request_headers() -> Optional[Dict[str, Any]]:
300
+ def _make_request_headers() -> dict[str, Any] | None:
363
301
  """Adds headers based on client context."""
364
- headers: Dict[str, Any] = {}
365
- client_version_header_values: List[Any] = []
366
- if _cloud_api_client_version is not None:
367
- client_version_header_values.append('ee-py/' + _cloud_api_client_version)
368
- if _user_agent is not None:
369
- headers[_USER_AGENT_HEADER] = _user_agent
302
+ state = _get_state()
303
+ headers: dict[str, Any] = {}
304
+ client_version_header_values: list[Any] = []
305
+ if state.cloud_api_client_version is not None:
306
+ client_version_header_values.append(
307
+ f'ee-py/{state.cloud_api_client_version}'
308
+ )
309
+ if state.user_agent is not None:
310
+ headers[_USER_AGENT_HEADER] = state.user_agent
370
311
  client_version_header_values.append('python/' + platform.python_version())
371
312
  headers[_API_CLIENT_VERSION_HEADER] = ' '.join(client_version_header_values)
372
313
  if _thread_locals.profile_hook:
373
314
  headers[_PROFILE_REQUEST_HEADER] = '1'
374
- if _cloud_api_user_project is not None:
375
- headers[_USER_PROJECT_OVERRIDE_HEADER] = _cloud_api_user_project
315
+ if state.cloud_api_user_project is not DEFAULT_CLOUD_API_USER_PROJECT:
316
+ headers[_USER_PROJECT_OVERRIDE_HEADER] = state.cloud_api_user_project
376
317
  if headers:
377
318
  return headers
378
319
  return None
@@ -388,7 +329,7 @@ def _handle_profiling_response(response: httplib2.Response) -> None:
388
329
 
389
330
 
390
331
  def _execute_cloud_call(
391
- call: googleapiclient.http.HttpRequest, num_retries: Optional[int] = None
332
+ call: googleapiclient.http.HttpRequest, num_retries: int | None = None
392
333
  ) -> Any:
393
334
  """Executes a Cloud API call and translates errors to EEExceptions.
394
335
 
@@ -403,7 +344,7 @@ def _execute_cloud_call(
403
344
  Raises:
404
345
  EEException if the call fails.
405
346
  """
406
- num_retries = _max_retries if num_retries is None else num_retries
347
+ num_retries = _get_state().max_retries if num_retries is None else num_retries
407
348
  try:
408
349
  return call.execute(num_retries=num_retries)
409
350
  except googleapiclient.errors.HttpError as e:
@@ -426,7 +367,7 @@ def _translate_cloud_exception(
426
367
  return ee_exception.EEException(http_error._get_reason()) # pylint: disable=protected-access
427
368
 
428
369
 
429
- def _maybe_populate_workload_tag(body: Dict[str, Any]) -> None:
370
+ def _maybe_populate_workload_tag(body: dict[str, Any]) -> None:
430
371
  """Populates the workload tag on the request body passed in if applicable.
431
372
 
432
373
  Defaults to the workload tag set by ee.data.setWorkloadTag() or related
@@ -446,24 +387,20 @@ def _maybe_populate_workload_tag(body: Dict[str, Any]) -> None:
446
387
 
447
388
  def setCloudApiKey(cloud_api_key: str) -> None:
448
389
  """Sets the Cloud API key parameter ("api_key") for all requests."""
449
- global _cloud_api_key
450
- _cloud_api_key = cloud_api_key
390
+ _get_state().cloud_api_key = cloud_api_key
451
391
  _install_cloud_api_resource()
452
392
 
453
393
 
454
394
  def setCloudApiUserProject(cloud_api_user_project: str) -> None:
455
- global _cloud_api_user_project
456
- _cloud_api_user_project = cloud_api_user_project
457
- _cloud_api_utils.set_cloud_api_user_project(_cloud_api_user_project)
395
+ _get_state().cloud_api_user_project = cloud_api_user_project
458
396
 
459
397
 
460
398
  def setUserAgent(user_agent: str) -> None:
461
- global _user_agent
462
- _user_agent = user_agent
399
+ _get_state().user_agent = user_agent
463
400
 
464
401
 
465
- def getUserAgent() -> Optional[str]:
466
- return _user_agent
402
+ def getUserAgent() -> str | None:
403
+ return _get_state().user_agent
467
404
 
468
405
 
469
406
  def setDeadline(milliseconds: float) -> None:
@@ -473,8 +410,7 @@ def setDeadline(milliseconds: float) -> None:
473
410
  milliseconds: The number of milliseconds to wait for a request
474
411
  before considering it timed out. 0 means no limit.
475
412
  """
476
- global _deadline_ms
477
- _deadline_ms = milliseconds
413
+ _get_state().deadline_ms = milliseconds
478
414
  _install_cloud_api_resource()
479
415
 
480
416
 
@@ -488,8 +424,7 @@ def setMaxRetries(max_retries: int) -> None:
488
424
  raise ValueError('max_retries must be non-negative')
489
425
  if max_retries >= 100:
490
426
  raise ValueError('Too many retries')
491
- global _max_retries
492
- _max_retries = max_retries
427
+ _get_state().max_retries = max_retries
493
428
 
494
429
 
495
430
  @contextlib.contextmanager
@@ -514,7 +449,7 @@ def profiling(hook: Any) -> Iterator[None]:
514
449
 
515
450
 
516
451
  @deprecation.Deprecated('Use getAsset')
517
- def getInfo(asset_id: str) -> Optional[Any]:
452
+ def getInfo(asset_id: str) -> Any | None:
518
453
  """Load info for an asset, given an asset id.
519
454
 
520
455
  Args:
@@ -531,7 +466,7 @@ def getInfo(asset_id: str) -> Optional[Any]:
531
466
  _get_cloud_projects()
532
467
  .assets()
533
468
  .get(name=name, prettyPrint=False)
534
- .execute(num_retries=_max_retries)
469
+ .execute(num_retries=_get_state().max_retries)
535
470
  )
536
471
  except googleapiclient.errors.HttpError as e:
537
472
  if e.resp.status == 404:
@@ -556,7 +491,7 @@ def getAsset(asset_id: str) -> Any:
556
491
 
557
492
 
558
493
  @deprecation.Deprecated('Use listAssets or listImages')
559
- def getList(params: Dict[str, Any]) -> Any:
494
+ def getList(params: dict[str, Any]) -> Any:
560
495
  """Get a list of contents for a collection asset.
561
496
 
562
497
  Args:
@@ -577,8 +512,8 @@ def getList(params: Dict[str, Any]) -> Any:
577
512
 
578
513
 
579
514
  def listImages(
580
- params: Union[str, Dict[str, Any]],
581
- ) -> Dict[str, Optional[List[Any]]]:
515
+ params: str | dict[str, Any],
516
+ ) -> dict[str, list[Any] | None]:
582
517
  """Returns the images in an image collection or folder.
583
518
 
584
519
  Args:
@@ -615,7 +550,7 @@ def listImages(
615
550
  return images
616
551
 
617
552
 
618
- def listAssets(params: Union[str, Dict[str, Any]]) -> Dict[str, List[Any]]:
553
+ def listAssets(params: str | dict[str, Any]) -> dict[str, list[Any]]:
619
554
  """Returns the assets in a folder.
620
555
 
621
556
  Args:
@@ -668,7 +603,7 @@ def listAssets(params: Union[str, Dict[str, Any]]) -> Dict[str, List[Any]]:
668
603
  return assets
669
604
 
670
605
 
671
- def listBuckets(project: Optional[str] = None) -> Any:
606
+ def listBuckets(project: str | None = None) -> Any:
672
607
  """Returns top-level assets and folders for the Cloud Project or user.
673
608
 
674
609
  Args:
@@ -687,7 +622,7 @@ def listBuckets(project: Optional[str] = None) -> Any:
687
622
  return _execute_cloud_call(_get_cloud_projects().listAssets(parent=project))
688
623
 
689
624
 
690
- def getMapId(params: Dict[str, Any]) -> Dict[str, Any]:
625
+ def getMapId(params: dict[str, Any]) -> dict[str, Any]:
691
626
  """Get a Map ID for a given asset.
692
627
 
693
628
  Args:
@@ -752,17 +687,18 @@ def getMapId(params: Dict[str, Any]) -> Dict[str, Any]:
752
687
  .maps()
753
688
  .create(parent=_get_projects_path(), **queryParams)
754
689
  )
690
+ state = _get_state()
755
691
  map_name = result['name']
756
- url_format = '%s/%s/%s/tiles/{z}/{x}/{y}' % (
757
- _tile_base_url, _cloud_api_utils.VERSION, map_name)
758
- if _cloud_api_key:
759
- url_format += '?key=%s' % _cloud_api_key
692
+ url_format = '{}/{}/{}/tiles/{{z}}/{{x}}/{{y}}'.format(
693
+ state.tile_base_url, _cloud_api_utils.VERSION, map_name)
694
+ if state.cloud_api_key:
695
+ url_format += f'?key={state.cloud_api_key}'
760
696
 
761
697
  return {'mapid': map_name, 'token': '',
762
698
  'tile_fetcher': TileFetcher(url_format, map_name=map_name)}
763
699
 
764
700
 
765
- def getFeatureViewTilesKey(params: Dict[str, Any]) -> Dict[str, Any]:
701
+ def getFeatureViewTilesKey(params: dict[str, Any]) -> dict[str, Any]:
766
702
  """Get a tiles key for a given map or asset.
767
703
 
768
704
  Args:
@@ -792,8 +728,9 @@ def getFeatureViewTilesKey(params: Dict[str, Any]) -> Dict[str, Any]:
792
728
  )
793
729
  name = result['name']
794
730
  version = _cloud_api_utils.VERSION
795
- format_tile_url = (
796
- lambda x, y, z: f'{_tile_base_url}/{version}/{name}/tiles/{z}/{x}/{y}')
731
+ format_tile_url = lambda x, y, z: (
732
+ f'{_get_state().tile_base_url}/{version}/{name}/tiles/{z}/{x}/{y}'
733
+ )
797
734
  token = name.rsplit('/', 1).pop()
798
735
  return {
799
736
  'token': token,
@@ -801,7 +738,7 @@ def getFeatureViewTilesKey(params: Dict[str, Any]) -> Dict[str, Any]:
801
738
  }
802
739
 
803
740
 
804
- def _extract_table_converter(params: Dict[str, Any]) -> Optional[Any]:
741
+ def _extract_table_converter(params: dict[str, Any]) -> Any | None:
805
742
  if 'fileFormat' in params:
806
743
  file_format = params.get('fileFormat')
807
744
  converter = table_converter.from_file_format(file_format)
@@ -812,7 +749,7 @@ def _extract_table_converter(params: Dict[str, Any]) -> Optional[Any]:
812
749
 
813
750
 
814
751
  def _extract_image_converter(
815
- params: Dict[str, Any]
752
+ params: dict[str, Any]
816
753
  ) -> image_converter.ImageConverter:
817
754
  file_format = params.get('fileFormat')
818
755
  converter = image_converter.from_file_format(file_format)
@@ -826,14 +763,13 @@ def _generate(func, list_key: str, **kwargs) -> Iterator[Any]:
826
763
  args = kwargs.copy()
827
764
  while True:
828
765
  response = func(**args)
829
- for obj in response.get(list_key, []):
830
- yield obj
766
+ yield from response.get(list_key, [])
831
767
  if _NEXT_PAGE_TOKEN_KEY not in response:
832
768
  break
833
769
  args['params'].update({'pageToken': response[_NEXT_PAGE_TOKEN_KEY]})
834
770
 
835
771
 
836
- def listFeatures(params: Dict[str, Any]) -> Any:
772
+ def listFeatures(params: dict[str, Any]) -> Any:
837
773
  """List features for a given table or FeatureView asset.
838
774
 
839
775
  Args:
@@ -878,7 +814,7 @@ def listFeatures(params: Dict[str, Any]) -> Any:
878
814
  return call(params)
879
815
 
880
816
 
881
- def getPixels(params: Dict[str, Any]) -> Any:
817
+ def getPixels(params: dict[str, Any]) -> Any:
882
818
  """Fetches pixels from an image asset.
883
819
 
884
820
  Args:
@@ -921,7 +857,7 @@ def getPixels(params: Dict[str, Any]) -> Any:
921
857
  return data
922
858
 
923
859
 
924
- def computePixels(params: Dict[str, Any]) -> Any:
860
+ def computePixels(params: dict[str, Any]) -> Any:
925
861
  """Computes a tile by performing an arbitrary computation on image data.
926
862
 
927
863
  Args:
@@ -961,7 +897,7 @@ def computePixels(params: Dict[str, Any]) -> Any:
961
897
  return data
962
898
 
963
899
 
964
- def computeImages(params: Dict[str, Any]) -> Any:
900
+ def computeImages(params: dict[str, Any]) -> Any:
965
901
  """Computes a list of images by applying a computation to features.
966
902
 
967
903
  Args:
@@ -987,7 +923,7 @@ def computeImages(params: Dict[str, Any]) -> Any:
987
923
  )
988
924
 
989
925
 
990
- def computeFeatures(params: Dict[str, Any]) -> Any:
926
+ def computeFeatures(params: dict[str, Any]) -> Any:
991
927
  """Computes a list of features by applying a computation to features.
992
928
 
993
929
  Args:
@@ -1033,7 +969,7 @@ def computeFeatures(params: Dict[str, Any]) -> Any:
1033
969
  return call(params)
1034
970
 
1035
971
 
1036
- def getTileUrl(mapid: Dict[str, Any], x: float, y: float, z: float) -> str:
972
+ def getTileUrl(mapid: dict[str, Any], x: float, y: float, z: float) -> str:
1037
973
  """Generate a URL for map tiles from a Map ID and coordinates.
1038
974
 
1039
975
  Args:
@@ -1134,7 +1070,7 @@ def computeValue(obj: computedobject.ComputedObject) -> Any:
1134
1070
 
1135
1071
  @deprecation.Deprecated('Use getThumbId and makeThumbUrl')
1136
1072
  def getThumbnail(
1137
- params: Dict[str, Any], thumbType: Optional[str] = None
1073
+ params: dict[str, Any], thumbType: str | None = None
1138
1074
  ) -> Any:
1139
1075
  """Get a Thumbnail for a given asset.
1140
1076
 
@@ -1169,8 +1105,8 @@ def getThumbnail(
1169
1105
 
1170
1106
 
1171
1107
  def getThumbId(
1172
- params: Dict[str, Any], thumbType: Optional[str] = None
1173
- ) -> Dict[str, str]:
1108
+ params: dict[str, Any], thumbType: str | None = None
1109
+ ) -> dict[str, str]:
1174
1110
  """Get a Thumbnail ID for a given asset.
1175
1111
 
1176
1112
  Args:
@@ -1253,7 +1189,7 @@ def getThumbId(
1253
1189
  return {'thumbid': result['name'], 'token': ''}
1254
1190
 
1255
1191
 
1256
- def makeThumbUrl(thumbId: Dict[str, str]) -> str:
1192
+ def makeThumbUrl(thumbId: dict[str, str]) -> str:
1257
1193
  """Create a thumbnail URL from the given thumbid.
1258
1194
 
1259
1195
  Args:
@@ -1262,14 +1198,16 @@ def makeThumbUrl(thumbId: Dict[str, str]) -> str:
1262
1198
  Returns:
1263
1199
  A URL from which the thumbnail can be obtained.
1264
1200
  """
1265
- url = '%s/%s/%s:getPixels' % (_tile_base_url, _cloud_api_utils.VERSION,
1266
- thumbId['thumbid'])
1267
- if _cloud_api_key:
1268
- url += '?key=%s' % _cloud_api_key
1201
+ state = _get_state()
1202
+ url = '{}/{}/{}:getPixels'.format(
1203
+ state.tile_base_url, _cloud_api_utils.VERSION, thumbId['thumbid']
1204
+ )
1205
+ if state.cloud_api_key:
1206
+ url += f'?key={state.cloud_api_key}'
1269
1207
  return url
1270
1208
 
1271
1209
 
1272
- def getDownloadId(params: Dict[str, Any]) -> Dict[str, str]:
1210
+ def getDownloadId(params: dict[str, Any]) -> dict[str, str]:
1273
1211
  """Get a Download ID.
1274
1212
 
1275
1213
  Args:
@@ -1385,7 +1323,7 @@ def getDownloadId(params: Dict[str, Any]) -> Dict[str, str]:
1385
1323
  return {'docid': result['name'], 'token': ''}
1386
1324
 
1387
1325
 
1388
- def makeDownloadUrl(downloadId: Dict[str, str]) -> str:
1326
+ def makeDownloadUrl(downloadId: dict[str, str]) -> str:
1389
1327
  """Create a download URL from the given docid.
1390
1328
 
1391
1329
  Args:
@@ -1394,11 +1332,12 @@ def makeDownloadUrl(downloadId: Dict[str, str]) -> str:
1394
1332
  Returns:
1395
1333
  A URL from which the download can be obtained.
1396
1334
  """
1397
- return '%s/%s/%s:getPixels' % (_tile_base_url, _cloud_api_utils.VERSION,
1398
- downloadId['docid'])
1335
+ return '{}/{}/{}:getPixels'.format(
1336
+ _get_state().tile_base_url, _cloud_api_utils.VERSION, downloadId['docid']
1337
+ )
1399
1338
 
1400
1339
 
1401
- def getTableDownloadId(params: Dict[str, Any]) -> Dict[str, str]:
1340
+ def getTableDownloadId(params: dict[str, Any]) -> dict[str, str]:
1402
1341
  """Get a Download ID.
1403
1342
 
1404
1343
  Args:
@@ -1447,7 +1386,7 @@ def getTableDownloadId(params: Dict[str, Any]) -> Dict[str, str]:
1447
1386
  return {'docid': result['name'], 'token': ''}
1448
1387
 
1449
1388
 
1450
- def makeTableDownloadUrl(downloadId: Dict[str, str]) -> str:
1389
+ def makeTableDownloadUrl(downloadId: dict[str, str]) -> str:
1451
1390
  """Create a table download URL from a docid.
1452
1391
 
1453
1392
  Args:
@@ -1456,19 +1395,20 @@ def makeTableDownloadUrl(downloadId: Dict[str, str]) -> str:
1456
1395
  Returns:
1457
1396
  A Url from which the download can be obtained.
1458
1397
  """
1459
- return '%s/%s/%s:getFeatures' % (
1460
- _tile_base_url, _cloud_api_utils.VERSION, downloadId['docid'])
1398
+ return '{}/{}/{}:getFeatures'.format(
1399
+ _get_state().tile_base_url, _cloud_api_utils.VERSION, downloadId['docid']
1400
+ )
1461
1401
 
1462
1402
 
1463
1403
  def getAlgorithms() -> Any:
1464
1404
  """Get the list of algorithms.
1465
1405
 
1466
1406
  Returns:
1467
- The dictionary of algorithms. Each algorithm is a dictionary containing
1407
+ The dictionary of algorithms. Each algorithm is a dictionary containing
1468
1408
  the following fields:
1469
1409
  "description" - (string) A text description of the algorithm.
1470
1410
  "returns" - (string) The return type of the algorithm.
1471
- "args" - An array of arguments. Each argument specifies the following:
1411
+ "args" - An array of arguments. Each argument specifies the following:
1472
1412
  "name" - (string) The name of the argument.
1473
1413
  "description" - (string) A text description of the argument.
1474
1414
  "type" - (string) The type of the argument.
@@ -1501,10 +1441,10 @@ def getAlgorithms() -> Any:
1501
1441
 
1502
1442
  @_utils.accept_opt_prefix('opt_path', 'opt_force', 'opt_properties')
1503
1443
  def createAsset(
1504
- value: Dict[str, Any],
1505
- path: Optional[str] = None,
1506
- properties: Optional[Dict[str, Any]] = None,
1507
- ) -> Dict[str, Any]:
1444
+ value: dict[str, Any],
1445
+ path: str | None = None,
1446
+ properties: dict[str, Any] | None = None,
1447
+ ) -> dict[str, Any]:
1508
1448
  """Creates an asset from a JSON value.
1509
1449
 
1510
1450
  To create an empty image collection or folder, pass in a "value" object
@@ -1560,7 +1500,7 @@ def createAsset(
1560
1500
  )
1561
1501
 
1562
1502
 
1563
- def createFolder(path: str) -> Dict[str, Any]:
1503
+ def createFolder(path: str) -> dict[str, Any]:
1564
1504
  """Creates an asset folder.
1565
1505
 
1566
1506
  Returns a description of the newly created folder.
@@ -1624,7 +1564,7 @@ def deleteAsset(assetId: str) -> None:
1624
1564
  _execute_cloud_call(_get_cloud_projects().assets().delete(name=name))
1625
1565
 
1626
1566
 
1627
- def newTaskId(count: int = 1) -> List[str]:
1567
+ def newTaskId(count: int = 1) -> list[str]:
1628
1568
  """Generate an ID for a long-running task.
1629
1569
 
1630
1570
  Args:
@@ -1637,7 +1577,7 @@ def newTaskId(count: int = 1) -> List[str]:
1637
1577
 
1638
1578
 
1639
1579
  @deprecation.Deprecated('Use listOperations')
1640
- def getTaskList() -> List[Any]:
1580
+ def getTaskList() -> list[Any]:
1641
1581
  """Retrieves a list of the user's tasks.
1642
1582
 
1643
1583
  Returns:
@@ -1649,7 +1589,7 @@ def getTaskList() -> List[Any]:
1649
1589
  for o in listOperations()]
1650
1590
 
1651
1591
 
1652
- def listOperations(project: Optional[str] = None) -> List[Any]:
1592
+ def listOperations(project: str | None = None) -> list[Any]:
1653
1593
  """Retrieves a list of the user's tasks.
1654
1594
 
1655
1595
  Args:
@@ -1676,11 +1616,12 @@ def listOperations(project: Optional[str] = None) -> List[Any]:
1676
1616
 
1677
1617
 
1678
1618
  @deprecation.Deprecated('Use getOperation')
1679
- def getTaskStatus(taskId: Union[List[str], str]) -> List[Any]:
1619
+ def getTaskStatus(taskId: list[str] | str) -> list[Any]:
1680
1620
  """Retrieve status of one or more long-running tasks.
1681
1621
 
1682
1622
  Args:
1683
- taskId: ID of the task or a list of multiple IDs.
1623
+ taskId: ID of the task or a list of multiple IDs. These will be assumed to
1624
+ be running in the currently initialized project.
1684
1625
 
1685
1626
  Returns:
1686
1627
  List containing one object for each queried task, in the same order as
@@ -1693,21 +1634,25 @@ def getTaskStatus(taskId: Union[List[str], str]) -> List[Any]:
1693
1634
  """
1694
1635
  if isinstance(taskId, str):
1695
1636
  taskId = [taskId]
1637
+ state = _get_state()
1696
1638
  result = []
1697
1639
  for one_id in taskId:
1640
+ # Don't use getOperation as it will translate the exception, and we need
1641
+ # to handle 404s specially.
1642
+ name = _cloud_api_utils.convert_task_id_to_operation_name(
1643
+ state.cloud_api_user_project, one_id
1644
+ )
1698
1645
  try:
1699
- # Don't use getOperation as it will translate the exception, and we need
1700
- # to handle 404s specially.
1701
1646
  operation = (
1702
1647
  _get_cloud_projects()
1703
1648
  .operations()
1704
- .get(name=_cloud_api_utils.convert_task_id_to_operation_name(one_id))
1705
- .execute(num_retries=_max_retries)
1649
+ .get(name=name)
1650
+ .execute(num_retries=state.max_retries)
1706
1651
  )
1707
1652
  result.append(_cloud_api_utils.convert_operation_to_task(operation))
1708
1653
  except googleapiclient.errors.HttpError as e:
1709
1654
  if e.resp.status == 404:
1710
- result.append({'id': one_id, 'state': 'UNKNOWN'})
1655
+ result.append({'id': one_id, 'state': 'UNKNOWN', 'name': name})
1711
1656
  else:
1712
1657
  raise _translate_cloud_exception(e) # pylint: disable=raise-missing-from
1713
1658
  return result
@@ -1731,7 +1676,11 @@ def getOperation(operation_name: str) -> Any:
1731
1676
  @deprecation.Deprecated('Use cancelOperation')
1732
1677
  def cancelTask(taskId: str) -> None:
1733
1678
  """Cancels a batch task."""
1734
- cancelOperation(_cloud_api_utils.convert_task_id_to_operation_name(taskId))
1679
+ cancelOperation(
1680
+ _cloud_api_utils.convert_task_id_to_operation_name(
1681
+ _get_state().cloud_api_user_project, taskId
1682
+ )
1683
+ )
1735
1684
 
1736
1685
 
1737
1686
  def cancelOperation(operation_name: str) -> None:
@@ -1740,7 +1689,7 @@ def cancelOperation(operation_name: str) -> None:
1740
1689
  )
1741
1690
 
1742
1691
 
1743
- def exportImage(request_id: str, params: Dict[str, Any]) -> Any:
1692
+ def exportImage(request_id: str, params: dict[str, Any]) -> Any:
1744
1693
  """Starts an image export task running.
1745
1694
 
1746
1695
  This is a low-level method. The higher-level ee.batch.Export.image object
@@ -1767,7 +1716,7 @@ def exportImage(request_id: str, params: Dict[str, Any]) -> Any:
1767
1716
  )
1768
1717
 
1769
1718
 
1770
- def exportTable(request_id: str, params: Dict[str, Any]) -> Any:
1719
+ def exportTable(request_id: str, params: dict[str, Any]) -> Any:
1771
1720
  """Starts a table export task running.
1772
1721
 
1773
1722
  This is a low-level method. The higher-level ee.batch.Export.table object
@@ -1794,7 +1743,7 @@ def exportTable(request_id: str, params: Dict[str, Any]) -> Any:
1794
1743
  )
1795
1744
 
1796
1745
 
1797
- def exportVideo(request_id: str, params: Dict[str, Any]) -> Any:
1746
+ def exportVideo(request_id: str, params: dict[str, Any]) -> Any:
1798
1747
  """Starts a video export task running.
1799
1748
 
1800
1749
  This is a low-level method. The higher-level ee.batch.Export.video object
@@ -1821,7 +1770,7 @@ def exportVideo(request_id: str, params: Dict[str, Any]) -> Any:
1821
1770
  )
1822
1771
 
1823
1772
 
1824
- def exportMap(request_id: str, params: Dict[str, Any]) -> Any:
1773
+ def exportMap(request_id: str, params: dict[str, Any]) -> Any:
1825
1774
  """Starts a map export task running.
1826
1775
 
1827
1776
  This is a low-level method. The higher-level ee.batch.Export.map object
@@ -1848,7 +1797,7 @@ def exportMap(request_id: str, params: Dict[str, Any]) -> Any:
1848
1797
  )
1849
1798
 
1850
1799
 
1851
- def exportClassifier(request_id: str, params: Dict[str, Any]) -> Any:
1800
+ def exportClassifier(request_id: str, params: dict[str, Any]) -> Any:
1852
1801
  """Starts a classifier export task.
1853
1802
 
1854
1803
  This is a low-level method. The higher-level ee.batch.Export.classifier
@@ -1876,7 +1825,7 @@ def exportClassifier(request_id: str, params: Dict[str, Any]) -> Any:
1876
1825
 
1877
1826
 
1878
1827
  def _prepare_and_run_export(
1879
- request_id: str, params: Dict[str, Any], export_endpoint: Any
1828
+ request_id: str, params: dict[str, Any], export_endpoint: Any
1880
1829
  ) -> Any:
1881
1830
  """Starts an export task running.
1882
1831
 
@@ -1905,7 +1854,7 @@ def _prepare_and_run_export(
1905
1854
  if isinstance(params['expression'], encodable.Encodable):
1906
1855
  params['expression'] = serializer.encode(
1907
1856
  params['expression'], for_cloud_api=True)
1908
- num_retries = _max_retries if request_id else 0
1857
+ num_retries = _get_state().max_retries if request_id else 0
1909
1858
  return _execute_cloud_call(
1910
1859
  export_endpoint(project=_get_projects_path(), body=params),
1911
1860
  num_retries=num_retries)
@@ -1917,10 +1866,10 @@ _EXTERNAL_IMPORT = 'EXTERNAL_IMPORT'
1917
1866
 
1918
1867
  def _startIngestion(
1919
1868
  request_id: Any,
1920
- params: Dict[str, Any],
1869
+ params: dict[str, Any],
1921
1870
  allow_overwrite: bool = False,
1922
- import_mode: Optional[str] = _INTERNAL_IMPORT,
1923
- ) -> Dict[str, Any]:
1871
+ import_mode: str | None = _INTERNAL_IMPORT,
1872
+ ) -> dict[str, Any]:
1924
1873
  """Starts an ingestion task or creates an external image."""
1925
1874
  request = {
1926
1875
  'imageManifest':
@@ -1931,7 +1880,7 @@ def _startIngestion(
1931
1880
 
1932
1881
  # It's only safe to retry the request if there's a unique ID to make it
1933
1882
  # idempotent.
1934
- num_retries = _max_retries if request_id else 0
1883
+ num_retries = _get_state().max_retries if request_id else 0
1935
1884
 
1936
1885
  image = _get_cloud_projects().image()
1937
1886
  if import_mode == _INTERNAL_IMPORT:
@@ -1941,9 +1890,7 @@ def _startIngestion(
1941
1890
  project=_get_projects_path(), body=request
1942
1891
  )
1943
1892
  else:
1944
- raise ee_exception.EEException(
1945
- '{} is not a valid import mode'.format(import_mode)
1946
- )
1893
+ raise ee_exception.EEException(f'{import_mode} is not a valid import mode')
1947
1894
 
1948
1895
  result = _execute_cloud_call(
1949
1896
  import_request,
@@ -1964,9 +1911,9 @@ def _startIngestion(
1964
1911
 
1965
1912
  def startIngestion(
1966
1913
  request_id: Any,
1967
- params: Dict[str, Any],
1914
+ params: dict[str, Any],
1968
1915
  allow_overwrite: bool = False,
1969
- ) -> Dict[str, Any]:
1916
+ ) -> dict[str, Any]:
1970
1917
  """Creates an image asset import task.
1971
1918
 
1972
1919
  Args:
@@ -2001,9 +1948,9 @@ def startIngestion(
2001
1948
 
2002
1949
 
2003
1950
  def startExternalImageIngestion(
2004
- image_manifest: Dict[str, Any],
1951
+ image_manifest: dict[str, Any],
2005
1952
  allow_overwrite: bool = False,
2006
- ) -> Dict[str, Any]:
1953
+ ) -> dict[str, Any]:
2007
1954
  """Creates an external image.
2008
1955
 
2009
1956
  Args:
@@ -2033,8 +1980,8 @@ def startExternalImageIngestion(
2033
1980
 
2034
1981
 
2035
1982
  def startTableIngestion(
2036
- request_id: str, params: Dict[str, Any], allow_overwrite: bool = False
2037
- ) -> Dict[str, Any]:
1983
+ request_id: str, params: dict[str, Any], allow_overwrite: bool = False
1984
+ ) -> dict[str, Any]:
2038
1985
  """Creates a table asset import task.
2039
1986
 
2040
1987
  Args:
@@ -2068,7 +2015,7 @@ def startTableIngestion(
2068
2015
  }
2069
2016
  # It's only safe to retry the request if there's a unique ID to make it
2070
2017
  # idempotent.
2071
- num_retries = _max_retries if request_id else 0
2018
+ num_retries = _get_state().max_retries if request_id else 0
2072
2019
  operation = _execute_cloud_call(
2073
2020
  _get_cloud_projects()
2074
2021
  .table()
@@ -2103,7 +2050,7 @@ def getAssetRoots() -> Any:
2103
2050
  listBuckets())
2104
2051
 
2105
2052
 
2106
- def getAssetRootQuota(rootId: str) -> Dict[str, Any]:
2053
+ def getAssetRootQuota(rootId: str) -> dict[str, Any]:
2107
2054
  """Returns quota usage details for the asset root with the given ID.
2108
2055
 
2109
2056
  Usage notes:
@@ -2123,7 +2070,7 @@ def getAssetRootQuota(rootId: str) -> Dict[str, Any]:
2123
2070
  """
2124
2071
  asset = getAsset(rootId)
2125
2072
  if 'quota' not in asset:
2126
- raise ee_exception.EEException('{} is not a root folder.'.format(rootId))
2073
+ raise ee_exception.EEException(f'{rootId} is not a root folder.')
2127
2074
  quota = asset['quota']
2128
2075
  # The quota fields are int64s, and int64s are represented as strings in
2129
2076
  # JSON. Turn them back.
@@ -2179,7 +2126,7 @@ def getIamPolicy(asset_id: str) -> Any:
2179
2126
 
2180
2127
 
2181
2128
  @deprecation.Deprecated('Use setIamPolicy')
2182
- def setAssetAcl(assetId: str, aclUpdate: Union[str, Dict[str, Any]]) -> None:
2129
+ def setAssetAcl(assetId: str, aclUpdate: str | dict[str, Any]) -> None:
2183
2130
  """Sets the access control list of the asset with the given ID.
2184
2131
 
2185
2132
  The owner ACL cannot be changed, and the final ACL of the asset
@@ -2214,8 +2161,9 @@ def setIamPolicy(asset_id: str, policy: Any) -> None:
2214
2161
  .setIamPolicy(resource=name, body={'policy': policy}, prettyPrint=False)
2215
2162
  )
2216
2163
 
2164
+
2217
2165
  @deprecation.Deprecated('Use ee.data.updateAsset().')
2218
- def setAssetProperties(assetId: str, properties: Dict[str, Any]) -> None:
2166
+ def setAssetProperties(assetId: str, properties: dict[str, Any]) -> None:
2219
2167
  """Sets metadata properties of the asset with the given ID.
2220
2168
 
2221
2169
  To delete a property, set its value to None.
@@ -2280,7 +2228,7 @@ def _get_config_path() -> str:
2280
2228
  return f'{_get_projects_path()}/config'
2281
2229
 
2282
2230
 
2283
- def getProjectConfig() -> Dict[str, Any]:
2231
+ def getProjectConfig() -> dict[str, Any]:
2284
2232
  """Gets the project config for the current project.
2285
2233
 
2286
2234
  Returns:
@@ -2292,8 +2240,8 @@ def getProjectConfig() -> Dict[str, Any]:
2292
2240
 
2293
2241
 
2294
2242
  def updateProjectConfig(
2295
- project_config: Dict[str, Any], update_mask: Optional[Sequence[str]] = None
2296
- ) -> Dict[str, Any]:
2243
+ project_config: dict[str, Any], update_mask: Sequence[str] | None = None
2244
+ ) -> dict[str, Any]:
2297
2245
  """Updates the project config for the current project.
2298
2246
 
2299
2247
  Args:
@@ -2321,8 +2269,8 @@ def updateProjectConfig(
2321
2269
 
2322
2270
 
2323
2271
  def authorizeHttp(http: Any) -> Any:
2324
- if _credentials:
2325
- return google_auth_httplib2.AuthorizedHttp(_credentials)
2272
+ if credentials := _get_state().credentials:
2273
+ return google_auth_httplib2.AuthorizedHttp(credentials)
2326
2274
  else:
2327
2275
  return http
2328
2276
 
@@ -2364,12 +2312,12 @@ def convert_asset_id_to_asset_name(asset_id: str) -> str:
2364
2312
  return _cloud_api_utils.convert_asset_id_to_asset_name(asset_id)
2365
2313
 
2366
2314
 
2367
- def getWorkloadTag() -> Optional[Union[int, str]]:
2315
+ def getWorkloadTag() -> int | str | None:
2368
2316
  """Returns the currently set workload tag."""
2369
- return _workloadTag.get()
2317
+ return _get_state().workload_tag.get()
2370
2318
 
2371
2319
 
2372
- def setWorkloadTag(tag: Optional[Union[int, str]]) -> None:
2320
+ def setWorkloadTag(tag: int | str | None) -> None:
2373
2321
  """Sets the workload tag, used to label computation and exports.
2374
2322
 
2375
2323
  Workload tag must be 1 - 63 characters, beginning and ending with an
@@ -2379,11 +2327,11 @@ def setWorkloadTag(tag: Optional[Union[int, str]]) -> None:
2379
2327
  Args:
2380
2328
  tag: The tag to set.
2381
2329
  """
2382
- _workloadTag.set(tag)
2330
+ _get_state().workload_tag.set(tag)
2383
2331
 
2384
2332
 
2385
2333
  @contextlib.contextmanager
2386
- def workloadTagContext(tag: Optional[Union[int, str]]) -> Iterator[None]:
2334
+ def workloadTagContext(tag: int | str | None) -> Iterator[None]:
2387
2335
  """Produces a context manager which sets the workload tag, then resets it.
2388
2336
 
2389
2337
  Workload tag must be 1 - 63 characters, beginning and ending with an
@@ -2403,7 +2351,7 @@ def workloadTagContext(tag: Optional[Union[int, str]]) -> Iterator[None]:
2403
2351
  resetWorkloadTag()
2404
2352
 
2405
2353
 
2406
- def setDefaultWorkloadTag(tag: Optional[Union[int, str]]) -> None:
2354
+ def setDefaultWorkloadTag(tag: int | str | None) -> None:
2407
2355
  """Sets the workload tag, and as the default for which to reset back to.
2408
2356
 
2409
2357
  For example, calling `ee.data.resetWorkloadTag()` will reset the workload tag
@@ -2418,8 +2366,9 @@ def setDefaultWorkloadTag(tag: Optional[Union[int, str]]) -> None:
2418
2366
  Args:
2419
2367
  tag: The tag to set.
2420
2368
  """
2421
- _workloadTag.setDefault(tag)
2422
- _workloadTag.set(tag)
2369
+ state = _get_state()
2370
+ state.workload_tag.set_default(tag)
2371
+ state.workload_tag.set(tag)
2423
2372
 
2424
2373
 
2425
2374
  @_utils.accept_opt_prefix('opt_resetDefault')
@@ -2432,58 +2381,7 @@ def resetWorkloadTag(resetDefault: bool = False) -> None:
2432
2381
  Args:
2433
2382
  resetDefault: Whether to reset the default back to empty.
2434
2383
  """
2384
+ state = _get_state()
2435
2385
  if resetDefault:
2436
- _workloadTag.setDefault('')
2437
- _workloadTag.reset()
2438
-
2439
-
2440
- # TODO(user): Consider only returning str even for ints.
2441
- class _WorkloadTag:
2442
- """A helper class to manage the workload tag."""
2443
- _tag: Optional[Union[int, str]]
2444
- _default: Optional[Union[int, str]]
2445
-
2446
- def __init__(self):
2447
- # TODO(user): Consider using None as default and setting them above.
2448
- self._tag = ''
2449
- self._default = ''
2450
-
2451
- def get(self) -> Union[int, str, None]:
2452
- return self._tag
2453
-
2454
- def set(self, tag: Optional[Union[int, str]]) -> None:
2455
- self._tag = self.validate(tag)
2456
-
2457
- def setDefault(self, newDefault: Optional[Union[int, str]]) -> None:
2458
- self._default = self.validate(newDefault)
2459
-
2460
- def reset(self) -> None:
2461
- self._tag = self._default
2462
-
2463
- def validate(self, tag: Optional[Union[int, str]]) -> str:
2464
- """Throws an error if setting an invalid tag.
2465
-
2466
- Args:
2467
- tag: the tag to validate.
2468
-
2469
- Returns:
2470
- The validated tag.
2471
-
2472
- Raises:
2473
- ValueError if the tag does not match the expected format.
2474
- """
2475
- if not tag and tag != 0:
2476
- return ''
2477
- tag = str(tag)
2478
- if not re.fullmatch(r'([a-z0-9]|[a-z0-9][-_a-z0-9]{0,61}[a-z0-9])', tag):
2479
- validationMessage = (
2480
- 'Tags must be 1-63 characters, '
2481
- 'beginning and ending with a lowercase alphanumeric character '
2482
- '([a-z0-9]) with dashes (-), underscores (_), '
2483
- 'and lowercase alphanumerics between.')
2484
- raise ValueError(f'Invalid tag, "{tag}". {validationMessage}')
2485
- return tag
2486
-
2487
-
2488
- # Tracks the currently set workload tag.
2489
- _workloadTag = _WorkloadTag()
2386
+ state.workload_tag.set_default('')
2387
+ state.workload_tag.reset()