Changeset View
Standalone View
swh/fuse/fs/entry.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 __future__ import annotations | |||||
from dataclasses import dataclass, field | |||||
from enum import IntEnum | from enum import IntEnum | ||||
from stat import S_IFDIR, S_IFREG | from pathlib import Path | ||||
from stat import S_IFDIR, S_IFLNK, S_IFREG | |||||
from typing import Any, AsyncIterator, Union | |||||
# Avoid cycling import | # Avoid cycling import | ||||
Fuse = "Fuse" | Fuse = "Fuse" | ||||
class EntryMode(IntEnum): | class EntryMode(IntEnum): | ||||
""" Default entry mode and permissions for the FUSE. | """ Default entry mode and permissions for the FUSE. | ||||
The FUSE mount is always read-only, even if permissions contradict this | The FUSE mount is always read-only, even if permissions contradict this | ||||
statement (in a context of a directory, entries are listed with permissions | statement (in a context of a directory, entries are listed with permissions | ||||
taken from the archive). | taken from the archive). | ||||
""" | """ | ||||
RDONLY_FILE = S_IFREG | 0o444 | RDONLY_FILE = S_IFREG | 0o444 | ||||
RDONLY_DIR = S_IFDIR | 0o555 | RDONLY_DIR = S_IFDIR | 0o555 | ||||
SYMLINK = S_IFLNK | 0o444 | |||||
@dataclass | |||||
class FuseEntry: | class FuseEntry: | ||||
""" Main wrapper class to manipulate virtual FUSE entries | """ Main wrapper class to manipulate virtual FUSE entries | ||||
Attributes: | Attributes: | ||||
name: entry filename | name: entry filename | ||||
mode: entry permission mode | mode: entry permission mode | ||||
fuse: internal reference to the main FUSE class | fuse: internal reference to the main FUSE class | ||||
inode: unique integer identifying the entry | inode: unique integer identifying the entry | ||||
""" | """ | ||||
def __init__(self, name: str, mode: int, fuse: Fuse): | name: str | ||||
self.name = name | mode: int | ||||
self.mode = mode | depth: int | ||||
self.fuse = fuse | fuse: Fuse | ||||
self.inode = fuse._alloc_inode(self) | inode: int = field(init=False) | ||||
async def length(self) -> int: | def __post_init__(self): | ||||
return 0 | self.inode = self.fuse._alloc_inode(self) | ||||
async def get_content(self) -> bytes: | |||||
""" Return the content of a file entry """ | |||||
async def content(self): | |||||
return None | return None | ||||
async def __aiter__(self): | async def size(self) -> int: | ||||
""" Return the size of a file entry """ | |||||
return 0 | |||||
async def __aiter__(self) -> AsyncIterator[FuseEntry]: | |||||
""" Return the child entries of a directory entry """ | |||||
yield None | |||||
def get_target(self) -> Union[str, bytes, Path]: | |||||
""" Return the path target of a symlink entry """ | |||||
return None | return None | ||||
seirl: Probably renaming that to `get_relative_root_path()` would be more explicit. | |||||
def get_relative_root_path(self) -> str: | |||||
return "../" * (self.depth - 1) | |||||
Not Done Inline Actionsthis method is idempotent, right? i.e., if we call it multiple times it won't attempt to create an entry (say, under archive/) multiple times and the second time it's invoked it will just return what had been created the first time, right? (the code calling it seems to expect that property) If that's the case, the name create_* is misleading, as it doesn't imply idempotency. I'm not sure I've a great alternative suggestion, but ensure_* sounds marginally better (other options could be get_*, init_*, none of which sounds perfect). No matter how it's called, idempotency being an important property of the contract of using a method, we need a docstring here stating the method is idempotent. zack: this method is idempotent, right? i.e., if we call it multiple times it won't attempt to create… | |||||
Done Inline ActionsIt is not idempotent, everytime you call create_child a new object is created. However, with the inode <-> entry mapping we re-use parts of the objects, and there is a TODO in the code about caching the entries of an iterable FuseEntry so we don't need to recreate the same child objects everytime. Maybe we could discuss/measure this memory/performance topic in a separate diff? haltode: It is not idempotent, everytime you call `create_child` a new object is created. However, with… | |||||
Not Done Inline Actions@zack Idempotency is not a concern here as this method doesn't have any side effects. It returns the newly created child. seirl: @zack Idempotency is not a concern here as this method doesn't have any side effects. It… | |||||
def create_child(self, constructor: Any, **kwargs) -> FuseEntry: | |||||
return constructor(depth=self.depth + 1, fuse=self.fuse, **kwargs) |
Probably renaming that to get_relative_root_path() would be more explicit.