Repository: fsspec/filesystem_spec

Issue:
`LocalFileSystem.ls()` does not work on filenames
Repro:

```
fs = LocalFileSystem()

fs.touch("a.json")
fs.ls("a.json")
```

I expected this to return a single-entry list with the stat object for `a.json`, but instead it raises a `NotADirectoryError` because `os.scandir()` does not allow file-like inputs:

https://github.com/fsspec/filesystem_spec/blob/069e107fa056166fd4e7db75e909ebac85f372fd/fsspec/implementations/local.py#L63-L64

In contrast, a subsequent `ls -la a.json` behaves as expected, showing the single file stat result.

Discussion:
martindurant: After some thought, it would be a better idea not to postentially call, exists(), isdir(), scandir()/info() one after the other. Perhaps info() once at the start (which would raise if no such file or directory), followed by scandir() if needed?
nicholasjng: > After some thought, it would be a better idea not to postentially call, exists(), isdir(), scandir()/info() one after the other. Perhaps info() once at the start (which would raise if no such file or directory), followed by scandir() if needed?

Sure, give me a second.

Patch:
diff --git a/fsspec/implementations/local.py b/fsspec/implementations/local.py
index e7861e2..17f96c1 100644
--- a/fsspec/implementations/local.py
+++ b/fsspec/implementations/local.py
@@ -3,7 +3,6 @@ import io
 import logging
 import os
 import os.path as osp
-import posixpath
 import re
 import shutil
 import stat
@@ -59,11 +58,16 @@ class LocalFileSystem(AbstractFileSystem):
 
     def ls(self, path, detail=False, **kwargs):
         path = self._strip_protocol(path)
-        if detail:
+        info = self.info(path)
+        if info["type"] == "directory":
             with os.scandir(path) as it:
-                return [self.info(f) for f in it]
+                infos = [self.info(f) for f in it]
         else:
-            return [posixpath.join(path, f) for f in os.listdir(path)]
+            infos = [info]
+
+        if not detail:
+            return [i["name"] for i in infos]
+        return infos
 
     def info(self, path, **kwargs):
         if isinstance(path, os.DirEntry):


Test patch:
diff --git a/fsspec/implementations/tests/test_local.py b/fsspec/implementations/tests/test_local.py
index 2980eb7..ffa5cbb 100644
--- a/fsspec/implementations/tests/test_local.py
+++ b/fsspec/implementations/tests/test_local.py
@@ -367,6 +367,16 @@ def test_directories(tmpdir):
     assert fs.ls(fs.root_marker)
 
 
+def test_ls_on_file(tmpdir):
+    tmpdir = make_path_posix(str(tmpdir))
+    fs = LocalFileSystem()
+    resource = tmpdir + "/a.json"
+    fs.touch(resource)
+    assert fs.exists(resource)
+    assert fs.ls(tmpdir) == fs.ls(resource)
+    assert fs.ls(resource, detail=True)[0] == fs.info(resource)
+
+
 @pytest.mark.parametrize("file_protocol", ["", "file://"])
 def test_file_ops(tmpdir, file_protocol):
     tmpdir = make_path_posix(str(tmpdir))