From 386b200e9d7c58d46808999b0c5313765b5a36c2 Mon Sep 17 00:00:00 2001 From: Eric Torres Date: Sun, 14 Apr 2019 14:56:09 -0700 Subject: [PATCH] Docstring cleanups for documentation --- rbackup/config.py | 4 +- rbackup/package_managers/packagemanager.py | 28 ++--- rbackup/package_managers/pacman.py | 4 +- rbackup/rsync.py | 2 +- rbackup/struct/hierarchy.py | 43 +++----- rbackup/struct/repository.py | 113 +++++++++++---------- rbackup/struct/snapshot.py | 25 ++--- tests/test_config.py | 2 +- tests/test_hierarchy.py | 2 +- tests/test_packagemanager.py | 2 +- tests/test_repository.py | 2 +- tests/test_snapshot.py | 2 +- 12 files changed, 108 insertions(+), 121 deletions(-) diff --git a/rbackup/config.py b/rbackup/config.py index de7ebc2..535a141 100644 --- a/rbackup/config.py +++ b/rbackup/config.py @@ -1,7 +1,7 @@ """ .. :author:: Eric Torres .. :module:: rbackup.config -.. :synopsis: Functions for handling config files. + :synopsis: Functions for handling config files. """ import configparser import logging @@ -45,7 +45,7 @@ def merge_files(files): Any files included that do not exist send a warning to the log. - >>> merge_files(get_files_by_suffix('')) # doctest: +ELLIPSIS + >>> merge_files(get_files_by_suffix('-include.conf')) # doctest: +ELLIPSIS PosixPath('/tmp/...') :param files: files including paths to read from diff --git a/rbackup/package_managers/packagemanager.py b/rbackup/package_managers/packagemanager.py index 071df2b..ff61bb2 100644 --- a/rbackup/package_managers/packagemanager.py +++ b/rbackup/package_managers/packagemanager.py @@ -1,7 +1,7 @@ """ -.. author:: Eric Torres -.. module:: rbackup.packagemanager -:synopsis: Module for package manager plugins +.. moduleauthor:: Eric Torres +.. module:: rbackup.package_managers.packagemanager + :synopsis: Module for package manager plugins """ import logging import subprocess @@ -41,8 +41,8 @@ class PackageManager: it is to be assumed that no file was created, therefore there is no file to cleanup. - Note that this method is internal and is - meant to be called from a subclass in a separate module. + .. note:: This method is internal and is meant to be called from + a subclass in a separate module. :returns: path to temporary file :rtype: path-like object @@ -63,12 +63,12 @@ class PackageManager: def gen_db_archive(self, compress=None): """Generate a database archive for this package manager. - Note that this method is internal and is - meant to be called from a subclass in a separate module. - All arguments and keyword-only arguments are passed directly to the PackageManager object. + .. note:: This method is internal and is meant to be called from + a subclass in a separate module. + :param compress: compression mode :type compress: str :returns: the path to the created file @@ -95,24 +95,24 @@ class PackageManager: @property def cache_directory(self): - """Return the cache directory of this package manager. - + """ + :returns: the cache directory of this package manager. :rtype: path-like object """ return self._cachedir @property def database_path(self): - """Return the database path of this package manager. - + """ + :returns: the database path of this package manager. :rtype: path-like object """ return self._db_path @property def pkglist_cmd(self): - """Return the package listing command of this package manager. - + """ + :returns: the package listing command of this package manager. :rtype: iterable or str """ return self._pkglist_cmd diff --git a/rbackup/package_managers/pacman.py b/rbackup/package_managers/pacman.py index eedad37..38980f1 100644 --- a/rbackup/package_managers/pacman.py +++ b/rbackup/package_managers/pacman.py @@ -1,7 +1,7 @@ """ -.. author:: Eric Torres +.. moduleauthor:: Eric Torres .. module:: rbackup.package_managers.pacman -:synopsis: Implementation class for the Pacman package manager. + :synopsis: Implementation class for the Pacman package manager. """ from rbackup.package_managers.packagemanager import PackageManager diff --git a/rbackup/rsync.py b/rbackup/rsync.py index 44acd79..84dc93e 100644 --- a/rbackup/rsync.py +++ b/rbackup/rsync.py @@ -1,5 +1,5 @@ """ -.. author:: Eric Torres +.. moduleauthor:: Eric Torres .. module:: rbackup.rsync :synopsis: helper functions for running the rsync backend """ diff --git a/rbackup/struct/hierarchy.py b/rbackup/struct/hierarchy.py index dfe7ffe..4bc700d 100644 --- a/rbackup/struct/hierarchy.py +++ b/rbackup/struct/hierarchy.py @@ -1,7 +1,7 @@ """ -.. author:: Eric Torres +.. moduleauthor:: Eric Torres .. module:: rbackup.struct.hierarchy -:synopsis: Classes for creating the backup hierarchy. + :synopsis: Classes for creating the backup hierarchy. """ import json import logging @@ -22,23 +22,10 @@ class Hierarchy(PathLike): """A class for organizing the backup root hierarchy. Upon creation of a Hierarchy object, it is up to the caller - to call either shutil.mkdir() or a related method to create + to call either :func:`shutil.mkdir` or a related method to create the directory structure it emulates. - For consistency, Hierarchy objects always store and return absolute paths. - - Attributes - ---------- - * Hierarchy.path - * Hierarchy.name - * Hierarchy.metadata_path - - Methods - ------- - * gen_metadata - generate the metadata for this Hierarchy - * read_metadata - read this Hierarchy's metadata from a file - and return it - * write_metadata - write this Hierarchy's metadata to a file + For consistency, ``Hierarchy`` objects always store and return absolute paths. """ def __init__(self, dest): @@ -60,30 +47,31 @@ class Hierarchy(PathLike): @property def path(self): - """Return the base directory of this hierarchy. - + """ + :returns: the base directory of this hierarchy :rtype: path-like object """ return self._path @property def name(self): - """Return the name of this hierarchy. - + """ + :returns: the name of this hierarchy. :rtype: str """ return self._name @property def metadata_path(self): - """Return the path of this hierarchy's metadata file. - + """ + :returns: the path of this hierarchy's metadata file. :rtype: path-like object """ return self._metadata_path def gen_metadata(self): """Generate metadata for this repository. + After this method is called, the data necessary for this hierarchy has been created. """ raise NotImplementedError("This method must be called in a child class.") @@ -99,11 +87,12 @@ class Hierarchy(PathLike): return json.load(mfile) def write_metadata(self, attr): - """Write this repository's metadata to its file. - Note that this write operation is atomic to the caller. + """Write this repository's metadata to its metadata file. - :param attr: data to write to file - :type attr: class member to write + .. note:: This write operation is atomic to the caller. + + :param attr: class data to write to file + :type attr: any type """ syslog.debug(f"Writing metadata to {self.metadata_path}") diff --git a/rbackup/struct/repository.py b/rbackup/struct/repository.py index 72ea4f4..fc391b8 100644 --- a/rbackup/struct/repository.py +++ b/rbackup/struct/repository.py @@ -1,8 +1,7 @@ """ -.. author:: Eric Torres +.. moduleauthor:: Eric Torres .. module:: rbackup.struct.repository - -:synopsis: Module for helpers for structuring a backup repository. + :synopsis: Classes for structuring a backup repository. """ import datetime import logging @@ -27,60 +26,62 @@ VALID_SNAPSHOT_NAME = r"[\w._+-]+[^/]*" class Repository(Hierarchy): """A class for interacting with a backup repository. - Repository is a mutable, stateful class for representing a - directory that contains backup data sequestered into snapshots - and a symlink to the most recently created snapshot. + Repository is a mutable, stateful class for representing a + directory that contains backup data sequestered into snapshots + and a symlink to the most recently created snapshot. + + Properties * Each snapshot in a repository is unaware of one another, this is the job of the repository to organize * The only way snapshots are linked together is in files that are hard-linked together - Attributes - ---------- - * Repository.path (inherited from Hierarchy) - * Repository.name (inherited from Hierarchy) - * Repository.metadata_path (inherited from Hierarchy) - * Repository.snapshots - a list of snapshots stored in this repository - * Repository.snapshot_dir - the snapshot storage location of this repository + Snapshots can be accessed on a one-by-one basis through iteration. - Methods - ------- - * cleanup - clean all repository data - * create_snapshot - create a new snapshot - * gen_metadata (inherited from Hierarchy) - * is_valid_snapshot_name - validate a potential name for a snapshot - * read_metadata (inherited from Hierarchy) - * write_metadata (inherited from Hierarchy) + :: - Directory Structure - ------------------- - * "data" directory for storing snapshots - * Each snapshot is its own directory with its own sub-hierarchy - * Each snapshot has an "old" directory for storing deleted data - * rsync hardlinks unchanged files between snapshots - * A symlink in the root of the repository symlinking to the - most recent snapshot + >>> for snapshot in Repository('backup'): + >>> print(snapshot.path) + first + second + ... - Iteration - --------- - To support checking all snapshots for hardlinking, the Repository class - can be iterated through. + Snapshots on repositories can be retrieved by index using python's + list slicing syntax. + + :: + + >>> print(Repository('backup')[:]) + [Snapshot(...), ...] + + Membership of a snapshot in a repository can be checked by name. + + :: + + >>> Repository('backup').create_snapshot('test') + >>> 'test' in Repository('backup') + True + + Number of snapshots in a repository can be checked as well + + :: + + >>> Repository('backup').create_snapshot() + >>> len(Repository('backup')) + 1 """ - """Snapshots are serialized as their names relative to the repository data directory, but have their full paths during runtime. Private Attributes - ------------------ * _snapshots - list of Snapshot objects created and accessed at runtime * _snapshot_metadata - list of Snapshot names serialized and deserialized when this Repository is first created """ def __init__(self, dest): - """Default constructor for the Repository class. - """ + """Default constructor for the Repository class.""" super().__init__(dest) if self.metadata_path.exists(): @@ -95,8 +96,9 @@ class Repository(Hierarchy): self._snapshot_iterator = iter(self._snapshots) def __contains__(self, name): - """Check whether a Snapshot is in this Repository by name. + """Check membership of a Snapshot in this Repository by name. + :returns: True if name is the name of a Snapshot in this Repository :type name: str :rtype: bool """ @@ -126,43 +128,43 @@ class Repository(Hierarchy): """Check if the given name is a valid name. Invalid Names: - -------------- + * Contain slashes * Are empty values - Valid names match the regex - r'[\\w]+[^/]*' + Valid names: + + * Match the regex r'[\\w]+[^/]*' :param name: name to validate :type name: str - :returns: true if this name is deemed valid + :returns: true if this name is deemed valid, otherwise False :rtype: bool """ return bool(re.match(VALID_SNAPSHOT_NAME, name)) @property def snapshot_dir(self): - """Return the directory in this Repository in which snapshots - are stored. - + """ + :returns: the directory in this Repository in which snapshots + are stored. :rtype: path-like object """ return self.path / "data" @property def snapshots(self): - """Return a list of snapshots stored in this Repository. - - :returns: the names of all snapshots in this repository sorted by - date + """ + :returns: all snapshots stored in this repository :rtype: list of Snapshot objects """ return self._snapshots @property def empty(self): - """Determine whether or not this Repository is empty. - + """ + :returns: True if there are no Snapshots in this Repository, + False otherwise :rtype: bool """ return not self.snapshots @@ -179,9 +181,14 @@ class Repository(Hierarchy): def create_snapshot(self, name=None): """Create a new snapshot in this repository. - This method is non-intrusive in that it will not + This operation is non-intrusive in that it will not make any changes in the filesystem when called. + If name is not given, then the new snapshot's name is the current + UTC date in ISO format. + + If name is given, then it is the name for the new snapshot. + If name is given and it is the name of a snapshot already on the repository, that snapshot is overwritten instead. @@ -221,7 +228,7 @@ class Repository(Hierarchy): :param remove_snapshots: delete the data directory of this repository :type remove_snapshots: bool - :param remove_repo_dir: remove the top-directory level of this repository + :param remove_repo_dir: remove the top-level directory of this repository :type remove_repo_dir: bool """ # We don't want to risk symlink attacks diff --git a/rbackup/struct/snapshot.py b/rbackup/struct/snapshot.py index 1465b48..339e183 100644 --- a/rbackup/struct/snapshot.py +++ b/rbackup/struct/snapshot.py @@ -1,5 +1,5 @@ """ -.. author:: Eric Torres +.. moduleauthor:: Eric Torres .. module:: rbackup.struct.snapshot :synopsis: Classes for creating the backup hierarchy. """ @@ -14,18 +14,12 @@ syslog = logging.getLogger(__name__) # ========== Classes ========== class Snapshot(Hierarchy): """Hierarchy for a single snapshot. - Attributes - ---------- - * Snapshot.path (inherited from Hierarchy) - * Snapshot.name (inherited from Hierarchy) - * Snapshot.metadata_path (inherited from Hierarchy) - * Snapshot.pkg_dir - Methods - ------- - * gen_metadata (inherited from Hierarchy) - * read_metadata (inherited from Hierarchy) - * write_metadata (inherited from Hierarchy) + Data from each run of a backup script is intended to go here. + + Snapshots are unaware of one another, it is up to a third-party caller + to orchestrate operations such as hardlinking between snapshots and + ordering snapshots. """ def __init__(self, path): @@ -40,16 +34,13 @@ class Snapshot(Hierarchy): @property def pkg_dir(self): - """Retrieve the package manager backup directory of this snapshot. - + """ + :returns: the package manager backup directory of this snapshot. :rtype: path-like object """ return self._pkg_dir def gen_metadata(self): - """Generate metadata for this repository. - After this method is called, the data necessary for this snapshot has been created. - """ pass diff --git a/tests/test_config.py b/tests/test_config.py index 7db2b00..4403934 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -1,5 +1,5 @@ """ -.. author:: Eric Torres +.. moduleauthor:: Eric Torres Tests for the rbackup.config module. """ diff --git a/tests/test_hierarchy.py b/tests/test_hierarchy.py index a3b429f..c65ace0 100644 --- a/tests/test_hierarchy.py +++ b/tests/test_hierarchy.py @@ -1,5 +1,5 @@ """ -.. author:: Eric Torres +.. moduleauthor:: Eric Torres Tests for the rbackup.struct.hierarchy module. """ diff --git a/tests/test_packagemanager.py b/tests/test_packagemanager.py index ba5f5b8..41bccd3 100644 --- a/tests/test_packagemanager.py +++ b/tests/test_packagemanager.py @@ -1,5 +1,5 @@ """ -.. author:: Eric Torres +.. moduleauthor:: Eric Torres :synopsis: Unit tests for the PackageManager module. """ import subprocess diff --git a/tests/test_repository.py b/tests/test_repository.py index 66226cc..5f00266 100644 --- a/tests/test_repository.py +++ b/tests/test_repository.py @@ -1,5 +1,5 @@ """ -.. author:: Eric Torres +.. moduleauthor:: Eric Torres Tests for the rbackup.struct.repository module. """ diff --git a/tests/test_snapshot.py b/tests/test_snapshot.py index cd00cf1..32bac2b 100644 --- a/tests/test_snapshot.py +++ b/tests/test_snapshot.py @@ -1,5 +1,5 @@ """ -.. author:: Eric Torres +.. moduleauthor:: Eric Torres Unit tests for the rbackup.struct.snapshot module. """