diff --git a/swh/fuse/fs/artifact.py b/swh/fuse/fs/artifact.py --- a/swh/fuse/fs/artifact.py +++ b/swh/fuse/fs/artifact.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020 The Software Heritage developers +# Copyright (C) 2020-2021 The Software Heritage developers # See the AUTHORS file at the top-level directory of this distribution # License: GNU General Public License version 3, or any later version # See top-level LICENSE file for more information @@ -10,7 +10,7 @@ import os from pathlib import Path import re -from typing import Any, AsyncIterator, Dict, List +from typing import Any, AsyncIterator, Dict, List, Optional, cast from swh.fuse.fs.entry import ( EntryMode, @@ -90,7 +90,7 @@ # 1. Symlink (check symlink first because condition is less restrictive) if mode == DentryPerms.symlink: - target = "" + target = b"" try: # Symlink target is stored in the blob content target = await self.fuse.get_blob(swhid) @@ -238,11 +238,14 @@ self.fuse.cache.direntry.invalidate(by_date_dir) async def compute_entries(self) -> AsyncIterator[FuseEntry]: - by_date_dir = self.create_child( + by_date_dir = cast( RevisionHistoryShardByDate, - name="by-date", - mode=int(EntryMode.RDONLY_DIR), - history_swhid=self.swhid, + self.create_child( + RevisionHistoryShardByDate, + name="by-date", + mode=int(EntryMode.RDONLY_DIR), + history_swhid=self.swhid, + ), ) # Run it concurrently because of the many API calls necessary @@ -391,7 +394,7 @@ """ Revision virtual `history/by-page` sharded directory """ history_swhid: SWHID - prefix: int = field(default=None) + prefix: Optional[int] = field(default=None) PAGE_SIZE = 10_000 PAGE_FMT = "{page_number:03d}" @@ -444,7 +447,7 @@ swhid: SWHID - async def find_root_directory(self, swhid: SWHID) -> SWHID: + async def find_root_directory(self, swhid: SWHID) -> Optional[SWHID]: if swhid.object_type == RELEASE: metadata = await self.fuse.get_metadata(swhid) return await self.find_root_directory(metadata["target"]) diff --git a/swh/fuse/fs/entry.py b/swh/fuse/fs/entry.py --- a/swh/fuse/fs/entry.py +++ b/swh/fuse/fs/entry.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020 The Software Heritage developers +# Copyright (C) 2020-2021 The Software Heritage developers # See the AUTHORS file at the top-level directory of this distribution # License: GNU General Public License version 3, or any later version # See top-level LICENSE file for more information @@ -10,10 +10,10 @@ from pathlib import Path import re from stat import S_IFDIR, S_IFLNK, S_IFREG -from typing import Any, AsyncIterator, Dict, Optional, Pattern, Sequence, Union +from typing import TYPE_CHECKING, Any, AsyncIterator, Dict, Optional, Pattern, Union -# Avoid cycling import -Fuse = "Fuse" +if TYPE_CHECKING: # avoid cyclic import + from swh.fuse.fuse import Fuse class EntryMode(IntEnum): @@ -98,11 +98,11 @@ expression, and false otherwise """ if self.ENTRIES_REGEXP: - return re.match(self.ENTRIES_REGEXP, name) + return bool(re.match(self.ENTRIES_REGEXP, name)) else: return True - async def compute_entries(self) -> Sequence[FuseEntry]: + async def compute_entries(self): """ Return the child entries of a directory entry """ raise NotImplementedError @@ -122,7 +122,7 @@ for i in range(offset, len(entries)): yield entries[i] - async def lookup(self, name: str) -> FuseEntry: + async def lookup(self, name: str) -> Optional[FuseEntry]: """ Look up a FUSE entry by name """ async for entry in self.get_entries(): diff --git a/swh/fuse/fs/mountpoint.py b/swh/fuse/fs/mountpoint.py --- a/swh/fuse/fs/mountpoint.py +++ b/swh/fuse/fs/mountpoint.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020 The Software Heritage developers +# Copyright (C) 2020-2021 The Software Heritage developers # See the AUTHORS file at the top-level directory of this distribution # License: GNU General Public License version 3, or any later version # See top-level LICENSE file for more information @@ -7,7 +7,7 @@ import json from pathlib import Path import re -from typing import AsyncIterator +from typing import AsyncIterator, Optional from swh.fuse.fs.artifact import OBJTYPE_GETTERS, SWHID_REGEXP, Origin from swh.fuse.fs.entry import ( @@ -27,7 +27,7 @@ class Root(FuseDirEntry): """ The FUSE mountpoint, consisting of the archive/ and origin/ directories """ - name: str = field(init=False, default=None) + name: str = field(init=False, default="") mode: int = field(init=False, default=int(EntryMode.RDONLY_DIR)) depth: int = field(init=False, default=1) @@ -56,7 +56,7 @@ return yield - async def lookup(self, name: str) -> FuseEntry: + async def lookup(self, name: str) -> Optional[FuseEntry]: # On the fly mounting of a new artifact try: if name.endswith(JSON_SUFFIX): @@ -114,16 +114,16 @@ ENTRIES_REGEXP = re.compile(r"^.*%3A.*$") # %3A is the encoded version of ':' - def create_child(self, url_encoded: str) -> FuseEntry: + def create_origin_child(self, url_encoded: str) -> FuseEntry: return super().create_child( Origin, name=url_encoded, mode=int(EntryMode.RDONLY_DIR), ) async def compute_entries(self) -> AsyncIterator[FuseEntry]: async for url in self.fuse.cache.get_cached_visits(): - yield self.create_child(url) + yield self.create_origin_child(url) - async def lookup(self, name: str) -> FuseEntry: + async def lookup(self, name: str) -> Optional[FuseEntry]: entry = await super().lookup(name) if entry: return entry @@ -132,7 +132,7 @@ try: url_encoded = name await self.fuse.get_visits(url_encoded) - return self.create_child(url_encoded) + return self.create_origin_child(url_encoded) except ValueError: return None diff --git a/swh/fuse/fuse.py b/swh/fuse/fuse.py --- a/swh/fuse/fuse.py +++ b/swh/fuse/fuse.py @@ -1,4 +1,4 @@ -# Copyright (C) 2020 The Software Heritage developers +# Copyright (C) 2020-2021 The Software Heritage developers # See the AUTHORS file at the top-level directory of this distribution # License: GNU General Public License version 3, or any later version # See top-level LICENSE file for more information @@ -29,15 +29,15 @@ """ Software Heritage Filesystem in Userspace (FUSE). Locally mount parts of the archive and navigate it as a virtual file system. """ - def __init__( - self, root_path: Path, cache: FuseCache, conf: Dict[str, Any], - ): + def __init__(self, root_path: Path, cache: FuseCache, conf: Dict[str, Any]): super(Fuse, self).__init__() self._next_inode: int = pyfuse3.ROOT_INODE self._inode2entry: Dict[int, FuseEntry] = {} - self.root = Root(fuse=self) + # The fuse constructor keyword is propagated up to FuseEntry dataclass, but mypy + # 0.812 considers it an unexpected kwarg. Skip typing it. + self.root = Root(fuse=self) # type: ignore self.conf = conf self.logger = logging.getLogger(LOGGER_NAME)