We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Rasterio does not properly tear down GDAL environment
If a Rasterio managed environment does not exist, using `with rasterio.Env()` creates an environment that is not fully cleaned up when the context manager exits.  I'm working on a fix.

The simplest test is:

```python
import rasterio as rio
from rasterio._env import get_gdal_config

key = 'CHECK_WITH_INVERT_PROJ'

print(get_gdal_config(key))
with rio.Env():
    print(get_gdal_config(key))
print(get_gdal_config(key))
```

and a more complex test is:

```python
from osgeo import gdal
import rasterio as rio
from rasterio._env import get_gdal_config


key = 'CHECK_WITH_INVERT_PROJ'


def show(position):

    print(position)
    for k in (key, 'opt'):
        print("osgeo.gdal {}={}".format(k, gdal.GetConfigOption(k)))
        print("rasterio   {}={}".format(k, get_gdal_config(k)))
    print("")


show("Before Env()")

with rio.Env():
    show("In Env() 1")

    # Another inner environment
    with rio.Env(opt='val'):
        show("Inner Env()")

    show("Outside inner Env()")

show("Outside Env()")


rio.env.delenv()
show("Post-delete")
```

produces:

```
Before Env()
osgeo.gdal CHECK_WITH_INVERT_PROJ=None
rasterio   CHECK_WITH_INVERT_PROJ=None
osgeo.gdal opt=None
rasterio   opt=None

In Env() 1
osgeo.gdal CHECK_WITH_INVERT_PROJ=ON
rasterio   CHECK_WITH_INVERT_PROJ=True
osgeo.gdal opt=None
rasterio   opt=None

Inner Env()
osgeo.gdal CHECK_WITH_INVERT_PROJ=ON
rasterio   CHECK_WITH_INVERT_PROJ=True
osgeo.gdal opt=val
rasterio   opt=val

Outside inner Env()
osgeo.gdal CHECK_WITH_INVERT_PROJ=ON
rasterio   CHECK_WITH_INVERT_PROJ=True
osgeo.gdal opt=None
rasterio   opt=None

Outside Env()
osgeo.gdal CHECK_WITH_INVERT_PROJ=ON
rasterio   CHECK_WITH_INVERT_PROJ=True
osgeo.gdal opt=None
rasterio   opt=None

Post-delete
osgeo.gdal CHECK_WITH_INVERT_PROJ=None
rasterio   CHECK_WITH_INVERT_PROJ=None
osgeo.gdal opt=None
rasterio   opt=None
```

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `rasterio/env.py`
Content:
```
1 """Rasterio's GDAL/AWS environment"""
2 
3 from functools import wraps
4 import logging
5 
6 from rasterio._env import (
7     GDALEnv, del_gdal_config, get_gdal_config, set_gdal_config)
8 from rasterio.dtypes import check_dtype
9 from rasterio.errors import EnvError
10 from rasterio.compat import string_types
11 from rasterio.transform import guard_transform
12 from rasterio.vfs import parse_path, vsi_path
13 
14 
15 # The currently active GDAL/AWS environment is a private attribute.
16 _env = None
17 
18 log = logging.getLogger(__name__)
19 
20 # Rasterio defaults
21 default_options = {
22     'CHECK_WITH_INVERT_PROJ': True,
23     'GTIFF_IMPLICIT_JPEG_OVR': False,
24     "I'M_ON_RASTERIO": True
25 }
26 
27 class Env(object):
28     """Abstraction for GDAL and AWS configuration
29 
30     The GDAL library is stateful: it has a registry of format drivers,
31     an error stack, and dozens of configuration options.
32 
33     Rasterio's approach to working with GDAL is to wrap all the state
34     up using a Python context manager (see PEP 343,
35     https://www.python.org/dev/peps/pep-0343/). When the context is
36     entered GDAL drivers are registered, error handlers are
37     configured, and configuration options are set. When the context
38     is exited, drivers are removed from the registry and other
39     configurations are removed.
40 
41     Example:
42 
43         with rasterio.Env(GDAL_CACHEMAX=512) as env:
44             # All drivers are registered, GDAL's raster block cache
45             # size is set to 512MB.
46             # Commence processing...
47             ...
48             # End of processing.
49 
50         # At this point, configuration options are set to their
51         # previous (possible unset) values.
52 
53     A boto3 session or boto3 session constructor arguments
54     `aws_access_key_id`, `aws_secret_access_key`, `aws_session_token`
55     may be passed to Env's constructor. In the latter case, a session
56     will be created as soon as needed. AWS credentials are configured
57     for GDAL as needed.
58     """
59 
60     def __init__(self, aws_session=None, aws_access_key_id=None,
61                  aws_secret_access_key=None, aws_session_token=None,
62                  region_name=None, profile_name=None, **options):
63         """Create a new GDAL/AWS environment.
64 
65         Note: this class is a context manager. GDAL isn't configured
66         until the context is entered via `with rasterio.Env():`
67 
68         Parameters
69         ----------
70         aws_session: object, optional
71             A boto3 session.
72         aws_access_key_id: string, optional
73             An access key id, as per boto3.
74         aws_secret_access_key: string, optional
75             A secret access key, as per boto3.
76         aws_session_token: string, optional
77             A session token, as per boto3.
78         region_name: string, optional
79             A region name, as per boto3.
80         profile_name: string, optional
81             A shared credentials profile name, as per boto3.
82         **options: optional
83             A mapping of GDAL configuration options, e.g.,
84             `CPL_DEBUG=True, CHECK_WITH_INVERT_PROJ=False`.
85 
86         Returns
87         -------
88         A new instance of Env.
89 
90         Note: We raise EnvError if the GDAL config options
91         AWS_ACCESS_KEY_ID or AWS_SECRET_ACCESS_KEY are given. AWS
92         credentials are handled exclusively by boto3.
93         """
94         if ('AWS_ACCESS_KEY_ID' in options or
95                 'AWS_SECRET_ACCESS_KEY' in options):
96             raise EnvError(
97                 "GDAL's AWS config options can not be directly set. "
98                 "AWS credentials are handled exclusively by boto3.")
99         self.aws_access_key_id = aws_access_key_id
100         self.aws_secret_access_key = aws_secret_access_key
101         self.aws_session_token = aws_session_token
102         self.region_name = region_name
103         self.profile_name = profile_name
104         self.aws_session = aws_session
105         self._creds = (
106             self.aws_session._session.get_credentials()
107             if self.aws_session else None)
108         self.options = options.copy()
109         self.context_options = {}
110 
111     def get_aws_credentials(self):
112         """Get credentials and configure GDAL."""
113         import boto3
114         options = {}
115         if not self.aws_session:
116             self.aws_session = boto3.Session(
117                 aws_access_key_id=self.aws_access_key_id,
118                 aws_secret_access_key=self.aws_secret_access_key,
119                 aws_session_token=self.aws_session_token,
120                 region_name=self.region_name,
121                 profile_name=self.profile_name)
122         self._creds = self.aws_session._session.get_credentials()
123 
124         # Pass these credentials to the GDAL environment.
125         if self._creds.access_key:  # pragma: no branch
126             options.update(aws_access_key_id=self._creds.access_key)
127         if self._creds.secret_key:  # pragma: no branch
128             options.update(aws_secret_access_key=self._creds.secret_key)
129         if self._creds.token:
130             options.update(aws_session_token=self._creds.token)
131         if self.aws_session.region_name:
132             options.update(aws_region=self.aws_session.region_name)
133 
134         # Pass these credentials to the GDAL environment.
135         global _env
136         _env.update_config_options(**options)
137 
138     def drivers(self):
139         """Return a mapping of registered drivers."""
140         global _env
141         return _env.drivers()
142 
143     def __enter__(self):
144         log.debug("Entering env context: %r", self)
145         defenv()
146         self.context_options = getenv()
147         setenv(**self.options)
148         log.debug("Entered env context: %r", self)
149         return self
150 
151     def __exit__(self, exc_type=None, exc_val=None, exc_tb=None):
152         log.debug("Exiting env context: %r", self)
153         delenv()
154         setenv(**self.context_options)
155         log.debug("Exited env context: %r", self)
156 
157 
158 def defenv():
159     """Create a default environment if necessary."""
160     global _env
161     if _env:
162         log.debug("GDAL environment exists: %r", _env)
163     else:
164         log.debug("No GDAL environment exists")
165         _env = GDALEnv()
166         _env.update_config_options(**default_options)
167         log.debug(
168             "New GDAL environment %r created", _env)
169     _env.start()
170 
171 
172 def getenv():
173     """Get a mapping of current options."""
174     global _env
175     if not _env:
176         raise EnvError("No GDAL environment exists")
177     else:
178         log.debug("Got a copy of environment %r options", _env)
179         return _env.options.copy()
180 
181 
182 def setenv(**options):
183     """Set options in the existing environment."""
184     global _env
185     if not _env:
186         raise EnvError("No GDAL environment exists")
187     else:
188         _env.update_config_options(**options)
189         log.debug("Updated existing %r with options %r", _env, options)
190 
191 
192 def delenv():
193     """Delete options in the existing environment."""
194     global _env
195     if not _env:
196         raise EnvError("No GDAL environment exists")
197     else:
198         _env.clear_config_options()
199         log.debug("Cleared existing %r options", _env)
200     _env.stop()
201 
202 
203 def ensure_env(f):
204     """A decorator that ensures an env exists before a function
205     calls any GDAL C functions."""
206     @wraps(f)
207     def wrapper(*args, **kwds):
208         with Env(WITH_RASTERIO_ENV=True):
209             return f(*args, **kwds)
210     return wrapper
211
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/rasterio/env.py b/rasterio/env.py
--- a/rasterio/env.py
+++ b/rasterio/env.py
@@ -1,6 +1,7 @@
 """Rasterio's GDAL/AWS environment"""
 
 from functools import wraps
+import itertools as it
 import logging
 
 from rasterio._env import (
@@ -15,6 +16,32 @@
 # The currently active GDAL/AWS environment is a private attribute.
 _env = None
 
+
+# When the outermost 'rasterio.Env()' executes '__enter__' it probes the
+# GDAL environment to see if any of the supplied config options already
+# exist, the assumption being that they were set with
+# 'osgeo.gdal.SetConfigOption()' or possibly 'rasterio.env.set_gdal_config()'.
+# The discovered options are reinstated when the outermost Rasterio environment
+# exits.  Without this check any environment options that are present in the
+# GDAL environment and are also passed to 'rasterio.Env()' will be unset
+# when 'rasterio.Env()' tears down, regardless of their value.  For example:
+#
+#   from osgeo import gdal
+#   import rasterio
+#
+#   gdal.SetConfigOption('key', 'value')
+#   with rasterio.Env(key='something'):
+#       pass
+#
+# The config option 'key' would be unset when 'Env()' exits.  A more
+# comprehensive solution would also leverage https://trac.osgeo.org/gdal/changeset/37273
+# but this gets Rasterio + older versions of GDAL halfway there.  One major
+# assumption is that environment variables are not set directly with
+# 'osgeo.gdal.SetConfigOption()' OR 'rasterio.env.set_gdal_config()' inside
+# of a 'rasterio.Env()'.
+_discovered_options = None
+
+
 log = logging.getLogger(__name__)
 
 # Rasterio defaults
@@ -141,17 +168,54 @@
         return _env.drivers()
 
     def __enter__(self):
+        global _env
+        global _discovered_options
         log.debug("Entering env context: %r", self)
-        defenv()
-        self.context_options = getenv()
+
+        # No parent Rasterio environment exists.
+        if _env is None:
+            logging.debug("Starting outermost env")
+            self._has_parent_env = False
+
+            # See note directly above where _discovered_options is globally
+            # defined.  This MUST happen before calling 'defenv()'.
+            _discovered_options = {}
+            # Don't want to reinstate the "I'M_ON_RASTERIO" option.
+            probe_env = {k for k in default_options if k != "I'M_ON_RASTERIO"}
+            probe_env |= set(self.options.keys())
+            for key in probe_env:
+                val = get_gdal_config(key, normalize=False)
+                if val is not None:
+                    _discovered_options[key] = val
+                    logging.debug("Discovered option: %s=%s", key, val)
+
+            defenv()
+            self.context_options = {}
+        else:
+            self._has_parent_env = True
+            self.context_options = getenv()
         setenv(**self.options)
         log.debug("Entered env context: %r", self)
         return self
 
     def __exit__(self, exc_type=None, exc_val=None, exc_tb=None):
+        global _env
+        global _discovered_options
         log.debug("Exiting env context: %r", self)
         delenv()
-        setenv(**self.context_options)
+        if self._has_parent_env:
+            defenv()
+            setenv(**self.context_options)
+        else:
+            logging.debug("Exiting outermost env")
+            # See note directly above where _discovered_options is globally
+            # defined.
+            while _discovered_options:
+                key, val = _discovered_options.popitem()
+                set_gdal_config(key, val, normalize=False)
+                logging.debug(
+                    "Set discovered option back to: '%s=%s", key, val)
+            _discovered_options = None
         log.debug("Exited env context: %r", self)
 
 
@@ -198,6 +262,7 @@
         _env.clear_config_options()
         log.debug("Cleared existing %r options", _env)
     _env.stop()
+    _env = None
 
 
 def ensure_env(f):

{"golden_diff": "diff --git a/rasterio/env.py b/rasterio/env.py\n--- a/rasterio/env.py\n+++ b/rasterio/env.py\n@@ -1,6 +1,7 @@\n \"\"\"Rasterio's GDAL/AWS environment\"\"\"\n \n from functools import wraps\n+import itertools as it\n import logging\n \n from rasterio._env import (\n@@ -15,6 +16,32 @@\n # The currently active GDAL/AWS environment is a private attribute.\n _env = None\n \n+\n+# When the outermost 'rasterio.Env()' executes '__enter__' it probes the\n+# GDAL environment to see if any of the supplied config options already\n+# exist, the assumption being that they were set with\n+# 'osgeo.gdal.SetConfigOption()' or possibly 'rasterio.env.set_gdal_config()'.\n+# The discovered options are reinstated when the outermost Rasterio environment\n+# exits.  Without this check any environment options that are present in the\n+# GDAL environment and are also passed to 'rasterio.Env()' will be unset\n+# when 'rasterio.Env()' tears down, regardless of their value.  For example:\n+#\n+#   from osgeo import gdal\n+#   import rasterio\n+#\n+#   gdal.SetConfigOption('key', 'value')\n+#   with rasterio.Env(key='something'):\n+#       pass\n+#\n+# The config option 'key' would be unset when 'Env()' exits.  A more\n+# comprehensive solution would also leverage https://trac.osgeo.org/gdal/changeset/37273\n+# but this gets Rasterio + older versions of GDAL halfway there.  One major\n+# assumption is that environment variables are not set directly with\n+# 'osgeo.gdal.SetConfigOption()' OR 'rasterio.env.set_gdal_config()' inside\n+# of a 'rasterio.Env()'.\n+_discovered_options = None\n+\n+\n log = logging.getLogger(__name__)\n \n # Rasterio defaults\n@@ -141,17 +168,54 @@\n         return _env.drivers()\n \n     def __enter__(self):\n+        global _env\n+        global _discovered_options\n         log.debug(\"Entering env context: %r\", self)\n-        defenv()\n-        self.context_options = getenv()\n+\n+        # No parent Rasterio environment exists.\n+        if _env is None:\n+            logging.debug(\"Starting outermost env\")\n+            self._has_parent_env = False\n+\n+            # See note directly above where _discovered_options is globally\n+            # defined.  This MUST happen before calling 'defenv()'.\n+            _discovered_options = {}\n+            # Don't want to reinstate the \"I'M_ON_RASTERIO\" option.\n+            probe_env = {k for k in default_options if k != \"I'M_ON_RASTERIO\"}\n+            probe_env |= set(self.options.keys())\n+            for key in probe_env:\n+                val = get_gdal_config(key, normalize=False)\n+                if val is not None:\n+                    _discovered_options[key] = val\n+                    logging.debug(\"Discovered option: %s=%s\", key, val)\n+\n+            defenv()\n+            self.context_options = {}\n+        else:\n+            self._has_parent_env = True\n+            self.context_options = getenv()\n         setenv(**self.options)\n         log.debug(\"Entered env context: %r\", self)\n         return self\n \n     def __exit__(self, exc_type=None, exc_val=None, exc_tb=None):\n+        global _env\n+        global _discovered_options\n         log.debug(\"Exiting env context: %r\", self)\n         delenv()\n-        setenv(**self.context_options)\n+        if self._has_parent_env:\n+            defenv()\n+            setenv(**self.context_options)\n+        else:\n+            logging.debug(\"Exiting outermost env\")\n+            # See note directly above where _discovered_options is globally\n+            # defined.\n+            while _discovered_options:\n+                key, val = _discovered_options.popitem()\n+                set_gdal_config(key, val, normalize=False)\n+                logging.debug(\n+                    \"Set discovered option back to: '%s=%s\", key, val)\n+            _discovered_options = None\n         log.debug(\"Exited env context: %r\", self)\n \n \n@@ -198,6 +262,7 @@\n         _env.clear_config_options()\n         log.debug(\"Cleared existing %r options\", _env)\n     _env.stop()\n+    _env = None\n \n \n def ensure_env(f):\n", "issue": "Rasterio does not properly tear down GDAL environment\nIf a Rasterio managed environment does not exist, using `with rasterio.Env()` creates an environment that is not fully cleaned up when the context manager exits.  I'm working on a fix.\r\n\r\nThe simplest test is:\r\n\r\n```python\r\nimport rasterio as rio\r\nfrom rasterio._env import get_gdal_config\r\n\r\nkey = 'CHECK_WITH_INVERT_PROJ'\r\n\r\nprint(get_gdal_config(key))\r\nwith rio.Env():\r\n    print(get_gdal_config(key))\r\nprint(get_gdal_config(key))\r\n```\r\n\r\nand a more complex test is:\r\n\r\n```python\r\nfrom osgeo import gdal\r\nimport rasterio as rio\r\nfrom rasterio._env import get_gdal_config\r\n\r\n\r\nkey = 'CHECK_WITH_INVERT_PROJ'\r\n\r\n\r\ndef show(position):\r\n\r\n    print(position)\r\n    for k in (key, 'opt'):\r\n        print(\"osgeo.gdal {}={}\".format(k, gdal.GetConfigOption(k)))\r\n        print(\"rasterio   {}={}\".format(k, get_gdal_config(k)))\r\n    print(\"\")\r\n\r\n\r\nshow(\"Before Env()\")\r\n\r\nwith rio.Env():\r\n    show(\"In Env() 1\")\r\n\r\n    # Another inner environment\r\n    with rio.Env(opt='val'):\r\n        show(\"Inner Env()\")\r\n\r\n    show(\"Outside inner Env()\")\r\n\r\nshow(\"Outside Env()\")\r\n\r\n\r\nrio.env.delenv()\r\nshow(\"Post-delete\")\r\n```\r\n\r\nproduces:\r\n\r\n```\r\nBefore Env()\r\nosgeo.gdal CHECK_WITH_INVERT_PROJ=None\r\nrasterio   CHECK_WITH_INVERT_PROJ=None\r\nosgeo.gdal opt=None\r\nrasterio   opt=None\r\n\r\nIn Env() 1\r\nosgeo.gdal CHECK_WITH_INVERT_PROJ=ON\r\nrasterio   CHECK_WITH_INVERT_PROJ=True\r\nosgeo.gdal opt=None\r\nrasterio   opt=None\r\n\r\nInner Env()\r\nosgeo.gdal CHECK_WITH_INVERT_PROJ=ON\r\nrasterio   CHECK_WITH_INVERT_PROJ=True\r\nosgeo.gdal opt=val\r\nrasterio   opt=val\r\n\r\nOutside inner Env()\r\nosgeo.gdal CHECK_WITH_INVERT_PROJ=ON\r\nrasterio   CHECK_WITH_INVERT_PROJ=True\r\nosgeo.gdal opt=None\r\nrasterio   opt=None\r\n\r\nOutside Env()\r\nosgeo.gdal CHECK_WITH_INVERT_PROJ=ON\r\nrasterio   CHECK_WITH_INVERT_PROJ=True\r\nosgeo.gdal opt=None\r\nrasterio   opt=None\r\n\r\nPost-delete\r\nosgeo.gdal CHECK_WITH_INVERT_PROJ=None\r\nrasterio   CHECK_WITH_INVERT_PROJ=None\r\nosgeo.gdal opt=None\r\nrasterio   opt=None\r\n```\n", "before_files": [{"content": "\"\"\"Rasterio's GDAL/AWS environment\"\"\"\n\nfrom functools import wraps\nimport logging\n\nfrom rasterio._env import (\n    GDALEnv, del_gdal_config, get_gdal_config, set_gdal_config)\nfrom rasterio.dtypes import check_dtype\nfrom rasterio.errors import EnvError\nfrom rasterio.compat import string_types\nfrom rasterio.transform import guard_transform\nfrom rasterio.vfs import parse_path, vsi_path\n\n\n# The currently active GDAL/AWS environment is a private attribute.\n_env = None\n\nlog = logging.getLogger(__name__)\n\n# Rasterio defaults\ndefault_options = {\n    'CHECK_WITH_INVERT_PROJ': True,\n    'GTIFF_IMPLICIT_JPEG_OVR': False,\n    \"I'M_ON_RASTERIO\": True\n}\n\nclass Env(object):\n    \"\"\"Abstraction for GDAL and AWS configuration\n\n    The GDAL library is stateful: it has a registry of format drivers,\n    an error stack, and dozens of configuration options.\n\n    Rasterio's approach to working with GDAL is to wrap all the state\n    up using a Python context manager (see PEP 343,\n    https://www.python.org/dev/peps/pep-0343/). When the context is\n    entered GDAL drivers are registered, error handlers are\n    configured, and configuration options are set. When the context\n    is exited, drivers are removed from the registry and other\n    configurations are removed.\n\n    Example:\n\n        with rasterio.Env(GDAL_CACHEMAX=512) as env:\n            # All drivers are registered, GDAL's raster block cache\n            # size is set to 512MB.\n            # Commence processing...\n            ...\n            # End of processing.\n\n        # At this point, configuration options are set to their\n        # previous (possible unset) values.\n\n    A boto3 session or boto3 session constructor arguments\n    `aws_access_key_id`, `aws_secret_access_key`, `aws_session_token`\n    may be passed to Env's constructor. In the latter case, a session\n    will be created as soon as needed. AWS credentials are configured\n    for GDAL as needed.\n    \"\"\"\n\n    def __init__(self, aws_session=None, aws_access_key_id=None,\n                 aws_secret_access_key=None, aws_session_token=None,\n                 region_name=None, profile_name=None, **options):\n        \"\"\"Create a new GDAL/AWS environment.\n\n        Note: this class is a context manager. GDAL isn't configured\n        until the context is entered via `with rasterio.Env():`\n\n        Parameters\n        ----------\n        aws_session: object, optional\n            A boto3 session.\n        aws_access_key_id: string, optional\n            An access key id, as per boto3.\n        aws_secret_access_key: string, optional\n            A secret access key, as per boto3.\n        aws_session_token: string, optional\n            A session token, as per boto3.\n        region_name: string, optional\n            A region name, as per boto3.\n        profile_name: string, optional\n            A shared credentials profile name, as per boto3.\n        **options: optional\n            A mapping of GDAL configuration options, e.g.,\n            `CPL_DEBUG=True, CHECK_WITH_INVERT_PROJ=False`.\n\n        Returns\n        -------\n        A new instance of Env.\n\n        Note: We raise EnvError if the GDAL config options\n        AWS_ACCESS_KEY_ID or AWS_SECRET_ACCESS_KEY are given. AWS\n        credentials are handled exclusively by boto3.\n        \"\"\"\n        if ('AWS_ACCESS_KEY_ID' in options or\n                'AWS_SECRET_ACCESS_KEY' in options):\n            raise EnvError(\n                \"GDAL's AWS config options can not be directly set. \"\n                \"AWS credentials are handled exclusively by boto3.\")\n        self.aws_access_key_id = aws_access_key_id\n        self.aws_secret_access_key = aws_secret_access_key\n        self.aws_session_token = aws_session_token\n        self.region_name = region_name\n        self.profile_name = profile_name\n        self.aws_session = aws_session\n        self._creds = (\n            self.aws_session._session.get_credentials()\n            if self.aws_session else None)\n        self.options = options.copy()\n        self.context_options = {}\n\n    def get_aws_credentials(self):\n        \"\"\"Get credentials and configure GDAL.\"\"\"\n        import boto3\n        options = {}\n        if not self.aws_session:\n            self.aws_session = boto3.Session(\n                aws_access_key_id=self.aws_access_key_id,\n                aws_secret_access_key=self.aws_secret_access_key,\n                aws_session_token=self.aws_session_token,\n                region_name=self.region_name,\n                profile_name=self.profile_name)\n        self._creds = self.aws_session._session.get_credentials()\n\n        # Pass these credentials to the GDAL environment.\n        if self._creds.access_key:  # pragma: no branch\n            options.update(aws_access_key_id=self._creds.access_key)\n        if self._creds.secret_key:  # pragma: no branch\n            options.update(aws_secret_access_key=self._creds.secret_key)\n        if self._creds.token:\n            options.update(aws_session_token=self._creds.token)\n        if self.aws_session.region_name:\n            options.update(aws_region=self.aws_session.region_name)\n\n        # Pass these credentials to the GDAL environment.\n        global _env\n        _env.update_config_options(**options)\n\n    def drivers(self):\n        \"\"\"Return a mapping of registered drivers.\"\"\"\n        global _env\n        return _env.drivers()\n\n    def __enter__(self):\n        log.debug(\"Entering env context: %r\", self)\n        defenv()\n        self.context_options = getenv()\n        setenv(**self.options)\n        log.debug(\"Entered env context: %r\", self)\n        return self\n\n    def __exit__(self, exc_type=None, exc_val=None, exc_tb=None):\n        log.debug(\"Exiting env context: %r\", self)\n        delenv()\n        setenv(**self.context_options)\n        log.debug(\"Exited env context: %r\", self)\n\n\ndef defenv():\n    \"\"\"Create a default environment if necessary.\"\"\"\n    global _env\n    if _env:\n        log.debug(\"GDAL environment exists: %r\", _env)\n    else:\n        log.debug(\"No GDAL environment exists\")\n        _env = GDALEnv()\n        _env.update_config_options(**default_options)\n        log.debug(\n            \"New GDAL environment %r created\", _env)\n    _env.start()\n\n\ndef getenv():\n    \"\"\"Get a mapping of current options.\"\"\"\n    global _env\n    if not _env:\n        raise EnvError(\"No GDAL environment exists\")\n    else:\n        log.debug(\"Got a copy of environment %r options\", _env)\n        return _env.options.copy()\n\n\ndef setenv(**options):\n    \"\"\"Set options in the existing environment.\"\"\"\n    global _env\n    if not _env:\n        raise EnvError(\"No GDAL environment exists\")\n    else:\n        _env.update_config_options(**options)\n        log.debug(\"Updated existing %r with options %r\", _env, options)\n\n\ndef delenv():\n    \"\"\"Delete options in the existing environment.\"\"\"\n    global _env\n    if not _env:\n        raise EnvError(\"No GDAL environment exists\")\n    else:\n        _env.clear_config_options()\n        log.debug(\"Cleared existing %r options\", _env)\n    _env.stop()\n\n\ndef ensure_env(f):\n    \"\"\"A decorator that ensures an env exists before a function\n    calls any GDAL C functions.\"\"\"\n    @wraps(f)\n    def wrapper(*args, **kwds):\n        with Env(WITH_RASTERIO_ENV=True):\n            return f(*args, **kwds)\n    return wrapper\n", "path": "rasterio/env.py"}], "after_files": [{"content": "\"\"\"Rasterio's GDAL/AWS environment\"\"\"\n\nfrom functools import wraps\nimport itertools as it\nimport logging\n\nfrom rasterio._env import (\n    GDALEnv, del_gdal_config, get_gdal_config, set_gdal_config)\nfrom rasterio.dtypes import check_dtype\nfrom rasterio.errors import EnvError\nfrom rasterio.compat import string_types\nfrom rasterio.transform import guard_transform\nfrom rasterio.vfs import parse_path, vsi_path\n\n\n# The currently active GDAL/AWS environment is a private attribute.\n_env = None\n\n\n# When the outermost 'rasterio.Env()' executes '__enter__' it probes the\n# GDAL environment to see if any of the supplied config options already\n# exist, the assumption being that they were set with\n# 'osgeo.gdal.SetConfigOption()' or possibly 'rasterio.env.set_gdal_config()'.\n# The discovered options are reinstated when the outermost Rasterio environment\n# exits.  Without this check any environment options that are present in the\n# GDAL environment and are also passed to 'rasterio.Env()' will be unset\n# when 'rasterio.Env()' tears down, regardless of their value.  For example:\n#\n#   from osgeo import gdal\n#   import rasterio\n#\n#   gdal.SetConfigOption('key', 'value')\n#   with rasterio.Env(key='something'):\n#       pass\n#\n# The config option 'key' would be unset when 'Env()' exits.  A more\n# comprehensive solution would also leverage https://trac.osgeo.org/gdal/changeset/37273\n# but this gets Rasterio + older versions of GDAL halfway there.  One major\n# assumption is that environment variables are not set directly with\n# 'osgeo.gdal.SetConfigOption()' OR 'rasterio.env.set_gdal_config()' inside\n# of a 'rasterio.Env()'.\n_discovered_options = None\n\n\nlog = logging.getLogger(__name__)\n\n# Rasterio defaults\ndefault_options = {\n    'CHECK_WITH_INVERT_PROJ': True,\n    'GTIFF_IMPLICIT_JPEG_OVR': False,\n    \"I'M_ON_RASTERIO\": True\n}\n\nclass Env(object):\n    \"\"\"Abstraction for GDAL and AWS configuration\n\n    The GDAL library is stateful: it has a registry of format drivers,\n    an error stack, and dozens of configuration options.\n\n    Rasterio's approach to working with GDAL is to wrap all the state\n    up using a Python context manager (see PEP 343,\n    https://www.python.org/dev/peps/pep-0343/). When the context is\n    entered GDAL drivers are registered, error handlers are\n    configured, and configuration options are set. When the context\n    is exited, drivers are removed from the registry and other\n    configurations are removed.\n\n    Example:\n\n        with rasterio.Env(GDAL_CACHEMAX=512) as env:\n            # All drivers are registered, GDAL's raster block cache\n            # size is set to 512MB.\n            # Commence processing...\n            ...\n            # End of processing.\n\n        # At this point, configuration options are set to their\n        # previous (possible unset) values.\n\n    A boto3 session or boto3 session constructor arguments\n    `aws_access_key_id`, `aws_secret_access_key`, `aws_session_token`\n    may be passed to Env's constructor. In the latter case, a session\n    will be created as soon as needed. AWS credentials are configured\n    for GDAL as needed.\n    \"\"\"\n\n    def __init__(self, aws_session=None, aws_access_key_id=None,\n                 aws_secret_access_key=None, aws_session_token=None,\n                 region_name=None, profile_name=None, **options):\n        \"\"\"Create a new GDAL/AWS environment.\n\n        Note: this class is a context manager. GDAL isn't configured\n        until the context is entered via `with rasterio.Env():`\n\n        Parameters\n        ----------\n        aws_session: object, optional\n            A boto3 session.\n        aws_access_key_id: string, optional\n            An access key id, as per boto3.\n        aws_secret_access_key: string, optional\n            A secret access key, as per boto3.\n        aws_session_token: string, optional\n            A session token, as per boto3.\n        region_name: string, optional\n            A region name, as per boto3.\n        profile_name: string, optional\n            A shared credentials profile name, as per boto3.\n        **options: optional\n            A mapping of GDAL configuration options, e.g.,\n            `CPL_DEBUG=True, CHECK_WITH_INVERT_PROJ=False`.\n\n        Returns\n        -------\n        A new instance of Env.\n\n        Note: We raise EnvError if the GDAL config options\n        AWS_ACCESS_KEY_ID or AWS_SECRET_ACCESS_KEY are given. AWS\n        credentials are handled exclusively by boto3.\n        \"\"\"\n        if ('AWS_ACCESS_KEY_ID' in options or\n                'AWS_SECRET_ACCESS_KEY' in options):\n            raise EnvError(\n                \"GDAL's AWS config options can not be directly set. \"\n                \"AWS credentials are handled exclusively by boto3.\")\n        self.aws_access_key_id = aws_access_key_id\n        self.aws_secret_access_key = aws_secret_access_key\n        self.aws_session_token = aws_session_token\n        self.region_name = region_name\n        self.profile_name = profile_name\n        self.aws_session = aws_session\n        self._creds = (\n            self.aws_session._session.get_credentials()\n            if self.aws_session else None)\n        self.options = options.copy()\n        self.context_options = {}\n\n    def get_aws_credentials(self):\n        \"\"\"Get credentials and configure GDAL.\"\"\"\n        import boto3\n        options = {}\n        if not self.aws_session:\n            self.aws_session = boto3.Session(\n                aws_access_key_id=self.aws_access_key_id,\n                aws_secret_access_key=self.aws_secret_access_key,\n                aws_session_token=self.aws_session_token,\n                region_name=self.region_name,\n                profile_name=self.profile_name)\n        self._creds = self.aws_session._session.get_credentials()\n\n        # Pass these credentials to the GDAL environment.\n        if self._creds.access_key:  # pragma: no branch\n            options.update(aws_access_key_id=self._creds.access_key)\n        if self._creds.secret_key:  # pragma: no branch\n            options.update(aws_secret_access_key=self._creds.secret_key)\n        if self._creds.token:\n            options.update(aws_session_token=self._creds.token)\n        if self.aws_session.region_name:\n            options.update(aws_region=self.aws_session.region_name)\n\n        # Pass these credentials to the GDAL environment.\n        global _env\n        _env.update_config_options(**options)\n\n    def drivers(self):\n        \"\"\"Return a mapping of registered drivers.\"\"\"\n        global _env\n        return _env.drivers()\n\n    def __enter__(self):\n        global _env\n        global _discovered_options\n        log.debug(\"Entering env context: %r\", self)\n\n        # No parent Rasterio environment exists.\n        if _env is None:\n            logging.debug(\"Starting outermost env\")\n            self._has_parent_env = False\n\n            # See note directly above where _discovered_options is globally\n            # defined.  This MUST happen before calling 'defenv()'.\n            _discovered_options = {}\n            # Don't want to reinstate the \"I'M_ON_RASTERIO\" option.\n            probe_env = {k for k in default_options if k != \"I'M_ON_RASTERIO\"}\n            probe_env |= set(self.options.keys())\n            for key in probe_env:\n                val = get_gdal_config(key, normalize=False)\n                if val is not None:\n                    _discovered_options[key] = val\n                    logging.debug(\"Discovered option: %s=%s\", key, val)\n\n            defenv()\n            self.context_options = {}\n        else:\n            self._has_parent_env = True\n            self.context_options = getenv()\n        setenv(**self.options)\n        log.debug(\"Entered env context: %r\", self)\n        return self\n\n    def __exit__(self, exc_type=None, exc_val=None, exc_tb=None):\n        global _env\n        global _discovered_options\n        log.debug(\"Exiting env context: %r\", self)\n        delenv()\n        if self._has_parent_env:\n            defenv()\n            setenv(**self.context_options)\n        else:\n            logging.debug(\"Exiting outermost env\")\n            # See note directly above where _discovered_options is globally\n            # defined.\n            while _discovered_options:\n                key, val = _discovered_options.popitem()\n                set_gdal_config(key, val, normalize=False)\n                logging.debug(\n                    \"Set discovered option back to: '%s=%s\", key, val)\n            _discovered_options = None\n        log.debug(\"Exited env context: %r\", self)\n\n\ndef defenv():\n    \"\"\"Create a default environment if necessary.\"\"\"\n    global _env\n    if _env:\n        log.debug(\"GDAL environment exists: %r\", _env)\n    else:\n        log.debug(\"No GDAL environment exists\")\n        _env = GDALEnv()\n        _env.update_config_options(**default_options)\n        log.debug(\n            \"New GDAL environment %r created\", _env)\n    _env.start()\n\n\ndef getenv():\n    \"\"\"Get a mapping of current options.\"\"\"\n    global _env\n    if not _env:\n        raise EnvError(\"No GDAL environment exists\")\n    else:\n        log.debug(\"Got a copy of environment %r options\", _env)\n        return _env.options.copy()\n\n\ndef setenv(**options):\n    \"\"\"Set options in the existing environment.\"\"\"\n    global _env\n    if not _env:\n        raise EnvError(\"No GDAL environment exists\")\n    else:\n        _env.update_config_options(**options)\n        log.debug(\"Updated existing %r with options %r\", _env, options)\n\n\ndef delenv():\n    \"\"\"Delete options in the existing environment.\"\"\"\n    global _env\n    if not _env:\n        raise EnvError(\"No GDAL environment exists\")\n    else:\n        _env.clear_config_options()\n        log.debug(\"Cleared existing %r options\", _env)\n    _env.stop()\n    _env = None\n\n\ndef ensure_env(f):\n    \"\"\"A decorator that ensures an env exists before a function\n    calls any GDAL C functions.\"\"\"\n    @wraps(f)\n    def wrapper(*args, **kwds):\n        with Env(WITH_RASTERIO_ENV=True):\n            return f(*args, **kwds)\n    return wrapper\n", "path": "rasterio/env.py"}]}