From ea0a629fd9802e28f2de39fcf0398a763b2f2ced Mon Sep 17 00:00:00 2001 From: Eric Torres Date: Tue, 9 Apr 2019 17:45:38 -0700 Subject: [PATCH] Rename README and hierarchy package to decrease verbosity --- README.rst => README | 0 rbackup/{hierarchy => struct}/__init__.py | 0 rbackup/{hierarchy => struct}/hierarchy.py | 2 +- rbackup/{hierarchy => struct}/repository.py | 80 ++++++++++++++------- rbackup/{hierarchy => struct}/snapshot.py | 8 ++- tests/test_hierarchy.py | 4 +- tests/test_repository.py | 8 +-- tests/test_snapshot.py | 4 +- 8 files changed, 71 insertions(+), 35 deletions(-) rename README.rst => README (100%) rename rbackup/{hierarchy => struct}/__init__.py (100%) rename rbackup/{hierarchy => struct}/hierarchy.py (98%) rename rbackup/{hierarchy => struct}/repository.py (73%) rename rbackup/{hierarchy => struct}/snapshot.py (82%) diff --git a/README.rst b/README similarity index 100% rename from README.rst rename to README diff --git a/rbackup/hierarchy/__init__.py b/rbackup/struct/__init__.py similarity index 100% rename from rbackup/hierarchy/__init__.py rename to rbackup/struct/__init__.py diff --git a/rbackup/hierarchy/hierarchy.py b/rbackup/struct/hierarchy.py similarity index 98% rename from rbackup/hierarchy/hierarchy.py rename to rbackup/struct/hierarchy.py index e4e048c..c5c99c2 100644 --- a/rbackup/hierarchy/hierarchy.py +++ b/rbackup/struct/hierarchy.py @@ -1,6 +1,6 @@ """ .. author:: Eric Torres -.. module:: rbackup.hierarchy.hierarchy +.. module:: rbackup.struct.hierarchy :synopsis: Classes for creating the backup hierarchy. """ import logging diff --git a/rbackup/hierarchy/repository.py b/rbackup/struct/repository.py similarity index 73% rename from rbackup/hierarchy/repository.py rename to rbackup/struct/repository.py index 3f0d33b..9f63d23 100644 --- a/rbackup/hierarchy/repository.py +++ b/rbackup/struct/repository.py @@ -1,13 +1,13 @@ """ .. author:: Eric Torres -.. module:: rbackup.hierarchy.repository +.. module:: rbackup.struct.repository :synopsis: Class for structuring a backup repository. """ import logging import datetime -from rbackup.hierarchy.hierarchy import Hierarchy -from rbackup.hierarchy.snapshot import Snapshot +from rbackup.struct.hierarchy import Hierarchy +from rbackup.struct.snapshot import Snapshot # ========== Logging Setup =========== @@ -26,6 +26,7 @@ class Repository(Hierarchy): 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. + * 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 @@ -53,7 +54,6 @@ class Repository(Hierarchy): Methods ------- * create_snapshot - create a new snapshot, then update current_snapshot - * gen_snapshot_path - generate a path for a snapshot given by name * read_metadata (inherited from Hierarchy) * write_metadata (inherited from Hierarchy) @@ -91,6 +91,41 @@ class Repository(Hierarchy): self.write_metadata() + def _is_duplicate_name(self, name): + """Check whether or not a snapshot of a given name is already + on this Repository. + + If the repository is empty, then this method always returns False. + + :param name: the name to check for + :returns: True if this name is already in a snapshot. + :type name: str + :rtype: bool + """ + for s in self._data["snapshots"]: + if name == s.name: + return True + return False + + def _is_valid_name(self, name): + """Check if the given name is a valid name. If it is a duplicate, + log a warning. If it is invalid, raise a ValueError. + + Invalid Names: + -------------- + * Contain slashes + * Are empty values i.e. '' or [] + + :param name: name to validate + :type name: str + :returns: true if this name is deemed valid + :rtype: bool + """ + if not str(name) or "/" in name: + return False + else: + return True + def __len__(self): """Return the number of snapshots in this Repository.""" return len(self._data["snapshots"]) @@ -128,19 +163,6 @@ class Repository(Hierarchy): """ return self.path / "data" - def gen_snapshot_path(self, name): - """Generate a path for a Snapshot by name. - - :param name: name of the Snapshot - :type name: str or path-like object - :rtype: path-like object - :raises: ValueError if name contains slashes - """ - if "/" in str(name): - raise ValueError("Names cannot contain slashes") - - return self.snapshot_dir / name - @property def snapshots(self): """Return a list of snapshots stored in this Repository. @@ -167,16 +189,21 @@ class Repository(Hierarchy): """ return self._data["current_snapshot"] + # TODO search for the name of snapshots + # add ability to use 'in' operator def create_snapshot(self, name=None): """Create a new snapshot in this repository. This method is non-intrusive in that it will not make any changes in the filesystem when called. + If name is given and it is the name of a snapshot already + on the repository, that snapshot is overwritten instead. + :param name: the name of the snapshot :type name: str :return: a new Snapshot object - :raises: FileExistsError if snapshot directory already exists + :raises: ValueError if name is an invalid value """ syslog.debug("Creating snapshot") @@ -185,15 +212,20 @@ class Repository(Hierarchy): if name is not None else datetime.datetime.utcnow().isoformat().replace(":", "_") ) - self._data["current_snapshot"] = Snapshot(self.gen_snapshot_path(snapshot_name)) - self._data["snapshots"].append(self._data["current_snapshot"]) + + if not self._is_valid_name(snapshot_name): + raise ValueError(f"{name} is an invalid name") + elif self._is_duplicate_name(snapshot_name): + syslog.warning("Snapshot already exists, data will be overwritten.") + else: + self._data["current_snapshot"] = Snapshot(self.snapshot_dir / snapshot_name) + self._data["snapshots"].append(self._data["current_snapshot"]) self.write_metadata() - try: - self._data["current_snapshot"].path.mkdir(mode=DIRMODE, parents=True) - except FileExistsError as e: - raise e + self._data["current_snapshot"].path.mkdir( + mode=DIRMODE, parents=True, exist_ok=True + ) syslog.debug("Snapshot created") syslog.debug(f"Snapshot name: {self.current_snapshot.name}") diff --git a/rbackup/hierarchy/snapshot.py b/rbackup/struct/snapshot.py similarity index 82% rename from rbackup/hierarchy/snapshot.py rename to rbackup/struct/snapshot.py index 3cbf789..5ef8a9e 100644 --- a/rbackup/hierarchy/snapshot.py +++ b/rbackup/struct/snapshot.py @@ -1,11 +1,11 @@ """ .. author:: Eric Torres -.. module:: rbackup.hierarchy.snapshot +.. module:: rbackup.struct.snapshot :synopsis: Classes for creating the backup hierarchy. """ import logging -from rbackup.hierarchy.hierarchy import Hierarchy +from rbackup.struct.hierarchy import Hierarchy # ========== Logging Setup =========== @@ -32,6 +32,10 @@ class Snapshot(Hierarchy): """Default constructor for the Snapshot class.""" super().__init__(path) + def __repr__(self): + """Return a string representation of this Snapshot.""" + return f"{self.__class__.__name__}('{self.name}')" + @property def pkg_dir(self): """Retrieve the package manager backup directory of this snapshot. diff --git a/tests/test_hierarchy.py b/tests/test_hierarchy.py index 238e742..36b38b2 100644 --- a/tests/test_hierarchy.py +++ b/tests/test_hierarchy.py @@ -4,10 +4,10 @@ import unittest from hypothesis import given from hypothesis.strategies import booleans, characters, iterables, one_of, none, text from pathlib import Path -from rbackup.hierarchy.hierarchy import Hierarchy +from rbackup.struct.hierarchy import Hierarchy # ========== Constants ========== -TESTING_MODULE = "rbackup.hierarchy.hierarchy" +TESTING_MODULE = "rbackup.struct.struct" # ========== Functions ========== diff --git a/tests/test_repository.py b/tests/test_repository.py index 5126527..cce6181 100644 --- a/tests/test_repository.py +++ b/tests/test_repository.py @@ -4,12 +4,12 @@ import unittest from hypothesis import given from hypothesis.strategies import builds, lists, text from pathlib import Path -from rbackup.hierarchy.repository import Repository -from rbackup.hierarchy.snapshot import Snapshot -from unittest.mock import PropertyMock, patch +from rbackup.struct.repository import Repository +from rbackup.struct.snapshot import Snapshot +from unittest.mock import MagicMock, PropertyMock, patch # ========== Constants ========== -TESTING_PACKAGE = "rbackup.hierarchy" +TESTING_PACKAGE = "rbackup.struct" REPO_MODULE = f"{TESTING_PACKAGE}.repository" SS_MODULE = f"{TESTING_PACKAGE}.snapshot" diff --git a/tests/test_snapshot.py b/tests/test_snapshot.py index a736482..eb493f5 100644 --- a/tests/test_snapshot.py +++ b/tests/test_snapshot.py @@ -6,10 +6,10 @@ Unit tests for the Snapshot class. import doctest import unittest -from rbackup.hierarchy.snapshot import Snapshot +from rbackup.struct.snapshot import Snapshot # ========== Constants ========== -TESTING_MODULE = "rbackup.hierarchy.snapshot" +TESTING_MODULE = "rbackup.struct.snapshot" # ========== Functions ==========