REF: Consistent optional dependency handling
State: closed

Standardizes how we do our optional dependency checking.

1. Adds a new `import_optional_dependency` helper that handles optional importing, raising with a nice message, and warning / raising if the version is not correct.
2. Changes inline `try / except ImportError` to use `import_optional_dependency`
3. Documents all(?) optional dependencies with their minimum version.
4. Documents that this is how devs should do optional dependencies. 

```pytb
>>> pd.read_gbq('')
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/Users/taugspurger/sandbox/pandas/pandas/io/gbq.py", line 140, in read_gbq
    pandas_gbq = _try_import()
  File "/Users/taugspurger/sandbox/pandas/pandas/io/gbq.py", line 14, in _try_import
    extra=msg,
  File "/Users/taugspurger/sandbox/pandas/pandas/compat/_optional.py", line 82, in import_optional_dependency
    raise ImportError(message.format(name=name, extra=extra)) from None
ImportError: Missing optional dependency 'pandas_gbq'. pandas-gbq is required to load data from Google BigQuery. See the docs: https://pandas-gbq.readthedocs.io. Use pip or conda to install pandas_gbq.
```

Labels:
name: Refactor
description: Internal refactoring of code

Comments:
This is a great idea! Just to be clear did you purposely only select a subset of optional dependencies to update? I opened #26782 which could either be included here or done as a follow up
> did you purposely only select a subset of optional dependencies to update?

I grepped for `except ImportError`s and did the ones that didn't look too complex (I think `sql.py` was a bit strange).

I seem to have just missed the xlrd one. Do you want me to handle it here?
Looks like I missed bottleneck too, sorry.
> I seem to have just missed the xlrd one. Do you want me to handle it here?

Up to you
# [Codecov](https://codecov.io/gh/pandas-dev/pandas/pull/26802?src=pr&el=h1) Report
> Merging [#26802](https://codecov.io/gh/pandas-dev/pandas/pull/26802?src=pr&el=desc) into [master](https://codecov.io/gh/pandas-dev/pandas/commit/429078b01af47b356084c1da270b5b275fbf50f4?src=pr&el=desc) will **increase** coverage by `0.08%`.
> The diff coverage is `91.75%`.

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

```diff
@@            Coverage Diff             @@
##           master   #26802      +/-   ##
==========================================
+ Coverage   91.76%   91.85%   +0.08%     
==========================================
  Files         178      179       +1     
  Lines       50751    50707      -44     
==========================================
+ Hits        46574    46579       +5     
+ Misses       4177     4128      -49
```

| Flag | Coverage Δ | |
|---|---|---|
| #multiple | `90.45% <91.75%> (+0.09%)` | :arrow_up: |
| #single | `41.1% <48.45%> (-0.02%)` | :arrow_down: |

| [Impacted Files](https://codecov.io/gh/pandas-dev/pandas/pull/26802?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [pandas/io/formats/style.py](https://codecov.io/gh/pandas-dev/pandas/pull/26802/diff?src=pr&el=tree#diff-cGFuZGFzL2lvL2Zvcm1hdHMvc3R5bGUucHk=) | `97.14% <100%> (+0.46%)` | :arrow_up: |
| [pandas/core/missing.py](https://codecov.io/gh/pandas-dev/pandas/pull/26802/diff?src=pr&el=tree#diff-cGFuZGFzL2NvcmUvbWlzc2luZy5weQ==) | `95.75% <100%> (+1.8%)` | :arrow_up: |
| [pandas/io/html.py](https://codecov.io/gh/pandas-dev/pandas/pull/26802/diff?src=pr&el=tree#diff-cGFuZGFzL2lvL2h0bWwucHk=) | `94.29% <100%> (+1.62%)` | :arrow_up: |
| [pandas/core/generic.py](https://codecov.io/gh/pandas-dev/pandas/pull/26802/diff?src=pr&el=tree#diff-cGFuZGFzL2NvcmUvZ2VuZXJpYy5weQ==) | `94.19% <100%> (+0.09%)` | :arrow_up: |
| [pandas/compat/\_optional.py](https://codecov.io/gh/pandas-dev/pandas/pull/26802/diff?src=pr&el=tree#diff-cGFuZGFzL2NvbXBhdC9fb3B0aW9uYWwucHk=) | `100% <100%> (ø)` | |
| [pandas/io/s3.py](https://codecov.io/gh/pandas-dev/pandas/pull/26802/diff?src=pr&el=tree#diff-cGFuZGFzL2lvL3MzLnB5) | `100% <100%> (+10.52%)` | :arrow_up: |
| [pandas/core/nanops.py](https://codecov.io/gh/pandas-dev/pandas/pull/26802/diff?src=pr&el=tree#diff-cGFuZGFzL2NvcmUvbmFub3BzLnB5) | `94.76% <100%> (+0.64%)` | :arrow_up: |
| [pandas/core/window.py](https://codecov.io/gh/pandas-dev/pandas/pull/26802/diff?src=pr&el=tree#diff-cGFuZGFzL2NvcmUvd2luZG93LnB5) | `96.63% <100%> (+0.23%)` | :arrow_up: |
| [pandas/io/gcs.py](https://codecov.io/gh/pandas-dev/pandas/pull/26802/diff?src=pr&el=tree#diff-cGFuZGFzL2lvL2djcy5weQ==) | `100% <100%> (+20%)` | :arrow_up: |
| [pandas/core/arrays/sparse.py](https://codecov.io/gh/pandas-dev/pandas/pull/26802/diff?src=pr&el=tree#diff-cGFuZGFzL2NvcmUvYXJyYXlzL3NwYXJzZS5weQ==) | `93.71% <100%> (+0.22%)` | :arrow_up: |
| ... and [11 more](https://codecov.io/gh/pandas-dev/pandas/pull/26802/diff?src=pr&el=tree-more) | |

------

[Continue to review full report at Codecov](https://codecov.io/gh/pandas-dev/pandas/pull/26802?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/26802?src=pr&el=footer). Last update [429078b...d04425d](https://codecov.io/gh/pandas-dev/pandas/pull/26802?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).

small comment, otherwise lgtm.
Great change @TomAugspurger !
actually @TomAugspurger see my comments. This duplicates a ton of code in _print_versions. Can you (or others) do a followup (or create an issue) about de-duplicating.
see #26814
FYI @pandas-dev/pandas-core you may want to glance through http://pandas-docs.github.io/pandas-docs-travis/development/contributing.html#optional-dependencies, when we get PRs adding a new optional dependency.
Nice!