CI for boto: fix errors; add coverage; add skip for uncatchable ResourceWarning
closes #23680 
closes #23754
- [x] fixture modified / tests pass
- [x] passes `git diff upstream/master -u -- "*.py" | flake8 --diff`

EDIT2: The warning has been identified as being caused by a vendored `requests` from `botocore<1.11`, which is solved by raising the minimum version to `1.11` for the only CI job (`travis-36`) that is testing `boto`. 

This would then simultaneously run into #23754 due to a moto bug (spulec/moto#1924 / spulec/moto#1941), but setting the environment variables `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` to any dummy value fixes the issue (taken from spulec/moto#1952).

I'm also adding the `boto` tests to the `travis-37` job, just to have some more coverage in general (and the `travis-37` is by far the fastest job right now).

~EDIT: The warning has been identified as being caused by a vendored `requests` from `botocore<1.11`. Unfortunately, it's not possible to (just) increase the minimum version, as `botocore>=1.11` currently runs into #23754 due to a moto bug (spulec/moto#1941), which would (once #24073 is merged) that these  would just be skipped silently. Thus, I'm adding a the boto tests to the `travis-37` build with `botocore>=1.11`, which will start working once #23754 is skipped, while still testing `boto` on the `travis-36` job by forcing `botocore<1.11`.~
Hello @h-vetinari! Thanks for submitting the PR.

 - There are no PEP8 issues in the file [`pandas/io/excel.py`](https://github.com/h-vetinari/pandas/blob/a4858ae984a5b1c76508b7b708854b10c1418b81/pandas/io/excel.py) !

 - There are no PEP8 issues in the file [`pandas/io/parquet.py`](https://github.com/h-vetinari/pandas/blob/a4858ae984a5b1c76508b7b708854b10c1418b81/pandas/io/parquet.py) !

 - There are no PEP8 issues in the file [`pandas/tests/io/conftest.py`](https://github.com/h-vetinari/pandas/blob/a4858ae984a5b1c76508b7b708854b10c1418b81/pandas/tests/io/conftest.py) !

 - There are no PEP8 issues in the file [`pandas/tests/io/json/test_compression.py`](https://github.com/h-vetinari/pandas/blob/a4858ae984a5b1c76508b7b708854b10c1418b81/pandas/tests/io/json/test_compression.py) !

 - There are no PEP8 issues in the file [`pandas/tests/io/test_excel.py`](https://github.com/h-vetinari/pandas/blob/a4858ae984a5b1c76508b7b708854b10c1418b81/pandas/tests/io/test_excel.py) !

 - There are no PEP8 issues in the file [`pandas/tests/io/test_s3.py`](https://github.com/h-vetinari/pandas/blob/a4858ae984a5b1c76508b7b708854b10c1418b81/pandas/tests/io/test_s3.py) !

 # [Codecov](https://codecov.io/gh/pandas-dev/pandas/pull/23731?src=pr&el=h1) Report
> Merging [#23731](https://codecov.io/gh/pandas-dev/pandas/pull/23731?src=pr&el=desc) into [master](https://codecov.io/gh/pandas-dev/pandas/commit/7b0fa8e94e22aa4a491fa574bc56f8ca791f7608?src=pr&el=desc) will **increase** coverage by `0.05%`.
> The diff coverage is `100%`.

[![Impacted file tree graph](https://codecov.io/gh/pandas-dev/pandas/pull/23731/graphs/tree.svg?width=650&token=eZ4WkYLtcO&height=150&src=pr)](https://codecov.io/gh/pandas-dev/pandas/pull/23731?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master   #23731      +/-   ##
==========================================
+ Coverage   92.22%   92.28%   +0.05%     
==========================================
  Files         162      162              
  Lines       51824    51830       +6     
==========================================
+ Hits        47795    47830      +35     
+ Misses       4029     4000      -29
```

| Flag | Coverage Δ | |
|---|---|---|
| #multiple | `90.68% <100%> (+0.06%)` | :arrow_up: |
| #single | `43.01% <16.66%> (-0.01%)` | :arrow_down: |

| [Impacted Files](https://codecov.io/gh/pandas-dev/pandas/pull/23731?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [pandas/util/testing.py](https://codecov.io/gh/pandas-dev/pandas/pull/23731/diff?src=pr&el=tree#diff-cGFuZGFzL3V0aWwvdGVzdGluZy5weQ==) | `87.48% <100%> (+0.06%)` | :arrow_up: |
| [pandas/io/common.py](https://codecov.io/gh/pandas-dev/pandas/pull/23731/diff?src=pr&el=tree#diff-cGFuZGFzL2lvL2NvbW1vbi5weQ==) | `72.86% <0%> (+0.77%)` | :arrow_up: |
| [pandas/io/parquet.py](https://codecov.io/gh/pandas-dev/pandas/pull/23731/diff?src=pr&el=tree#diff-cGFuZGFzL2lvL3BhcnF1ZXQucHk=) | `84.61% <0%> (+7.69%)` | :arrow_up: |
| [pandas/io/s3.py](https://codecov.io/gh/pandas-dev/pandas/pull/23731/diff?src=pr&el=tree#diff-cGFuZGFzL2lvL3MzLnB5) | `86.36% <0%> (+86.36%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/pandas-dev/pandas/pull/23731?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/pandas-dev/pandas/pull/23731?src=pr&el=footer). Last update [7b0fa8e...b532696](https://codecov.io/gh/pandas-dev/pandas/pull/23731?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).
 After an absurd amount of time trying to hunt down these warnings, I think I found the culprit/solution boto/botocore#1464.

The warning is from a vendored requests/urrlib3 in botocore, which didn't close a session/socket. Unfortunately, there are no means that I found (and I tried a lot) that can catch this warning. Failed attempts include:
* `warnings.catch_warnings()` with `simplefilter` or `filterwarning`
* `capsys`/`capfd`/`capsysbinary`/`capsysfd` fixtures from pytest
* `tm.capture_stderr` and `tm.capture_stdout`
* setting `os.environ["PYTHONWARNINGS"]`
* passing `-W ignore::ResourceWarning` to the pytest call

The *only* thing that had an effect (but still didn't work) was `-W error::ResourceWarning`:
```
(pandas-dev) C:\[...]\pddev>pytest pandas/tests/io/test_parquet.py -W error::ResourceWarning
============================= test session starts =============================
[...]
========== 1 failed, 39 passed, 6 skipped, 2 xpassed in 6.35 seconds ==========
Exception ignored in: <socket.socket fd=2680, family=AddressFamily.AF_INET, type=SocketKind.SOCK_DGRAM, proto=0>
ResourceWarning: unclosed <socket.socket fd=2680, family=AddressFamily.AF_INET, type=SocketKind.SOCK_DGRAM, proto=0>
```
i.e. even more spurious output (the failure is platform-specific and not worth mentioning here).

After I upgraded to the latest botocore (>=1.11 is the cutoff), things are working fine. As such, I decided to just (try to) force the `travis-36` job to load that, and skip the s3-tests otherwise. I restricted the skips to the case that `PANDAS_TESTING_MODE = "deprecate"`, because otherwise, the ResourceWarnings are filtered out anyway.
 So, the last run had no warnings anymore (failure was again only #23726), but I realized that the reason for this is that now the `s3_resource` is just skipping through some more restrictive authentication checks in the newer boto (#23754). I'm trying to cover more boto by adding it to the `travis-37` job (which is also around 10min faster than the others). Moto is supporting 3.7. starting with `1.3.7` (the latest version), but that isn't reflected in the requirements yet: spulec/moto#1886. Therefore, I'm installing through pip (after failing through conda). OK, good news is that the ResourceWarnings are gone from the `travis-36` job (https://travis-ci.org/pandas-dev/pandas/jobs/456410643), which, I guess is a nice and closed-off extent for this PR.

The errors now in the `travis-37` job due to #23754 will still need to be solved, but I guess that's something for a follow-up.

 @jreback
I responded to your feedback couple days ago, PTAL @TomAugspurger Would you mind opining on https://github.com/pandas-dev/pandas/pull/23731#discussion_r235544075? :) @TomAugspurger Thanks!

@jreback Should you agree as well, don't merge quite yet - I still need to set up boto to be tested in another CI job (as explained in the comment above). Will wait for your input here What do you mean by "another" CI job? Can we take an existing one and pin moto, boto, and botocore to known versions? That's of course what I meant... Sorry for the confusing choice of words. @TomAugspurger 
> From what I understand there's a conflict between the latest boto/botocore and moto
#23754

Yes, there seems to be an error with the newer moto that will hopefully be fixed soon.

> We could also try to fix this at the PANDAS_TESTING_MODE level. That adds a `warnings.simplefilter` at the start of the test. Perhaps the fixture could add an ignore to our filters? Though maybe @h-vetinari already tried that.

The warning unfortunately cannot be caught by `warnings.simplefilter` (or anything else I could try, see https://github.com/pandas-dev/pandas/pull/23731#issuecomment-439522832), because AFAICT, it is emitted from a finally/teardown state, where the usual mechanics don't apply anymore.

@jreback
> botocore is only a test dep, so i don't mind switching it to a higher version. Then simply add this to other builds until we are actually testing this.

It's actually an indirect (optional) dependency through `boto3`, which directly depends on it (in version lockstep: `boto3 1.x.yy <-> botocore 1.[x+3].yy`).

I added the newer moto to to the `travis-37` job, where those boto tests should now produce errors (after re-verifying that, I'll then add a specific `except` to the fixture-teardown). I'll also add `PANDAS_TESTING_MODE="deprecate"` to make sure these `boto`-tests are tested for warnings (once moto is fixed upstream).

On the other hand, I'm forcing `botocore<1.11` on the `travis-36` job (and removing `PANDAS_TESTING_MODE="deprecate"`), to make sure boto is tested until #23754 is solved.

EDIT: clarification about shifting `PANDAS_TESTING_MODE="deprecate"` can you rebase Failure in azure is unrelated. I had closed this to avoid being merged, as I saw that there were some things that still need ironing out (but had no time to comment at work).
I'll split off the part that deals with the `s3_resource` fixture in a separate PR and leave the CI stuff here. OK, so I split off #24073 which handles the `s3_resource` fixture part, but I actually realised that getting rid of the warning while still testing boto is impossible until #23754 is solved. 
Any CI job using `botocore<1.11` will have a ResourceWarning, and any job having `botocore>=1.11` will fail (or rather, silently skip the boto tests).

I would suggest to merge this nevertheless, as this was several hours worth of investigation and will help solve the problem further down the line. At the very least, this PR should remain open. Merged master to see if https://dev.azure.com/pandas-dev/pandas/_build/results?buildId=4665 is fixed.  @TomAugspurger The failed build you linked was due to linting...? Did you mean the moto/boto failure? I meant the linting one, it was before I started investigating.

Do you have a recommendation on the moto / boto failures? Do your changes here solve all the issues we're seeing on master? I'm still trying to piece it all together. I have no idea about the failure in https://travis-ci.org/pandas-dev/pandas/jobs/463373613

```
INTERNALERROR> Traceback (most recent call last):
INTERNALERROR>   File "/home/travis/miniconda3/envs/pandas-dev/lib/python3.7/site-packages/execnet/gateway_base.py", line 1383, in _save
INTERNALERROR>     dispatch = self._dispatch[tp]
INTERNALERROR> KeyError: <class 'ssl.SSLSocket'>
INTERNALERROR> 
INTERNALERROR> During handling of the above exception, another exception occurred:
INTERNALERROR> 
INTERNALERROR> Traceback (most recent call last):
INTERNALERROR>   File "/home/travis/miniconda3/envs/pandas-dev/lib/python3.7/site-packages/_pytest/main.py", line 185, in wrap_session
INTERNALERROR>     session.exitstatus = doit(config, session) or 0
INTERNALERROR>   File "/home/travis/miniconda3/envs/pandas-dev/lib/python3.7/site-packages/_pytest/main.py", line 225, in _main
INTERNALERROR>     config.hook.pytest_runtestloop(session=session)
INTERNALERROR>   File "/home/travis/miniconda3/envs/pandas-dev/lib/python3.7/site-packages/pluggy/hooks.py", line 284, in __call__
INTERNALERROR>     return self._hookexec(self, self.get_hookimpls(), kwargs)
INTERNALERROR>   File "/home/travis/miniconda3/envs/pandas-dev/lib/python3.7/site-packages/pluggy/manager.py", line 67, in _hookexec
INTERNALERROR>     return self._inner_hookexec(hook, methods, kwargs)
INTERNALERROR>   File "/home/travis/miniconda3/envs/pandas-dev/lib/python3.7/site-packages/pluggy/manager.py", line 61, in <lambda>
INTERNALERROR>     firstresult=hook.spec.opts.get("firstresult") if hook.spec else False,
INTERNALERROR>   File "/home/travis/miniconda3/envs/pandas-dev/lib/python3.7/site-packages/pluggy/callers.py", line 208, in _multicall
INTERNALERROR>     return outcome.get_result()
INTERNALERROR>   File "/home/travis/miniconda3/envs/pandas-dev/lib/python3.7/site-packages/pluggy/callers.py", line 80, in get_result
INTERNALERROR>     raise ex[1].with_traceback(ex[2])
INTERNALERROR>   File "/home/travis/miniconda3/envs/pandas-dev/lib/python3.7/site-packages/pluggy/callers.py", line 187, in _multicall
INTERNALERROR>     res = hook_impl.function(*args)
INTERNALERROR>   File "<remote exec>", line 66, in pytest_runtestloop
INTERNALERROR>   File "<remote exec>", line 83, in run_one_test
INTERNALERROR>   File "/home/travis/miniconda3/envs/pandas-dev/lib/python3.7/site-packages/pluggy/hooks.py", line 284, in __call__
INTERNALERROR>     return self._hookexec(self, self.get_hookimpls(), kwargs)
INTERNALERROR>   File "/home/travis/miniconda3/envs/pandas-dev/lib/python3.7/site-packages/pluggy/manager.py", line 67, in _hookexec
INTERNALERROR>     return self._inner_hookexec(hook, methods, kwargs)
INTERNALERROR>   File "/home/travis/miniconda3/envs/pandas-dev/lib/python3.7/site-packages/pluggy/manager.py", line 61, in <lambda>
INTERNALERROR>     firstresult=hook.spec.opts.get("firstresult") if hook.spec else False,
INTERNALERROR>   File "/home/travis/miniconda3/envs/pandas-dev/lib/python3.7/site-packages/pluggy/callers.py", line 203, in _multicall
INTERNALERROR>     gen.send(outcome)
INTERNALERROR>   File "/home/travis/miniconda3/envs/pandas-dev/lib/python3.7/site-packages/_pytest/warnings.py", line 142, in pytest_runtest_protocol
INTERNALERROR>     yield
INTERNALERROR>   File "/home/travis/miniconda3/envs/pandas-dev/lib/python3.7/contextlib.py", line 119, in __exit__
INTERNALERROR>     next(self.gen)
INTERNALERROR>   File "/home/travis/miniconda3/envs/pandas-dev/lib/python3.7/site-packages/_pytest/warnings.py", line 101, in catch_warnings_for_item
INTERNALERROR>     kwargs=dict(warning_message=warning_message, when=when, item=item)
INTERNALERROR>   File "/home/travis/miniconda3/envs/pandas-dev/lib/python3.7/site-packages/pluggy/hooks.py", line 306, in call_historic
INTERNALERROR>     res = self._hookexec(self, self.get_hookimpls(), kwargs)
INTERNALERROR>   File "/home/travis/miniconda3/envs/pandas-dev/lib/python3.7/site-packages/pluggy/manager.py", line 67, in _hookexec
INTERNALERROR>     return self._inner_hookexec(hook, methods, kwargs)
INTERNALERROR>   File "/home/travis/miniconda3/envs/pandas-dev/lib/python3.7/site-packages/pluggy/manager.py", line 61, in <lambda>
INTERNALERROR>     firstresult=hook.spec.opts.get("firstresult") if hook.spec else False,
INTERNALERROR>   File "/home/travis/miniconda3/envs/pandas-dev/lib/python3.7/site-packages/pluggy/callers.py", line 208, in _multicall
INTERNALERROR>     return outcome.get_result()
INTERNALERROR>   File "/home/travis/miniconda3/envs/pandas-dev/lib/python3.7/site-packages/pluggy/callers.py", line 80, in get_result
INTERNALERROR>     raise ex[1].with_traceback(ex[2])
INTERNALERROR>   File "/home/travis/miniconda3/envs/pandas-dev/lib/python3.7/site-packages/pluggy/callers.py", line 187, in _multicall
INTERNALERROR>     res = hook_impl.function(*args)
INTERNALERROR>   File "<remote exec>", line 136, in pytest_warning_captured
INTERNALERROR>   File "<remote exec>", line 30, in sendevent
INTERNALERROR>   File "/home/travis/miniconda3/envs/pandas-dev/lib/python3.7/site-packages/execnet/gateway_base.py", line 717, in send
INTERNALERROR>     self.gateway._send(Message.CHANNEL_DATA, self.id, dumps_internal(item))
INTERNALERROR>   File "/home/travis/miniconda3/envs/pandas-dev/lib/python3.7/site-packages/execnet/gateway_base.py", line 1354, in dumps_internal
INTERNALERROR>     return _Serializer().save(obj)
INTERNALERROR>   File "/home/travis/miniconda3/envs/pandas-dev/lib/python3.7/site-packages/execnet/gateway_base.py", line 1372, in save
INTERNALERROR>     self._save(obj)
INTERNALERROR>   File "/home/travis/miniconda3/envs/pandas-dev/lib/python3.7/site-packages/execnet/gateway_base.py", line 1390, in _save
INTERNALERROR>     dispatch(self, obj)
INTERNALERROR>   File "/home/travis/miniconda3/envs/pandas-dev/lib/python3.7/site-packages/execnet/gateway_base.py", line 1475, in save_tuple
INTERNALERROR>     self._save(item)
INTERNALERROR>   File "/home/travis/miniconda3/envs/pandas-dev/lib/python3.7/site-packages/execnet/gateway_base.py", line 1390, in _save
INTERNALERROR>     dispatch(self, obj)
INTERNALERROR>   File "/home/travis/miniconda3/envs/pandas-dev/lib/python3.7/site-packages/execnet/gateway_base.py", line 1471, in save_dict
INTERNALERROR>     self._write_setitem(key, value)
INTERNALERROR>   File "/home/travis/miniconda3/envs/pandas-dev/lib/python3.7/site-packages/execnet/gateway_base.py", line 1465, in _write_setitem
INTERNALERROR>     self._save(value)
INTERNALERROR>   File "/home/travis/miniconda3/envs/pandas-dev/lib/python3.7/site-packages/execnet/gateway_base.py", line 1390, in _save
INTERNALERROR>     dispatch(self, obj)
INTERNALERROR>   File "/home/travis/miniconda3/envs/pandas-dev/lib/python3.7/site-packages/execnet/gateway_base.py", line 1471, in save_dict
INTERNALERROR>     self._write_setitem(key, value)
INTERNALERROR>   File "/home/travis/miniconda3/envs/pandas-dev/lib/python3.7/site-packages/execnet/gateway_base.py", line 1465, in _write_setitem
INTERNALERROR>     self._save(value)
INTERNALERROR>   File "/home/travis/miniconda3/envs/pandas-dev/lib/python3.7/site-packages/execnet/gateway_base.py", line 1388, in _save
INTERNALERROR>     raise DumpError("can't serialize %s" % (tp,))
INTERNALERROR> execnet.gateway_base.DumpError: can't serialize <class 'ssl.SSLSocket'>
INTERNALERROR> Traceback (most recent call last):
INTERNALERROR>   File "/home/travis/miniconda3/envs/pandas-dev/lib/python3.7/site-packages/_pytest/main.py", line 185, in wrap_session
INTERNALERROR>     session.exitstatus = doit(config, session) or 0
INTERNALERROR>   File "/home/travis/miniconda3/envs/pandas-dev/lib/python3.7/site-packages/_pytest/main.py", line 225, in _main
INTERNALERROR>     config.hook.pytest_runtestloop(session=session)
INTERNALERROR>   File "/home/travis/miniconda3/envs/pandas-dev/lib/python3.7/site-packages/pluggy/hooks.py", line 284, in __call__
INTERNALERROR>     return self._hookexec(self, self.get_hookimpls(), kwargs)
INTERNALERROR>   File "/home/travis/miniconda3/envs/pandas-dev/lib/python3.7/site-packages/pluggy/manager.py", line 67, in _hookexec
INTERNALERROR>     return self._inner_hookexec(hook, methods, kwargs)
INTERNALERROR>   File "/home/travis/miniconda3/envs/pandas-dev/lib/python3.7/site-packages/pluggy/manager.py", line 61, in <lambda>
INTERNALERROR>     firstresult=hook.spec.opts.get("firstresult") if hook.spec else False,
INTERNALERROR>   File "/home/travis/miniconda3/envs/pandas-dev/lib/python3.7/site-packages/pluggy/callers.py", line 208, in _multicall
INTERNALERROR>     return outcome.get_result()
INTERNALERROR>   File "/home/travis/miniconda3/envs/pandas-dev/lib/python3.7/site-packages/pluggy/callers.py", line 80, in get_result
INTERNALERROR>     raise ex[1].with_traceback(ex[2])
INTERNALERROR>   File "/home/travis/miniconda3/envs/pandas-dev/lib/python3.7/site-packages/pluggy/callers.py", line 187, in _multicall
INTERNALERROR>     res = hook_impl.function(*args)
INTERNALERROR>   File "/home/travis/miniconda3/envs/pandas-dev/lib/python3.7/site-packages/xdist/dsession.py", line 115, in pytest_runtestloop
INTERNALERROR>     self.loop_once()
INTERNALERROR>   File "/home/travis/miniconda3/envs/pandas-dev/lib/python3.7/site-packages/xdist/dsession.py", line 138, in loop_once
INTERNALERROR>     call(**kwargs)
INTERNALERROR>   File "/home/travis/miniconda3/envs/pandas-dev/lib/python3.7/site-packages/xdist/dsession.py", line 180, in worker_workerfinished
INTERNALERROR>     assert not crashitem, (crashitem, node)
INTERNALERROR> AssertionError: ('pandas/tests/io/json/test_pandas.py::TestPandasContainer::test_default_handler_raises', <WorkerController gw1>)
INTERNALERROR> assert not 'pandas/tests/io/json/test_pandas.py::TestPandasContainer::test_default_handler_raises'
```

The plotting / pickle failures from https://travis-ci.org/pandas-dev/pandas/jobs/463373616 are fixed on master now. The ssl issue showed up in https://travis-ci.org/pandas-dev/pandas/jobs/463484629, so it's not just a one off. Taking a look... Well, the failure does involve `TestPandasContainer`, which only appears in `tests/io/json/test_pandas.py`, which was touched by my recent #24073. One way to test if that is responsible for the errors is just to skip that test.

I cannot grok the test error though. It seems to fail within `pytest` itself (as `execnet` is part of that https://github.com/pytest-dev/execnet). Nevermind, I just saw on closer look that this fails in `test_default_handler_raises`. Truth be told, I'm not sure this PR would help with the current failures. It doesn't fundamentally change the handling of the boto errors (it just narrows down which kinds of exceptions are caught), and even more so, the failure in (e.g. https://travis-ci.org/pandas-dev/pandas/jobs/463496968) is in the `pytest.importorskip('boto.plugin')` itself... (which is itself a band-aid, sheesh...) @TomAugspurger 
One error in https://travis-ci.org/pandas-dev/pandas/jobs/463508130 is:
```execnet.gateway_base.DumpError: can't serialize <class 'ssl.SSLSocket'>```
This might be ever so vaguely related: pytest-dev/pytest-xdist#364

Could be that pytest stumbles over something it cant serialize?

Other observations from the 3 failed CI jobs here:
* `travis-37` uses `moto 1.3.7` and crashes completely, see https://travis-ci.org/pandas-dev/pandas/jobs/463508130. Note that this PR added boto coverage to this job.
* `travis-27` uses `moto 1.3.4` and errors once(!) on `moto = pytest.importorskip('moto')` (which then tries to import `boto.plugin`, I think), see https://travis-ci.org/pandas-dev/pandas/jobs/463508132
* `travis-36` uses `moto 1.3.6` and errors 21 times, mainly(?) on `moto = pytest.importorskip('moto')`, see https://travis-ci.org/pandas-dev/pandas/jobs/463508133

 My other PR introduced a merge conflict, sorry. > My other PR introduced a merge conflict, sorry.

Completely normal part of developing. ;-) Failure is (thankfully!) just a flaky hypothesis test. Could someone restart please? (@TomAugspurger? @gfyoung?) @jreback @TomAugspurger 

I was finally able to finish this off for good, as well as the moto compat issue (with the help of an open moto PR), see OP.

PTAL (the failure is a timeout on azure). **DO NOT MERGE**

Nevermind, I don't think the tests are actually being run at the moment, due to the *other* moto error about importing `boto.plugin`.  Argggggh....