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.
- {earthengine_api-1.5.13rc0.dist-info → earthengine_api-1.7.4.dist-info}/METADATA +3 -3
- earthengine_api-1.7.4.dist-info/RECORD +109 -0
- {earthengine_api-1.5.13rc0.dist-info → earthengine_api-1.7.4.dist-info}/WHEEL +1 -1
- ee/__init__.py +29 -28
- ee/_arg_types.py +7 -6
- ee/_cloud_api_utils.py +95 -78
- ee/_helpers.py +17 -13
- ee/_state.py +105 -0
- ee/_utils.py +2 -1
- ee/apifunction.py +21 -19
- ee/apitestcase.py +33 -38
- ee/batch.py +87 -77
- ee/blob.py +10 -12
- ee/classifier.py +57 -59
- ee/cli/commands.py +178 -114
- ee/cli/eecli.py +1 -1
- ee/cli/utils.py +61 -42
- ee/clusterer.py +39 -41
- ee/collection.py +64 -54
- ee/computedobject.py +19 -16
- ee/confusionmatrix.py +9 -9
- ee/customfunction.py +13 -12
- ee/data.py +220 -322
- ee/daterange.py +10 -10
- ee/deprecation.py +21 -13
- ee/deserializer.py +25 -20
- ee/dictionary.py +11 -11
- ee/ee_array.py +22 -20
- ee/ee_date.py +23 -23
- ee/ee_list.py +15 -16
- ee/ee_number.py +11 -21
- ee/ee_string.py +24 -32
- ee/ee_types.py +4 -4
- ee/element.py +15 -15
- ee/encodable.py +7 -4
- ee/errormargin.py +4 -4
- ee/feature.py +68 -71
- ee/featurecollection.py +41 -40
- ee/filter.py +90 -92
- ee/function.py +8 -8
- ee/geometry.py +95 -93
- ee/image.py +238 -236
- ee/image_converter.py +4 -4
- ee/imagecollection.py +30 -27
- ee/join.py +13 -15
- ee/kernel.py +55 -57
- ee/mapclient.py +9 -9
- ee/model.py +29 -31
- ee/oauth.py +76 -63
- ee/pixeltype.py +6 -6
- ee/projection.py +5 -4
- ee/reducer.py +41 -41
- ee/serializer.py +14 -14
- ee/table_converter.py +7 -6
- ee/terrain.py +7 -9
- ee/tests/_cloud_api_utils_test.py +21 -6
- ee/tests/_helpers_test.py +57 -4
- ee/tests/_state_test.py +49 -0
- ee/tests/algorithms.json +85 -2
- ee/tests/apifunction_test.py +5 -5
- ee/tests/batch_test.py +135 -57
- ee/tests/blob_test.py +5 -5
- ee/tests/classifier_test.py +3 -3
- ee/tests/clusterer_test.py +3 -3
- ee/tests/collection_test.py +48 -13
- ee/tests/confusionmatrix_test.py +3 -3
- ee/tests/data_test.py +484 -55
- ee/tests/daterange_test.py +4 -4
- ee/tests/deprecation_test.py +6 -4
- ee/tests/deserializer_test.py +64 -5
- ee/tests/dictionary_test.py +12 -12
- ee/tests/ee_array_test.py +3 -3
- ee/tests/ee_date_test.py +4 -4
- ee/tests/ee_list_test.py +3 -3
- ee/tests/ee_number_test.py +75 -30
- ee/tests/ee_string_test.py +11 -3
- ee/tests/ee_test.py +40 -22
- ee/tests/element_test.py +2 -2
- ee/tests/errormargin_test.py +1 -1
- ee/tests/feature_test.py +10 -10
- ee/tests/featurecollection_test.py +3 -3
- ee/tests/filter_test.py +4 -4
- ee/tests/function_test.py +5 -5
- ee/tests/geometry_point_test.py +3 -3
- ee/tests/geometry_test.py +93 -52
- ee/tests/image_converter_test.py +1 -3
- ee/tests/image_test.py +3 -3
- ee/tests/imagecollection_test.py +3 -3
- ee/tests/join_test.py +3 -3
- ee/tests/kernel_test.py +7 -3
- ee/tests/model_test.py +17 -5
- ee/tests/oauth_test.py +189 -7
- ee/tests/pixeltype_test.py +6 -7
- ee/tests/projection_test.py +5 -6
- ee/tests/reducer_test.py +16 -3
- ee/tests/serializer_test.py +39 -12
- ee/tests/table_converter_test.py +51 -7
- ee/tests/terrain_test.py +11 -3
- earthengine_api-1.5.13rc0.dist-info/RECORD +0 -107
- {earthengine_api-1.5.13rc0.dist-info → earthengine_api-1.7.4.dist-info}/entry_points.txt +0 -0
- {earthengine_api-1.5.13rc0.dist-info → earthengine_api-1.7.4.dist-info}/licenses/LICENSE +0 -0
- {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,
|
|
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".
|
|
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:
|
|
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) ->
|
|
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) ->
|
|
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) ->
|
|
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
|
|
215
|
-
|
|
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
|
-
|
|
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
|
|
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]].
|
|
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) ->
|
|
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:
|
|
340
|
-
command_dict:
|
|
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.
|
|
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:
|
|
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:
|
|
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) ->
|
|
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:
|
|
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
|
|
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
|
|
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
|
-
|
|
1164
|
+
operations = ee.data.listOperations()
|
|
1133
1165
|
else:
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
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 "
|
|
1140
|
-
elif state == '
|
|
1141
|
-
print('Canceling task "
|
|
1142
|
-
ee.data.
|
|
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 "
|
|
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,
|
|
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('
|
|
1164
|
-
|
|
1165
|
-
|
|
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:
|
|
1168
|
-
print(' Description:
|
|
1169
|
-
print(' Created:
|
|
1170
|
-
if '
|
|
1171
|
-
print(' Started:
|
|
1172
|
-
if '
|
|
1173
|
-
print(' Updated:
|
|
1174
|
-
if '
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
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',
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
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 =
|
|
1207
|
-
|
|
1208
|
-
|
|
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} {
|
|
1211
|
-
for
|
|
1212
|
-
|
|
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(
|
|
1215
|
-
task_type = TASK_TYPES.get(
|
|
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
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
trailing_extras =
|
|
1223
|
-
trailing_extras.append(
|
|
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
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
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(
|
|
1232
|
-
|
|
1233
|
-
|
|
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
|
-
|
|
1263
|
-
for
|
|
1264
|
-
if
|
|
1265
|
-
task_ids.append(
|
|
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
|
-
|
|
1268
|
-
|
|
1269
|
-
state =
|
|
1270
|
-
task_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 "
|
|
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) ->
|
|
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) ->
|
|
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_{}'
|
|
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.
|
|
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.',
|