forked from GISGIT/GEE-Python-API
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathapifunction.py
More file actions
executable file
·247 lines (198 loc) · 8.06 KB
/
apifunction.py
File metadata and controls
executable file
·247 lines (198 loc) · 8.06 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
#!/usr/bin/env python
"""A class for representing built-in EE API Function.
Earth Engine can dynamically produce a JSON array listing the
algorithms available to the user. Each item in the dictionary identifies
the name and return type of the algorithm, the name and type of its
arguments, whether they're required or optional, default values and docs
for each argument and the algorithms as a whole.
This class manages the algorithm dictionary and creates JavaScript functions
to apply each EE algorithm.
"""
# Using lowercase function naming to match the JavaScript names.
# pylint: disable=g-bad-name
import copy
import keyword
import re
from . import computedobject
from . import data
from . import deprecation
from . import ee_exception
from . import ee_types
from . import function
class ApiFunction(function.Function):
"""An object representing an EE API Function."""
# A dictionary of functions defined by the API server.
_api = None
# A set of algorithm names containing all algorithms that have been bound to
# a function so far using importApi().
_bound_signatures = set()
def __init__(self, name, opt_signature=None):
"""Creates a function defined by the EE API.
Args:
name: The name of the function.
opt_signature: The signature of the function. If unspecified,
looked up dynamically.
"""
if opt_signature is None:
opt_signature = ApiFunction.lookup(name).getSignature()
# The signature of this API function.
self._signature = copy.deepcopy(opt_signature)
self._signature['name'] = name
def __eq__(self, other):
return (isinstance(other, ApiFunction) and
self.getSignature() == other.getSignature())
# For Python 3, __hash__ is needed because __eq__ is defined.
# See https://docs.python.org/3/reference/datamodel.html#object.__hash__
def __hash__(self):
return hash(computedobject.ComputedObject.freeze(self.getSignature()))
def __ne__(self, other):
return not self.__eq__(other)
@classmethod
def call_(cls, name, *args, **kwargs):
"""Call a named API function with positional and keyword arguments.
Args:
name: The name of the API function to call.
*args: Positional arguments to pass to the function.
**kwargs: Keyword arguments to pass to the function.
Returns:
An object representing the called function. If the signature specifies
a recognized return type, the returned value will be cast to that type.
"""
return cls.lookup(name).call(*args, **kwargs)
@classmethod
def apply_(cls, name, named_args):
"""Call a named API function with a dictionary of named arguments.
Args:
name: The name of the API function to call.
named_args: A dictionary of arguments to the function.
Returns:
An object representing the called function. If the signature specifies
a recognized return type, the returned value will be cast to that type.
"""
return cls.lookup(name).apply(named_args)
def encode_invocation(self, unused_encoder):
return self._signature['name']
def encode_cloud_invocation(self, unused_encoder):
return {'functionName': self._signature['name']}
def getSignature(self):
"""Returns a description of the interface provided by this function."""
return self._signature
@classmethod
def allSignatures(cls):
"""Returns a map from the name to signature for all API functions."""
cls.initialize()
return dict([(name, func.getSignature())
for name, func in cls._api.items()])
@classmethod
def unboundFunctions(cls):
"""Returns the functions that have not been bound using importApi() yet."""
cls.initialize()
return dict([(name, func) for name, func in cls._api.items()
if name not in cls._bound_signatures])
@classmethod
def lookup(cls, name):
"""Looks up an API function by name.
Args:
name: The name of the function to get.
Returns:
The requested ApiFunction.
"""
result = cls.lookupInternal(name)
if not name:
raise ee_exception.EEException(
'Unknown built-in function name: %s' % name)
return result
@classmethod
def lookupInternal(cls, name):
"""Looks up an API function by name.
Args:
name: The name of the function to get.
Returns:
The requested ApiFunction or None if not found.
"""
cls.initialize()
return cls._api.get(name, None)
@classmethod
def initialize(cls):
"""Initializes the list of signatures from the Earth Engine front-end."""
if not cls._api:
signatures = data.getAlgorithms()
api = {}
for name, sig in signatures.items():
# Strip type parameters.
sig['returns'] = re.sub('<.*>', '', sig['returns'])
for arg in sig['args']:
arg['type'] = re.sub('<.*>', '', arg['type'])
api[name] = cls(name, sig)
cls._api = api
@classmethod
def reset(cls):
"""Clears the API functions list so it will be reloaded from the server."""
cls._api = None
cls._bound_signatures = set()
@classmethod
def importApi(cls, target, prefix, type_name, opt_prepend=None):
"""Adds all API functions that begin with a given prefix to a target class.
Args:
target: The class to add to.
prefix: The prefix to search for in the signatures.
type_name: The name of the object's type. Functions whose
first argument matches this type are bound as instance methods, and
those whose first argument doesn't match are bound as static methods.
opt_prepend: An optional string to prepend to the names of the
added functions.
"""
cls.initialize()
prepend = opt_prepend or ''
for name, api_func in cls._api.items():
parts = name.split('.')
if len(parts) == 2 and parts[0] == prefix:
fname = prepend + parts[1]
signature = api_func.getSignature()
cls._bound_signatures.add(name)
# Specifically handle the function names that are illegal in python.
if keyword.iskeyword(fname):
fname = fname.title()
# Don't overwrite existing versions of this function.
if (hasattr(target, fname) and
not hasattr(getattr(target, fname), 'signature')):
continue
# Create a new function so we can attach properties to it.
def MakeBoundFunction(func):
# We need the lambda to capture "func" from the enclosing scope.
return lambda *args, **kwargs: func.call(*args, **kwargs) # pylint: disable=unnecessary-lambda
bound_function = MakeBoundFunction(api_func)
# Add docs. If there are non-ASCII characters in the docs, and we're in
# Python 2, use a hammer to force them into a str.
try:
setattr(bound_function, '__name__', str(name))
except TypeError:
setattr(bound_function, '__name__', name.encode('utf8'))
try:
bound_function.__doc__ = str(api_func)
except UnicodeEncodeError:
bound_function.__doc__ = api_func.__str__().encode('utf8')
# Attach the signature object for documentation generators.
bound_function.signature = signature
# Mark as deprecated if needed.
if signature.get('deprecated'):
deprecated_decorator = deprecation.Deprecated(signature['deprecated'])
bound_function = deprecated_decorator(bound_function)
# Decide whether this is a static or an instance function.
is_instance = (signature['args'] and
ee_types.isSubtype(signature['args'][0]['type'],
type_name))
if not is_instance:
bound_function = staticmethod(bound_function)
# Attach the function as a method.
setattr(target, fname, bound_function)
@staticmethod
def clearApi(target):
"""Removes all methods added by importApi() from a target class.
Args:
target: The class to remove from.
"""
for attr_name in dir(target):
attr_value = getattr(target, attr_name)
if callable(attr_value) and hasattr(attr_value, 'signature'):
delattr(target, attr_name)