-
Notifications
You must be signed in to change notification settings - Fork 523
/
env.py
290 lines (230 loc) · 8.24 KB
/
env.py
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
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
"""Rasterio's GDAL/AWS environment."""
from functools import wraps
import logging
from rasterio._env import GDALEnv
from rasterio.errors import EnvError
__all__ = ['Env', 'ensure_env']
# The currently active GDAL/AWS environment is a private attribute.
_ENV = None
log = logging.getLogger(__name__)
class Env(GDALEnv):
"""Abstraction for GDAL and AWS configuration.
The GDAL library is stateful: it has a registry of format drivers,
an error stack, and dozens of configuration options.
Rasterio's approach to working with GDAL is to wrap all the state
up using a Python context manager (see PEP 343,
https://www.python.org/dev/peps/pep-0343/). When the context is
entered GDAL drivers are registered, error handlers are
configured, and configuration options are set. When the context
is exited, drivers are removed from the registry and other
configurations are removed.
Example:
with rasterio.Env(GDAL_CACHEMAX=512) as env:
# All drivers are registered, GDAL's raster block cache
# size is set to 512MB.
# Commence processing...
...
# End of processing.
# At this point, configuration options are set to their
# previous (possible unset) values.
A boto3 session or boto3 session constructor arguments
`aws_access_key_id`, `aws_secret_access_key`, `aws_session_token`
may be passed to Env's constructor. In the latter case, a session
will be created as soon as needed. AWS credentials are configured
for GDAL as needed.
"""
def __init__(self, aws_session=None, aws_access_key_id=None,
aws_secret_access_key=None, aws_session_token=None,
aws_region_name=None, aws_profile_name=None, **options):
"""Create a new GDAL/AWS environment.
Note: this class is a context manager. GDAL isn't configured
until the context is entered via `with rasterio.Env()`.
Parameters
----------
aws_session: object, optional
A boto3 session.
aws_access_key_id: string, optional
An access key id, as per boto3.
aws_secret_access_key: string, optional
A secret access key, as per boto3.
aws_session_token: string, optional
A session token, as per boto3.
region_name: string, optional
A region name, as per boto3.
profile_name: string, optional
A shared credentials profile name, as per boto3.
**options: optional
A mapping of GDAL configuration options, e.g.,
`CPL_DEBUG=True, CHECK_WITH_INVERT_PROJ=False`.
Raises
------
EnvError
If the GDAL config options `AWS_ACCESS_KEY_ID` or
`AWS_SECRET_ACCESS_KEY` are given. AWS credentials are handled
exclusively by `boto3`.
Returns
-------
Env
A new instance of Env.
"""
super(Env, self).__init__()
if ('AWS_ACCESS_KEY_ID' in options or
'AWS_SECRET_ACCESS_KEY' in options):
raise EnvError(
"GDAL's AWS config options can not be directly set. "
"AWS credentials are handled exclusively by boto3.")
self._parent_config_options = None
# These aren't populated until __enter__ so they need to live
# somewhere
self._init_options = options.copy()
self.aws_session = aws_session
self.aws_access_key_id = aws_access_key_id
self.aws_secret_access_key = aws_secret_access_key
self.aws_session_token = aws_session_token
self.aws_region_name = aws_region_name
self.aws_profile_name = aws_profile_name
self._aws_creds = (
self.aws_session._session.get_credentials()
if self.aws_session else None)
def auth_aws(self):
"""Use `boto3` to get AWS credentials and set the appropriate GDAL
environment options.
"""
import boto3
if not self.aws_session:
self.aws_session = boto3.Session(
aws_access_key_id=self.aws_access_key_id,
aws_secret_access_key=self.aws_secret_access_key,
aws_session_token=self.aws_session_token,
region_name=self.aws_region_name,
profile_name=self.aws_profile_name)
self._aws_creds = self.aws_session.get_credentials()
# Pass these credentials to the GDAL environment.
if self._aws_creds.access_key: # pragma: no branch
self.set_config(aws_access_key_id=self._aws_creds.access_key)
if self._aws_creds.secret_key: # pragma: no branch
self.set_config(aws_secret_access_key=self._aws_creds.secret_key)
if self._aws_creds.token:
self.set_config(aws_session_token=self._aws_creds.token)
if self.aws_session.region_name:
self.set_config(aws_region=self.aws_session.region_name)
@staticmethod
def default_options():
"""Use these options when instantiating from ``self.from_defaults()``.
Returns
-------
dict
"""
return {
'CHECK_WITH_INVERT_PROJ': True,
'GTIFF_IMPLICIT_JPEG_OVR': False,
'DEFAULT_RASTERIO_ENV': True
}
@classmethod
def from_defaults(cls, *args, **kwargs):
"""Instantiate a new ``Env()`` with a set of default config
options. Additional options can be given.
Parameters
----------
args : *args
For ``Env()``.
kwargs : **kwargs
For ``Env()``.
Returns
-------
Env
"""
options = Env.default_options().copy()
options.update(**kwargs)
return cls(*args, **options)
def __enter__(self):
"""Start the GDAL environment and set environment options."""
global _ENV
if _ENV is not None:
self._parent_config_options = _ENV.config_options.copy()
_ENV.close()
self._start()
self.set_config(**self._init_options)
_ENV = self
return self
def __exit__(self, exc_type, exc_val, exc_tb):
"""Teardown the GDAL environment, unset environment options, and
reset if this is a nested environment, reinstate the parent context.
"""
global _ENV
self.close()
if self._parent_config_options is not None:
_ENV = Env(**self._parent_config_options)
_ENV._start()
else:
_ENV = None
def close(self):
"""Stop the GDAL environment."""
self._stop()
def __getitem__(self, key):
"""Get a GDAL environment option.
Parameters
----------
key : str
Option name.
Returns
-------
str or bool or None
"""
return self.get_config(key)
def __setitem__(self, key, value):
"""Set a GDAL environment option.
Parameters
----------
key : str
Option name.
value : str or bool or None
Desired value.
"""
self.set_config(key=value)
def __delitem__(self, key):
"""Unset a GDAL environment option.
Parameters
----------
key : str
Option name.
"""
self.del_config(key)
def ensure_env(f):
"""A decorator that ensures a ``rasterio.Env()`` exists before a function
executes. If one already exists nothing is changed, if not then one is
created from ``rasterio.Env.from_defaults()`` and is torn down when the
function exits.
Parameter
---------
f : function
Object to wrap.
Returns
-------
function
Wrapped function.
"""
@wraps(f)
def wrapper(*args, **kwds):
global _ENV
if _ENV is not None:
return f(*args, **kwds)
else:
with Env.from_defaults():
return f(*args, **kwds)
return wrapper
def _current_env():
"""Get the current ``rasterio.Env()``.
Raises
------
EnvError
If an environment does not exist.
Returns
-------
Env
"""
global _ENV
if _ENV is None:
raise EnvError("A 'rasterio.Env()' does not exist.")
else:
return _ENV