Repository: rasterio/rasterio

Issue:
`boto3` required to open local file
## Expected behavior and actual behavior.

**Expected behavior:** installing `rasterio` from `pip` on a fresh Python environment, and trying to open a local file with it. This should succeed without needing additional requirements.

**Actual behavior:** `rio info a.tiff` on a local `a.tiff` file, for example, gives
```
Traceback (most recent call last):
  File "/Users/guillaumelostis/.venv/rio/bin/rio", line 10, in <module>
    sys.exit(main_group())
  File "/Users/guillaumelostis/.venv/rio/lib/python3.7/site-packages/click/core.py", line 764, in __call__
    return self.main(*args, **kwargs)
  File "/Users/guillaumelostis/.venv/rio/lib/python3.7/site-packages/click/core.py", line 717, in main
    rv = self.invoke(ctx)
  File "/Users/guillaumelostis/.venv/rio/lib/python3.7/site-packages/click/core.py", line 1134, in invoke
    Command.invoke(self, ctx)
  File "/Users/guillaumelostis/.venv/rio/lib/python3.7/site-packages/click/core.py", line 956, in invoke
    return ctx.invoke(self.callback, **ctx.params)
  File "/Users/guillaumelostis/.venv/rio/lib/python3.7/site-packages/click/core.py", line 555, in invoke
    return callback(*args, **kwargs)
  File "/Users/guillaumelostis/.venv/rio/lib/python3.7/site-packages/click/decorators.py", line 17, in new_func
    return f(get_current_context(), *args, **kwargs)
  File "/Users/guillaumelostis/.venv/rio/lib/python3.7/site-packages/rasterio/rio/main.py", line 103, in main_group
    ctx.obj["env"] = rasterio.Env(**envopts)
  File "/Users/guillaumelostis/.venv/rio/lib/python3.7/site-packages/rasterio/env.py", line 195, in __init__
    self.session = AWSSession()
  File "/Users/guillaumelostis/.venv/rio/lib/python3.7/site-packages/rasterio/session.py", line 198, in __init__
    import boto3
ModuleNotFoundError: No module named 'boto3'
```

## Steps to reproduce the problem.

`pip install rasterio` on a fresh Python environment. This installs `rasterio==1.0.24` using the Mac OS wheels on my machine.

Then running `rio info a.tiff` on a local file gives the traceback above. This is due to the fact that I have the `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` environment variables set on my machine since I occasionally work with files from `S3`.
This causes this condition: https://github.com/mapbox/rasterio/blob/4aefb4674d19f29cce8a408819aa001151b204d1/rasterio/env.py#L194-L195 to be true, and therefore an `AWSSession` is used to open my local file.

I am aware that I can easily solve my problem by running `pip install "rasterio[s3]"`, or by removing the environment variables when working with local files, but both these solutions require extra work and could mislead new users. I know how to work around this issue, but I am just wondering of you agree that this is a bug, or if you think that this is the expected behavior, in which case I don't mind you closing the issue.

I have also found out that opening a local file using:
```python
import rasterio
f = rasterio.open("a.tiff")
```

works, while this does not:
```python
import rasterio
with rasterio.open("a.tiff") as f:
    pass
```

This is logical since the second solution uses `rasterio.env` while the first one doesn't.

I don't have a good knowledge of the inner workings of `rasterio`, but falling back to a basic session if `boto3` requirement is not met could maybe be a way to workaround the problem I am facing?

## Operating system

All

## Rasterio version and provenance

The 1.0.24 Mac OS wheel installed from PyPI using pip 19.0.3.


Discussion:
Yep, this is a bug. Thanks for the report!

Resolving patch:
diff --git a/rasterio/__init__.py b/rasterio/__init__.py
--- a/rasterio/__init__.py
+++ b/rasterio/__init__.py
@@ -42,7 +42,7 @@ def emit(self, record):
 
 
 __all__ = ['band', 'open', 'pad', 'Env']
-__version__ = "1.0.24"
+__version__ = "1.0.25dev"
 __gdal_version__ = gdal_version()
 
 # Rasterio attaches NullHandler to the 'rasterio' logger and its
diff --git a/rasterio/env.py b/rasterio/env.py
--- a/rasterio/env.py
+++ b/rasterio/env.py
@@ -51,6 +51,12 @@ def __init__(self):
 
 log = logging.getLogger(__name__)
 
+try:
+    import boto3
+except ImportError:
+    log.info("failed to import boto3, continuing.")
+    boto3 = None
+
 
 class Env(object):
     """Abstraction for GDAL and AWS configuration
@@ -191,8 +197,10 @@ def __init__(self, session=None, aws_unsigned=False, profile_name=None,
                 region_name=region_name,
                 profile_name=profile_name,
                 aws_unsigned=aws_unsigned)
+
         elif 'AWS_ACCESS_KEY_ID' in os.environ and 'AWS_SECRET_ACCESS_KEY' in os.environ:
-            self.session = AWSSession()
+            self.session = AWSSession() if boto3 is not None else DummySession()
+
         else:
             self.session = DummySession()
 
@@ -348,10 +356,13 @@ def delenv():
 
 
 class NullContextManager(object):
+
     def __init__(self):
         pass
+
     def __enter__(self):
         return self
+
     def __exit__(self, *args):
         pass
 


Test patch:
diff --git a/tests/test_env.py b/tests/test_env.py
--- a/tests/test_env.py
+++ b/tests/test_env.py
@@ -20,7 +20,7 @@
 from rasterio.env import GDALVersion, require_gdal_version
 from rasterio.errors import EnvError, RasterioIOError, GDALVersionError
 from rasterio.rio.main import main_group
-from rasterio.session import AWSSession, OSSSession
+from rasterio.session import AWSSession, DummySession, OSSSession
 
 from .conftest import requires_gdal21
 
@@ -823,3 +823,13 @@ def test_oss_session_credentials(gdalenv):
         assert getenv()['OSS_ACCESS_KEY_ID'] == 'id'
         assert getenv()['OSS_SECRET_ACCESS_KEY'] == 'key'
         assert getenv()['OSS_ENDPOINT'] == 'null-island-1'
+
+
+def test_dummy_session_without_boto3(monkeypatch, caplog):
+    """Without boto3, always revert to dummy session"""
+    # Confirm fix of #1708.
+    with monkeypatch.context() as mpctx:
+        mpctx.setattr("rasterio.env.boto3", None)
+        mpctx.setenv('AWS_ACCESS_KEY_ID', 'lol')
+        mpctx.setenv('AWS_SECRET_ACCESS_KEY', 'wut')
+        isinstance(rasterio.env.Env().session, DummySession)