API: Relax is-file-like conditions
State: closed

Previously, we were requiring that all file-like objects had "read," "write," "seek," and "tell" methods,
but that was too strict (e.g. read-only buffers). This commit relaxes those requirements to having EITHER "read" or "write" as attributes.

Closes #16135.

Labels:
name: IO CSV
description: read_csv, to_csv

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

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

```diff
@@            Coverage Diff             @@
##           master   #16150      +/-   ##
==========================================
+ Coverage   90.83%   90.84%   +<.01%     
==========================================
  Files         159      159              
  Lines       50796    50794       -2     
==========================================
- Hits        46143    46142       -1     
+ Misses       4653     4652       -1
```

| Flag | Coverage Δ | |
|---|---|---|
| #multiple | `88.62% <100%> (ø)` | :arrow_up: |
| #single | `40.31% <50%> (-0.01%)` | :arrow_down: |

| [Impacted Files](https://codecov.io/gh/pandas-dev/pandas/pull/16150?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [pandas/core/dtypes/inference.py](https://codecov.io/gh/pandas-dev/pandas/pull/16150?src=pr&el=tree#diff-cGFuZGFzL2NvcmUvZHR5cGVzL2luZmVyZW5jZS5weQ==) | `98.33% <100%> (-0.06%)` | :arrow_down: |
| [pandas/core/common.py](https://codecov.io/gh/pandas-dev/pandas/pull/16150?src=pr&el=tree#diff-cGFuZGFzL2NvcmUvY29tbW9uLnB5) | `91.03% <0%> (+0.34%)` | :arrow_up: |

------

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

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

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

```diff
@@            Coverage Diff             @@
##           master   #16150      +/-   ##
==========================================
+ Coverage   90.83%   90.84%   +<.01%     
==========================================
  Files         159      159              
  Lines       50796    50794       -2     
==========================================
- Hits        46143    46142       -1     
+ Misses       4653     4652       -1
```

| Flag | Coverage Δ | |
|---|---|---|
| #multiple | `88.62% <100%> (ø)` | :arrow_up: |
| #single | `40.31% <50%> (-0.01%)` | :arrow_down: |

| [Impacted Files](https://codecov.io/gh/pandas-dev/pandas/pull/16150?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [pandas/core/dtypes/inference.py](https://codecov.io/gh/pandas-dev/pandas/pull/16150?src=pr&el=tree#diff-cGFuZGFzL2NvcmUvZHR5cGVzL2luZmVyZW5jZS5weQ==) | `98.33% <100%> (-0.06%)` | :arrow_down: |
| [pandas/core/common.py](https://codecov.io/gh/pandas-dev/pandas/pull/16150?src=pr&el=tree#diff-cGFuZGFzL2NvcmUvY29tbW9uLnB5) | `91.03% <0%> (+0.34%)` | :arrow_up: |

------

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

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

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

```diff
@@            Coverage Diff             @@
##           master   #16150      +/-   ##
==========================================
+ Coverage   90.85%   90.88%   +0.02%     
==========================================
  Files         159      159              
  Lines       50785    50783       -2     
==========================================
+ Hits        46141    46152      +11     
+ Misses       4644     4631      -13
```

| Flag | Coverage Δ | |
|---|---|---|
| #multiple | `88.66% <100%> (+0.02%)` | :arrow_up: |
| #single | `40.32% <50%> (-0.01%)` | :arrow_down: |

| [Impacted Files](https://codecov.io/gh/pandas-dev/pandas/pull/16150?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [pandas/core/dtypes/inference.py](https://codecov.io/gh/pandas-dev/pandas/pull/16150?src=pr&el=tree#diff-cGFuZGFzL2NvcmUvZHR5cGVzL2luZmVyZW5jZS5weQ==) | `98.33% <100%> (-0.06%)` | :arrow_down: |
| [pandas/core/indexes/datetimes.py](https://codecov.io/gh/pandas-dev/pandas/pull/16150?src=pr&el=tree#diff-cGFuZGFzL2NvcmUvaW5kZXhlcy9kYXRldGltZXMucHk=) | `95.43% <0%> (+0.09%)` | :arrow_up: |
| [pandas/plotting/\_converter.py](https://codecov.io/gh/pandas-dev/pandas/pull/16150?src=pr&el=tree#diff-cGFuZGFzL3Bsb3R0aW5nL19jb252ZXJ0ZXIucHk=) | `65.35% <0%> (+1.81%)` | :arrow_up: |

------

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

``my_s3_object = boto3.client('s3').get_object(Bucket=<S3 bucket>, Key=<S3 path>)
pd.read_csv(my_s3_object['Body'])``

should work with one of the s3 buckets (.e.g. ``s3://pandas-test/tips.csv``), can you add a test as well (should fail prior to this PR, and work with). 
@jreback : Just to make sure I got it right, it's:

Bucket = "pandas-test"
Key = "pandas-test/tips.csv" (or is it just "/tips.csv", don't remember)

Also, do we install `boto3` during testing?  I don't see it explicitly in the codebase.
we *already* have s3 tests just not streaming ones.

```
(pandas) bash-3.2$ grep -R s3 pandas/tests/io/parser/
Binary file pandas/tests/io/parser//__pycache__/test_network.cpython-36-PYTEST.pyc matches
pandas/tests/io/parser//test_network.py:            import s3fs  # noqa
pandas/tests/io/parser//test_network.py:            pytest.skip("s3fs not installed")
pandas/tests/io/parser//test_network.py:    def test_parse_public_s3_bucket(self):
pandas/tests/io/parser//test_network.py:            df = read_csv('s3://pandas-test/tips.csv' +
pandas/tests/io/parser//test_network.py:        df = read_csv('s3://cant_get_it/tips.csv')
pandas/tests/io/parser//test_network.py:    def test_parse_public_s3n_bucket(self):
pandas/tests/io/parser//test_network.py:        # Read from AWS s3 as "s3n" URL
pandas/tests/io/parser//test_network.py:        df = read_csv('s3n://pandas-test/tips.csv', nrows=10)
pandas/tests/io/parser//test_network.py:    def test_parse_public_s3a_bucket(self):
pandas/tests/io/parser//test_network.py:        # Read from AWS s3 as "s3a" URL
pandas/tests/io/parser//test_network.py:        df = read_csv('s3a://pandas-test/tips.csv', nrows=10)
pandas/tests/io/parser//test_network.py:    def test_parse_public_s3_bucket_nrows(self):
pandas/tests/io/parser//test_network.py:            df = read_csv('s3://pandas-test/tips.csv' +
pandas/tests/io/parser//test_network.py:    def test_parse_public_s3_bucket_chunked(self):
pandas/tests/io/parser//test_network.py:            df_reader = read_csv('s3://pandas-test/tips.csv' + ext,
pandas/tests/io/parser//test_network.py:    def test_parse_public_s3_bucket_chunked_python(self):
pandas/tests/io/parser//test_network.py:            df_reader = read_csv('s3://pandas-test/tips.csv' + ext,
pandas/tests/io/parser//test_network.py:    def test_parse_public_s3_bucket_python(self):
pandas/tests/io/parser//test_network.py:            df = read_csv('s3://pandas-test/tips.csv' + ext, engine='python',
pandas/tests/io/parser//test_network.py:    def test_infer_s3_compression(self):
pandas/tests/io/parser//test_network.py:            df = read_csv('s3://pandas-test/tips.csv' + ext,
pandas/tests/io/parser//test_network.py:    def test_parse_public_s3_bucket_nrows_python(self):
pandas/tests/io/parser//test_network.py:            df = read_csv('s3://pandas-test/tips.csv' + ext, engine='python',
pandas/tests/io/parser//test_network.py:    def test_s3_fails(self):
pandas/tests/io/parser//test_network.py:            read_csv('s3://nyqpug/asdf.csv')
pandas/tests/io/parser//test_network.py:            read_csv('s3://cant_get_it/')
```
you can import ``boto3`` its a dependency of ``s3fs`` which makes a nicer layer on top.
I was asking about the proper syntax for calling `get_object` (i.e. what are the bucket and key names exactly just to make sure).
bucket is ``pandas-test``, path is ``\tip.csv`` I think.
@jreback : `boto` test has been added, and everything is green.
ok then, thanks!