earthengine-api 1.7.1rc0__tar.gz → 1.7.2rc0__tar.gz

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.
Files changed (116) hide show
  1. {earthengine_api-1.7.1rc0/earthengine_api.egg-info → earthengine_api-1.7.2rc0}/PKG-INFO +1 -1
  2. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0/earthengine_api.egg-info}/PKG-INFO +1 -1
  3. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/__init__.py +1 -1
  4. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/data.py +1 -0
  5. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/oauth.py +2 -2
  6. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/tests/data_test.py +295 -1
  7. earthengine_api-1.7.2rc0/ee/tests/oauth_test.py +263 -0
  8. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/tests/table_converter_test.py +47 -3
  9. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/pyproject.toml +1 -1
  10. earthengine_api-1.7.1rc0/ee/tests/oauth_test.py +0 -100
  11. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/LICENSE +0 -0
  12. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/MANIFEST.in +0 -0
  13. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/README.md +0 -0
  14. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/earthengine_api.egg-info/SOURCES.txt +0 -0
  15. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/earthengine_api.egg-info/dependency_links.txt +0 -0
  16. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/earthengine_api.egg-info/entry_points.txt +0 -0
  17. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/earthengine_api.egg-info/requires.txt +0 -0
  18. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/earthengine_api.egg-info/top_level.txt +0 -0
  19. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/_arg_types.py +0 -0
  20. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/_cloud_api_utils.py +0 -0
  21. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/_helpers.py +0 -0
  22. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/_state.py +0 -0
  23. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/_utils.py +0 -0
  24. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/apifunction.py +0 -0
  25. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/apitestcase.py +0 -0
  26. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/batch.py +0 -0
  27. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/blob.py +0 -0
  28. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/classifier.py +0 -0
  29. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/cli/__init__.py +0 -0
  30. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/cli/commands.py +0 -0
  31. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/cli/eecli.py +0 -0
  32. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/cli/eecli_wrapper.py +0 -0
  33. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/cli/utils.py +0 -0
  34. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/clusterer.py +0 -0
  35. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/collection.py +0 -0
  36. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/computedobject.py +0 -0
  37. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/confusionmatrix.py +0 -0
  38. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/customfunction.py +0 -0
  39. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/daterange.py +0 -0
  40. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/deprecation.py +0 -0
  41. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/deserializer.py +0 -0
  42. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/dictionary.py +0 -0
  43. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/ee_array.py +0 -0
  44. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/ee_date.py +0 -0
  45. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/ee_exception.py +0 -0
  46. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/ee_list.py +0 -0
  47. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/ee_number.py +0 -0
  48. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/ee_string.py +0 -0
  49. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/ee_types.py +0 -0
  50. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/element.py +0 -0
  51. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/encodable.py +0 -0
  52. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/errormargin.py +0 -0
  53. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/feature.py +0 -0
  54. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/featurecollection.py +0 -0
  55. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/filter.py +0 -0
  56. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/function.py +0 -0
  57. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/geometry.py +0 -0
  58. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/image.py +0 -0
  59. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/image_converter.py +0 -0
  60. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/imagecollection.py +0 -0
  61. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/join.py +0 -0
  62. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/kernel.py +0 -0
  63. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/mapclient.py +0 -0
  64. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/model.py +0 -0
  65. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/pixeltype.py +0 -0
  66. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/projection.py +0 -0
  67. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/py.typed +0 -0
  68. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/reducer.py +0 -0
  69. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/serializer.py +0 -0
  70. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/table_converter.py +0 -0
  71. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/terrain.py +0 -0
  72. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/tests/_cloud_api_utils_test.py +0 -0
  73. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/tests/_helpers_test.py +0 -0
  74. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/tests/_state_test.py +0 -0
  75. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/tests/_utils_test.py +0 -0
  76. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/tests/algorithms.json +0 -0
  77. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/tests/apifunction_test.py +0 -0
  78. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/tests/batch_test.py +0 -0
  79. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/tests/blob_test.py +0 -0
  80. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/tests/classifier_test.py +0 -0
  81. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/tests/cloud_api_discovery_document.json +0 -0
  82. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/tests/clusterer_test.py +0 -0
  83. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/tests/collection_test.py +0 -0
  84. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/tests/computedobject_test.py +0 -0
  85. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/tests/confusionmatrix_test.py +0 -0
  86. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/tests/daterange_test.py +0 -0
  87. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/tests/deprecation_test.py +0 -0
  88. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/tests/deserializer_test.py +0 -0
  89. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/tests/dictionary_test.py +0 -0
  90. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/tests/ee_array_test.py +0 -0
  91. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/tests/ee_date_test.py +0 -0
  92. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/tests/ee_list_test.py +0 -0
  93. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/tests/ee_number_test.py +0 -0
  94. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/tests/ee_string_test.py +0 -0
  95. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/tests/ee_test.py +0 -0
  96. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/tests/ee_types_test.py +0 -0
  97. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/tests/element_test.py +0 -0
  98. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/tests/errormargin_test.py +0 -0
  99. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/tests/feature_test.py +0 -0
  100. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/tests/featurecollection_test.py +0 -0
  101. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/tests/filter_test.py +0 -0
  102. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/tests/function_test.py +0 -0
  103. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/tests/geometry_point_test.py +0 -0
  104. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/tests/geometry_test.py +0 -0
  105. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/tests/image_converter_test.py +0 -0
  106. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/tests/image_test.py +0 -0
  107. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/tests/imagecollection_test.py +0 -0
  108. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/tests/join_test.py +0 -0
  109. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/tests/kernel_test.py +0 -0
  110. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/tests/model_test.py +0 -0
  111. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/tests/pixeltype_test.py +0 -0
  112. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/tests/projection_test.py +0 -0
  113. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/tests/reducer_test.py +0 -0
  114. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/tests/serializer_test.py +0 -0
  115. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/ee/tests/terrain_test.py +0 -0
  116. {earthengine_api-1.7.1rc0 → earthengine_api-1.7.2rc0}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: earthengine-api
3
- Version: 1.7.1rc0
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,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: earthengine-api
3
- Version: 1.7.1rc0
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,6 +1,6 @@
1
1
  """The EE Python library."""
2
2
 
3
- __version__ = '1.7.1rc0'
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
@@ -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.
@@ -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>
@@ -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
 
@@ -0,0 +1,263 @@
1
+ #!/usr/bin/env python3
2
+ """Test for the oauth module."""
3
+
4
+ import http.client
5
+ import io
6
+ import json
7
+ import sys
8
+ import tempfile
9
+ from unittest import mock
10
+ import urllib.parse
11
+
12
+ import unittest
13
+ from ee import ee_exception
14
+ from ee import oauth
15
+
16
+
17
+ class OAuthTest(unittest.TestCase):
18
+
19
+ def setUp(self):
20
+ super().setUp()
21
+ self.test_tmpdir = tempfile.mkdtemp()
22
+
23
+ def test_request_token(self):
24
+
25
+ class MockResponse:
26
+
27
+ def __init__(self, code):
28
+ self.code = code.decode()
29
+
30
+ def read(self):
31
+ return ('{"refresh_token": "' + self.code + '456"}').encode()
32
+
33
+ def mock_urlopen(url, param):
34
+ del url # Unused.
35
+ parsed = urllib.parse.parse_qs(param)
36
+ self.assertEqual('xyz', parsed[b'code_verifier'][0].decode())
37
+ return MockResponse(parsed[b'code'][0])
38
+
39
+ with mock.patch.object(urllib.request, 'urlopen', new=mock_urlopen):
40
+ auth_code = '123'
41
+ verifier = 'xyz'
42
+ refresh_token = oauth.request_token(auth_code, verifier)
43
+ self.assertEqual('123456', refresh_token)
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
+
58
+ def test_write_token(self):
59
+
60
+ def mock_credentials_path():
61
+ return f'{self.test_tmpdir}/tempfile'
62
+
63
+ with mock.patch.object(
64
+ oauth, 'get_credentials_path', new=mock_credentials_path
65
+ ):
66
+ client_info = dict(refresh_token='123')
67
+ oauth.write_private_json(oauth.get_credentials_path(), client_info)
68
+
69
+ with open(mock_credentials_path()) as f:
70
+ token = json.load(f)
71
+ self.assertEqual({'refresh_token': '123'}, token)
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
+
113
+ def test_in_colab_shell(self):
114
+ with mock.patch.dict(sys.modules, {'google.colab': None}):
115
+ self.assertFalse(oauth.in_colab_shell())
116
+
117
+ with mock.patch.dict(sys.modules, {'google.colab': mock.MagicMock()}):
118
+ self.assertTrue(oauth.in_colab_shell())
119
+
120
+ def test_is_sdk_credentials(self):
121
+ sdk_project = oauth.SDK_PROJECTS[0]
122
+ self.assertFalse(oauth.is_sdk_credentials(None))
123
+ self.assertFalse(oauth.is_sdk_credentials(mock.MagicMock()))
124
+ self.assertFalse(
125
+ oauth.is_sdk_credentials(mock.MagicMock(client_id='123'))
126
+ )
127
+ self.assertTrue(
128
+ oauth.is_sdk_credentials(mock.MagicMock(client_id=sdk_project))
129
+ )
130
+ self.assertTrue(
131
+ oauth.is_sdk_credentials(
132
+ mock.MagicMock(client_id=f'{sdk_project}-somethingelse')
133
+ )
134
+ )
135
+
136
+ def test_colab_mode_with_nonstandard_scopes_raises_exception(self):
137
+ with self.assertRaisesRegex(
138
+ ee_exception.EEException,
139
+ 'Scopes cannot be customized when auth_mode is "colab".'
140
+ ):
141
+ oauth.authenticate(
142
+ auth_mode='colab',
143
+ scopes=['https://www.googleapis.com/auth/earthengine.readonly']
144
+ )
145
+
146
+ def test_colab_auth_mode_with_standard_scopes_succeeds(self):
147
+ # Should not raise an exception if the scopes are not narrowed.
148
+ with mock.patch.dict(sys.modules, {'google.colab': mock.MagicMock()}):
149
+ try:
150
+ oauth.authenticate(auth_mode='colab', scopes=oauth.SCOPES)
151
+ except ee_exception.EEException:
152
+ self.fail('authenticate raised an exception unexpectedly.')
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
+
262
+ if __name__ == '__main__':
263
+ unittest.main()