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/cli/commands.py CHANGED
@@ -8,6 +8,7 @@ the actions to be taken when the command is executed.
8
8
  import argparse
9
9
  import calendar
10
10
  import collections
11
+ from collections.abc import Sequence
11
12
  import datetime
12
13
  import json
13
14
  import logging
@@ -16,7 +17,7 @@ import re
16
17
  import shutil
17
18
  import sys
18
19
  import tempfile
19
- from typing import Any, Dict, List, Sequence, Tuple, Type, Union
20
+ from typing import Any, Union
20
21
  import urllib.parse
21
22
 
22
23
  # Prevent TensorFlow from logging anything at the native level.
@@ -51,25 +52,6 @@ except TypeError:
51
52
  finally:
52
53
  logging.getLogger().setLevel(old_level)
53
54
 
54
- TENSORFLOW_ADDONS_INSTALLED = False
55
- # pylint: disable=g-import-not-at-top
56
- if TENSORFLOW_INSTALLED:
57
- try:
58
- # This import is enough to register TFA ops though isn't directly used
59
- # (for now).
60
- # pylint: disable=unused-import
61
- import tensorflow_addons as tfa
62
- tfa.register_all(custom_kernels=False) # pytype: disable=module-attr
63
- TENSORFLOW_ADDONS_INSTALLED = True
64
- except ImportError:
65
- pass
66
- except AttributeError:
67
- # This can be thrown by "tfa.register_all()" which means the
68
- # tensorflow_addons version is registering ops the old way, i.e.
69
- # automatically at import time. If this is the case, we've actually
70
- # successfully registered TFA.
71
- TENSORFLOW_ADDONS_INSTALLED = True
72
-
73
55
  # pylint: disable=g-import-not-at-top, g-bad-import-order
74
56
  import ee
75
57
  from ee.cli import utils
@@ -87,7 +69,7 @@ TYPE_STRING = 'string'
87
69
  SYSTEM_TIME_START = 'system:time_start'
88
70
  SYSTEM_TIME_END = 'system:time_end'
89
71
 
90
- # A regex that parses properties of the form "[(type)]name=value". The
72
+ # A regex that parses properties of the form "[(type)]name=value". The
91
73
  # second, third, and fourth group are type, name, and number, respectively.
92
74
  PROPERTY_RE = re.compile(r'(\(([^\)]*)\))?([^=]+)=(.*)')
93
75
 
@@ -129,7 +111,7 @@ def _add_overwrite_arg(parser: argparse.ArgumentParser) -> None:
129
111
 
130
112
 
131
113
  def _upload(
132
- args: argparse.Namespace, request: Dict[str, Any], ingestion_function: Any
114
+ args: argparse.Namespace, request: dict[str, Any], ingestion_function: Any
133
115
  ) -> None:
134
116
  if 0 <= args.wait < 10:
135
117
  raise ee.EEException('Wait time should be at least 10 seconds.')
@@ -142,20 +124,20 @@ def _upload(
142
124
 
143
125
 
144
126
  # Argument types
145
- def _comma_separated_strings(string: str) -> List[str]:
127
+ def _comma_separated_strings(string: str) -> list[str]:
146
128
  """Parses an input consisting of comma-separated strings."""
147
129
  error_msg = 'Argument should be a comma-separated list of strings: {}'
148
130
  values = string.split(',')
149
- if not values:
131
+ if not all(values):
150
132
  raise argparse.ArgumentTypeError(error_msg.format(string))
151
133
  return values
152
134
 
153
135
 
154
- def _comma_separated_numbers(string: str) -> List[float]:
136
+ def _comma_separated_numbers(string: str) -> list[float]:
155
137
  """Parses an input consisting of comma-separated numbers."""
156
138
  error_msg = 'Argument should be a comma-separated list of numbers: {}'
157
139
  values = string.split(',')
158
- if not values:
140
+ if not all(values):
159
141
  raise argparse.ArgumentTypeError(error_msg.format(string))
160
142
  numbervalues = []
161
143
  for value in values:
@@ -170,12 +152,12 @@ def _comma_separated_numbers(string: str) -> List[float]:
170
152
  return numbervalues
171
153
 
172
154
 
173
- def _comma_separated_pyramiding_policies(string: str) -> List[str]:
155
+ def _comma_separated_pyramiding_policies(string: str) -> list[str]:
174
156
  """Parses an input consisting of comma-separated pyramiding policies."""
175
157
  error_msg = ('Argument should be a comma-separated list of: '
176
- '{{"mean", "sample", "min", "max", "mode"}}: {}')
158
+ '{{"mean", "median", "sample", "min", "max", "mode"}}: {}')
177
159
  values = string.split(',')
178
- if not values:
160
+ if not all(values):
179
161
  raise argparse.ArgumentTypeError(error_msg.format(string))
180
162
  redvalues = []
181
163
  for value in values:
@@ -211,11 +193,40 @@ def _cloud_timestamp_for_timestamp_ms(timestamp_ms: float) -> str:
211
193
  return timestamp.replace(tzinfo=None).isoformat() + 'Z'
212
194
 
213
195
 
214
- def _parse_millis(millis: float) -> datetime.datetime:
215
- return datetime.datetime.fromtimestamp(millis / 1000)
196
+ def _datetime_from_cloud_timestamp(
197
+ cloud_timestamp: str | None,
198
+ ) -> datetime.datetime:
199
+ """Returns a datetime object for the given Cloud-formatted timestamp.
200
+
201
+ Args:
202
+ cloud_timestamp: A timestamp string in the format 'YYYY-MM-DDTHH:MM:SS.mmmZ'
203
+ or 'YYYY-MM-DDTHH:MM:SSZ'. If None, returns the epoch.
204
+
205
+ Returns:
206
+ A datetime object for the given Cloud-formatted timestamp. If the timestamp
207
+ is None, returns the epoch.
208
+ """
209
+ if not cloud_timestamp:
210
+ return datetime.datetime.fromtimestamp(0)
211
+ # Replace 'Z' with the UTC offset +00:00 for fromisoformat compatibility.
212
+ return datetime.datetime.fromisoformat(cloud_timestamp.replace('Z', '+00:00'))
213
+
216
214
 
215
+ def _format_cloud_timestamp(timestamp: str | None) -> str:
216
+ """Returns a formatted datetime for the given Cloud-formatted timestamp.
217
217
 
218
- def _decode_date(string: str) -> Union[float, str]:
218
+ Args:
219
+ timestamp: A timestamp string in the format 'YYYY-MM-DDTHH:MM:SS.mmmZ'
220
+ or 'YYYY-MM-DDTHH:MM:SSZ'.
221
+
222
+ Returns:
223
+ A formatted datetime string in the format 'YYYY-MM-DD HH:MM:SS'. If the
224
+ timestamp is None, returns the formatted epoch.
225
+ """
226
+ return _datetime_from_cloud_timestamp(timestamp).strftime('%Y-%m-%d %H:%M:%S')
227
+
228
+
229
+ def _decode_date(string: str) -> float | str:
219
230
  """Decodes a date from a command line argument, returning msec since epoch".
220
231
 
221
232
  Args:
@@ -248,13 +259,34 @@ def _decode_date(string: str) -> Union[float, str]:
248
259
  'Invalid value for property of type "date": "%s".' % string)
249
260
 
250
261
 
251
- def _decode_property(string: str) -> Tuple[str, Any]:
262
+ def _task_id_to_operation_name(task_id: str) -> str:
263
+ """Converts a task ID to an operation name."""
264
+ # pylint: disable=protected-access
265
+ return ee._cloud_api_utils.convert_task_id_to_operation_name(
266
+ ee.data._get_state().cloud_api_user_project, task_id
267
+ )
268
+ # pylint: enable=protected-access
269
+
270
+
271
+ def _operation_name_to_task_id(operation_name: str) -> str:
272
+ """Converts an operation name to a task ID."""
273
+ # pylint: disable-next=protected-access
274
+ return ee._cloud_api_utils.convert_operation_name_to_task_id(operation_name)
275
+
276
+
277
+ def _convert_to_operation_state(state: str) -> str:
278
+ """Converts a task or operation state to an operation state."""
279
+ # pylint: disable-next=protected-access
280
+ return ee._cloud_api_utils.convert_to_operation_state(state)
281
+
282
+
283
+ def _decode_property(string: str) -> tuple[str, Any]:
252
284
  """Decodes a general key-value property from a command-line argument.
253
285
 
254
286
  Args:
255
287
  string: The string must have the form name=value or (type)name=value, where
256
288
  type is one of 'number', 'string', or 'date'. The value format for dates
257
- is YYYY-MM-DD[THH:MM:SS[.MS]]. The value 'null' is special: it evaluates
289
+ is YYYY-MM-DD[THH:MM:SS[.MS]]. The value 'null' is special: it evaluates
258
290
  to None unless it is cast to a string of 'null'.
259
291
 
260
292
  Returns:
@@ -311,7 +343,7 @@ def _add_property_flags(parser: argparse.ArgumentParser) -> None:
311
343
  type=_decode_date)
312
344
 
313
345
 
314
- def _decode_property_flags(args: argparse.Namespace) -> Dict[str, Any]:
346
+ def _decode_property_flags(args: argparse.Namespace) -> dict[str, Any]:
315
347
  """Decodes metadata properties from args as a name->value dict."""
316
348
  property_list = list(args.property or [])
317
349
  names = [name for name, _ in property_list]
@@ -336,8 +368,8 @@ def _pretty_print_json(json_obj: Any) -> None:
336
368
 
337
369
  class Dispatcher:
338
370
  """Dispatches to a set of commands implemented as command classes."""
339
- COMMANDS: List[Any]
340
- command_dict: Dict[str, Any]
371
+ COMMANDS: list[Any]
372
+ command_dict: dict[str, Any]
341
373
  dest: str
342
374
  name: str
343
375
 
@@ -418,7 +450,7 @@ class AuthenticateCommand:
418
450
  args_auth['auth_mode'] = 'notebook'
419
451
 
420
452
  if ee.Authenticate(**args_auth):
421
- print('Authenticate: Credentials already exist. Use --force to refresh.')
453
+ print('Authenticate: Credentials already exist. Use --force to refresh.')
422
454
 
423
455
 
424
456
  class SetProjectCommand:
@@ -509,7 +541,7 @@ class AclChCommand:
509
541
  ee.data.setAssetAcl(args.asset_id, json.dumps(acl))
510
542
 
511
543
  def _set_permission(
512
- self, permissions: Dict[str, str], grant: str, prefix: str
544
+ self, permissions: dict[str, str], grant: str, prefix: str
513
545
  ) -> None:
514
546
  """Sets the permission for a given user/group."""
515
547
  parts = grant.rsplit(':', 1)
@@ -526,7 +558,7 @@ class AclChCommand:
526
558
  permissions[prefixed_user] = role
527
559
 
528
560
  def _remove_permission(
529
- self, permissions: Dict[str, str], user: str, prefix: str
561
+ self, permissions: dict[str, str], user: str, prefix: str
530
562
  ) -> None:
531
563
  """Removes permissions for a given user/group."""
532
564
  prefixed_user = user
@@ -546,7 +578,7 @@ class AclChCommand:
546
578
  else:
547
579
  return 'user:'
548
580
 
549
- def _parse_permissions(self, args: argparse.Namespace) -> Dict[str, str]:
581
+ def _parse_permissions(self, args: argparse.Namespace) -> dict[str, str]:
550
582
  """Decodes and sanity-checks the permissions in the arguments."""
551
583
  # A dictionary mapping from user ids to one of 'R', 'W', or 'D'.
552
584
  permissions = {}
@@ -566,7 +598,7 @@ class AclChCommand:
566
598
  return permissions
567
599
 
568
600
  def _apply_permissions(
569
- self, acl: Dict[str, Union[bool, List[str]]], permissions: Dict[str, str]
601
+ self, acl: dict[str, bool | list[str]], permissions: dict[str, str]
570
602
  ) -> None:
571
603
  """Applies the given permission edits to the given acl."""
572
604
  for user, role in permissions.items():
@@ -1088,7 +1120,7 @@ class RmCommand:
1088
1120
  return ee.data.getInfo(asset_id)
1089
1121
  except ee.EEException as e:
1090
1122
  if verbose:
1091
- print('Failed to get info for %s. %s' % (asset_id, e))
1123
+ print(f'Failed to get info for {asset_id}. {e}')
1092
1124
  return None
1093
1125
 
1094
1126
  def _delete_asset(self, asset_id, recursive, verbose, dry_run):
@@ -1108,7 +1140,7 @@ class RmCommand:
1108
1140
  try:
1109
1141
  ee.data.deleteAsset(asset_id)
1110
1142
  except ee.EEException as e:
1111
- print('Failed to delete %s. %s' % (asset_id, e))
1143
+ print(f'Failed to delete {asset_id}. {e}')
1112
1144
 
1113
1145
 
1114
1146
  class TaskCancelCommand:
@@ -1129,19 +1161,21 @@ class TaskCancelCommand:
1129
1161
  config.ee_init()
1130
1162
  cancel_all = args.task_ids == ['all']
1131
1163
  if cancel_all:
1132
- statuses = ee.data.getTaskList()
1164
+ operations = ee.data.listOperations()
1133
1165
  else:
1134
- statuses = ee.data.getTaskStatus(args.task_ids)
1135
- for status in statuses:
1136
- state = status['state']
1137
- task_id = status['id']
1166
+ operation_names = map(_task_id_to_operation_name, args.task_ids)
1167
+ operations = map(ee.data.getOperation, operation_names)
1168
+ for operation in operations:
1169
+ name = operation['name']
1170
+ state = operation['metadata']['state']
1171
+ task_id = _operation_name_to_task_id(name)
1138
1172
  if state == 'UNKNOWN':
1139
- raise ee.EEException('Unknown task id "%s"' % task_id)
1140
- elif state == 'READY' or state == 'RUNNING':
1141
- print('Canceling task "%s"' % task_id)
1142
- ee.data.cancelTask(task_id)
1173
+ raise ee.EEException(f'Unknown task id "{task_id}"')
1174
+ elif state == 'PENDING' or state == 'RUNNING':
1175
+ print(f'Canceling task "{task_id}"')
1176
+ ee.data.cancelOperation(name)
1143
1177
  elif not cancel_all:
1144
- print('Task "%s" already in state "%s".' % (status['id'], state))
1178
+ print(f'Task "{task_id}" already in state "{state}".')
1145
1179
 
1146
1180
 
1147
1181
  class TaskInfoCommand:
@@ -1157,26 +1191,30 @@ class TaskInfoCommand:
1157
1191
  ) -> None:
1158
1192
  """Runs the TaskInfo command."""
1159
1193
  config.ee_init()
1160
- for i, status in enumerate(ee.data.getTaskStatus(args.task_id)):
1194
+ for i, task_id in enumerate(args.task_id):
1195
+ operation = ee.data.getOperation(_task_id_to_operation_name(task_id))
1161
1196
  if i:
1162
1197
  print()
1163
- print('%s:' % status['id'])
1164
- print(' State: %s' % status['state'])
1165
- if status['state'] == 'UNKNOWN':
1198
+ print(f'{task_id}:')
1199
+ metadata = operation['metadata']
1200
+ state = metadata['state']
1201
+ print(f' State: {state}')
1202
+ if state == 'UNKNOWN':
1166
1203
  continue
1167
- print(' Type: %s' % TASK_TYPES.get(status.get('task_type'), 'Unknown'))
1168
- print(' Description: %s' % status.get('description'))
1169
- print(' Created: %s' % _parse_millis(status['creation_timestamp_ms']))
1170
- if 'start_timestamp_ms' in status:
1171
- print(' Started: %s' % _parse_millis(status['start_timestamp_ms']))
1172
- if 'update_timestamp_ms' in status:
1173
- print(' Updated: %s' % _parse_millis(status['update_timestamp_ms']))
1174
- if 'error_message' in status:
1175
- print(' Error: %s' % status['error_message'])
1176
- if 'destination_uris' in status:
1177
- print(' Destination URIs: %s' % ', '.join(status['destination_uris']))
1178
- if 'priority' in status:
1179
- print(' Priority: %s' % status['priority'])
1204
+ print(f' Type: {TASK_TYPES.get(metadata.get("type"), "Unknown")}')
1205
+ print(f' Description: {metadata.get("description")}')
1206
+ print(f' Created: {_format_cloud_timestamp(metadata["createTime"])}')
1207
+ if start_time := metadata.get('startTime'):
1208
+ print(f' Started: {_format_cloud_timestamp(start_time)}')
1209
+ if update_time := metadata.get('updateTime'):
1210
+ print(f' Updated: {_format_cloud_timestamp(update_time)}')
1211
+ if error := operation.get('error'):
1212
+ if error_message := error.get('message'):
1213
+ print(f' Error: {error_message}')
1214
+ if destination_uris := metadata.get('destinationUris'):
1215
+ print(f' Destination URIs: {destination_uris}')
1216
+ if priority := metadata.get('priority'):
1217
+ print(f' Priority: {priority}')
1180
1218
 
1181
1219
 
1182
1220
  class TaskListCommand:
@@ -1186,10 +1224,27 @@ class TaskListCommand:
1186
1224
 
1187
1225
  def __init__(self, parser: argparse.ArgumentParser):
1188
1226
  parser.add_argument(
1189
- '--status', '-s', required=False, nargs='*',
1190
- choices=['READY', 'RUNNING', 'COMPLETED', 'FAILED',
1191
- 'CANCELLED', 'UNKNOWN'],
1192
- help=('List tasks only with a given status'))
1227
+ '--status',
1228
+ '-s',
1229
+ required=False,
1230
+ nargs='*',
1231
+ choices=[
1232
+ 'CANCELLED',
1233
+ 'CANCELLING',
1234
+ 'COMPLETED', # Kept for backward compatibility.
1235
+ 'FAILED',
1236
+ 'PENDING',
1237
+ 'READY', # Kept for backward compatibility.
1238
+ 'RUNNING',
1239
+ 'SUCCEEDED',
1240
+ 'UNKNOWN',
1241
+ ],
1242
+ help=(
1243
+ 'List tasks only with a given status. Note: for backward'
1244
+ ' compatibility, "READY" is an alias for "PENDING" and "COMPLETED"'
1245
+ ' is an alias for "SUCCEEDED".'
1246
+ ),
1247
+ )
1193
1248
  parser.add_argument(
1194
1249
  '--long_format',
1195
1250
  '-l',
@@ -1203,34 +1258,47 @@ class TaskListCommand:
1203
1258
  ) -> None:
1204
1259
  """Lists tasks present for a user, maybe filtering by state."""
1205
1260
  config.ee_init()
1206
- status = args.status
1207
- tasks = ee.data.getTaskList()
1208
- descs = [utils.truncate(task.get('description', ''), 40) for task in tasks]
1261
+ status = list(
1262
+ map(_convert_to_operation_state, args.status) if args.status else []
1263
+ )
1264
+ operations = ee.data.listOperations()
1265
+ descs = [
1266
+ utils.truncate(op.get('metadata', {}).get('description', ''), 40)
1267
+ for op in operations
1268
+ ]
1209
1269
  desc_length = max((len(word) for word in descs), default=0)
1210
- format_str = '{:25s} {:13s} {:%ds} {:10s} {:s}' % (desc_length + 1)
1211
- for task in tasks:
1212
- if status and task['state'] not in status:
1270
+ format_str = f'{{:25s}} {{:13s}} {{:{desc_length + 1}s}} {{:10s}} {{:s}}'
1271
+ for operation in operations:
1272
+ metadata = operation['metadata']
1273
+ if status and metadata['state'] not in status:
1213
1274
  continue
1214
- truncated_desc = utils.truncate(task.get('description', ''), 40)
1215
- task_type = TASK_TYPES.get(task['task_type'], 'Unknown')
1275
+ truncated_desc = utils.truncate(metadata.get('description', ''), 40)
1276
+ task_type = TASK_TYPES.get(metadata['type'], 'Unknown')
1216
1277
  extra = ''
1217
1278
  if args.long_format:
1218
- show_date = lambda ms: _parse_millis(ms).strftime('%Y-%m-%d %H:%M:%S')
1219
- eecu = '{:.4f}'.format(
1220
- task['batch_eecu_usage_seconds']
1221
- ) if 'batch_eecu_usage_seconds' in task else '-'
1222
- trailing_extras = task.get('destination_uris', [])
1223
- trailing_extras.append(task.get('priority', '-'))
1279
+ if eecu := metadata.get('batchEecuUsageSeconds'):
1280
+ eecu = f'{eecu:.4f}'
1281
+ else:
1282
+ eecu = '-'
1283
+ trailing_extras = metadata.get('destination_uris', [])
1284
+ trailing_extras.append(metadata.get('priority', '-'))
1224
1285
  extra = ' {:20s} {:20s} {:20s} {:11s} {}'.format(
1225
- show_date(task['creation_timestamp_ms']),
1226
- show_date(task['start_timestamp_ms']),
1227
- show_date(task['update_timestamp_ms']),
1286
+ _format_cloud_timestamp(operation.get('createTime')),
1287
+ _format_cloud_timestamp(operation.get('startTime')),
1288
+ _format_cloud_timestamp(operation.get('updateTime')),
1228
1289
  eecu,
1229
1290
  ' '.join(map(str, trailing_extras)),
1230
1291
  )
1231
- print(format_str.format(
1232
- task['id'], task_type, truncated_desc,
1233
- task['state'], task.get('error_message', '---')) + extra)
1292
+ print(
1293
+ format_str.format(
1294
+ _operation_name_to_task_id(operation['name']),
1295
+ task_type,
1296
+ truncated_desc,
1297
+ metadata['state'],
1298
+ operation.get('error', {}).get('message', '---'),
1299
+ )
1300
+ + extra
1301
+ )
1234
1302
 
1235
1303
 
1236
1304
  class TaskWaitCommand:
@@ -1259,17 +1327,17 @@ class TaskWaitCommand:
1259
1327
  config.ee_init()
1260
1328
  task_ids = []
1261
1329
  if args.task_ids == ['all']:
1262
- tasks = ee.data.getTaskList()
1263
- for task in tasks:
1264
- if task['state'] not in utils.TASK_FINISHED_STATES:
1265
- task_ids.append(task['id'])
1330
+ operations = ee.data.listOperations()
1331
+ for operation in operations:
1332
+ if not operation.get('done', False):
1333
+ task_ids.append(_operation_name_to_task_id(operation['name']))
1266
1334
  else:
1267
- statuses = ee.data.getTaskStatus(args.task_ids)
1268
- for status in statuses:
1269
- state = status['state']
1270
- task_id = status['id']
1335
+ for task_id in args.task_ids:
1336
+ operation = ee.data.getOperation(_task_id_to_operation_name(task_id))
1337
+ state = operation['metadata']['state']
1338
+ task_id = _operation_name_to_task_id(operation['name'])
1271
1339
  if state == 'UNKNOWN':
1272
- raise ee.EEException('Unknown task id "%s"' % task_id)
1340
+ raise ee.EEException(f'Unknown task id "{task_id}"')
1273
1341
  else:
1274
1342
  task_ids.append(task_id)
1275
1343
 
@@ -1375,7 +1443,7 @@ class UploadImageCommand:
1375
1443
  manifest = self.manifest_from_args(args)
1376
1444
  _upload(args, manifest, ee.data.startIngestion)
1377
1445
 
1378
- def manifest_from_args(self, args: argparse.Namespace) -> Dict[str, Any]:
1446
+ def manifest_from_args(self, args: argparse.Namespace) -> dict[str, Any]:
1379
1447
  """Constructs an upload manifest from the command-line flags."""
1380
1448
 
1381
1449
  def is_tf_record(path: str) -> bool:
@@ -1490,7 +1558,7 @@ class UploadExternalImageCommand:
1490
1558
  name = ee.data.startExternalImageIngestion(manifest, args.force)['name']
1491
1559
  print('Created asset %s' % name)
1492
1560
 
1493
- def manifest_from_args(self, args: argparse.Namespace) -> Dict[str, Any]:
1561
+ def manifest_from_args(self, args: argparse.Namespace) -> dict[str, Any]:
1494
1562
  """Constructs an upload manifest from the command-line flags."""
1495
1563
 
1496
1564
  if args.manifest:
@@ -1862,8 +1930,7 @@ def _make_rpc_friendly(model_dir, tag, in_map, out_map, vars_path):
1862
1930
 
1863
1931
  # Create new input placeholders to receive RPC TensorProto payloads
1864
1932
  in_op_map = {
1865
- k: tf.placeholder(
1866
- tf.string, shape=[None], name='earthengine_in_{}'.format(i))
1933
+ k: tf.placeholder(tf.string, shape=[None], name=f'earthengine_in_{i}')
1867
1934
  for (i, k) in enumerate(input_new_keys)
1868
1935
  }
1869
1936
 
@@ -1888,7 +1955,9 @@ def _make_rpc_friendly(model_dir, tag, in_map, out_map, vars_path):
1888
1955
  out_tensor = saved_model_utils.build_tensor_info(
1889
1956
  _encode_op(
1890
1957
  tf.get_default_graph().get_tensor_by_name(k),
1891
- name='earthengine_out_{}'.format(index)))
1958
+ name=f'earthengine_out_{index}',
1959
+ )
1960
+ )
1892
1961
 
1893
1962
  sig_out[_strip_index(v)] = out_tensor
1894
1963
 
@@ -2000,11 +2069,6 @@ def check_tensorflow_installed():
2000
2069
  '1.14 is installed; you can do this by executing \'pip install '
2001
2070
  'tensorflow\' in your shell.'
2002
2071
  )
2003
- else:
2004
- if not TENSORFLOW_ADDONS_INSTALLED:
2005
- print(
2006
- 'Warning: TensorFlow Addons not found. Models that use '
2007
- 'non-standard ops may not work.')
2008
2072
 
2009
2073
 
2010
2074
  class ModelCommand(Dispatcher):
ee/cli/eecli.py CHANGED
@@ -35,7 +35,7 @@ def _run_command(*argv):
35
35
  'Defaults to "~/%s".' % utils.DEFAULT_EE_CONFIG_FILE_RELATIVE)
36
36
  parser.add_argument(
37
37
  '--service_account_file', help='Path to a service account credentials'
38
- 'file. Overrides any ee_config if specified.')
38
+ 'file. Overrides any ee_config if specified.')
39
39
  parser.add_argument(
40
40
  '--project',
41
41
  help='Specifies a Google Cloud Platform Project id to override the call.',