Changeset View
Changeset View
Standalone View
Standalone View
swh/fuse/fs/mountpoint.py
# Copyright (C) 2020 The Software Heritage developers | # Copyright (C) 2020 The Software Heritage developers | ||||
# See the AUTHORS file at the top-level directory of this distribution | # See the AUTHORS file at the top-level directory of this distribution | ||||
# License: GNU General Public License version 3, or any later version | # License: GNU General Public License version 3, or any later version | ||||
# See top-level LICENSE file for more information | # See top-level LICENSE file for more information | ||||
from dataclasses import dataclass, field | |||||
import json | import json | ||||
from typing import AsyncIterator | from typing import AsyncIterator | ||||
from swh.fuse.fs.artifact import typify | from swh.fuse.fs.artifact import OBJTYPE_GETTERS | ||||
from swh.fuse.fs.entry import EntryMode, FuseEntry | from swh.fuse.fs.entry import EntryMode, FuseEntry | ||||
from swh.model.identifiers import CONTENT, SWHID | from swh.model.identifiers import CONTENT, SWHID | ||||
# Avoid cycling import | |||||
Fuse = "Fuse" | |||||
@dataclass | |||||
class Root(FuseEntry): | class Root(FuseEntry): | ||||
""" The FUSE mountpoint, consisting of the archive/ and meta/ directories """ | """ The FUSE mountpoint, consisting of the archive/ and meta/ directories """ | ||||
def __init__(self, fuse: Fuse): | name: str = field(init=False, default="root") | ||||
super().__init__(name="root", mode=int(EntryMode.RDONLY_DIR), fuse=fuse) | mode: int = field(init=False, default=int(EntryMode.RDONLY_DIR)) | ||||
depth: int = field(init=False, default=1) | |||||
zack: Is the name property on the root entry ever needed/accessed? I guess/hope not.
Either way, it'd… | |||||
Done Inline ActionsThis property is never accessed on the root node indeed, we can make it None. haltode: This property is never accessed on the root node indeed, we can make it `None`. | |||||
async def __aiter__(self) -> AsyncIterator[FuseEntry]: | async def __aiter__(self) -> AsyncIterator[FuseEntry]: | ||||
for entry in [ArchiveDir(self.fuse), MetaDir(self.fuse)]: | entries = [self.create_child(ArchiveDir), self.create_child(MetaDir)] | ||||
for entry in entries: | |||||
yield entry | yield entry | ||||
Done Inline ActionsAgain, don't create an intermediate list seirl: Again, don't create an intermediate list | |||||
@dataclass | |||||
class ArchiveDir(FuseEntry): | class ArchiveDir(FuseEntry): | ||||
""" The archive/ directory is lazily populated with one entry per accessed | """ The archive/ directory is lazily populated with one entry per accessed | ||||
SWHID, having actual SWHIDs as names """ | SWHID, having actual SWHIDs as names """ | ||||
def __init__(self, fuse: Fuse): | name: str = field(init=False, default="archive") | ||||
super().__init__(name="archive", mode=int(EntryMode.RDONLY_DIR), fuse=fuse) | mode: int = field(init=False, default=int(EntryMode.RDONLY_DIR)) | ||||
async def __aiter__(self) -> AsyncIterator[FuseEntry]: | async def __aiter__(self) -> AsyncIterator[FuseEntry]: | ||||
async for swhid in self.fuse.cache.get_cached_swhids(): | async for swhid in self.fuse.cache.get_cached_swhids(): | ||||
if swhid.object_type == CONTENT: | if swhid.object_type == CONTENT: | ||||
mode = EntryMode.RDONLY_FILE | mode = EntryMode.RDONLY_FILE | ||||
else: | else: | ||||
mode = EntryMode.RDONLY_DIR | mode = EntryMode.RDONLY_DIR | ||||
yield typify(str(swhid), int(mode), self.fuse, swhid) | yield self.create_child( | ||||
OBJTYPE_GETTERS[swhid.object_type], | |||||
name=str(swhid), | |||||
mode=int(mode), | |||||
swhid=swhid, | |||||
) | |||||
@dataclass | |||||
class MetaDir(FuseEntry): | class MetaDir(FuseEntry): | ||||
""" The meta/ directory contains one SWHID.json file for each SWHID entry | """ The meta/ directory contains one SWHID.json file for each SWHID entry | ||||
under archive/. The JSON file contain all available meta information about | under archive/. The JSON file contain all available meta information about | ||||
the given SWHID, as returned by the Software Heritage Web API for that | the given SWHID, as returned by the Software Heritage Web API for that | ||||
object. Note that, in case of pagination (e.g., snapshot objects with many | object. Note that, in case of pagination (e.g., snapshot objects with many | ||||
branches) the JSON file will contain a complete version with all pages | branches) the JSON file will contain a complete version with all pages | ||||
merged together. """ | merged together. """ | ||||
def __init__(self, fuse: Fuse): | name: str = field(init=False, default="meta") | ||||
super().__init__(name="meta", mode=int(EntryMode.RDONLY_DIR), fuse=fuse) | mode: int = field(init=False, default=int(EntryMode.RDONLY_DIR)) | ||||
async def __aiter__(self) -> AsyncIterator[FuseEntry]: | async def __aiter__(self) -> AsyncIterator[FuseEntry]: | ||||
async for swhid in self.fuse.cache.get_cached_swhids(): | async for swhid in self.fuse.cache.get_cached_swhids(): | ||||
yield MetaEntry(swhid, self.fuse) | yield self.create_child( | ||||
MetaEntry, | |||||
name=f"{swhid}.json", | |||||
mode=int(EntryMode.RDONLY_FILE), | |||||
swhid=swhid, | |||||
) | |||||
@dataclass | |||||
class MetaEntry(FuseEntry): | class MetaEntry(FuseEntry): | ||||
""" An entry from the meta/ directory, containing for each accessed SWHID a | """ An entry from the meta/ directory, containing for each accessed SWHID a | ||||
corresponding SWHID.json file with all the metadata from the Software | corresponding SWHID.json file with all the metadata from the Software | ||||
Heritage archive. """ | Heritage archive. """ | ||||
def __init__(self, swhid: SWHID, fuse: Fuse): | swhid: SWHID | ||||
super().__init__( | |||||
name=str(swhid) + ".json", mode=int(EntryMode.RDONLY_FILE), fuse=fuse | |||||
) | |||||
self.swhid = swhid | |||||
async def content(self) -> bytes: | async def content(self) -> bytes: | ||||
# Get raw JSON metadata from API (un-typified) | # Get raw JSON metadata from API (un-typified) | ||||
metadata = await self.fuse.cache.metadata.get(self.swhid, typify=False) | metadata = await self.fuse.cache.metadata.get(self.swhid, typify=False) | ||||
return json.dumps(metadata).encode() | return json.dumps(metadata).encode() | ||||
async def length(self) -> int: | async def length(self) -> int: | ||||
return len(await self.content()) | return len(await self.content()) |
Is the name property on the root entry ever needed/accessed? I guess/hope not.
Either way, it'd be better to make its dummy value much more clearly dummy, e.g. "DUMMY_ROOT_PATH". Unless it could be made explicitly None, which I doubt it can.