earthengine-api 1.5.13rc0__py3-none-any.whl → 1.7.4__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of earthengine-api might be problematic. Click here for more details.

Files changed (102) hide show
  1. {earthengine_api-1.5.13rc0.dist-info → earthengine_api-1.7.4.dist-info}/METADATA +3 -3
  2. earthengine_api-1.7.4.dist-info/RECORD +109 -0
  3. {earthengine_api-1.5.13rc0.dist-info → earthengine_api-1.7.4.dist-info}/WHEEL +1 -1
  4. ee/__init__.py +29 -28
  5. ee/_arg_types.py +7 -6
  6. ee/_cloud_api_utils.py +95 -78
  7. ee/_helpers.py +17 -13
  8. ee/_state.py +105 -0
  9. ee/_utils.py +2 -1
  10. ee/apifunction.py +21 -19
  11. ee/apitestcase.py +33 -38
  12. ee/batch.py +87 -77
  13. ee/blob.py +10 -12
  14. ee/classifier.py +57 -59
  15. ee/cli/commands.py +178 -114
  16. ee/cli/eecli.py +1 -1
  17. ee/cli/utils.py +61 -42
  18. ee/clusterer.py +39 -41
  19. ee/collection.py +64 -54
  20. ee/computedobject.py +19 -16
  21. ee/confusionmatrix.py +9 -9
  22. ee/customfunction.py +13 -12
  23. ee/data.py +220 -322
  24. ee/daterange.py +10 -10
  25. ee/deprecation.py +21 -13
  26. ee/deserializer.py +25 -20
  27. ee/dictionary.py +11 -11
  28. ee/ee_array.py +22 -20
  29. ee/ee_date.py +23 -23
  30. ee/ee_list.py +15 -16
  31. ee/ee_number.py +11 -21
  32. ee/ee_string.py +24 -32
  33. ee/ee_types.py +4 -4
  34. ee/element.py +15 -15
  35. ee/encodable.py +7 -4
  36. ee/errormargin.py +4 -4
  37. ee/feature.py +68 -71
  38. ee/featurecollection.py +41 -40
  39. ee/filter.py +90 -92
  40. ee/function.py +8 -8
  41. ee/geometry.py +95 -93
  42. ee/image.py +238 -236
  43. ee/image_converter.py +4 -4
  44. ee/imagecollection.py +30 -27
  45. ee/join.py +13 -15
  46. ee/kernel.py +55 -57
  47. ee/mapclient.py +9 -9
  48. ee/model.py +29 -31
  49. ee/oauth.py +76 -63
  50. ee/pixeltype.py +6 -6
  51. ee/projection.py +5 -4
  52. ee/reducer.py +41 -41
  53. ee/serializer.py +14 -14
  54. ee/table_converter.py +7 -6
  55. ee/terrain.py +7 -9
  56. ee/tests/_cloud_api_utils_test.py +21 -6
  57. ee/tests/_helpers_test.py +57 -4
  58. ee/tests/_state_test.py +49 -0
  59. ee/tests/algorithms.json +85 -2
  60. ee/tests/apifunction_test.py +5 -5
  61. ee/tests/batch_test.py +135 -57
  62. ee/tests/blob_test.py +5 -5
  63. ee/tests/classifier_test.py +3 -3
  64. ee/tests/clusterer_test.py +3 -3
  65. ee/tests/collection_test.py +48 -13
  66. ee/tests/confusionmatrix_test.py +3 -3
  67. ee/tests/data_test.py +484 -55
  68. ee/tests/daterange_test.py +4 -4
  69. ee/tests/deprecation_test.py +6 -4
  70. ee/tests/deserializer_test.py +64 -5
  71. ee/tests/dictionary_test.py +12 -12
  72. ee/tests/ee_array_test.py +3 -3
  73. ee/tests/ee_date_test.py +4 -4
  74. ee/tests/ee_list_test.py +3 -3
  75. ee/tests/ee_number_test.py +75 -30
  76. ee/tests/ee_string_test.py +11 -3
  77. ee/tests/ee_test.py +40 -22
  78. ee/tests/element_test.py +2 -2
  79. ee/tests/errormargin_test.py +1 -1
  80. ee/tests/feature_test.py +10 -10
  81. ee/tests/featurecollection_test.py +3 -3
  82. ee/tests/filter_test.py +4 -4
  83. ee/tests/function_test.py +5 -5
  84. ee/tests/geometry_point_test.py +3 -3
  85. ee/tests/geometry_test.py +93 -52
  86. ee/tests/image_converter_test.py +1 -3
  87. ee/tests/image_test.py +3 -3
  88. ee/tests/imagecollection_test.py +3 -3
  89. ee/tests/join_test.py +3 -3
  90. ee/tests/kernel_test.py +7 -3
  91. ee/tests/model_test.py +17 -5
  92. ee/tests/oauth_test.py +189 -7
  93. ee/tests/pixeltype_test.py +6 -7
  94. ee/tests/projection_test.py +5 -6
  95. ee/tests/reducer_test.py +16 -3
  96. ee/tests/serializer_test.py +39 -12
  97. ee/tests/table_converter_test.py +51 -7
  98. ee/tests/terrain_test.py +11 -3
  99. earthengine_api-1.5.13rc0.dist-info/RECORD +0 -107
  100. {earthengine_api-1.5.13rc0.dist-info → earthengine_api-1.7.4.dist-info}/entry_points.txt +0 -0
  101. {earthengine_api-1.5.13rc0.dist-info → earthengine_api-1.7.4.dist-info}/licenses/LICENSE +0 -0
  102. {earthengine_api-1.5.13rc0.dist-info → earthengine_api-1.7.4.dist-info}/top_level.txt +0 -0
ee/reducer.py CHANGED
@@ -1,8 +1,6 @@
1
1
  """A wrapper for Reducers."""
2
2
  from __future__ import annotations
3
3
 
4
- from typing import Optional
5
-
6
4
  from ee import _arg_types
7
5
  from ee import apifunction
8
6
  from ee import computedobject
@@ -100,11 +98,11 @@ class Reducer(computedobject.ComputedObject):
100
98
  @staticmethod
101
99
  def autoHistogram(
102
100
  # pylint: disable=invalid-name
103
- maxBuckets: Optional[_arg_types.Integer] = None,
104
- minBucketWidth: Optional[_arg_types.Number] = None,
105
- maxRaw: Optional[_arg_types.Integer] = None,
101
+ maxBuckets: _arg_types.Integer | None = None,
102
+ minBucketWidth: _arg_types.Number | None = None,
103
+ maxRaw: _arg_types.Integer | None = None,
106
104
  # pylint: enable=invalid-name
107
- cumulative: Optional[_arg_types.Bool] = None,
105
+ cumulative: _arg_types.Bool | None = None,
108
106
  ) -> Reducer:
109
107
  """Returns an ee.Reducer that computes a histogram of the inputs.
110
108
 
@@ -201,8 +199,8 @@ class Reducer(computedobject.ComputedObject):
201
199
  self,
202
200
  reducer2: _arg_types.Reducer,
203
201
  # pylint: disable=invalid-name
204
- outputPrefix: Optional[_arg_types.String] = None,
205
- sharedInputs: Optional[_arg_types.Bool] = None,
202
+ outputPrefix: _arg_types.String | None = None,
203
+ sharedInputs: _arg_types.Bool | None = None,
206
204
  # pylint: enable=invalid-name
207
205
  ) -> Reducer:
208
206
  """Returns a Reducer that runs two reducers in parallel.
@@ -289,7 +287,7 @@ class Reducer(computedobject.ComputedObject):
289
287
 
290
288
  return apifunction.ApiFunction.call_('Reducer.covariance')
291
289
 
292
- def disaggregate(self, axis: Optional[_arg_types.Integer] = None) -> Reducer:
290
+ def disaggregate(self, axis: _arg_types.Integer | None = None) -> Reducer:
293
291
  """Returns a Reducer that separates aggregate inputs.
294
292
 
295
293
  Separates aggregate inputs (Arrays, Lists, or Dictionaries) into individual
@@ -363,7 +361,7 @@ class Reducer(computedobject.ComputedObject):
363
361
  min: _arg_types.Number, # pylint: disable=redefined-builtin
364
362
  max: _arg_types.Number, # pylint: disable=redefined-builtin
365
363
  steps: _arg_types.Integer,
366
- cumulative: Optional[_arg_types.Bool] = None,
364
+ cumulative: _arg_types.Bool | None = None,
367
365
  ) -> Reducer:
368
366
  """Returns a fixed histogram reducer.
369
367
 
@@ -443,9 +441,9 @@ class Reducer(computedobject.ComputedObject):
443
441
  @staticmethod
444
442
  def geometricMedian(
445
443
  numX: _arg_types.Integer, # pylint: disable=invalid-name
446
- eta: Optional[_arg_types.Number] = None,
444
+ eta: _arg_types.Number | None = None,
447
445
  # pylint: disable-next=invalid-name
448
- initialStepSize: Optional[_arg_types.Number] = None,
446
+ initialStepSize: _arg_types.Number | None = None,
449
447
  ) -> Reducer:
450
448
  """Returns a reducer that computes the geometric median across the inputs.
451
449
 
@@ -467,8 +465,10 @@ class Reducer(computedobject.ComputedObject):
467
465
 
468
466
  def group(
469
467
  self,
470
- groupField: Optional[_arg_types.Integer] = None,
471
- groupName: Optional[_arg_types.String] = None,
468
+ # pylint: disable=invalid-name
469
+ groupField: _arg_types.Integer | None = None,
470
+ groupName: _arg_types.String | None = None,
471
+ # pylint: enable=invalid-name
472
472
  ) -> Reducer:
473
473
  """Returns a reducer groups reducer records by the value of a given input.
474
474
 
@@ -487,9 +487,9 @@ class Reducer(computedobject.ComputedObject):
487
487
  @staticmethod
488
488
  def histogram(
489
489
  # pylint: disable=invalid-name
490
- maxBuckets: Optional[_arg_types.Integer] = None,
491
- minBucketWidth: Optional[_arg_types.Number] = None,
492
- maxRaw: Optional[_arg_types.Integer] = None,
490
+ maxBuckets: _arg_types.Integer | None = None,
491
+ minBucketWidth: _arg_types.Number | None = None,
492
+ maxRaw: _arg_types.Integer | None = None,
493
493
  # pylint: enable=invalid-name
494
494
  ) -> Reducer:
495
495
  """Returns a reducer that will compute a histogram of the inputs.
@@ -512,9 +512,9 @@ class Reducer(computedobject.ComputedObject):
512
512
  # pylint: disable=invalid-name
513
513
  minPercentile: _arg_types.Number,
514
514
  maxPercentile: _arg_types.Number,
515
- maxBuckets: Optional[_arg_types.Integer] = None,
516
- minBucketWidth: Optional[_arg_types.Number] = None,
517
- maxRaw: Optional[_arg_types.Integer] = None,
515
+ maxBuckets: _arg_types.Integer | None = None,
516
+ minBucketWidth: _arg_types.Number | None = None,
517
+ maxRaw: _arg_types.Integer | None = None,
518
518
  # pylint: enable=invalid-name
519
519
  ) -> Reducer:
520
520
  """Returns an interval mean reducer.
@@ -547,7 +547,7 @@ class Reducer(computedobject.ComputedObject):
547
547
  @staticmethod
548
548
  def kendallsCorrelation(
549
549
  # pylint: disable-next=invalid-name
550
- numInputs: Optional[_arg_types.Integer] = None,
550
+ numInputs: _arg_types.Integer | None = None,
551
551
  ) -> Reducer:
552
552
  """Returns a reducer that computes the Kendall's Tau-b rank correlation.
553
553
 
@@ -609,7 +609,7 @@ class Reducer(computedobject.ComputedObject):
609
609
  @staticmethod
610
610
  def linearRegression(
611
611
  numX: _arg_types.Integer, # pylint: disable=invalid-name
612
- numY: Optional[_arg_types.Integer] = None, # pylint: disable=invalid-name
612
+ numY: _arg_types.Integer | None = None, # pylint: disable=invalid-name
613
613
  ) -> Reducer:
614
614
  """Returns a linear regression reducer.
615
615
 
@@ -635,7 +635,7 @@ class Reducer(computedobject.ComputedObject):
635
635
  @staticmethod
636
636
  def max(
637
637
  # pylint: disable-next=invalid-name
638
- numInputs: Optional[_arg_types.Integer] = None,
638
+ numInputs: _arg_types.Integer | None = None,
639
639
  ) -> Reducer:
640
640
  """Returns a reducer that outputs the maximum value of its (first) input.
641
641
 
@@ -660,9 +660,9 @@ class Reducer(computedobject.ComputedObject):
660
660
  @staticmethod
661
661
  def median(
662
662
  # pylint: disable=invalid-name
663
- maxBuckets: Optional[_arg_types.Integer] = None,
664
- minBucketWidth: Optional[_arg_types.Number] = None,
665
- maxRaw: Optional[_arg_types.Integer] = None,
663
+ maxBuckets: _arg_types.Integer | None = None,
664
+ minBucketWidth: _arg_types.Number | None = None,
665
+ maxRaw: _arg_types.Integer | None = None,
666
666
  # pylint: enable=invalid-name
667
667
  ) -> Reducer:
668
668
  """Returns a reducer that will compute the median of the inputs.
@@ -687,7 +687,7 @@ class Reducer(computedobject.ComputedObject):
687
687
  @staticmethod
688
688
  def min(
689
689
  # pylint: disable-next=invalid-name
690
- numInputs: Optional[_arg_types.Integer] = None,
690
+ numInputs: _arg_types.Integer | None = None,
691
691
  ) -> Reducer:
692
692
  """Returns a reducer that outputs the minimum value of its first input.
693
693
 
@@ -712,9 +712,9 @@ class Reducer(computedobject.ComputedObject):
712
712
  @staticmethod
713
713
  def mode(
714
714
  # pylint: disable=invalid-name
715
- maxBuckets: Optional[_arg_types.Integer] = None,
716
- minBucketWidth: Optional[_arg_types.Number] = None,
717
- maxRaw: Optional[_arg_types.Integer] = None,
715
+ maxBuckets: _arg_types.Integer | None = None,
716
+ minBucketWidth: _arg_types.Number | None = None,
717
+ maxRaw: _arg_types.Integer | None = None,
718
718
  # pylint: enable=invalid-name
719
719
  ) -> Reducer:
720
720
  """Returns a reducer that will compute the mode of the inputs.
@@ -760,10 +760,10 @@ class Reducer(computedobject.ComputedObject):
760
760
  def percentile(
761
761
  percentiles: _arg_types.List,
762
762
  # pylint: disable=invalid-name
763
- outputNames: Optional[_arg_types.List] = None,
764
- maxBuckets: Optional[_arg_types.Integer] = None,
765
- minBucketWidth: Optional[_arg_types.Number] = None,
766
- maxRaw: Optional[_arg_types.Integer] = None,
763
+ outputNames: _arg_types.List | None = None,
764
+ maxBuckets: _arg_types.Integer | None = None,
765
+ minBucketWidth: _arg_types.Number | None = None,
766
+ maxRaw: _arg_types.Integer | None = None,
767
767
  # pylint: enable=invalid-name
768
768
  ) -> Reducer:
769
769
  """Returns a reducer that will compute the specified percentiles.
@@ -818,8 +818,8 @@ class Reducer(computedobject.ComputedObject):
818
818
  @staticmethod
819
819
  def ridgeRegression(
820
820
  numX: _arg_types.Integer, # pylint: disable=invalid-name
821
- numY: Optional[_arg_types.Integer] = None, # pylint: disable=invalid-name
822
- lambda_: Optional[_arg_types.Number] = None,
821
+ numY: _arg_types.Integer | None = None, # pylint: disable=invalid-name
822
+ lambda_: _arg_types.Number | None = None,
823
823
  **kwargs,
824
824
  ) -> Reducer:
825
825
  # pylint: disable=g-doc-args
@@ -862,8 +862,8 @@ class Reducer(computedobject.ComputedObject):
862
862
  @staticmethod
863
863
  def robustLinearRegression(
864
864
  numX: _arg_types.Integer, # pylint: disable=invalid-name
865
- numY: Optional[_arg_types.Integer] = None, # pylint: disable=invalid-name
866
- beta: Optional[_arg_types.Number] = None,
865
+ numY: _arg_types.Integer | None = None, # pylint: disable=invalid-name
866
+ beta: _arg_types.Number | None = None,
867
867
  ) -> Reducer:
868
868
  """Returns a robust linear regression reducer.
869
869
 
@@ -979,7 +979,7 @@ class Reducer(computedobject.ComputedObject):
979
979
  def toCollection(
980
980
  # pylint: disable=invalid-name
981
981
  propertyNames: _arg_types.List,
982
- numOptional: Optional[_arg_types.Integer] = None,
982
+ numOptional: _arg_types.Integer | None = None,
983
983
  # pylint: enable=invalid-name
984
984
  ) -> Reducer:
985
985
  """Returns a reducer that collects its inputs into a FeatureCollection.
@@ -998,8 +998,8 @@ class Reducer(computedobject.ComputedObject):
998
998
  @staticmethod
999
999
  def toList(
1000
1000
  # pylint: disable=invalid-name
1001
- tupleSize: Optional[_arg_types.Integer] = None,
1002
- numOptional: Optional[_arg_types.Integer] = None,
1001
+ tupleSize: _arg_types.Integer | None = None,
1002
+ numOptional: _arg_types.Integer | None = None,
1003
1003
  # pylint: enable=invalid-name
1004
1004
  ) -> Reducer:
1005
1005
  """Returns a reducer that collects its inputs into a list.
ee/serializer.py CHANGED
@@ -4,7 +4,7 @@ import collections
4
4
  import datetime
5
5
  import hashlib
6
6
  import json
7
- from typing import Any, Dict, List, Optional, Set
7
+ from typing import Any
8
8
 
9
9
  from ee import _cloud_api_utils
10
10
  from ee import _utils
@@ -32,23 +32,23 @@ def DatetimeToMicroseconds(date: datetime.datetime) -> int:
32
32
 
33
33
  class Serializer:
34
34
  """A serializer for EE object trees."""
35
- unbound_name: Optional[str]
35
+ unbound_name: str | None
36
36
 
37
37
  # Whether the encoding should factor out shared subtrees.
38
38
  _is_compound: bool
39
39
  _for_cloud_api: bool
40
40
  # A list of shared subtrees as [name, value] pairs.
41
- _scope: List[str]
41
+ _scope: list[str]
42
42
  # A lookup table from object hash to subtree names as stored in self._scope
43
- _encoded: Dict[Any, Any]
43
+ _encoded: dict[Any, Any]
44
44
  # A lookup table from object ID as retrieved by id() to md5 hash values.
45
- _hashcache: Dict[Any, Any]
45
+ _hashcache: dict[Any, Any]
46
46
 
47
47
  def __init__(
48
48
  self,
49
49
  is_compound: bool = True,
50
50
  for_cloud_api: bool = False,
51
- unbound_name: Optional[str] = None,
51
+ unbound_name: str | None = None,
52
52
  ):
53
53
  """Constructs a serializer.
54
54
 
@@ -160,8 +160,8 @@ class Serializer:
160
160
  'type':
161
161
  'Dictionary',
162
162
  'value':
163
- dict([(key, self._encode_value(value))
164
- for key, value in obj.items()])
163
+ {key: self._encode_value(value)
164
+ for key, value in obj.items()}
165
165
  }
166
166
  else:
167
167
  raise ee_exception.EEException('Cannot encode object: %s' % obj)
@@ -280,7 +280,7 @@ def encode(
280
280
  obj: Any,
281
281
  is_compound: bool = True,
282
282
  for_cloud_api: bool = True,
283
- unbound_name: Optional[str] = None,
283
+ unbound_name: str | None = None,
284
284
  ) -> Any:
285
285
  """Serialize an object to a JSON-compatible structure for API calls.
286
286
 
@@ -358,7 +358,7 @@ class _ExpressionOptimizer:
358
358
  - Collapse dicts and arrays of constants to constant dicts/arrays.
359
359
  """
360
360
 
361
- def __init__(self, result: Any, values: Optional[Any] = None):
361
+ def __init__(self, result: Any, values: Any | None = None):
362
362
  """Builds an ExpressionOptimizer.
363
363
 
364
364
  Args:
@@ -380,12 +380,12 @@ class _ExpressionOptimizer:
380
380
  def _is_compound(self) -> bool:
381
381
  return self._values is not None
382
382
 
383
- def _find_single_uses(self) -> Set[Any]:
383
+ def _find_single_uses(self) -> set[Any]:
384
384
  """Finds the names of all named values that are referred to only once."""
385
385
  reference_counts = collections.defaultdict(int)
386
386
  reference_counts[self._result] += 1
387
387
 
388
- def _contained_reference(value: Any) -> Optional[Any]:
388
+ def _contained_reference(value: Any) -> Any | None:
389
389
  """Gets a contained reference from a ValueNode, if there is one."""
390
390
  if 'functionDefinitionValue' in value:
391
391
  return value['functionDefinitionValue']['body']
@@ -403,8 +403,8 @@ class _ExpressionOptimizer:
403
403
  reference_counts[reference] += 1
404
404
 
405
405
  self._visit_all_values_in_expression(increment_reference_count)
406
- return set(reference for reference, count in reference_counts.items()
407
- if count == 1)
406
+ return {reference for reference, count in reference_counts.items()
407
+ if count == 1}
408
408
 
409
409
  def optimize(self) -> Any:
410
410
  """Optimises the expression, returning the optimised form."""
ee/table_converter.py CHANGED
@@ -1,6 +1,7 @@
1
1
  """Converters used in the table data fetching methods."""
2
2
 
3
- from typing import Any, Dict, Iterator, List, Optional, Type, Union
3
+ from collections.abc import Iterator
4
+ from typing import Any
4
5
 
5
6
 
6
7
  class TableConverter:
@@ -24,7 +25,7 @@ class PandasConverter(TableConverter):
24
25
 
25
26
  def _convert_to_records(
26
27
  self, features: Iterator[Any]
27
- ) -> Iterator[Dict[str, Any]]:
28
+ ) -> Iterator[dict[str, Any]]:
28
29
  for feature in features:
29
30
  yield {
30
31
  'geo': feature.get('geometry'),
@@ -46,20 +47,20 @@ class GeoPandasConverter(TableConverter):
46
47
  self._materialize_features(features)
47
48
  )
48
49
 
49
- def _materialize_features(self, features: Iterator[Any]) -> List[Any]:
50
+ def _materialize_features(self, features: Iterator[Any]) -> list[Any]:
50
51
  """Materializes the features, making several requests if necessary."""
51
52
  return list(features)
52
53
 
53
54
 
54
- _TABLE_DATA_CONVERTERS: Dict[str, Type[TableConverter]] = {
55
+ _TABLE_DATA_CONVERTERS: dict[str, type[TableConverter]] = {
55
56
  'PANDAS_DATAFRAME': PandasConverter,
56
57
  'GEOPANDAS_GEODATAFRAME': GeoPandasConverter,
57
58
  }
58
59
 
59
60
 
60
61
  def from_file_format(
61
- file_format: Union[str, TableConverter]
62
- ) -> Optional[TableConverter]:
62
+ file_format: str | TableConverter
63
+ ) -> TableConverter | None:
63
64
  if isinstance(file_format, TableConverter):
64
65
  return file_format
65
66
  if file_format in _TABLE_DATA_CONVERTERS:
ee/terrain.py CHANGED
@@ -1,8 +1,6 @@
1
1
  """A namespace for Terrain."""
2
2
  from __future__ import annotations
3
3
 
4
- from typing import Optional
5
-
6
4
  from ee import _arg_types
7
5
  from ee import apifunction
8
6
  from ee import image as ee_image
@@ -15,7 +13,7 @@ class Terrain:
15
13
 
16
14
  def __init__(self):
17
15
  raise RuntimeError(
18
- self.__name__
16
+ self.__class__.__name__
19
17
  + ' should not be used as an object. Only direct usage of Terrain'
20
18
  ' static methods is allowed. For example, use this: '
21
19
  ' `ee.Terrain.aspect(...)`'
@@ -54,8 +52,8 @@ class Terrain:
54
52
  @staticmethod
55
53
  def fillMinima(
56
54
  image: _arg_types.Image,
57
- borderValue: Optional[_arg_types.Integer] = None,
58
- neighborhood: Optional[_arg_types.Integer] = None,
55
+ borderValue: _arg_types.Integer | None = None,
56
+ neighborhood: _arg_types.Integer | None = None,
59
57
  ) -> ee_image.Image:
60
58
  """Returns an ee.Image with local minima filled.
61
59
 
@@ -76,8 +74,8 @@ class Terrain:
76
74
  image: _arg_types.Image,
77
75
  azimuth: _arg_types.Number,
78
76
  zenith: _arg_types.Number,
79
- neighborhoodSize: Optional[_arg_types.Integer] = None,
80
- hysteresis: Optional[_arg_types.Bool] = None,
77
+ neighborhoodSize: _arg_types.Integer | None = None,
78
+ hysteresis: _arg_types.Bool | None = None,
81
79
  ) -> ee_image.Image:
82
80
  """Returns an ee.Image with the hill shadow.
83
81
 
@@ -109,8 +107,8 @@ class Terrain:
109
107
  @staticmethod
110
108
  def hillshade(
111
109
  input: _arg_types.Image,
112
- azimuth: Optional[_arg_types.Number] = None,
113
- elevation: Optional[_arg_types.Number] = None,
110
+ azimuth: _arg_types.Number | None = None,
111
+ elevation: _arg_types.Number | None = None,
114
112
  ) -> ee_image.Image:
115
113
  """Returns an ee.Image with a simple hillshade from a DEM.
116
114
 
@@ -16,10 +16,6 @@ from ee import ee_exception
16
16
 
17
17
  class CloudApiUtilsTest(unittest.TestCase):
18
18
 
19
- def setUp(self):
20
- super().setUp()
21
- _cloud_api_utils.set_cloud_api_user_project('earthengine-legacy')
22
-
23
19
  def test_build_cloud_resource(self):
24
20
  base = 'https://earthengine.basetest'
25
21
  path = '$discovery/rest?version=v1&prettyPrint=false'
@@ -171,7 +167,10 @@ class CloudApiUtilsTest(unittest.TestCase):
171
167
  def test_convert_task_id_to_operation_name(self):
172
168
  self.assertEqual(
173
169
  'projects/earthengine-legacy/operations/taskId',
174
- _cloud_api_utils.convert_task_id_to_operation_name('taskId'))
170
+ _cloud_api_utils.convert_task_id_to_operation_name(
171
+ 'earthengine-legacy', 'taskId'
172
+ ),
173
+ )
175
174
 
176
175
  def test_encode_number_as_cloud_value(self):
177
176
  self.assertEqual({
@@ -414,7 +413,7 @@ class CloudApiUtilsTest(unittest.TestCase):
414
413
  self.assertEqual({'width': 123, 'height': 123},
415
414
  _cloud_api_utils.convert_to_grid_dimensions(123))
416
415
  self.assertEqual({'width': 123, 'height': 123},
417
- _cloud_api_utils.convert_to_grid_dimensions((123)))
416
+ _cloud_api_utils.convert_to_grid_dimensions(123))
418
417
  self.assertEqual({'width': 123, 'height': 234},
419
418
  _cloud_api_utils.convert_to_grid_dimensions((123, 234)))
420
419
 
@@ -551,6 +550,22 @@ class CloudApiUtilsTest(unittest.TestCase):
551
550
  expected,
552
551
  _cloud_api_utils.convert_sources_to_one_platform_sources(old_sources))
553
552
 
553
+ def test_convert_to_operation_state(self):
554
+ def convert(state):
555
+ return _cloud_api_utils.convert_to_operation_state(state)
556
+
557
+ self.assertEqual('CANCELLED', convert('CANCELLED'))
558
+ self.assertEqual('CANCELLING', convert('CANCEL_REQUESTED'))
559
+ self.assertEqual('CANCELLING', convert('CANCELLING'))
560
+ self.assertEqual('FAILED', convert('FAILED'))
561
+ self.assertEqual('PENDING', convert('PENDING'))
562
+ self.assertEqual('PENDING', convert('READY'))
563
+ self.assertEqual('RUNNING', convert('RUNNING'))
564
+ self.assertEqual('SUCCEEDED', convert('COMPLETED'))
565
+ self.assertEqual('SUCCEEDED', convert('SUCCEEDED'))
566
+ self.assertEqual('UNKNOWN', convert('random_string'))
567
+ self.assertEqual('UNKNOWN', convert('UNKNOWN'))
568
+
554
569
 
555
570
  if __name__ == '__main__':
556
571
  unittest.main()
ee/tests/_helpers_test.py CHANGED
@@ -7,14 +7,67 @@ name since that is the name we want to ensure works.
7
7
  """
8
8
 
9
9
  import io
10
+ import json
10
11
  import unittest
12
+ from unittest import mock
11
13
 
12
14
  import unittest
13
15
  import ee
16
+ from ee import _helpers
14
17
  from ee import apifunction
15
18
  from ee import apitestcase
16
19
  from ee import computedobject
17
20
  from ee import ee_exception
21
+ from ee import oauth
22
+
23
+
24
+ class ServiceAccountCredentialsTest(unittest.TestCase):
25
+
26
+ def test_no_args(self):
27
+ with self.assertRaisesRegex(ValueError, 'At least one of'):
28
+ ee.ServiceAccountCredentials()
29
+
30
+ @mock.patch('google.oauth2.service_account.Credentials')
31
+ def test_json_file(self, mock_credentials):
32
+ ee.ServiceAccountCredentials(key_file='foo.json')
33
+ mock_credentials.from_service_account_file.assert_called_with(
34
+ 'foo.json', scopes=oauth.SCOPES
35
+ )
36
+
37
+ @mock.patch('google.oauth2.service_account.Credentials')
38
+ def test_json_key_data(self, mock_credentials):
39
+ key_data = {'client_email': 'foo@bar.com'}
40
+ ee.ServiceAccountCredentials(key_data=json.dumps(key_data))
41
+ mock_credentials.from_service_account_info.assert_called_with(
42
+ key_data, scopes=oauth.SCOPES
43
+ )
44
+
45
+ @mock.patch('google.auth.crypt.RSASigner')
46
+ @mock.patch('google.oauth2.service_account.Credentials')
47
+ def test_pem_key_data(self, mock_credentials, mock_signer):
48
+ ee.ServiceAccountCredentials(email='foo@bar.com', key_data='pem_key_data')
49
+ mock_signer.from_string.assert_called_with('pem_key_data')
50
+ self.assertEqual(
51
+ mock_credentials.call_args[0][1], 'foo@bar.com'
52
+ )
53
+
54
+ @mock.patch('google.auth.crypt.RSASigner')
55
+ @mock.patch('google.oauth2.service_account.Credentials')
56
+ def test_pem_file(self, mock_credentials, mock_signer):
57
+ with mock.patch.object(
58
+ _helpers, 'open', mock.mock_open(read_data='pem_key_data')
59
+ ):
60
+ ee.ServiceAccountCredentials(email='foo@bar.com', key_file='foo.pem')
61
+ mock_signer.from_string.assert_called_with('pem_key_data')
62
+ self.assertEqual(
63
+ mock_credentials.call_args[0][1], 'foo@bar.com'
64
+ )
65
+
66
+ def test_bad_json_key_data(self):
67
+ # This causes a different failure based on where the test is run.
68
+ message = r'Could not deserialize key data|No key could be detected'
69
+ with self.assertRaisesRegex(ValueError, message):
70
+ ee.ServiceAccountCredentials(key_data='not json')
18
71
 
19
72
 
20
73
  class ProfilingTest(apitestcase.ApiTestCase):
@@ -25,23 +78,23 @@ class ProfilingTest(apitestcase.ApiTestCase):
25
78
  is_get_profiles = isinstance(
26
79
  value, computedobject.ComputedObject
27
80
  ) and value.func == apifunction.ApiFunction.lookup('Profile.getProfiles')
28
- return 'hooked=%s getProfiles=%s' % (hooked, is_get_profiles)
81
+ return f'hooked={hooked} getProfiles={is_get_profiles}'
29
82
 
30
- def testProfilePrinting(self):
83
+ def test_profile_printing(self):
31
84
  ee.data.computeValue = self.MockValue
32
85
  out = io.StringIO()
33
86
  with ee.profilePrinting(destination=out):
34
87
  self.assertEqual('hooked=True getProfiles=False', ee.Number(1).getInfo())
35
88
  self.assertEqual('hooked=False getProfiles=True', out.getvalue())
36
89
 
37
- def testProfilePrintingDefaultSmoke(self):
90
+ def test_profile_printing_default_smoke(self):
38
91
  # This will print to sys.stderr, so we can't make any assertions about the
39
92
  # output. But we can check that it doesn't fail.
40
93
  ee.data.computeValue = self.MockValue
41
94
  with ee.profilePrinting():
42
95
  self.assertEqual('hooked=True getProfiles=False', ee.Number(1).getInfo())
43
96
 
44
- def testProfilePrintingErrorGettingProfiles(self):
97
+ def test_profile_printing_error_getting_profiles(self):
45
98
  ee.data.computeValue = self.MockValue
46
99
  mock = unittest.mock.Mock()
47
100
  mock.call.side_effect = ee_exception.EEException('test')
@@ -0,0 +1,49 @@
1
+ #!/usr/bin/env python3
2
+ """Tests for ee._state."""
3
+
4
+ from absl.testing import parameterized
5
+
6
+ import unittest
7
+ from ee import _state
8
+
9
+
10
+ class StateTest(parameterized.TestCase):
11
+
12
+ def setUp(self):
13
+ super().setUp()
14
+ _state.reset_state()
15
+
16
+ @parameterized.named_parameters(
17
+ ('global_mode', False),
18
+ )
19
+ def test_get_state(self, use_context_mode: bool):
20
+ state = _state.get_state()
21
+
22
+ self.assertIsInstance(state, _state.EEState)
23
+ self.assertEqual(state, _state.EEState())
24
+
25
+ @parameterized.named_parameters(
26
+ ('global_mode', False),
27
+ )
28
+ def test_update_state(self, use_context_mode: bool):
29
+ state = _state.get_state()
30
+
31
+ # Modify the state and verify the global state has been updated.
32
+ state.cloud_api_user_project = 'my-project'
33
+
34
+ self.assertEqual(_state.get_state().cloud_api_user_project, 'my-project')
35
+
36
+ @parameterized.named_parameters(
37
+ ('global_mode', False),
38
+ )
39
+ def test_reset_state(self, use_context_mode: bool):
40
+ state = _state.get_state()
41
+ state.cloud_api_user_project = 'my-project'
42
+
43
+ _state.reset_state()
44
+
45
+ self.assertEqual(_state.get_state(), _state.EEState())
46
+
47
+
48
+ if __name__ == '__main__':
49
+ unittest.main()