earthengine-api 1.7.1rc0__py3-none-any.whl → 1.7.3__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of earthengine-api might be problematic. Click here for more details.
- {earthengine_api-1.7.1rc0.dist-info → earthengine_api-1.7.3.dist-info}/METADATA +1 -1
- {earthengine_api-1.7.1rc0.dist-info → earthengine_api-1.7.3.dist-info}/RECORD +14 -14
- ee/__init__.py +1 -1
- ee/batch.py +7 -0
- ee/data.py +1 -0
- ee/oauth.py +2 -2
- ee/tests/batch_test.py +56 -0
- ee/tests/data_test.py +295 -1
- ee/tests/oauth_test.py +170 -7
- ee/tests/table_converter_test.py +47 -3
- {earthengine_api-1.7.1rc0.dist-info → earthengine_api-1.7.3.dist-info}/WHEEL +0 -0
- {earthengine_api-1.7.1rc0.dist-info → earthengine_api-1.7.3.dist-info}/entry_points.txt +0 -0
- {earthengine_api-1.7.1rc0.dist-info → earthengine_api-1.7.3.dist-info}/licenses/LICENSE +0 -0
- {earthengine_api-1.7.1rc0.dist-info → earthengine_api-1.7.3.dist-info}/top_level.txt +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
earthengine_api-1.7.
|
|
2
|
-
ee/__init__.py,sha256=
|
|
1
|
+
earthengine_api-1.7.3.dist-info/licenses/LICENSE,sha256=xx0jnfkXJvxRnG63LTGOxlggYnIysveWIZ6H3PNdCrQ,11357
|
|
2
|
+
ee/__init__.py,sha256=D_fgl3mxnyoktwl1nKjZyAftuqaf7DccD610EJbIT38,16758
|
|
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
|
|
@@ -7,7 +7,7 @@ ee/_state.py,sha256=y2hdq_ZEgFkO9a-DbWFSthehlAyTbp--cz4C04XS058,2754
|
|
|
7
7
|
ee/_utils.py,sha256=ciocjvlvR73G07IV_iZjLi1IGowpEbT53NzFqqwSbDU,1356
|
|
8
8
|
ee/apifunction.py,sha256=mCCsLhBs_MofvMW49OFjT5psQGMbXWQSpAlhiqMEc4k,8732
|
|
9
9
|
ee/apitestcase.py,sha256=8N5y2-Tw7c5GjedbSA_HRpSjVUiVLoyM_FI2g5b9Tpc,13992
|
|
10
|
-
ee/batch.py,sha256=
|
|
10
|
+
ee/batch.py,sha256=pyhSmwH6hApRgdA8x6qbFn-mMEM8G-zf3OHW5PMziQI,82694
|
|
11
11
|
ee/blob.py,sha256=cnnqmrHl998U2zEmfKcxQglaGk14Ip6pjObk_Ody6Uk,3221
|
|
12
12
|
ee/classifier.py,sha256=3STLrLT6Ddwzjs2324E02wRWM8VEpeifgOO81geTHJQ,23410
|
|
13
13
|
ee/clusterer.py,sha256=986D68b2eJ4xiiOiIOTNZ1psF8Ro-MFNQFKed058FS4,12256
|
|
@@ -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=
|
|
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=
|
|
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
|
|
@@ -61,7 +61,7 @@ ee/tests/_state_test.py,sha256=AKe5Vopzt24FJPiPcEce_oOT861gOkd6mUOAWZr6C0g,1175
|
|
|
61
61
|
ee/tests/_utils_test.py,sha256=bOarVj3U-VFo9Prog8WQN_hAOMwJOiWKJxevUbdFPBQ,2753
|
|
62
62
|
ee/tests/algorithms.json,sha256=Vx1Kx_MhHv0z0B3WTeVAvchM8xVd3zYE7L-qT3gDGzA,729368
|
|
63
63
|
ee/tests/apifunction_test.py,sha256=_6xpkJOd7hScYYQxzFR-8flraTQ6xfkm7gyER6JoBog,3779
|
|
64
|
-
ee/tests/batch_test.py,sha256=
|
|
64
|
+
ee/tests/batch_test.py,sha256=NhR5CsNNSbuMgrmJWoc0grn7gjmlUY37oMIXCwA2T-0,65116
|
|
65
65
|
ee/tests/blob_test.py,sha256=uCrM-ubRfAmNgHwhmUhWn7MiXqbNQutybcXIKdVsD_Q,3587
|
|
66
66
|
ee/tests/classifier_test.py,sha256=K6-wNZ2uh9oPYo7BV0vtfU73SBFpeNcFRMRmvEFc6Pg,19087
|
|
67
67
|
ee/tests/cloud_api_discovery_document.json,sha256=SnOeL8One57YdeHa7XxGZM-ptDPCeDSymBc7-Bo_hkA,41154
|
|
@@ -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=
|
|
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=
|
|
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=
|
|
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.
|
|
106
|
-
earthengine_api-1.7.
|
|
107
|
-
earthengine_api-1.7.
|
|
108
|
-
earthengine_api-1.7.
|
|
109
|
-
earthengine_api-1.7.
|
|
105
|
+
earthengine_api-1.7.3.dist-info/METADATA,sha256=p0QwkV2XZ5VmJSMDAqVSJhBtvVKcdJOKmze5e4JXPfg,2144
|
|
106
|
+
earthengine_api-1.7.3.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
107
|
+
earthengine_api-1.7.3.dist-info/entry_points.txt,sha256=-Ax4SCU-S474r8OD2LIxata6PRmkZoDrppQ4fP_exNc,50
|
|
108
|
+
earthengine_api-1.7.3.dist-info/top_level.txt,sha256=go5zOwCgm5lIS3yTR-Vsxp1gNI4qdS-MP5eY-7zMxVY,3
|
|
109
|
+
earthengine_api-1.7.3.dist-info/RECORD,,
|
ee/__init__.py
CHANGED
ee/batch.py
CHANGED
|
@@ -308,6 +308,7 @@ class Export:
|
|
|
308
308
|
crsTransform=None,
|
|
309
309
|
maxPixels=None,
|
|
310
310
|
priority=None,
|
|
311
|
+
overwrite=False,
|
|
311
312
|
**kwargs,
|
|
312
313
|
) -> Task:
|
|
313
314
|
"""Creates a task to export an EE Image to an EE Asset.
|
|
@@ -344,6 +345,7 @@ class Export:
|
|
|
344
345
|
priority: The priority of the task within the project. Higher priority
|
|
345
346
|
tasks are scheduled sooner. Must be an integer between 0 and 9999.
|
|
346
347
|
Defaults to 100.
|
|
348
|
+
overwrite: If an existing asset can be overwritten by this export.
|
|
347
349
|
**kwargs: Holds other keyword arguments that may have been deprecated
|
|
348
350
|
such as 'crs_transform'.
|
|
349
351
|
|
|
@@ -739,6 +741,7 @@ class Export:
|
|
|
739
741
|
assetId=None,
|
|
740
742
|
maxVertices=None,
|
|
741
743
|
priority=None,
|
|
744
|
+
overwrite=False,
|
|
742
745
|
**kwargs,
|
|
743
746
|
) -> Task:
|
|
744
747
|
"""Creates a task to export a FeatureCollection to an EE table asset.
|
|
@@ -753,6 +756,7 @@ class Export:
|
|
|
753
756
|
priority: The priority of the task within the project. Higher priority
|
|
754
757
|
tasks are scheduled sooner. Must be an integer between 0 and 9999.
|
|
755
758
|
Defaults to 100.
|
|
759
|
+
overwrite: If an existing asset can be overwritten by this export.
|
|
756
760
|
**kwargs: Holds other keyword arguments that may have been deprecated.
|
|
757
761
|
|
|
758
762
|
Returns:
|
|
@@ -763,6 +767,7 @@ class Export:
|
|
|
763
767
|
'assetId': assetId,
|
|
764
768
|
'maxVertices': maxVertices,
|
|
765
769
|
'priority': priority,
|
|
770
|
+
'overwrite': overwrite,
|
|
766
771
|
}
|
|
767
772
|
config = {k: v for k, v, in config.items() if v is not None}
|
|
768
773
|
config = _prepare_table_export_config(collection, config,
|
|
@@ -1738,6 +1743,8 @@ def _build_earth_engine_destination(config: dict[str, Any]) -> dict[str, Any]:
|
|
|
1738
1743
|
'name':
|
|
1739
1744
|
_cloud_api_utils.convert_asset_id_to_asset_name(
|
|
1740
1745
|
config.pop('assetId')),
|
|
1746
|
+
'overwrite':
|
|
1747
|
+
config.pop('overwrite', False)
|
|
1741
1748
|
}
|
|
1742
1749
|
|
|
1743
1750
|
|
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/batch_test.py
CHANGED
|
@@ -274,6 +274,7 @@ class BatchTestCase(apitestcase.ApiTestCase):
|
|
|
274
274
|
'name': (
|
|
275
275
|
'projects/earthengine-legacy/assets/users/foo/bar'
|
|
276
276
|
),
|
|
277
|
+
'overwrite': False,
|
|
277
278
|
}
|
|
278
279
|
},
|
|
279
280
|
'description': 'myExportImageTask',
|
|
@@ -425,6 +426,7 @@ class BatchTestCase(apitestcase.ApiTestCase):
|
|
|
425
426
|
'name': (
|
|
426
427
|
'projects/earthengine-legacy/assets/users/foo/bar'
|
|
427
428
|
),
|
|
429
|
+
'overwrite': False,
|
|
428
430
|
},
|
|
429
431
|
'pyramidingPolicyOverrides': {'B1': 'MIN'},
|
|
430
432
|
},
|
|
@@ -451,6 +453,7 @@ class BatchTestCase(apitestcase.ApiTestCase):
|
|
|
451
453
|
'name': (
|
|
452
454
|
'projects/earthengine-legacy/assets/users/foo/bar'
|
|
453
455
|
),
|
|
456
|
+
'overwrite': False,
|
|
454
457
|
},
|
|
455
458
|
'tileSize': {'value': 4},
|
|
456
459
|
},
|
|
@@ -460,6 +463,43 @@ class BatchTestCase(apitestcase.ApiTestCase):
|
|
|
460
463
|
task_ordered.config,
|
|
461
464
|
)
|
|
462
465
|
|
|
466
|
+
task_with_overwrite = ee.batch.Export.image.toAsset(
|
|
467
|
+
image=config['image'],
|
|
468
|
+
assetId=config['assetId'],
|
|
469
|
+
overwrite=True,
|
|
470
|
+
)
|
|
471
|
+
self.assertTrue(
|
|
472
|
+
task_with_overwrite.config['assetExportOptions'][
|
|
473
|
+
'earthEngineDestination'
|
|
474
|
+
]['overwrite']
|
|
475
|
+
)
|
|
476
|
+
|
|
477
|
+
task_with_priority = ee.batch.Export.image.toAsset(
|
|
478
|
+
image=config['image'],
|
|
479
|
+
assetId=config['assetId'],
|
|
480
|
+
priority=999,
|
|
481
|
+
)
|
|
482
|
+
self.assertIsNone(task_with_priority.id)
|
|
483
|
+
self.assertIsNone(task_with_priority.name)
|
|
484
|
+
self.assertEqual('EXPORT_IMAGE', task_with_priority.task_type)
|
|
485
|
+
self.assertEqual('UNSUBMITTED', task_with_priority.state)
|
|
486
|
+
self.assertEqual(
|
|
487
|
+
{
|
|
488
|
+
'expression': expected_expression,
|
|
489
|
+
'description': 'myExportImageTask',
|
|
490
|
+
'assetExportOptions': {
|
|
491
|
+
'earthEngineDestination': {
|
|
492
|
+
'name': (
|
|
493
|
+
'projects/earthengine-legacy/assets/users/foo/bar'
|
|
494
|
+
),
|
|
495
|
+
'overwrite': False,
|
|
496
|
+
}
|
|
497
|
+
},
|
|
498
|
+
'priority': {'value': 999},
|
|
499
|
+
},
|
|
500
|
+
task_with_priority.config,
|
|
501
|
+
)
|
|
502
|
+
|
|
463
503
|
def test_export_image_to_asset_cloud_api_with_tile_size(self):
|
|
464
504
|
"""Verifies the Asset export task created by Export.image.toAsset()."""
|
|
465
505
|
with apitestcase.UsingCloudApi():
|
|
@@ -489,6 +529,7 @@ class BatchTestCase(apitestcase.ApiTestCase):
|
|
|
489
529
|
'name': (
|
|
490
530
|
'projects/earthengine-legacy/assets/users/foo/bar'
|
|
491
531
|
),
|
|
532
|
+
'overwrite': False,
|
|
492
533
|
},
|
|
493
534
|
'tileSize': {'value': 4},
|
|
494
535
|
},
|
|
@@ -1133,11 +1174,25 @@ class BatchTestCase(apitestcase.ApiTestCase):
|
|
|
1133
1174
|
'name': (
|
|
1134
1175
|
'projects/earthengine-legacy/assets/users/foo/bar'
|
|
1135
1176
|
),
|
|
1177
|
+
'overwrite': False,
|
|
1136
1178
|
}
|
|
1137
1179
|
},
|
|
1138
1180
|
},
|
|
1139
1181
|
task.config,
|
|
1140
1182
|
)
|
|
1183
|
+
|
|
1184
|
+
task_with_overwrite = ee.batch.Export.table.toAsset(
|
|
1185
|
+
collection=ee.FeatureCollection('foo'),
|
|
1186
|
+
description='foo',
|
|
1187
|
+
assetId='users/foo/bar',
|
|
1188
|
+
overwrite=True,
|
|
1189
|
+
)
|
|
1190
|
+
self.assertTrue(
|
|
1191
|
+
task_with_overwrite.config['assetExportOptions'][
|
|
1192
|
+
'earthEngineDestination'
|
|
1193
|
+
]['overwrite']
|
|
1194
|
+
)
|
|
1195
|
+
|
|
1141
1196
|
task_with_priority = ee.batch.Export.table.toAsset(
|
|
1142
1197
|
collection=ee.FeatureCollection('foo'),
|
|
1143
1198
|
description='foo',
|
|
@@ -1153,6 +1208,7 @@ class BatchTestCase(apitestcase.ApiTestCase):
|
|
|
1153
1208
|
'name': (
|
|
1154
1209
|
'projects/earthengine-legacy/assets/users/foo/bar'
|
|
1155
1210
|
),
|
|
1211
|
+
'overwrite': False,
|
|
1156
1212
|
}
|
|
1157
1213
|
},
|
|
1158
1214
|
'priority': {'value': 999},
|
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
|
-
|
|
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(
|
|
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
|
|
61
|
+
return f'{self.test_tmpdir}/tempfile'
|
|
47
62
|
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
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
|
|
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
|
|
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()
|
ee/tests/table_converter_test.py
CHANGED
|
@@ -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
|
-
)
|
|
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
|
|
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
|
|
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()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|