earthengine-api 1.6.13__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 (44) hide show
  1. {earthengine_api-1.6.13.dist-info → earthengine_api-1.7.4.dist-info}/METADATA +2 -3
  2. {earthengine_api-1.6.13.dist-info → earthengine_api-1.7.4.dist-info}/RECORD +44 -44
  3. ee/__init__.py +13 -13
  4. ee/_cloud_api_utils.py +29 -28
  5. ee/_helpers.py +6 -6
  6. ee/_utils.py +2 -1
  7. ee/apitestcase.py +10 -10
  8. ee/batch.py +7 -0
  9. ee/cli/commands.py +6 -10
  10. ee/cli/utils.py +28 -23
  11. ee/collection.py +3 -2
  12. ee/computedobject.py +4 -1
  13. ee/customfunction.py +2 -1
  14. ee/data.py +32 -31
  15. ee/deprecation.py +8 -2
  16. ee/deserializer.py +10 -10
  17. ee/ee_number.py +6 -16
  18. ee/encodable.py +2 -1
  19. ee/image_converter.py +3 -3
  20. ee/imagecollection.py +2 -2
  21. ee/model.py +29 -31
  22. ee/oauth.py +37 -37
  23. ee/reducer.py +2 -0
  24. ee/serializer.py +6 -6
  25. ee/table_converter.py +3 -3
  26. ee/terrain.py +1 -1
  27. ee/tests/batch_test.py +67 -3
  28. ee/tests/collection_test.py +35 -0
  29. ee/tests/data_test.py +300 -4
  30. ee/tests/deprecation_test.py +4 -2
  31. ee/tests/deserializer_test.py +47 -0
  32. ee/tests/ee_number_test.py +40 -1
  33. ee/tests/image_converter_test.py +1 -3
  34. ee/tests/kernel_test.py +4 -0
  35. ee/tests/model_test.py +12 -0
  36. ee/tests/oauth_test.py +170 -7
  37. ee/tests/reducer_test.py +13 -0
  38. ee/tests/serializer_test.py +29 -2
  39. ee/tests/table_converter_test.py +49 -5
  40. ee/tests/terrain_test.py +8 -0
  41. {earthengine_api-1.6.13.dist-info → earthengine_api-1.7.4.dist-info}/WHEEL +0 -0
  42. {earthengine_api-1.6.13.dist-info → earthengine_api-1.7.4.dist-info}/entry_points.txt +0 -0
  43. {earthengine_api-1.6.13.dist-info → earthengine_api-1.7.4.dist-info}/licenses/LICENSE +0 -0
  44. {earthengine_api-1.6.13.dist-info → earthengine_api-1.7.4.dist-info}/top_level.txt +0 -0
ee/cli/utils.py CHANGED
@@ -14,7 +14,7 @@ import re
14
14
  import tempfile
15
15
  import threading
16
16
  import time
17
- from typing import Any, AnyStr, Optional, Union
17
+ from typing import Any, AnyStr
18
18
  import urllib.parse
19
19
 
20
20
  from google.cloud import storage
@@ -32,7 +32,7 @@ DEFAULT_EE_CONFIG_FILE_RELATIVE = os.path.join(
32
32
  DEFAULT_EE_CONFIG_FILE = os.path.join(
33
33
  HOMEDIR, DEFAULT_EE_CONFIG_FILE_RELATIVE)
34
34
 
35
- CONFIG_PARAMS: dict[str, Union[str, list[str], None]] = {
35
+ CONFIG_PARAMS: dict[str, str | list[str] | None] = {
36
36
  'account': None,
37
37
  'cloud_api_key': None,
38
38
  'private_key': None,
@@ -145,7 +145,10 @@ class CommandLineConfig:
145
145
 
146
146
 
147
147
  def _split_gcs_path(path):
148
- m = re.search('gs://([a-z0-9-_.]*)/(.*)', path, re.IGNORECASE)
148
+ # This only catches some troubles. For complete details on naming, see:
149
+ # https://cloud.google.com/storage/docs/buckets
150
+ # https://cloud.google.com/storage/docs/objects#naming
151
+ m = re.search('gs://([a-z0-9][a-z0-9-_.]*)/(.*)', path, re.IGNORECASE)
149
152
  if not m:
150
153
  raise ValueError(f"'{path}' is not a valid GCS path")
151
154
 
@@ -197,7 +200,7 @@ class GcsHelper:
197
200
  if not os.path.exists(dir_path):
198
201
  os.makedirs(dir_path)
199
202
 
200
- if output_path[-1:] != '/':
203
+ if not output_path.endswith('/'):
201
204
  blob.download_to_filename(output_path)
202
205
 
203
206
  return temp_dir
@@ -206,7 +209,7 @@ class GcsHelper:
206
209
  """Uploads a directory to cloud storage."""
207
210
  canonical_path = _canonicalize_dir_path(source_path)
208
211
 
209
- files = list()
212
+ files = []
210
213
  for dirpath, _, filenames in os.walk(canonical_path):
211
214
  files += [os.path.join(dirpath, f) for f in filenames]
212
215
 
@@ -224,7 +227,7 @@ def is_gcs_path(path: str) -> bool:
224
227
 
225
228
 
226
229
  def query_yes_no(msg: str) -> bool:
227
- print('%s (y/n)' % msg)
230
+ print(f'{msg} (y/n)')
228
231
  while True:
229
232
  confirm = input().lower()
230
233
  if confirm == 'y':
@@ -264,10 +267,9 @@ def wait_for_task(
264
267
  state = status['metadata']['state']
265
268
  if status.get('done', False):
266
269
  error_message = status.get('error', {}).get('message')
267
- print('Task %s ended at state: %s after %.2f seconds'
268
- % (task_id, state, elapsed))
270
+ print(f'Task {task_id} ended at state: {state} after {elapsed:.2f} seconds')
269
271
  if error_message:
270
- raise ee.ee_exception.EEException('Error: %s' % error_message)
272
+ raise ee.ee_exception.EEException(f'Error: {error_message}')
271
273
  return
272
274
  if log_progress and elapsed - last_check >= 30:
273
275
  print('[{:%H:%M:%S}] Current state for task {}: {}'
@@ -309,17 +311,20 @@ def wait_for_tasks(
309
311
  status_counts = collections.defaultdict(int)
310
312
  for status in status_list:
311
313
  status_counts[status] += 1
314
+ succeeded = status_counts['SUCCEEDED']
315
+ failed = status_counts['FAILED']
316
+ cancelled = status_counts['CANCELLED']
312
317
  num_incomplete = (
313
318
  len(status_list)
314
- - status_counts['SUCCEEDED']
315
- - status_counts['FAILED']
316
- - status_counts['CANCELLED']
319
+ - succeeded
320
+ - failed
321
+ - cancelled
317
322
  )
318
323
  print('Finished waiting for tasks.\n Status summary:')
319
- print(' %d tasks completed successfully.' % status_counts['SUCCEEDED'])
320
- print(' %d tasks failed.' % status_counts['FAILED'])
321
- print(' %d tasks cancelled.' % status_counts['CANCELLED'])
322
- print(' %d tasks are still incomplete (timed-out)' % num_incomplete)
324
+ print(f' {succeeded} tasks completed successfully.')
325
+ print(f' {failed} tasks failed.')
326
+ print(f' {cancelled} tasks cancelled.')
327
+ print(f' {num_incomplete} tasks are still incomplete (timed-out)')
323
328
 
324
329
 
325
330
  def expand_gcs_wildcards(source_files: list[str]) -> Iterable[str]:
@@ -351,7 +356,7 @@ def expand_gcs_wildcards(source_files: list[str]) -> Iterable[str]:
351
356
  bucket, rest = bucket_match.group(1, 2)
352
357
  else:
353
358
  raise ee.ee_exception.EEException(
354
- 'Badly formatted source file or bucket: %s' % source)
359
+ f'Badly formatted source file or bucket: {source}')
355
360
  prefix = rest[:rest.find('*')] # Everything before the first wildcard
356
361
 
357
362
  bucket_files = _gcs_ls(bucket, prefix)
@@ -395,23 +400,23 @@ def _gcs_ls(bucket: str, prefix: str = '') -> Iterable[str]:
395
400
  try:
396
401
  response, content = http.request(url, method=method)
397
402
  except httplib2.HttpLib2Error as e:
398
- raise ee.ee_exception.EEException('Unexpected HTTP error: %s' % str(e))
403
+ raise ee.ee_exception.EEException(f'Unexpected HTTP error: {e}') from e
399
404
 
400
405
  if response.status < 100 or response.status >= 300:
401
406
  raise ee.ee_exception.EEException(
402
- 'Error retrieving bucket %s; Server returned HTTP code: %d'
403
- % (bucket, response.status)
407
+ f'Error retrieving bucket {bucket}; '
408
+ f'Server returned HTTP code: {response.status}'
404
409
  )
405
410
 
406
411
  json_content = json.loads(content)
407
412
  if 'error' in json_content:
408
413
  json_error = json_content['error']['message']
409
- raise ee.ee_exception.EEException('Error retrieving bucket %s: %s' %
410
- (bucket, json_error))
414
+ raise ee.ee_exception.EEException(
415
+ f'Error retrieving bucket {bucket}: {json_error}')
411
416
 
412
417
  if 'items' not in json_content:
413
418
  raise ee.ee_exception.EEException(
414
- 'Cannot find items list in the response from GCS: %s' % json_content)
419
+ f'Cannot find items list in the response from GCS: {json_content}')
415
420
  objects = json_content['items']
416
421
  object_names = [str(gc_object['name']) for gc_object in objects]
417
422
 
ee/collection.py CHANGED
@@ -5,8 +5,9 @@ This class is never intended to be instantiated by the user.
5
5
 
6
6
  from __future__ import annotations
7
7
 
8
+ from collections.abc import Callable
8
9
  import datetime
9
- from typing import Any, Callable, Generic, TypeVar
10
+ from typing import Any, Generic, TypeVar
10
11
 
11
12
  from ee import _arg_types
12
13
  from ee import _utils
@@ -204,7 +205,7 @@ class Collection(Generic[ElementType], element.Element):
204
205
 
205
206
  # pylint: disable-next=redefined-builtin
206
207
  def aggregate_product(self, property: _arg_types.String) -> ee_number.Number:
207
- """Returns the product of the values ofthe selected property.
208
+ """Returns the product of the values of the selected property.
208
209
 
209
210
  Aggregates over a given property of the objects in a collection, calculating
210
211
  the product of the values of the selected property.
ee/computedobject.py CHANGED
@@ -2,7 +2,8 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from typing import Any, Callable
5
+ from collections.abc import Callable
6
+ from typing import Any
6
7
 
7
8
  from ee import _utils
8
9
  from ee import data
@@ -233,9 +234,11 @@ class ComputedObject(encodable.Encodable, metaclass=ComputedObjectMetaclass):
233
234
  return obj
234
235
  else:
235
236
  result = cls.__new__(cls) # pylint: disable=no-value-for-parameter
237
+ # pylint: disable=attribute-error
236
238
  result.func = obj.func
237
239
  result.args = obj.args
238
240
  result.varName = obj.varName
241
+ # pylint: enable=attribute-error
239
242
  return result
240
243
 
241
244
  @staticmethod
ee/customfunction.py CHANGED
@@ -2,7 +2,8 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from typing import Any, Callable
5
+ from collections.abc import Callable
6
+ from typing import Any
6
7
 
7
8
  from ee import computedobject
8
9
  from ee import ee_exception
ee/data.py CHANGED
@@ -5,14 +5,14 @@
5
5
 
6
6
  from __future__ import annotations
7
7
 
8
- from collections.abc import Iterator, Sequence
8
+ from collections.abc import Callable, Iterator, Sequence
9
9
  import contextlib
10
10
  import json
11
11
  import platform
12
12
  import re
13
13
  import sys
14
14
  import threading
15
- from typing import Any, Callable, Optional, Union
15
+ from typing import Any
16
16
  import uuid
17
17
  import warnings
18
18
 
@@ -112,7 +112,7 @@ class _ThreadLocals(threading.local):
112
112
  # and the user would have to modify each call to profile, rather than
113
113
  # enabling profiling as a wrapper around the entire program (with
114
114
  # ee.data.profiling, defined below).
115
- self.profile_hook: Optional[Callable[[str], None]] = None
115
+ self.profile_hook: Callable[[str], None] | None = None
116
116
 
117
117
 
118
118
  _thread_locals = _ThreadLocals()
@@ -125,11 +125,11 @@ def _get_state() -> _state.EEState:
125
125
 
126
126
  def initialize(
127
127
  credentials: Any = None,
128
- api_base_url: Optional[str] = None,
129
- tile_base_url: Optional[str] = None,
130
- cloud_api_base_url: Optional[str] = None,
131
- cloud_api_key: Optional[str] = None,
132
- 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,
133
133
  http_transport: Any = None,
134
134
  ) -> None:
135
135
  """Initializes the data module, setting credentials and base URLs.
@@ -297,7 +297,7 @@ def _get_cloud_projects_raw() -> Any:
297
297
  return state.cloud_api_resource_raw.projects()
298
298
 
299
299
 
300
- def _make_request_headers() -> Optional[dict[str, Any]]:
300
+ def _make_request_headers() -> dict[str, Any] | None:
301
301
  """Adds headers based on client context."""
302
302
  state = _get_state()
303
303
  headers: dict[str, Any] = {}
@@ -329,7 +329,7 @@ def _handle_profiling_response(response: httplib2.Response) -> None:
329
329
 
330
330
 
331
331
  def _execute_cloud_call(
332
- call: googleapiclient.http.HttpRequest, num_retries: Optional[int] = None
332
+ call: googleapiclient.http.HttpRequest, num_retries: int | None = None
333
333
  ) -> Any:
334
334
  """Executes a Cloud API call and translates errors to EEExceptions.
335
335
 
@@ -399,7 +399,7 @@ def setUserAgent(user_agent: str) -> None:
399
399
  _get_state().user_agent = user_agent
400
400
 
401
401
 
402
- def getUserAgent() -> Optional[str]:
402
+ def getUserAgent() -> str | None:
403
403
  return _get_state().user_agent
404
404
 
405
405
 
@@ -449,7 +449,7 @@ def profiling(hook: Any) -> Iterator[None]:
449
449
 
450
450
 
451
451
  @deprecation.Deprecated('Use getAsset')
452
- def getInfo(asset_id: str) -> Optional[Any]:
452
+ def getInfo(asset_id: str) -> Any | None:
453
453
  """Load info for an asset, given an asset id.
454
454
 
455
455
  Args:
@@ -512,8 +512,8 @@ def getList(params: dict[str, Any]) -> Any:
512
512
 
513
513
 
514
514
  def listImages(
515
- params: Union[str, dict[str, Any]],
516
- ) -> dict[str, Optional[list[Any]]]:
515
+ params: str | dict[str, Any],
516
+ ) -> dict[str, list[Any] | None]:
517
517
  """Returns the images in an image collection or folder.
518
518
 
519
519
  Args:
@@ -550,7 +550,7 @@ def listImages(
550
550
  return images
551
551
 
552
552
 
553
- def listAssets(params: Union[str, dict[str, Any]]) -> dict[str, list[Any]]:
553
+ def listAssets(params: str | dict[str, Any]) -> dict[str, list[Any]]:
554
554
  """Returns the assets in a folder.
555
555
 
556
556
  Args:
@@ -603,7 +603,7 @@ def listAssets(params: Union[str, dict[str, Any]]) -> dict[str, list[Any]]:
603
603
  return assets
604
604
 
605
605
 
606
- def listBuckets(project: Optional[str] = None) -> Any:
606
+ def listBuckets(project: str | None = None) -> Any:
607
607
  """Returns top-level assets and folders for the Cloud Project or user.
608
608
 
609
609
  Args:
@@ -689,7 +689,7 @@ def getMapId(params: dict[str, Any]) -> dict[str, Any]:
689
689
  )
690
690
  state = _get_state()
691
691
  map_name = result['name']
692
- url_format = '%s/%s/%s/tiles/{z}/{x}/{y}' % (
692
+ url_format = '{}/{}/{}/tiles/{{z}}/{{x}}/{{y}}'.format(
693
693
  state.tile_base_url, _cloud_api_utils.VERSION, map_name)
694
694
  if state.cloud_api_key:
695
695
  url_format += f'?key={state.cloud_api_key}'
@@ -738,7 +738,7 @@ def getFeatureViewTilesKey(params: dict[str, Any]) -> dict[str, Any]:
738
738
  }
739
739
 
740
740
 
741
- def _extract_table_converter(params: dict[str, Any]) -> Optional[Any]:
741
+ def _extract_table_converter(params: dict[str, Any]) -> Any | None:
742
742
  if 'fileFormat' in params:
743
743
  file_format = params.get('fileFormat')
744
744
  converter = table_converter.from_file_format(file_format)
@@ -1070,7 +1070,7 @@ def computeValue(obj: computedobject.ComputedObject) -> Any:
1070
1070
 
1071
1071
  @deprecation.Deprecated('Use getThumbId and makeThumbUrl')
1072
1072
  def getThumbnail(
1073
- params: dict[str, Any], thumbType: Optional[str] = None
1073
+ params: dict[str, Any], thumbType: str | None = None
1074
1074
  ) -> Any:
1075
1075
  """Get a Thumbnail for a given asset.
1076
1076
 
@@ -1105,7 +1105,7 @@ def getThumbnail(
1105
1105
 
1106
1106
 
1107
1107
  def getThumbId(
1108
- params: dict[str, Any], thumbType: Optional[str] = None
1108
+ params: dict[str, Any], thumbType: str | None = None
1109
1109
  ) -> dict[str, str]:
1110
1110
  """Get a Thumbnail ID for a given asset.
1111
1111
 
@@ -1442,8 +1442,8 @@ def getAlgorithms() -> Any:
1442
1442
  @_utils.accept_opt_prefix('opt_path', 'opt_force', 'opt_properties')
1443
1443
  def createAsset(
1444
1444
  value: dict[str, Any],
1445
- path: Optional[str] = None,
1446
- properties: Optional[dict[str, Any]] = None,
1445
+ path: str | None = None,
1446
+ properties: dict[str, Any] | None = None,
1447
1447
  ) -> dict[str, Any]:
1448
1448
  """Creates an asset from a JSON value.
1449
1449
 
@@ -1589,7 +1589,7 @@ def getTaskList() -> list[Any]:
1589
1589
  for o in listOperations()]
1590
1590
 
1591
1591
 
1592
- def listOperations(project: Optional[str] = None) -> list[Any]:
1592
+ def listOperations(project: str | None = None) -> list[Any]:
1593
1593
  """Retrieves a list of the user's tasks.
1594
1594
 
1595
1595
  Args:
@@ -1616,7 +1616,7 @@ def listOperations(project: Optional[str] = None) -> list[Any]:
1616
1616
 
1617
1617
 
1618
1618
  @deprecation.Deprecated('Use getOperation')
1619
- def getTaskStatus(taskId: Union[list[str], str]) -> list[Any]:
1619
+ def getTaskStatus(taskId: list[str] | str) -> list[Any]:
1620
1620
  """Retrieve status of one or more long-running tasks.
1621
1621
 
1622
1622
  Args:
@@ -1868,7 +1868,7 @@ def _startIngestion(
1868
1868
  request_id: Any,
1869
1869
  params: dict[str, Any],
1870
1870
  allow_overwrite: bool = False,
1871
- import_mode: Optional[str] = _INTERNAL_IMPORT,
1871
+ import_mode: str | None = _INTERNAL_IMPORT,
1872
1872
  ) -> dict[str, Any]:
1873
1873
  """Starts an ingestion task or creates an external image."""
1874
1874
  request = {
@@ -2126,7 +2126,7 @@ def getIamPolicy(asset_id: str) -> Any:
2126
2126
 
2127
2127
 
2128
2128
  @deprecation.Deprecated('Use setIamPolicy')
2129
- def setAssetAcl(assetId: str, aclUpdate: Union[str, dict[str, Any]]) -> None:
2129
+ def setAssetAcl(assetId: str, aclUpdate: str | dict[str, Any]) -> None:
2130
2130
  """Sets the access control list of the asset with the given ID.
2131
2131
 
2132
2132
  The owner ACL cannot be changed, and the final ACL of the asset
@@ -2161,6 +2161,7 @@ def setIamPolicy(asset_id: str, policy: Any) -> None:
2161
2161
  .setIamPolicy(resource=name, body={'policy': policy}, prettyPrint=False)
2162
2162
  )
2163
2163
 
2164
+
2164
2165
  @deprecation.Deprecated('Use ee.data.updateAsset().')
2165
2166
  def setAssetProperties(assetId: str, properties: dict[str, Any]) -> None:
2166
2167
  """Sets metadata properties of the asset with the given ID.
@@ -2239,7 +2240,7 @@ def getProjectConfig() -> dict[str, Any]:
2239
2240
 
2240
2241
 
2241
2242
  def updateProjectConfig(
2242
- project_config: dict[str, Any], update_mask: Optional[Sequence[str]] = None
2243
+ project_config: dict[str, Any], update_mask: Sequence[str] | None = None
2243
2244
  ) -> dict[str, Any]:
2244
2245
  """Updates the project config for the current project.
2245
2246
 
@@ -2311,12 +2312,12 @@ def convert_asset_id_to_asset_name(asset_id: str) -> str:
2311
2312
  return _cloud_api_utils.convert_asset_id_to_asset_name(asset_id)
2312
2313
 
2313
2314
 
2314
- def getWorkloadTag() -> Optional[Union[int, str]]:
2315
+ def getWorkloadTag() -> int | str | None:
2315
2316
  """Returns the currently set workload tag."""
2316
2317
  return _get_state().workload_tag.get()
2317
2318
 
2318
2319
 
2319
- def setWorkloadTag(tag: Optional[Union[int, str]]) -> None:
2320
+ def setWorkloadTag(tag: int | str | None) -> None:
2320
2321
  """Sets the workload tag, used to label computation and exports.
2321
2322
 
2322
2323
  Workload tag must be 1 - 63 characters, beginning and ending with an
@@ -2330,7 +2331,7 @@ def setWorkloadTag(tag: Optional[Union[int, str]]) -> None:
2330
2331
 
2331
2332
 
2332
2333
  @contextlib.contextmanager
2333
- def workloadTagContext(tag: Optional[Union[int, str]]) -> Iterator[None]:
2334
+ def workloadTagContext(tag: int | str | None) -> Iterator[None]:
2334
2335
  """Produces a context manager which sets the workload tag, then resets it.
2335
2336
 
2336
2337
  Workload tag must be 1 - 63 characters, beginning and ending with an
@@ -2350,7 +2351,7 @@ def workloadTagContext(tag: Optional[Union[int, str]]) -> Iterator[None]:
2350
2351
  resetWorkloadTag()
2351
2352
 
2352
2353
 
2353
- def setDefaultWorkloadTag(tag: Optional[Union[int, str]]) -> None:
2354
+ def setDefaultWorkloadTag(tag: int | str | None) -> None:
2354
2355
  """Sets the workload tag, and as the default for which to reset back to.
2355
2356
 
2356
2357
  For example, calling `ee.data.resetWorkloadTag()` will reset the workload tag
ee/deprecation.py CHANGED
@@ -2,12 +2,13 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ from collections.abc import Callable
5
6
  import dataclasses
6
7
  import datetime
7
8
  import functools
8
9
  import inspect
9
10
  import json
10
- from typing import Any, Callable
11
+ from typing import Any
11
12
  import urllib
12
13
  import warnings
13
14
 
@@ -36,7 +37,7 @@ def Deprecated(message: str):
36
37
  @functools.wraps(func)
37
38
  def Wrapper(*args, **kwargs):
38
39
  warnings.warn_explicit(
39
- '{}() is deprecated: {}'.format(func.__name__, message),
40
+ f'{func.__name__}() is deprecated: {message}',
40
41
  category=DeprecationWarning,
41
42
  filename=func.__code__.co_filename,
42
43
  lineno=func.__code__.co_firstlineno + 1,
@@ -204,6 +205,11 @@ def _IssueAssetDeprecationWarning(asset: DeprecatedAsset) -> None:
204
205
  formatted_date = removal_date.strftime('%B %d, %Y').replace(' 0', ' ')
205
206
  warning += f' by {formatted_date}'
206
207
  warning += '.'
208
+ if asset.replacement_id:
209
+ warning = (
210
+ warning
211
+ + f'\nThis dataset has been superseded by {asset.replacement_id}\n'
212
+ )
207
213
  if asset.learn_more_url:
208
214
  warning = warning + f'\nLearn more: {asset.learn_more_url}\n'
209
215
  warnings.warn(warning, category=DeprecationWarning)
ee/deserializer.py CHANGED
@@ -1,7 +1,7 @@
1
1
  """A deserializer that decodes EE object trees from JSON DAGs."""
2
2
 
3
3
  import json
4
- from typing import Any, Union
4
+ from typing import Any
5
5
 
6
6
  from ee import apifunction
7
7
  from ee import computedobject
@@ -13,7 +13,7 @@ from ee import function
13
13
  from ee import geometry
14
14
 
15
15
 
16
- def fromJSON(json_obj: Union[bytes, str]) -> Any: # pylint: disable=g-bad-name
16
+ def fromJSON(json_obj: bytes | str) -> Any: # pylint: disable=g-bad-name
17
17
  """Deserialize an object from a JSON string appropriate for API calls.
18
18
 
19
19
  Args:
@@ -76,7 +76,7 @@ def _decodeValue(json_obj: Any, named_values: dict[str, Any]) -> Any:
76
76
 
77
77
  # Ensure that we've got a proper object at this point.
78
78
  if not isinstance(json_obj, dict):
79
- raise ee_exception.EEException('Cannot decode object: ' + json_obj)
79
+ raise ee_exception.EEException(f'Cannot decode object: {json_obj}')
80
80
 
81
81
  # Check for explicitly typed values.
82
82
  type_name = json_obj['type']
@@ -88,12 +88,12 @@ def _decodeValue(json_obj: Any, named_values: dict[str, Any]) -> Any:
88
88
  elif type_name == 'ArgumentRef':
89
89
  var_name = json_obj['value']
90
90
  if not isinstance(var_name, str):
91
- raise ee_exception.EEException('Invalid variable name: ' + var_name)
92
- return customfunction.CustomFunction.variable(None, var_name) # pylint: disable=protected-access
91
+ raise ee_exception.EEException(f'Invalid variable name: {var_name}')
92
+ return customfunction.CustomFunction.variable(None, var_name)
93
93
  elif type_name == 'Date':
94
94
  microseconds = json_obj['value']
95
95
  if not isinstance(microseconds, (float, int)):
96
- raise ee_exception.EEException('Invalid date value: ' + microseconds)
96
+ raise ee_exception.EEException(f'Invalid date value: {microseconds}')
97
97
  return ee_date.Date(microseconds / 1e3)
98
98
  elif type_name == 'Bytes':
99
99
  result = encodable.Encodable()
@@ -131,7 +131,7 @@ def _decodeValue(json_obj: Any, named_values: dict[str, Any]) -> Any:
131
131
  elif type_name == 'CompoundValue':
132
132
  raise ee_exception.EEException('Nested CompoundValues are disallowed.')
133
133
  else:
134
- raise ee_exception.EEException('Unknown encoded object type: ' + type_name)
134
+ raise ee_exception.EEException(f'Unknown encoded object type: {type_name}')
135
135
 
136
136
 
137
137
  def _invocation(func: Any, args: dict[str, Any]) -> Any:
@@ -150,10 +150,10 @@ def _invocation(func: Any, args: dict[str, Any]) -> Any:
150
150
  'returns': 'ComputedObject'
151
151
  }
152
152
  return function.SecondOrderFunction(func, signature).apply(args)
153
- raise ee_exception.EEException('Invalid function value: %s' % func)
153
+ raise ee_exception.EEException(f'Invalid function value: {func}')
154
154
 
155
155
 
156
- def fromCloudApiJSON(json_obj: Union[str, bytes]) -> Any: # pylint: disable=g-bad-name
156
+ def fromCloudApiJSON(json_obj: str | bytes) -> Any: # pylint: disable=g-bad-name
157
157
  """Deserializes an object from the JSON string used in Cloud API calls.
158
158
 
159
159
  Args:
@@ -180,7 +180,7 @@ def decodeCloudApi(json_obj: dict[str, Any]) -> Any:
180
180
  def lookup(reference, kind):
181
181
  if reference not in decoded:
182
182
  if reference not in json_obj['values']:
183
- raise ee_exception.EEException('Cannot find {} {}'.format(reference, kind))
183
+ raise ee_exception.EEException(f'Cannot find {reference} {kind}')
184
184
  decoded[reference] = decode_node(json_obj['values'][reference])
185
185
  return decoded[reference]
186
186
 
ee/ee_number.py CHANGED
@@ -1,7 +1,7 @@
1
1
  """A wrapper for numbers."""
2
2
  from __future__ import annotations
3
3
 
4
- from typing import Any, Union
4
+ from typing import Any
5
5
 
6
6
  from ee import _arg_types
7
7
  from ee import _cloud_api_utils
@@ -586,9 +586,9 @@ class Number(computedobject.ComputedObject):
586
586
 
587
587
  return apifunction.ApiFunction.call_(self.name() + '.or', self, right)
588
588
 
589
+ @staticmethod
589
590
  def parse(
590
- # pylint: disable=redefined-builtin
591
- input: _arg_types.String,
591
+ input: _arg_types.String, # pylint: disable=redefined-builtin
592
592
  radix: _arg_types.Integer | None = None,
593
593
  ) -> Number:
594
594
  """Returns a number from a string.
@@ -772,27 +772,17 @@ class Number(computedobject.ComputedObject):
772
772
 
773
773
  return apifunction.ApiFunction.call_(self.name() + '.uint8', self)
774
774
 
775
- # pylint: disable=redefined-builtin
776
- # pytype: disable=invalid-annotation
777
- def unitScale(
778
- self,
779
- min: Union[int, float, computedobject.ComputedObject],
780
- max: Union[int, float, computedobject.ComputedObject],
781
- ) -> Number:
782
- """Scales the input so that [min, max] becomes [0, 1].
775
+ # pylint: disable-next=redefined-builtin
776
+ def unitScale(self, min: _arg_types.Number, max: _arg_types.Number) -> Number:
777
+ """Returns the input scaled so that [min, max] becomes [0, 1].
783
778
 
784
779
  Values outside the range are NOT clamped. If min == max, 0 is returned.
785
780
 
786
781
  Args:
787
782
  min: Minimum value of the input to be scaled to 0.
788
783
  max: Maximum value of the input to be scaled to 1.
789
-
790
- Returns:
791
- An ee.Number.
792
784
  """
793
785
 
794
786
  return apifunction.ApiFunction.call_(
795
787
  self.name() + '.unitScale', self, min, max
796
788
  )
797
- # pytype: enable=invalid-annotation
798
- # pylint: enable=redefined-builtin
ee/encodable.py CHANGED
@@ -1,6 +1,7 @@
1
1
  """Interfaces implemented by serializable objects."""
2
2
 
3
- from typing import Any, Callable
3
+ from collections.abc import Callable
4
+ from typing import Any
4
5
 
5
6
 
6
7
  class Encodable:
ee/image_converter.py CHANGED
@@ -1,7 +1,7 @@
1
1
  """Converters used in the image data fetching methods."""
2
2
 
3
3
  import io
4
- from typing import Any, Optional, Union
4
+ from typing import Any
5
5
 
6
6
 
7
7
  class ImageConverter:
@@ -49,8 +49,8 @@ _PIXEL_DATA_CONVERTERS: dict[str, type[ImageConverter]] = {
49
49
 
50
50
 
51
51
  def from_file_format(
52
- file_format: Union[str, ImageConverter]
53
- ) -> Optional[ImageConverter]:
52
+ file_format: str | ImageConverter,
53
+ ) -> ImageConverter | None:
54
54
  if isinstance(file_format, ImageConverter):
55
55
  return file_format
56
56
  if file_format in _PIXEL_DATA_CONVERTERS:
ee/imagecollection.py CHANGED
@@ -2,8 +2,8 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
- from collections.abc import Sequence
6
- from typing import Any, Callable
5
+ from collections.abc import Callable, Sequence
6
+ from typing import Any
7
7
 
8
8
  from ee import _arg_types
9
9
  from ee import _utils