emmental/emmental/path.py
Anna Schumaker ad3d4840e8 path: Add a readdir_async() function
This function creates a ReaddirThread object to read the directory in
a secondary thread. This will let us poll for results without hanging the
UI in the case where a music library is on very slow storage (such as
NFS).

The ReaddirThread will accumulate a list of files that can be polled
using the poll_result() function, which will return the files found since
the last call to poll_result().

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-04-12 10:42:14 -04:00

61 lines
1.7 KiB
Python

# Copyright 2022 (c) Anna Schumaker.
"""Extra path handling for URIs."""
import pathlib
import threading
import urllib
class ReaddirThread(threading.Thread):
"""An object to manager asynchronous tree iteration."""
def __init__(self, directory: pathlib.Path):
"""Initialize an IterTreeResult."""
super().__init__()
self.root = directory
self._files = []
self._lock = threading.Lock()
self._stop_event = threading.Event()
def __read_directory(self, directory: pathlib.Path) -> None:
if self._stop_event.is_set():
return
for path in directory.iterdir():
if path.is_dir():
self.__read_directory(path)
else:
with self._lock:
self._files.append(path)
def poll_result(self) -> list[pathlib.Path] | None:
"""Poll for the result of the IterTreeThread."""
with self._lock:
if self.is_alive() or len(self._files):
files = self._files
self._files = []
return files
return None
def run(self) -> None:
"""Run the IterTreeThread."""
self.__read_directory(self.root)
def stop(self) -> None:
"""Signal the thread to stop."""
self._stop_event.set()
def readdir_async(directory: pathlib.Path) -> ReaddirThread:
"""Iterate through a directory tree asynchronously."""
if directory.is_dir():
res = ReaddirThread(directory)
res.start()
return res
def from_uri(uri: str) -> pathlib.Path:
"""Make a path from a uri."""
if parsed := urllib.parse.urlparse(uri):
return pathlib.Path(urllib.parse.unquote(parsed.path))
return pathlib.Path(uri)