Repository: scikit-hep/awkward

Issue:
`from_parquet` does not work with file-like objects
For example

```pycon
>>> import awkward as ak
>>> array = ak.Array([1, 2, 3])
>>> ak.to_parquet(array, "array.parquet")
>>> f = open("array.parquet", "rb")
>>> array2 = ak.from_parquet(f)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/nikolai/.local/lib/python3.9/site-packages/awkward/operations/convert.py", line 3566, in from_parquet
    single_file = pyarrow.parquet.ParquetFile(filename)
  File "/home/nikolai/.local/lib/python3.9/site-packages/pyarrow/parquet.py", line 217, in __init__
    self.reader.open(source, use_memory_map=memory_map,
  File "pyarrow/_parquet.pyx", line 947, in pyarrow._parquet.ParquetReader.open
  File "pyarrow/io.pxi", line 1473, in pyarrow.lib.get_reader
  File "pyarrow/io.pxi", line 1466, in pyarrow.lib.get_native_file
TypeError: Cannot convert bytes to pyarrow.lib.NativeFile
```

presumably because file-like objects are instances of `Iterable`, so it tries to loop over them here:

https://github.com/scikit-hep/awkward-1.0/blob/0fe77918e1fa828e539ff287153150d028decfda/src/awkward/operations/convert.py#L3616-L3617

```python
>>> import awkward as ak
>>> ak.__version__
'1.3.0'
```



Discussion:
Actually noticed the issue when i tried to use an http file-like object from `fsspec`, so #886 could be related

```
python -m http.server
```
```pycon
>>> import fsspec
>>> import awkward as ak
>>> of = fsspec.open("http://localhost:8000/array.parquet", "rb")
>>> f = of.open()
>>> array = ak.from_parquet(f)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/nikolai/.local/lib/python3.9/site-packages/awkward/operations/convert.py", line 3624, in from_parquet
    single_file = pyarrow.parquet.ParquetFile(filename)
  File "/home/nikolai/.local/lib/python3.9/site-packages/pyarrow/parquet.py", line 217, in __init__
    self.reader.open(source, use_memory_map=memory_map,
  File "pyarrow/_parquet.pyx", line 947, in pyarrow._parquet.ParquetReader.open
  File "pyarrow/io.pxi", line 1473, in pyarrow.lib.get_reader
  File "pyarrow/io.pxi", line 1466, in pyarrow.lib.get_native_file
TypeError: Cannot convert bytes to pyarrow.lib.NativeFile
```

Edit: actually the python http server would not work since it does not support byte ranges, but one could spawn an nginx or similar with docker, e.g.
```
docker run --rm --name nginx -v $(pwd):/usr/share/nginx/html:ro -p 8000:80 nginx
```
@nikoladze you're right that the current implementation *appears* to be unable to handle `File`-like objects due to that `Iterable` check, which was introduced in #706. 

Addressing this and #886 is going to take a little more time that I have available right now unfortunately. Is this something that you need and can't work around?


I'd like to run some tests with parquet files over http (also making use of streaming I/O, not downloading the file). For this it should be sufficient to have the file-like objects passed through correctly to `pyarrow.parquet.ParquetFile`.


Resolving patch:
diff --git a/src/awkward/operations/convert.py b/src/awkward/operations/convert.py
--- a/src/awkward/operations/convert.py
+++ b/src/awkward/operations/convert.py
@@ -3613,7 +3613,11 @@ def from_parquet(
             relative_to = source
             source = sorted(glob.glob(source + "/**/*.parquet", recursive=True))
 
-    if not isinstance(source, str) and isinstance(source, Iterable):
+    if (
+        not isinstance(source, str)
+        and isinstance(source, Iterable)
+        and not hasattr(source, "read")
+    ):
         source = [_regularize_path(x) for x in source]
         if relative_to is None:
             relative_to = os.path.commonpath(source)


Test patch:
diff --git a/tests/test_0921-parquet-from-file-like.py b/tests/test_0921-parquet-from-file-like.py
new file mode 100644
--- /dev/null
+++ b/tests/test_0921-parquet-from-file-like.py
@@ -0,0 +1,21 @@
+# BSD 3-Clause License; see https://github.com/scikit-hep/awkward-1.0/blob/main/LICENSE
+
+from __future__ import absolute_import
+
+import io
+import pytest  # noqa: F401
+import numpy as np  # noqa: F401
+import awkward as ak  # noqa: F401
+
+
+pytest.importorskip("pyarrow.parquet")
+
+
+def test():
+    array = ak.Array([1, 2, 3])
+    file_ = io.BytesIO()
+    ak.to_parquet(array, file_)
+    file_.seek(0)
+
+    array_from_file = ak.from_parquet(file_)
+    assert ak.to_list(array) == ak.to_list(array_from_file)