earthengine-api 1.7.1__py3-none-any.whl → 1.7.2rc0__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.

@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: earthengine-api
3
- Version: 1.7.1
3
+ Version: 1.7.2rc0
4
4
  Summary: Earth Engine Python API
5
5
  Author-email: Google LLC <noreply@google.com>
6
6
  License: Apache-2.0
@@ -1,5 +1,5 @@
1
- earthengine_api-1.7.1.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
2
- ee/__init__.py,sha256=H9GBFE0QU68m7tlX_1Ew6TW37nyfwkL4tuRLOk_D8Y4,16758
1
+ earthengine_api-1.7.2rc0.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
2
+ ee/__init__.py,sha256=qXFqyr1frW_X1pYT3uUOMUT6rvGLGcCNejHvZ_g-VQI,16761
3
3
  ee/_arg_types.py,sha256=nrJrnPFnAS8fzMxAGmG3TbUOi_yFVrnSGW5IP8ATzDQ,2662
4
4
  ee/_cloud_api_utils.py,sha256=5WPzP5bcBIbjA7yYN-Cwmht_HU-XobbTrZvRiOEn0e0,32890
5
5
  ee/_helpers.py,sha256=afDDqoz1WESKE-lzqLtrCbvoQ1yVpfvJIgFcmUjO4L8,4821
@@ -15,7 +15,7 @@ ee/collection.py,sha256=c8OFIX9OVLxTbu8HGYiHOS3Yg6QjnxaO_FAhDhDQpic,32588
15
15
  ee/computedobject.py,sha256=Cdlccd4vw2neeRQlo5Jn3Q5R-VpQcQufNuxOHfpd1FU,9186
16
16
  ee/confusionmatrix.py,sha256=e6vz-FcT6acWxk5pDEK0vLIN1pUjYMh5Gyn1QSGJT3I,4233
17
17
  ee/customfunction.py,sha256=Me_iHDnjkYR33JMttGA7pKJlPCjUk8rO_kgaIhDm27M,7408
18
- ee/data.py,sha256=Q15duH7_O2gjIlDanmjien5z8ZZRuooyhNivUpXbM6E,85370
18
+ ee/data.py,sha256=TmtDZZXVJxKOVPNSabQ9DxtYALn1BXUHiZJKXzVL6w4,85371
19
19
  ee/daterange.py,sha256=nrRYkR2M2aVU0ZJyG7yiZStFt-W2TvYVuazoZK_WZqM,4948
20
20
  ee/deprecation.py,sha256=mAFdO7fBjOu9TJd8CJB_M_NsZ6VNSxtSe820QKJx8fE,6243
21
21
  ee/deserializer.py,sha256=TjhQjcBnFpz8Kodoj74eSbvkJ2zLjpTTfUpcNEoQR2o,8357
@@ -42,7 +42,7 @@ ee/join.py,sha256=idnuzRNFvKHRLHV5m9unp_xTsZb-E2kfgUCEYSg64ts,7578
42
42
  ee/kernel.py,sha256=eTzJFvW4nbyiFl2dDZ0_cA_o3KqCbBewY-BDp7tCB_M,15002
43
43
  ee/mapclient.py,sha256=feCFwL76juFFZPI4uycVxUoFMeEFbPFfIVETkmpvmcY,17491
44
44
  ee/model.py,sha256=ZLBT0bdo16V_fSR3eqVxvEmkpGGqMdqYBVZTwOAXvcU,12069
45
- ee/oauth.py,sha256=gqm2nUIAk_PN9yJes1Yy8eLzQ8vvPapZPOY05BVRvOY,22118
45
+ ee/oauth.py,sha256=i2R56YDJwC9A1Wpf9MOz-01L4PYoHvuIs0TnCpjlLxU,22194
46
46
  ee/pixeltype.py,sha256=ucUwJ5SvcOT849Ap4mlJL9z11IAbcT1w9ii8-zOoqdI,5196
47
47
  ee/projection.py,sha256=n7WvMeYEG9zksGN1tIX7RGweC4IyCrXAKsvPY0e2b8k,5868
48
48
  ee/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -69,7 +69,7 @@ ee/tests/clusterer_test.py,sha256=B4m06wAtBeqvnIhRD2lnNy1UHDB_caleK_CqcrAU8dk,11
69
69
  ee/tests/collection_test.py,sha256=zsBerYrFr390awe-wD8uN9VME_qEsuaEN197hom4Xps,8815
70
70
  ee/tests/computedobject_test.py,sha256=B27rDq9Urpvy0WqpdbKRYbt6AcT1i93HX-es7hrhWVY,4840
71
71
  ee/tests/confusionmatrix_test.py,sha256=46JJh1-91AiYISXWZ6-2lvY5_Njvc8ompO9kmwqlFdg,7437
72
- ee/tests/data_test.py,sha256=YyVh891MuLeDWx5Fj_uGHvpudy73E8cu1BhR1G-mlWA,40391
72
+ ee/tests/data_test.py,sha256=8oiSnzGoOtWx3YIld8owWYw1zC517uLkq1Uf2Xn4iCI,52063
73
73
  ee/tests/daterange_test.py,sha256=a5fpg2lko3kCJzxQPCoAc_vjXkKy2zYcXbeSZKAFovI,8583
74
74
  ee/tests/deprecation_test.py,sha256=CoVug7J9YdB2utZ-BikyJcjitYPQLz49JL_z89xuQSQ,8484
75
75
  ee/tests/deserializer_test.py,sha256=i_tlyzKgCo1-VpWPAQnTWNcr2klourcMcBbFZJN6Ivw,5050
@@ -95,15 +95,15 @@ ee/tests/imagecollection_test.py,sha256=qosRZXCbhwFuy8qR7DbA-FMl4ktW7Y2cUJHeXLcs
95
95
  ee/tests/join_test.py,sha256=pFILq3qM27rO64WYbBC1A_Gs8_pabRv68X7MU_EM_cw,7630
96
96
  ee/tests/kernel_test.py,sha256=-0nZVNSxE1IGs4y60oijISh2Hi1V7NCjrmUnEMDd0nQ,19378
97
97
  ee/tests/model_test.py,sha256=dgnWfqR4R4MIUljMBL1TOcztzA-lWEl7cT5f2Y0k2ck,12483
98
- ee/tests/oauth_test.py,sha256=jYFw8GWPFmLR0jh2NvbcrMizQo_qR_bRDBVRdIZ44g0,3067
98
+ ee/tests/oauth_test.py,sha256=8hldNiR3AYtQkWA4vwwSAO6jDIsh_fww7b_cOI9MJjo,9202
99
99
  ee/tests/pixeltype_test.py,sha256=00IWKnZ7xxkVwSSCuWOlCwlTsHAb3XPyKp1Arc4S12U,10024
100
100
  ee/tests/projection_test.py,sha256=fKXXxQPBvWdlMNtNsJze2pbsT0yHHlL7ON8Pdjm1Z7E,6871
101
101
  ee/tests/reducer_test.py,sha256=vqXpgLZ7fPyfZ12srpREgMpfrKYLcFQod3Qn1Niv7VM,31979
102
102
  ee/tests/serializer_test.py,sha256=d6IEW_dt-G900IRfQnZftrwjpc1wJ-ouaQQwaLO0FxI,9834
103
- ee/tests/table_converter_test.py,sha256=KubC_IY4SpXkpEwvisplI1KQgLzHFOfxKepGgcZCoLg,3360
103
+ ee/tests/table_converter_test.py,sha256=t4yIfy40R3U17I_2nVtKm_Q2Tae-UifwZLz8qOmoC9A,5091
104
104
  ee/tests/terrain_test.py,sha256=inZ2sy807nDG_HMutzGHaqcTUaLnZQOMdWyf0NrQzV0,4561
105
- earthengine_api-1.7.1.dist-info/METADATA,sha256=2zVSZ2_2zHMhC1H5OGcgqJ7HbEUwUhs4hcj9tr_lrVc,2144
106
- earthengine_api-1.7.1.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
107
- earthengine_api-1.7.1.dist-info/entry_points.txt,sha256=-Ax4SCU-S474r8OD2LIxata6PRmkZoDrppQ4fP_exNc,50
108
- earthengine_api-1.7.1.dist-info/top_level.txt,sha256=go5zOwCgm5lIS3yTR-Vsxp1gNI4qdS-MP5eY-7zMxVY,3
109
- earthengine_api-1.7.1.dist-info/RECORD,,
105
+ earthengine_api-1.7.2rc0.dist-info/METADATA,sha256=-fSFJkm8cxCurqIIWXyR5FNsmKOb8LaI72q9K7fqf2k,2147
106
+ earthengine_api-1.7.2rc0.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
107
+ earthengine_api-1.7.2rc0.dist-info/entry_points.txt,sha256=-Ax4SCU-S474r8OD2LIxata6PRmkZoDrppQ4fP_exNc,50
108
+ earthengine_api-1.7.2rc0.dist-info/top_level.txt,sha256=go5zOwCgm5lIS3yTR-Vsxp1gNI4qdS-MP5eY-7zMxVY,3
109
+ earthengine_api-1.7.2rc0.dist-info/RECORD,,
ee/__init__.py CHANGED
@@ -1,6 +1,6 @@
1
1
  """The EE Python library."""
2
2
 
3
- __version__ = '1.7.1'
3
+ __version__ = '1.7.2rc0'
4
4
 
5
5
  # Using lowercase function naming to match the JavaScript names.
6
6
  # pylint: disable=g-bad-name
ee/data.py CHANGED
@@ -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.
ee/oauth.py CHANGED
@@ -226,7 +226,7 @@ def _in_jupyter_shell() -> bool:
226
226
  """Tests if the code is being executed within Jupyter."""
227
227
  try:
228
228
  import ipykernel.zmqshell
229
- return isinstance(IPython.get_ipython(),
229
+ return isinstance(IPython.get_ipython(), # pylint: disable=undefined-variable
230
230
  ipykernel.zmqshell.ZMQInteractiveShell)
231
231
  except ImportError:
232
232
  return False
@@ -329,7 +329,7 @@ def _display_auth_instructions_with_html(
329
329
  ) -> None:
330
330
  """Displays instructions for authenticating using HTML code."""
331
331
  try:
332
- IPython.display.display(IPython.display.HTML(
332
+ IPython.display.display(IPython.display.HTML( # pylint: disable=undefined-variable
333
333
  """<p>To authorize access needed by Earth Engine, open the following
334
334
  URL in a web browser and follow the instructions:</p>
335
335
  <p><a href={0}>{0}</a></p>
ee/tests/data_test.py CHANGED
@@ -87,6 +87,26 @@ class DataTest(unittest.TestCase):
87
87
  _state.get_state().cloud_api_user_project, 'earthengine-legacy'
88
88
  )
89
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
+
90
110
  def test_set_max_retries_bad_values(self):
91
111
  with self.assertRaises(ValueError):
92
112
  ee.data.setMaxRetries(-1)
@@ -110,6 +130,46 @@ class DataTest(unittest.TestCase):
110
130
  .execute.call_args.kwargs['num_retries'],
111
131
  )
112
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
+
113
173
  def test_list_operations(self):
114
174
  mock_http = mock.MagicMock(httplib2.Http)
115
175
  # Return in three groups.
@@ -342,6 +402,14 @@ class DataTest(unittest.TestCase):
342
402
  asset = mock_create_asset.call_args.kwargs['body']
343
403
  self.assertEqual(asset, {'type': 'FOLDER'})
344
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
+
345
413
  def test_create_assets(self):
346
414
  cloud_api_resource = mock.MagicMock()
347
415
  with apitestcase.UsingCloudApi(cloud_api_resource=cloud_api_resource):
@@ -432,6 +500,68 @@ class DataTest(unittest.TestCase):
432
500
  )
433
501
  self.assertTrue(import_args['overwrite'])
434
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
+
435
565
  def test_set_asset_properties(self):
436
566
  mock_http = mock.MagicMock(httplib2.Http)
437
567
  with apitestcase.UsingCloudApi(mock_http=mock_http), mock.patch.object(
@@ -453,6 +583,18 @@ class DataTest(unittest.TestCase):
453
583
  {'properties.\"mYPropErTy\"',
454
584
  'properties.\"system:time_start\"'})
455
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
+
456
598
  def test_list_assets(self):
457
599
  cloud_api_resource = mock.MagicMock()
458
600
  with apitestcase.UsingCloudApi(cloud_api_resource=cloud_api_resource):
@@ -576,6 +718,16 @@ class DataTest(unittest.TestCase):
576
718
  cloud_api_resource.projects().listAssets().execute.assert_called_once()
577
719
  self.assertEqual(mock_result, actual_result)
578
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
+
579
731
  def test_simple_get_list_via_cloud_api(self):
580
732
  cloud_api_resource = mock.MagicMock()
581
733
  with apitestcase.UsingCloudApi(cloud_api_resource=cloud_api_resource):
@@ -881,6 +1033,50 @@ class DataTest(unittest.TestCase):
881
1033
  ).execute.assert_called_once()
882
1034
  self.assertEqual(mock_result, actual_result)
883
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
+
884
1080
  def test_get_feature_view_tiles_key(self):
885
1081
  cloud_api_resource = mock.MagicMock()
886
1082
  _state.get_state().tile_base_url = 'base_url'
@@ -922,8 +1118,31 @@ class DataTest(unittest.TestCase):
922
1118
  cloud_api_resource.projects().updateConfig().execute.return_value = (
923
1119
  mock_result
924
1120
  )
1121
+ project_config = {'maxConcurrentExports': 2}
925
1122
  actual_result = ee.data.updateProjectConfig(
926
- {'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',
927
1146
  )
928
1147
  cloud_api_resource.projects().updateConfig().execute.assert_called_once()
929
1148
  self.assertEqual(mock_result, actual_result)
@@ -1040,6 +1259,81 @@ class DataTest(unittest.TestCase):
1040
1259
  }
1041
1260
  self.assertEqual(expected, quota)
1042
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
+
1043
1337
 
1044
1338
  def DoCloudProfileStubHttp(test, expect_profiling):
1045
1339
 
ee/tests/oauth_test.py CHANGED
@@ -1,6 +1,8 @@
1
1
  #!/usr/bin/env python3
2
2
  """Test for the oauth module."""
3
3
 
4
+ import http.client
5
+ import io
4
6
  import json
5
7
  import sys
6
8
  import tempfile
@@ -34,20 +36,33 @@ class OAuthTest(unittest.TestCase):
34
36
  self.assertEqual('xyz', parsed[b'code_verifier'][0].decode())
35
37
  return MockResponse(parsed[b'code'][0])
36
38
 
37
- with mock.patch('urllib.request.urlopen', new=mock_urlopen):
39
+ with mock.patch.object(urllib.request, 'urlopen', new=mock_urlopen):
38
40
  auth_code = '123'
39
41
  verifier = 'xyz'
40
42
  refresh_token = oauth.request_token(auth_code, verifier)
41
43
  self.assertEqual('123456', refresh_token)
42
44
 
45
+ def test_request_token_http_error(self):
46
+ mock_fp = io.BytesIO(b'error details')
47
+ http_error = urllib.error.HTTPError(
48
+ 'url', 400, 'message', hdrs=http.client.HTTPMessage(), fp=mock_fp
49
+ )
50
+ with mock.patch.object(urllib.request, 'urlopen', side_effect=http_error):
51
+ with self.assertRaisesRegex(
52
+ Exception,
53
+ r"Problem requesting tokens.*HTTP Error 400: message.*b'error"
54
+ r" details'",
55
+ ):
56
+ oauth.request_token('auth_code', 'code_verifier')
57
+
43
58
  def test_write_token(self):
44
59
 
45
60
  def mock_credentials_path():
46
- return self.test_tmpdir + '/tempfile'
61
+ return f'{self.test_tmpdir}/tempfile'
47
62
 
48
- oauth_pkg = 'ee.oauth'
49
- with mock.patch(
50
- oauth_pkg + '.get_credentials_path', new=mock_credentials_path):
63
+ with mock.patch.object(
64
+ oauth, 'get_credentials_path', new=mock_credentials_path
65
+ ):
51
66
  client_info = dict(refresh_token='123')
52
67
  oauth.write_private_json(oauth.get_credentials_path(), client_info)
53
68
 
@@ -55,6 +70,46 @@ class OAuthTest(unittest.TestCase):
55
70
  token = json.load(f)
56
71
  self.assertEqual({'refresh_token': '123'}, token)
57
72
 
73
+ def test_get_credentials_arguments(self):
74
+ credentials_path = f'{self.test_tmpdir}/temp_creds'
75
+
76
+ creds = {
77
+ 'refresh_token': 'REFRESH_TOKEN',
78
+ 'client_id': 'CLIENT_ID',
79
+ 'project': 'PROJECT',
80
+ }
81
+ with open(credentials_path, 'w') as f:
82
+ json.dump(creds, f)
83
+
84
+ with mock.patch.object(
85
+ oauth, 'get_credentials_path', return_value=credentials_path
86
+ ):
87
+ args = oauth.get_credentials_arguments()
88
+
89
+ expected_args = {
90
+ 'token_uri': oauth.TOKEN_URI,
91
+ 'refresh_token': 'REFRESH_TOKEN',
92
+ 'client_id': 'CLIENT_ID',
93
+ 'client_secret': oauth.CLIENT_SECRET,
94
+ 'scopes': oauth.SCOPES,
95
+ 'quota_project_id': 'PROJECT',
96
+ }
97
+ self.assertEqual(expected_args, args)
98
+
99
+ def test_is_valid_credentials(self):
100
+ self.assertFalse(oauth.is_valid_credentials(None))
101
+
102
+ mock_credentials_valid = mock.MagicMock()
103
+ self.assertTrue(oauth.is_valid_credentials(mock_credentials_valid))
104
+ mock_credentials_valid.refresh.assert_called_once()
105
+
106
+ mock_credentials_invalid = mock.MagicMock()
107
+ mock_credentials_invalid.refresh.side_effect = (
108
+ oauth.google.auth.exceptions.RefreshError()
109
+ )
110
+ self.assertFalse(oauth.is_valid_credentials(mock_credentials_invalid))
111
+ mock_credentials_invalid.refresh.assert_called_once()
112
+
58
113
  def test_in_colab_shell(self):
59
114
  with mock.patch.dict(sys.modules, {'google.colab': None}):
60
115
  self.assertFalse(oauth.in_colab_shell())
@@ -78,7 +133,7 @@ class OAuthTest(unittest.TestCase):
78
133
  )
79
134
  )
80
135
 
81
- def testAuthenticate_colabAuthModeWithNonstandardScopes_raisesException(self):
136
+ def test_colab_mode_with_nonstandard_scopes_raises_exception(self):
82
137
  with self.assertRaisesRegex(
83
138
  ee_exception.EEException,
84
139
  'Scopes cannot be customized when auth_mode is "colab".'
@@ -88,7 +143,7 @@ class OAuthTest(unittest.TestCase):
88
143
  scopes=['https://www.googleapis.com/auth/earthengine.readonly']
89
144
  )
90
145
 
91
- def testAuthenticate_colabAuthModeWithStandardScopes_succeeds(self):
146
+ def test_colab_auth_mode_with_standard_scopes_succeeds(self):
92
147
  # Should not raise an exception if the scopes are not narrowed.
93
148
  with mock.patch.dict(sys.modules, {'google.colab': mock.MagicMock()}):
94
149
  try:
@@ -96,5 +151,113 @@ class OAuthTest(unittest.TestCase):
96
151
  except ee_exception.EEException:
97
152
  self.fail('authenticate raised an exception unexpectedly.')
98
153
 
154
+ def test_authenticate_appdefault(self):
155
+ with mock.patch.object(
156
+ oauth,
157
+ '_valid_credentials_exist',
158
+ return_value=False,
159
+ ), mock.patch.object(
160
+ sys, 'stderr', new_callable=io.StringIO
161
+ ) as mock_stderr:
162
+ oauth.authenticate(auth_mode='appdefault')
163
+ self.assertIn('appdefault no longer necessary', mock_stderr.getvalue())
164
+
165
+ @mock.patch.object(oauth, '_load_gcloud_credentials')
166
+ @mock.patch.object(oauth, '_valid_credentials_exist', return_value=False)
167
+ @mock.patch.object(oauth, 'in_colab_shell', return_value=False)
168
+ @mock.patch.object(oauth, '_in_jupyter_shell', return_value=False)
169
+ @mock.patch.object(oauth, '_localhost_is_viable', return_value=False)
170
+ def test_authenticate_default_gcloud(
171
+ self,
172
+ mock_localhost_viable,
173
+ mock_jupyter,
174
+ mock_colab,
175
+ mock_valid_creds,
176
+ mock_load_gcloud,
177
+ ):
178
+ del (
179
+ mock_localhost_viable,
180
+ mock_jupyter,
181
+ mock_colab,
182
+ mock_valid_creds,
183
+ ) # Unused
184
+ oauth.authenticate()
185
+ mock_load_gcloud.assert_called_once_with(None, None, False)
186
+
187
+ def test_localhost_fetch_code(self):
188
+ mock_server = mock.MagicMock()
189
+ mock_server.url = 'http://localhost:8085'
190
+ mock_server.fetch_code.return_value = 'FETCHED_CODE'
191
+ with mock.patch.object(
192
+ oauth, '_start_server', return_value=mock_server
193
+ ), mock.patch.object(oauth, '_obtain_and_write_token') as mock_obtain:
194
+ flow = oauth.Flow(auth_mode='localhost')
195
+ flow.save_code()
196
+ mock_server.fetch_code.assert_called_once()
197
+ mock_obtain.assert_called_once_with(
198
+ 'FETCHED_CODE',
199
+ flow.code_verifier,
200
+ flow.scopes,
201
+ 'http://localhost:8085',
202
+ )
203
+
204
+ @mock.patch.object(oauth, '_display_auth_instructions_for_noninteractive')
205
+ def test_display_instructions_quiet(self, mock_display):
206
+ flow = oauth.Flow(auth_mode='notebook')
207
+ self.assertTrue(flow.display_instructions(quiet=True))
208
+ mock_display.assert_called_once_with(flow.auth_url, flow.code_verifier)
209
+
210
+ @mock.patch.object(oauth, '_display_auth_instructions_with_print')
211
+ @mock.patch.object(oauth, 'in_colab_shell', return_value=True)
212
+ def test_display_instructions_colab(self, mock_in_colab, mock_display_print):
213
+ del mock_in_colab # Unused
214
+ flow = oauth.Flow(auth_mode='notebook')
215
+ self.assertTrue(flow.display_instructions())
216
+ mock_display_print.assert_called_once_with(flow.auth_url, None)
217
+
218
+ @mock.patch.object(oauth, '_display_auth_instructions_with_html')
219
+ @mock.patch.object(oauth, '_in_jupyter_shell', return_value=True)
220
+ def test_display_instructions_jupyter(
221
+ self, mock_in_jupyter, mock_display_html
222
+ ):
223
+ del mock_in_jupyter # Unused
224
+ flow = oauth.Flow(auth_mode='notebook')
225
+ self.assertTrue(flow.display_instructions())
226
+ mock_display_html.assert_called_once_with(flow.auth_url, None)
227
+
228
+ @mock.patch.object(oauth, '_display_auth_instructions_with_print')
229
+ @mock.patch.object(oauth, 'in_colab_shell', return_value=False)
230
+ @mock.patch.object(oauth, '_in_jupyter_shell', return_value=False)
231
+ def test_display_instructions_print(
232
+ self, mock_in_jupyter, mock_in_colab, mock_display_print
233
+ ):
234
+ del mock_in_jupyter, mock_in_colab # Unused
235
+ flow = oauth.Flow(auth_mode='notebook')
236
+ self.assertTrue(flow.display_instructions())
237
+ mock_display_print.assert_called_once_with(flow.auth_url, None)
238
+
239
+ @mock.patch.object(oauth, '_display_auth_instructions_with_print')
240
+ @mock.patch.object(oauth, 'in_colab_shell', return_value=True)
241
+ @mock.patch.object(oauth.http.server, 'HTTPServer')
242
+ def test_display_instructions_localhost_colab(
243
+ self, mock_http_server, mock_in_colab, mock_display_print
244
+ ):
245
+ del mock_http_server, mock_in_colab # Unused
246
+ flow = oauth.Flow(auth_mode='localhost')
247
+ self.assertTrue(flow.display_instructions())
248
+ mock_display_print.assert_called_once_with(
249
+ flow.auth_url, oauth.WAITING_CODA
250
+ )
251
+
252
+ def test_flow_unknown_auth_mode(self):
253
+ with self.assertRaisesRegex(ee_exception.EEException, 'Unknown auth_mode'):
254
+ oauth.Flow(auth_mode='unknown')
255
+
256
+ def test_flow_localhost_with_port(self):
257
+ with mock.patch.object(oauth, '_start_server') as mock_start_server:
258
+ oauth.Flow(auth_mode='localhost:1234')
259
+ mock_start_server.assert_called_once_with(1234)
260
+
261
+
99
262
  if __name__ == '__main__':
100
263
  unittest.main()
@@ -1,7 +1,9 @@
1
1
  #!/usr/bin/env python3
2
2
  """Tests for the table_converter module."""
3
3
 
4
+ import builtins
4
5
  from typing import Any
6
+ from unittest import mock
5
7
 
6
8
  from absl.testing import parameterized
7
9
  import geopandas
@@ -29,7 +31,7 @@ class TableConverterTest(parameterized.TestCase):
29
31
  self,
30
32
  data_format: str,
31
33
  expected: type[table_converter.TableConverter] | None,
32
- ) -> None:
34
+ ):
33
35
  """Verifies `from_file_format` returns the correct converter class."""
34
36
  if expected is None:
35
37
  self.assertIsNone(table_converter.from_file_format(data_format))
@@ -38,7 +40,17 @@ class TableConverterTest(parameterized.TestCase):
38
40
  table_converter.from_file_format(data_format), expected
39
41
  )
40
42
 
41
- def test_pandas_converter(self) -> None:
43
+ def test_from_file_format_instance(self):
44
+ """Verifies `from_file_format` returns the same instance."""
45
+ converter = table_converter.PandasConverter()
46
+ self.assertIs(table_converter.from_file_format(converter), converter)
47
+
48
+ def test_table_converter_fails(self):
49
+ """Verifies `TableConverter` cannot be used for conversion."""
50
+ with self.assertRaises(NotImplementedError):
51
+ table_converter.TableConverter().do_conversion(iter([]))
52
+
53
+ def test_pandas_converter(self):
42
54
  """Verifies `PandasConverter` does the correct conversion."""
43
55
  converter = table_converter.PandasConverter()
44
56
 
@@ -69,7 +81,23 @@ class TableConverterTest(parameterized.TestCase):
69
81
  ]),
70
82
  )
71
83
 
72
- def test_geopandas_converter(self) -> None:
84
+ def test_pandas_converter_importerror(self):
85
+ """Ensures ImportError is raised when pandas is not available."""
86
+ real_import = builtins.__import__
87
+
88
+ def mock_import(name, globals=None, locals=None, fromlist=(), level=0):
89
+ if name == 'pandas':
90
+ raise ImportError
91
+ return real_import(name, globals, locals, fromlist, level)
92
+
93
+ with mock.patch('builtins.__import__', mock_import):
94
+ converter = table_converter.PandasConverter()
95
+ with self.assertRaisesRegex(
96
+ ImportError, 'Using format PANDAS_DATAFRAME requires pandas.'
97
+ ):
98
+ converter.do_conversion(iter([]))
99
+
100
+ def test_geopandas_converter(self):
73
101
  """Verifies `GeoPandasConverter` does the correct conversion."""
74
102
  converter = table_converter.GeoPandasConverter()
75
103
 
@@ -105,6 +133,22 @@ class TableConverterTest(parameterized.TestCase):
105
133
  geopandas.GeoDataFrame.from_features(feature_coll),
106
134
  )
107
135
 
136
+ def test_geopandas_converter_importerror(self):
137
+ """Ensures ImportError is raised when geopandas is not available."""
138
+ real_import = builtins.__import__
139
+
140
+ def mock_import(name, globals=None, locals=None, fromlist=(), level=0):
141
+ if name == 'geopandas':
142
+ raise ImportError
143
+ return real_import(name, globals, locals, fromlist, level)
144
+
145
+ with mock.patch('builtins.__import__', mock_import):
146
+ converter = table_converter.GeoPandasConverter()
147
+ with self.assertRaisesRegex(
148
+ ImportError, 'Using format GEOPANDAS_GEODATAFRAME requires geopandas.'
149
+ ):
150
+ converter.do_conversion(iter([]))
151
+
108
152
 
109
153
  if __name__ == '__main__':
110
154
  unittest.main()