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/tests/data_test.py CHANGED
@@ -2,7 +2,7 @@
2
2
  """Test for the ee.data module."""
3
3
 
4
4
  import json
5
- from typing import Any, Optional
5
+ from typing import Any
6
6
  from unittest import mock
7
7
 
8
8
  import googleapiclient
@@ -28,7 +28,7 @@ def NotFoundError() -> googleapiclient.errors.HttpError:
28
28
 
29
29
 
30
30
  def NewFolderAsset(
31
- name: str, quota: Optional[dict[str, int]] = None
31
+ name: str, quota: dict[str, int] | None = None
32
32
  ) -> dict[str, Any]:
33
33
  return {
34
34
  'type': 'FOLDER',
@@ -66,7 +66,9 @@ class DataTest(unittest.TestCase):
66
66
  mock_install_cloud_api_resource.assert_called_once()
67
67
 
68
68
  @mock.patch.object(ee.data, '_install_cloud_api_resource', return_value=None)
69
- def test_initialize_with_project(self, unused_mock_install_cloud_api_resource):
69
+ def test_initialize_with_project(
70
+ self, unused_mock_install_cloud_api_resource
71
+ ):
70
72
  ee.data.initialize(project='my-project')
71
73
 
72
74
  self.assertTrue(ee.data.is_initialized())
@@ -85,6 +87,26 @@ class DataTest(unittest.TestCase):
85
87
  _state.get_state().cloud_api_user_project, 'earthengine-legacy'
86
88
  )
87
89
 
90
+ @mock.patch.object(ee.data, '_install_cloud_api_resource', return_value=None)
91
+ def test_initialize_with_credentials(
92
+ self, unused_mock_install_cloud_api_resource
93
+ ):
94
+ creds = mock.MagicMock()
95
+ ee.data.initialize(credentials=creds)
96
+
97
+ self.assertTrue(ee.data.is_initialized())
98
+ self.assertEqual(creds, _state.get_state().credentials)
99
+
100
+ @mock.patch.object(ee.data, '_install_cloud_api_resource', return_value=None)
101
+ def test_initialize_with_cloud_api_key(
102
+ self, unused_mock_install_cloud_api_resource
103
+ ):
104
+ cloud_api_key = 'a cloud api key'
105
+ ee.data.initialize(cloud_api_key=cloud_api_key)
106
+
107
+ self.assertTrue(ee.data.is_initialized())
108
+ self.assertEqual(cloud_api_key, _state.get_state().cloud_api_key)
109
+
88
110
  def test_set_max_retries_bad_values(self):
89
111
  with self.assertRaises(ValueError):
90
112
  ee.data.setMaxRetries(-1)
@@ -108,6 +130,46 @@ class DataTest(unittest.TestCase):
108
130
  .execute.call_args.kwargs['num_retries'],
109
131
  )
110
132
 
133
+ def test_set_cloud_api_key(self):
134
+ cloud_api_key = 'a cloud api key'
135
+ with mock.patch.object(
136
+ ee.data, '_install_cloud_api_resource', return_value=None
137
+ ) as mock_install_cloud_api_resource:
138
+ ee.data.setCloudApiKey(cloud_api_key)
139
+ self.assertEqual(cloud_api_key, _state.get_state().cloud_api_key)
140
+ mock_install_cloud_api_resource.assert_called_once()
141
+
142
+ def test_set_deadline(self):
143
+ deadline_ms = 12345
144
+ with mock.patch.object(
145
+ ee.data, '_install_cloud_api_resource', return_value=None
146
+ ) as mock_install_cloud_api_resource:
147
+ ee.data.setDeadline(deadline_ms)
148
+ self.assertEqual(deadline_ms, _state.get_state().deadline_ms)
149
+ mock_install_cloud_api_resource.assert_called_once()
150
+
151
+ def test_get_set_user_agent(self):
152
+ self.assertIsNone(ee.data.getUserAgent())
153
+ user_agent = 'user-agent'
154
+ ee.data.setUserAgent(user_agent)
155
+ self.assertEqual(user_agent, ee.data.getUserAgent())
156
+
157
+ def test_authorize_http_no_credentials(self):
158
+ self.assertIsNone(ee.data._get_state().credentials)
159
+ http = mock.MagicMock()
160
+ self.assertEqual(http, ee.data.authorizeHttp(http))
161
+
162
+ def test_authorize_http_with_credentials(self):
163
+ creds = mock.MagicMock()
164
+ ee.data._get_state().credentials = creds
165
+ http = mock.MagicMock()
166
+ with mock.patch.object(
167
+ ee.data.google_auth_httplib2, 'AuthorizedHttp'
168
+ ) as mock_authorized_http:
169
+ result = ee.data.authorizeHttp(http)
170
+ self.assertEqual(mock_authorized_http.return_value, result)
171
+ mock_authorized_http.assert_called_once_with(creds)
172
+
111
173
  def test_list_operations(self):
112
174
  mock_http = mock.MagicMock(httplib2.Http)
113
175
  # Return in three groups.
@@ -340,6 +402,14 @@ class DataTest(unittest.TestCase):
340
402
  asset = mock_create_asset.call_args.kwargs['body']
341
403
  self.assertEqual(asset, {'type': 'FOLDER'})
342
404
 
405
+ @mock.patch.object(ee.data, 'createAsset')
406
+ def test_create_asset_home(self, mock_create_asset):
407
+ ee.data.createAssetHome('users/test')
408
+ mock_create_asset.assert_called_once_with({
409
+ 'name': 'projects/earthengine-legacy/assets/users/test',
410
+ 'type': 'FOLDER',
411
+ })
412
+
343
413
  def test_create_assets(self):
344
414
  cloud_api_resource = mock.MagicMock()
345
415
  with apitestcase.UsingCloudApi(cloud_api_resource=cloud_api_resource):
@@ -430,6 +500,68 @@ class DataTest(unittest.TestCase):
430
500
  )
431
501
  self.assertTrue(import_args['overwrite'])
432
502
 
503
+ def test_start_table_ingestion(self):
504
+ cloud_api_resource = mock.MagicMock()
505
+ with apitestcase.UsingCloudApi(cloud_api_resource=cloud_api_resource):
506
+ mock_result = {'name': 'operations/ingestion', 'done': False}
507
+ cloud_api_resource.projects().table().import_.return_value.execute.return_value = (
508
+ mock_result
509
+ )
510
+ params = {
511
+ 'id': 'users/test/table',
512
+ 'sources': [{'uris': ['gs://bucket/file.shp'], 'charset': 'UTF-8'}],
513
+ }
514
+ result = ee.data.startTableIngestion('request_id', params, True)
515
+ self.assertEqual(result['id'], 'ingestion')
516
+ self.assertEqual(result['name'], 'operations/ingestion')
517
+
518
+ mock_import = cloud_api_resource.projects().table().import_
519
+ mock_import.assert_called_once()
520
+ call_kwargs = mock_import.call_args.kwargs
521
+ self.assertEqual(call_kwargs['project'], 'projects/earthengine-legacy')
522
+ body = call_kwargs['body']
523
+ self.assertEqual(
524
+ body['tableManifest'],
525
+ {
526
+ 'name': 'projects/earthengine-legacy/assets/users/test/table',
527
+ 'sources': [
528
+ {'uris': ['gs://bucket/file.shp'], 'charset': 'UTF-8'}
529
+ ],
530
+ },
531
+ )
532
+ self.assertEqual(body['requestId'], 'request_id')
533
+ self.assertTrue(body['overwrite'])
534
+
535
+ def test_start_external_image_ingestion(self):
536
+ cloud_api_resource = mock.MagicMock()
537
+ with apitestcase.UsingCloudApi(cloud_api_resource=cloud_api_resource):
538
+ cloud_api_resource.projects().image().importExternal.return_value.execute.return_value = (
539
+ {}
540
+ )
541
+ manifest = {
542
+ 'id': 'users/test/image',
543
+ 'tilesets': [{'sources': [{'uris': ['gs://bucket/file.tif']}]}],
544
+ }
545
+ result = ee.data.startExternalImageIngestion(manifest, True)
546
+ expected_name = 'projects/earthengine-legacy/assets/users/test/image'
547
+ self.assertEqual(result['name'], expected_name)
548
+
549
+ mock_import_external = (
550
+ cloud_api_resource.projects().image().importExternal
551
+ )
552
+ mock_import_external.assert_called_once()
553
+ call_kwargs = mock_import_external.call_args.kwargs
554
+ self.assertEqual(call_kwargs['project'], 'projects/earthengine-legacy')
555
+ body = call_kwargs['body']
556
+ self.assertEqual(
557
+ body['imageManifest'],
558
+ {
559
+ 'name': expected_name,
560
+ 'tilesets': [{'sources': [{'uris': ['gs://bucket/file.tif']}]}],
561
+ },
562
+ )
563
+ self.assertTrue(body['overwrite'])
564
+
433
565
  def test_set_asset_properties(self):
434
566
  mock_http = mock.MagicMock(httplib2.Http)
435
567
  with apitestcase.UsingCloudApi(mock_http=mock_http), mock.patch.object(
@@ -451,6 +583,18 @@ class DataTest(unittest.TestCase):
451
583
  {'properties.\"mYPropErTy\"',
452
584
  'properties.\"system:time_start\"'})
453
585
 
586
+ def test_update_asset(self):
587
+ cloud_api_resource = mock.MagicMock()
588
+ with apitestcase.UsingCloudApi(cloud_api_resource=cloud_api_resource):
589
+ asset_id = 'users/test/asset'
590
+ asset = {'properties': {'foo': 'bar'}}
591
+ update_mask = ['properties.foo']
592
+ ee.data.updateAsset(asset_id, asset, update_mask)
593
+ cloud_api_resource.projects().assets().patch.assert_called_once_with(
594
+ name='projects/earthengine-legacy/assets/users/test/asset',
595
+ body={'updateMask': {'paths': update_mask}, 'asset': asset},
596
+ )
597
+
454
598
  def test_list_assets(self):
455
599
  cloud_api_resource = mock.MagicMock()
456
600
  with apitestcase.UsingCloudApi(cloud_api_resource=cloud_api_resource):
@@ -574,6 +718,16 @@ class DataTest(unittest.TestCase):
574
718
  cloud_api_resource.projects().listAssets().execute.assert_called_once()
575
719
  self.assertEqual(mock_result, actual_result)
576
720
 
721
+ def test_get_asset_roots(self):
722
+ with mock.patch.object(
723
+ ee.data,
724
+ 'listBuckets',
725
+ return_value={'assets': [{'name': 'id1', 'type': 'FOLDER'}]},
726
+ ) as mock_list_buckets:
727
+ result = ee.data.getAssetRoots()
728
+ mock_list_buckets.assert_called_once()
729
+ self.assertEqual([{'id': 'id1', 'type': 'Folder'}], result)
730
+
577
731
  def test_simple_get_list_via_cloud_api(self):
578
732
  cloud_api_resource = mock.MagicMock()
579
733
  with apitestcase.UsingCloudApi(cloud_api_resource=cloud_api_resource):
@@ -879,6 +1033,50 @@ class DataTest(unittest.TestCase):
879
1033
  ).execute.assert_called_once()
880
1034
  self.assertEqual(mock_result, actual_result)
881
1035
 
1036
+ def test_get_pixels(self):
1037
+ cloud_api_resource_raw = mock.MagicMock()
1038
+ with apitestcase.UsingCloudApi(
1039
+ cloud_api_resource_raw=cloud_api_resource_raw
1040
+ ):
1041
+ assets = cloud_api_resource_raw.projects().assets()
1042
+ mock_result = b'pixel data'
1043
+ assets.getPixels.return_value.execute.return_value = mock_result
1044
+ asset_id = 'users/foo/bar'
1045
+ params = {'assetId': asset_id}
1046
+ result = ee.data.getPixels(params)
1047
+ self.assertEqual(mock_result, result)
1048
+ assets.getPixels.assert_called_once_with(
1049
+ name='projects/earthengine-legacy/assets/users/foo/bar',
1050
+ body={'fileFormat': 'AUTO_JPEG_PNG'},
1051
+ )
1052
+
1053
+ def test_compute_pixels(self):
1054
+ cloud_api_resource_raw = mock.MagicMock()
1055
+ with apitestcase.UsingCloudApi(
1056
+ cloud_api_resource_raw=cloud_api_resource_raw
1057
+ ):
1058
+ mock_result = b'pixel data'
1059
+ (
1060
+ cloud_api_resource_raw.projects()
1061
+ .image()
1062
+ .computePixels.return_value.execute.return_value
1063
+ ) = mock_result
1064
+ expression = ee.Image(1)
1065
+ params = {'expression': expression}
1066
+ result = ee.data.computePixels(params)
1067
+ self.assertEqual(mock_result, result)
1068
+ (
1069
+ cloud_api_resource_raw.projects()
1070
+ .image()
1071
+ .computePixels.assert_called_once_with(
1072
+ project='projects/earthengine-legacy',
1073
+ body={
1074
+ 'expression': ee.serializer.encode(expression),
1075
+ 'fileFormat': 'AUTO_JPEG_PNG',
1076
+ },
1077
+ )
1078
+ )
1079
+
882
1080
  def test_get_feature_view_tiles_key(self):
883
1081
  cloud_api_resource = mock.MagicMock()
884
1082
  _state.get_state().tile_base_url = 'base_url'
@@ -920,8 +1118,31 @@ class DataTest(unittest.TestCase):
920
1118
  cloud_api_resource.projects().updateConfig().execute.return_value = (
921
1119
  mock_result
922
1120
  )
1121
+ project_config = {'maxConcurrentExports': 2}
923
1122
  actual_result = ee.data.updateProjectConfig(
924
- {'maxConcurrentExports': 2}, ['max_concurrent_exports']
1123
+ project_config, ['max_concurrent_exports']
1124
+ )
1125
+ cloud_api_resource.projects().updateConfig.assert_called_with(
1126
+ name='projects/earthengine-legacy/config',
1127
+ body=project_config,
1128
+ updateMask='max_concurrent_exports',
1129
+ )
1130
+ cloud_api_resource.projects().updateConfig().execute.assert_called_once()
1131
+ self.assertEqual(mock_result, actual_result)
1132
+
1133
+ def test_update_project_config_no_mask(self) -> None:
1134
+ cloud_api_resource = mock.MagicMock()
1135
+ with apitestcase.UsingCloudApi(cloud_api_resource=cloud_api_resource):
1136
+ mock_result = {'fake-project-config-value': 1}
1137
+ cloud_api_resource.projects().updateConfig().execute.return_value = (
1138
+ mock_result
1139
+ )
1140
+ project_config = {'maxConcurrentExports': 2}
1141
+ actual_result = ee.data.updateProjectConfig(project_config)
1142
+ cloud_api_resource.projects().updateConfig.assert_called_with(
1143
+ name='projects/earthengine-legacy/config',
1144
+ body=project_config,
1145
+ updateMask='max_concurrent_exports',
925
1146
  )
926
1147
  cloud_api_resource.projects().updateConfig().execute.assert_called_once()
927
1148
  self.assertEqual(mock_result, actual_result)
@@ -1038,6 +1259,81 @@ class DataTest(unittest.TestCase):
1038
1259
  }
1039
1260
  self.assertEqual(expected, quota)
1040
1261
 
1262
+ def test_get_asset_root_quota_not_root(self):
1263
+ cloud_api_resource = mock.MagicMock()
1264
+ with apitestcase.UsingCloudApi(cloud_api_resource=cloud_api_resource):
1265
+ asset_id = 'users/test/asset'
1266
+ fake_asset = {
1267
+ 'type': 'IMAGE',
1268
+ 'name': 'projects/earthengine-legacy/assets/users/test/asset',
1269
+ }
1270
+ cloud_api_resource.projects().assets().get().execute.return_value = (
1271
+ fake_asset
1272
+ )
1273
+ with self.assertRaisesRegex(
1274
+ ee.ee_exception.EEException, f'{asset_id} is not a root folder.'
1275
+ ):
1276
+ ee.data.getAssetRootQuota(asset_id)
1277
+
1278
+ def test_get_iam_policy(self):
1279
+ cloud_api_resource = mock.MagicMock()
1280
+ with apitestcase.UsingCloudApi(cloud_api_resource=cloud_api_resource):
1281
+ asset_id = 'users/test/asset'
1282
+ ee.data.getIamPolicy(asset_id)
1283
+ cloud_api_resource.projects().assets().getIamPolicy.assert_called_once_with(
1284
+ resource='projects/earthengine-legacy/assets/users/test/asset',
1285
+ body={},
1286
+ prettyPrint=False,
1287
+ )
1288
+
1289
+ def test_get_asset_acl(self):
1290
+ asset_id = 'users/test/asset'
1291
+ policy = {'bindings': [{'role': 'roles/viewer', 'members': ['allUsers']}]}
1292
+ acl = {'readers': ['allUsers']}
1293
+ with mock.patch.object(
1294
+ ee.data, 'getIamPolicy', return_value=policy
1295
+ ) as mock_get_iam_policy, mock.patch.object(
1296
+ ee.data._cloud_api_utils, 'convert_iam_policy_to_acl', return_value=acl
1297
+ ) as mock_convert:
1298
+ result = ee.data.getAssetAcl(asset_id)
1299
+ mock_get_iam_policy.assert_called_once_with(asset_id)
1300
+ mock_convert.assert_called_once_with(policy)
1301
+ self.assertEqual(acl, result)
1302
+
1303
+ def test_set_asset_acl(self):
1304
+ asset_id = 'users/test/asset'
1305
+ acl_update_dict = {'readers': ['allUsers']}
1306
+ acl_update_str = '{"readers": ["allUsers"]}'
1307
+ policy = {'bindings': [{'role': 'roles/viewer', 'members': ['allUsers']}]}
1308
+ with mock.patch.object(
1309
+ ee.data._cloud_api_utils,
1310
+ 'convert_acl_to_iam_policy',
1311
+ return_value=policy,
1312
+ ) as mock_convert, mock.patch.object(
1313
+ ee.data, 'setIamPolicy'
1314
+ ) as mock_set_iam_policy:
1315
+ ee.data.setAssetAcl(asset_id, acl_update_dict)
1316
+ mock_convert.assert_called_once_with(acl_update_dict)
1317
+ mock_set_iam_policy.assert_called_once_with(asset_id, policy)
1318
+
1319
+ mock_convert.reset_mock()
1320
+ mock_set_iam_policy.reset_mock()
1321
+ ee.data.setAssetAcl(asset_id, acl_update_str)
1322
+ mock_convert.assert_called_once_with(acl_update_dict)
1323
+ mock_set_iam_policy.assert_called_once_with(asset_id, policy)
1324
+
1325
+ def test_set_iam_policy(self):
1326
+ cloud_api_resource = mock.MagicMock()
1327
+ with apitestcase.UsingCloudApi(cloud_api_resource=cloud_api_resource):
1328
+ asset_id = 'users/test/asset'
1329
+ policy = {'bindings': [{'role': 'roles/viewer', 'members': ['allUsers']}]}
1330
+ ee.data.setIamPolicy(asset_id, policy)
1331
+ cloud_api_resource.projects().assets().setIamPolicy.assert_called_once_with(
1332
+ resource='projects/earthengine-legacy/assets/users/test/asset',
1333
+ body={'policy': policy},
1334
+ prettyPrint=False,
1335
+ )
1336
+
1041
1337
 
1042
1338
  def DoCloudProfileStubHttp(test, expect_profiling):
1043
1339
 
@@ -100,7 +100,8 @@ _EXPECTED_WARNINGS = {
100
100
  r'Attention required for date_and_learn_more! You are using a'
101
101
  r' deprecated asset.\nTo make sure your code keeps working, please'
102
102
  r' update'
103
- r' it by July 1, 2024.\nLearn more: learn_more_url'
103
+ r' it by July 1, 2024.\nThis dataset has been superseded by'
104
+ r' replacement_id\n\nLearn more: learn_more_url'
104
105
  ),
105
106
  'date_only': (
106
107
  r'Attention required for date_only! You are using a deprecated asset.\n'
@@ -124,7 +125,8 @@ _EXPECTED_WARNINGS = {
124
125
  r'Attention required for learn_more_url_only! You are using a'
125
126
  r' deprecated asset.\nTo make sure your code keeps working, please'
126
127
  r' update'
127
- r' it.\nLearn more: learn_more_url'
128
+ r' it.\nThis dataset has been superseded by replacement_id\n\nLearn'
129
+ r' more: learn_more_url'
128
130
  ),
129
131
  }
130
132
 
@@ -80,6 +80,53 @@ class DeserializerTest(apitestcase.ApiTestCase):
80
80
  ):
81
81
  deserializer.decode(encoded)
82
82
 
83
+ def test_duplicate_scope_key(self):
84
+ """Verifies raising duplicate scope key in decode()."""
85
+ encoded = {
86
+ 'type': 'CompoundValue',
87
+ 'scope': [['a', 1], ['a', 2]],
88
+ 'value': 3,
89
+ }
90
+ with self.assertRaisesRegex(
91
+ ee.EEException, 'Duplicate scope key "a" in scope #1.'
92
+ ):
93
+ deserializer.decode(encoded)
94
+
95
+ def test_cannot_decode_object(self):
96
+ """Verifies raising EEException for non-dict objects in _decodeValue()."""
97
+ with self.assertRaisesRegex(
98
+ ee.EEException, r'Cannot decode object: \(1\+1j\)'
99
+ ):
100
+ deserializer.decode([1 + 1j])
101
+
102
+ def test_invalid_date_value(self):
103
+ """Verifies EEException for invalid date values in _decodeValue()."""
104
+ with self.assertRaisesRegex(
105
+ ee.EEException, r'Invalid date value: not-a-number'
106
+ ):
107
+ deserializer.decode({'type': 'Date', 'value': 'not-a-number'})
108
+
109
+ def test_invocation_of_custom_function(self):
110
+ """Verifies decoding of an Invocation of a CustomFunction."""
111
+ encoded = {
112
+ 'type': 'Invocation',
113
+ 'function': {
114
+ 'type': 'Function',
115
+ 'argumentNames': ['foo'],
116
+ 'body': {'type': 'ArgumentRef', 'value': 'foo'},
117
+ },
118
+ 'arguments': {'foo': 1},
119
+ }
120
+ decoded = deserializer.decode(encoded)
121
+ self.assertIsInstance(decoded, ee.ComputedObject)
122
+
123
+ def test_date_decode(self):
124
+ """Verifies decoding of a Date."""
125
+ encoded = {'type': 'Date', 'value': 1609459200000000}
126
+ decoded = deserializer.decode(encoded)
127
+ self.assertIsInstance(decoded, ee.Date)
128
+ self.assertEqual({'value': 1609459200000}, decoded.args)
129
+
83
130
 
84
131
  if __name__ == '__main__':
85
132
  unittest.main()
@@ -396,6 +396,31 @@ class NumberTest(apitestcase.ApiTestCase):
396
396
  result = json.loads(expression.serialize())
397
397
  self.assertEqual(expect, result)
398
398
 
399
+ def test_expression(self):
400
+ expression = ee.Number.expression('1 + 2')
401
+ self.assertIsInstance(expression, ee.Number)
402
+ self.assertEqual(
403
+ ee.ApiFunction.lookup('Number.expression'), expression.func
404
+ )
405
+
406
+ expect = make_expression_graph({
407
+ 'arguments': {'expression': {'constantValue': '1 + 2'}},
408
+ 'functionName': 'Number.expression',
409
+ })
410
+ result = json.loads(expression.serialize())
411
+ self.assertEqual(expect, result)
412
+
413
+ expression_vars = ee.Number.expression('a + b', {'a': 1, 'b': 2})
414
+ expect_vars = make_expression_graph({
415
+ 'arguments': {
416
+ 'expression': {'constantValue': 'a + b'},
417
+ 'vars': {'constantValue': {'a': 1, 'b': 2}},
418
+ },
419
+ 'functionName': 'Number.expression',
420
+ })
421
+ result_vars = json.loads(expression_vars.serialize())
422
+ self.assertEqual(expect_vars, result_vars)
423
+
399
424
  def test_first(self):
400
425
  expect = make_expression_graph({
401
426
  'arguments': {
@@ -795,7 +820,21 @@ class NumberTest(apitestcase.ApiTestCase):
795
820
  result = json.loads(expression.serialize())
796
821
  self.assertEqual(expect, result)
797
822
 
798
- # TODO: test_parse.
823
+ def test_parse(self):
824
+ expect = make_expression_graph({
825
+ 'arguments': {
826
+ 'input': {'constantValue': '1'},
827
+ 'radix': {'constantValue': 2},
828
+ },
829
+ 'functionName': 'Number.parse',
830
+ })
831
+ expression = ee.Number.parse('1', 2)
832
+ result = json.loads(expression.serialize())
833
+ self.assertEqual(expect, result)
834
+
835
+ expression = ee.Number.parse(input='1', radix=2)
836
+ result = json.loads(expression.serialize())
837
+ self.assertEqual(expect, result)
799
838
 
800
839
  def test_pow(self):
801
840
  expect = make_expression_graph({
@@ -1,8 +1,6 @@
1
1
  #!/usr/bin/env python3
2
2
  """Tests for the image_converter module."""
3
3
 
4
- from typing import Optional
5
-
6
4
  from absl.testing import parameterized
7
5
  import numpy
8
6
 
@@ -20,7 +18,7 @@ class ImageConverterTest(parameterized.TestCase):
20
18
  def test_from_file_format(
21
19
  self,
22
20
  data_format: str,
23
- expected: Optional[type[image_converter.ImageConverter]],
21
+ expected: type[image_converter.ImageConverter] | None,
24
22
  ) -> None:
25
23
  """Verifies `from_file_format` returns the correct converter class."""
26
24
  if expected is None:
ee/tests/kernel_test.py CHANGED
@@ -287,6 +287,10 @@ class KernelTest(apitestcase.ApiTestCase):
287
287
  result = json.loads(expression.serialize())
288
288
  self.assertEqual(expect, result)
289
289
 
290
+ def test_fixed_no_weights(self):
291
+ with self.assertRaisesRegex(ValueError, 'weights is required.'):
292
+ ee.Kernel.fixed()
293
+
290
294
  def test_gaussian(self):
291
295
  radius = 1.1
292
296
  sigma = 2.2
ee/tests/model_test.py CHANGED
@@ -115,6 +115,18 @@ class ModelTest(apitestcase.ApiTestCase):
115
115
  }
116
116
  self.assertEqual(expect, result)
117
117
 
118
+ def test_model_constructor_invalid(self):
119
+ with self.assertRaisesRegex(
120
+ TypeError, 'Model constructor can only cast to Model.'
121
+ ):
122
+ ee.Model('not a computed object') # pytype: disable=wrong-arg-types
123
+
124
+ def test_model_constructor_invalid_int(self):
125
+ with self.assertRaisesRegex(
126
+ TypeError, 'Model constructor can only cast to Model.'
127
+ ):
128
+ ee.Model(123) # pytype: disable=wrong-arg-types
129
+
118
130
  def test_from_ai_platform_predictor(self):
119
131
  project_name = 'some project'
120
132
  project_id = 'a project id'