2024-11-01 21:07:08 +00:00
|
|
|
import shutil
|
2024-10-30 23:22:09 +00:00
|
|
|
from abc import abstractmethod, ABC
|
|
|
|
from pathlib import Path
|
2024-11-01 21:07:08 +00:00
|
|
|
from typing import Sequence
|
2024-10-30 23:22:09 +00:00
|
|
|
|
2024-11-01 21:07:08 +00:00
|
|
|
from typing_extensions import Generic, TypeVar, Union
|
2024-10-30 23:22:09 +00:00
|
|
|
|
2024-11-01 21:07:08 +00:00
|
|
|
TNetworkHandle = TypeVar('TNetworkHandle')
|
|
|
|
TInitialMetadata = TypeVar('TInitialMetadata')
|
2024-10-30 23:22:09 +00:00
|
|
|
|
|
|
|
|
2024-11-03 11:29:29 +00:00
|
|
|
class DownloadHandle(ABC):
|
2024-11-05 15:18:47 +00:00
|
|
|
"""A :class:`DownloadHandle` is a reference to an ongoing download operation."""
|
2024-11-03 11:29:29 +00:00
|
|
|
|
|
|
|
@abstractmethod
|
|
|
|
def await_for_completion(self, timeout: float = 0) -> bool:
|
|
|
|
"""Blocks the current thread until either the download completes or a timeout expires.
|
|
|
|
|
|
|
|
:param timeout: Timeout in seconds.
|
|
|
|
:return: True if the download completed within the timeout, False otherwise."""
|
|
|
|
pass
|
|
|
|
|
|
|
|
|
2024-11-01 21:07:08 +00:00
|
|
|
class Node(ABC, Generic[TNetworkHandle, TInitialMetadata]):
|
2024-11-05 15:18:47 +00:00
|
|
|
"""A :class:`Node` represents a peer within a file sharing network."""
|
2024-10-30 23:22:09 +00:00
|
|
|
|
|
|
|
@abstractmethod
|
|
|
|
def seed(
|
|
|
|
self,
|
|
|
|
file: Path,
|
2024-11-01 21:07:08 +00:00
|
|
|
handle: Union[TInitialMetadata, TNetworkHandle],
|
|
|
|
) -> TNetworkHandle:
|
2024-10-30 23:22:09 +00:00
|
|
|
"""
|
|
|
|
Makes the current :class:`Node` a seeder for the specified file.
|
|
|
|
|
2024-11-01 21:07:08 +00:00
|
|
|
:param file: local path to the file to seed.
|
2024-11-05 15:18:47 +00:00
|
|
|
:param handle: file sharing typically requires some initial metadata when a file is first uploaded into the
|
|
|
|
network, and this will typically then result into a compact representation such as a manifest CID (Codex)
|
|
|
|
or a Torrent file (Bittorrent) which other nodes can then use to identify and locate both the file and its
|
|
|
|
metadata within the network. When doing an initial seed, this method should be called with the initial
|
|
|
|
metadata (TInitialMetadata). Subsequent calls should use the network handle (TNetworkHandle).
|
|
|
|
|
|
|
|
:return: The network handle (TNetworkHandle) for this file. This handle should be used for subsequent calls to
|
|
|
|
:meth:`seed`.
|
2024-10-30 23:22:09 +00:00
|
|
|
"""
|
|
|
|
pass
|
|
|
|
|
2024-11-01 21:07:08 +00:00
|
|
|
@abstractmethod
|
2024-11-03 11:29:29 +00:00
|
|
|
def leech(self, handle: TNetworkHandle) -> DownloadHandle:
|
2024-11-05 15:18:47 +00:00
|
|
|
"""Makes the current node a leecher for the provided handle.
|
|
|
|
|
|
|
|
:param handle: a :class:`DownloadHandle`, which can be used to interact with the download process.
|
|
|
|
"""
|
2024-10-30 23:22:09 +00:00
|
|
|
pass
|
|
|
|
|
|
|
|
|
2024-11-01 21:07:08 +00:00
|
|
|
class SharedFSNode(Node[TNetworkHandle, TInitialMetadata], ABC):
|
|
|
|
"""A `SharedFSNode` is a :class:`Node` which shares a network volume with us. This means
|
|
|
|
we are able to upload files to it by means of simple file copies."""
|
|
|
|
|
|
|
|
def __init__(self, volume: Path):
|
|
|
|
self.volume = volume
|
|
|
|
|
|
|
|
def upload(self, local: Path, name: str) -> Path:
|
|
|
|
target_path = self.volume / name
|
|
|
|
target_path.mkdir(parents=True, exist_ok=True)
|
|
|
|
target = target_path / local.name
|
|
|
|
if local.is_dir():
|
|
|
|
shutil.copytree(local, target)
|
|
|
|
else:
|
|
|
|
shutil.copy(local, target)
|
|
|
|
|
|
|
|
return target
|