diff --git a/README.md b/README.md index ccb444d..08f414d 100644 --- a/README.md +++ b/README.md @@ -114,6 +114,8 @@ It's possible to modify the transpose configuration file, `STORE_PATH/transpose. ``` transpose config add "NewEntry" "/path/to/location" transpose config get "NewEntry" +transpose config disable "NewEntry" +transpose config enable "NewEntry" transpose config list transpose config remove "NewEntry" transpose config update "NewEntry" "path" "/path/to/new/location" diff --git a/src/transpose/console.py b/src/transpose/console.py index 6af4213..d345ecd 100644 --- a/src/transpose/console.py +++ b/src/transpose/console.py @@ -34,6 +34,12 @@ def run(args, config_path) -> None: if args.config_action == "add": t.config.add(args.name, args.path) t.config.save(config_path) + elif args.config_action == "disable": + t.config.disable(args.name) + t.config.save(config_path) + elif args.config_action == "enable": + t.config.enable(args.name) + t.config.save(config_path) elif args.config_action == "get": print(t.config.get(args.name)) elif args.config_action == "list": @@ -115,7 +121,7 @@ def parse_arguments(args=None): apply_all_parser.add_argument( "--force", dest="force", - help="If original path already exists, existing path to .backup and continue", + help="Continue with apply even if original path already exists or entry is disabled in config", action="store_true", ) @@ -128,7 +134,12 @@ def parse_arguments(args=None): "name", help="The name of the stored entity to restore", ) - restore_parser.add_argument("--force", dest="force", action="store_true") + restore_parser.add_argument( + "--force", + dest="force", + help="Continue with restore even if original path already exists or entry is disabled in config", + action="store_true", + ) store_parser = subparsers.add_parser( "store", @@ -169,6 +180,26 @@ def parse_arguments(args=None): help="The path of the directory that should be symlinked to the store", ) + config_disable_parser = config_subparsers.add_parser( + "disable", + help="Disable an entry within the config", + parents=[base_parser], + ) + config_disable_parser.add_argument( + "name", + help="The name of the entry the config", + ) + + config_enable_parser = config_subparsers.add_parser( + "enable", + help="enable an entry within the config", + parents=[base_parser], + ) + config_enable_parser.add_argument( + "name", + help="The name of the entry the config", + ) + config_get_parser = config_subparsers.add_parser( "get", help="Retrieve the settings of a specific entity, such as the path", diff --git a/src/transpose/transpose.py b/src/transpose/transpose.py index 20ab575..e4048fa 100644 --- a/src/transpose/transpose.py +++ b/src/transpose/transpose.py @@ -17,6 +17,7 @@ class TransposeEntry: name: str path: str created: str # Should be datetime.datetime but not really necessary here + enabled: bool = True @dataclass @@ -48,6 +49,36 @@ class TransposeConfig: created=created, ) + def disable(self, name: str) -> None: + """ + Disable an entry by name. This ensures actions are not run against this entry, such as apply and restore + + Args: + name: The name of the entry (must exist) + + Returns: + None + """ + try: + self.entries[name].enabled = False + except KeyError: + raise TransposeError(f"'{name}' does not exist in Transpose config entries") + + def enable(self, name: str) -> None: + """ + Enable an entry by name + + Args: + name: The name of the entry (must exist) + + Returns: + None + """ + try: + self.entries[name].enabled = True + except KeyError: + raise TransposeError(f"'{name}' does not exist in Transpose config entries") + def get(self, name: str) -> TransposeEntry: """ Get an entry by the name @@ -105,11 +136,14 @@ class TransposeConfig: config = TransposeConfig() try: for name in in_config["entries"]: + entry = in_config["entries"][name] config.add( name, - in_config["entries"][name]["path"], - created=in_config["entries"].get("created"), + entry["path"], + created=entry.get("created"), ) + if "enabled" in entry and not entry["enabled"]: + config.disable(name) except (KeyError, TypeError) as e: raise TransposeError(f"Unrecognized Transpose config file format: {e}") @@ -162,7 +196,11 @@ class Transpose: if not self.config.entries.get(name): raise TransposeError(f"Entry does not exist: '{name}'") - entry_path = Path(self.config.entries[name].path) + entry = self.config.entries[name] + if hasattr(entry, "enabled") and not entry.enabled and not force: + raise TransposeError(f"Entry '{name}' is not enabled in the config") + + entry_path = Path(entry.path) if entry_path.exists(): if force: # Backup the existing path move(entry_path, entry_path.with_suffix(".backup")) @@ -190,7 +228,11 @@ class Transpose: if not self.config.entries.get(name): raise TransposeError(f"Could not locate entry by name: '{name}'") - entry_path = Path(self.config.entries[name].path) + entry = self.config.entries[name] + if hasattr(entry, "enabled") and not entry.enabled and not force: + raise TransposeError(f"Entry '{name}' is not enabled in the config") + + entry_path = Path(entry.path) if entry_path.exists(): if force: # Backup the existing path move(entry_path, entry_path.with_suffix(".backup")) diff --git a/tests/test_console.py b/tests/test_console.py index 950f5ad..9b5f2ad 100644 --- a/tests/test_console.py +++ b/tests/test_console.py @@ -184,6 +184,26 @@ def test_run_config_add(): assert config.entries.get(args.name) +@setup_restore() +def test_run_config_disable(): + args = RunConfigArgs("disable") + + run_console(args, TRANSPOSE_CONFIG_PATH) + config = TransposeConfig().load(TRANSPOSE_CONFIG_PATH) + + assert config.entries[args.name].enabled is False + + +@setup_restore() +def test_run_config_enable(): + args = RunConfigArgs("enable") + + run_console(args, TRANSPOSE_CONFIG_PATH) + config = TransposeConfig().load(TRANSPOSE_CONFIG_PATH) + + assert config.entries[args.name].enabled is True + + @setup_restore() def test_run_config_get(capsys): args = RunConfigArgs("get") diff --git a/tests/test_transpose.py b/tests/test_transpose.py index a147a98..8e1a090 100644 --- a/tests/test_transpose.py +++ b/tests/test_transpose.py @@ -52,11 +52,26 @@ def test_apply(): assert TARGET_PATH.is_symlink() assert ENTRY_STORE_PATH.is_dir() + # Target is disabled in the config + t.config.entries[ENTRY_NAME].enabled = False + with pytest.raises( + TransposeError, match=f"Entry '{ENTRY_NAME}' is not enabled in the config" + ): + t.apply(ENTRY_NAME) + @setup_restore() def test_restore(): t = Transpose(config_path=TRANSPOSE_CONFIG_PATH) + # Target is disabled in the config + t.config.entries[ENTRY_NAME].enabled = False + with pytest.raises( + TransposeError, match=f"Entry '{ENTRY_NAME}' is not enabled in the config" + ): + t.restore(ENTRY_NAME) + t.config.entries[ENTRY_NAME].enabled = True + # Success t.restore(ENTRY_NAME) assert TARGET_PATH.is_dir() @@ -125,6 +140,28 @@ def test_config_add(): assert config.entries["NewEntry"].path == str(TARGET_PATH) +@setup_store() +def test_config_disable(): + config = TransposeConfig.load(TRANSPOSE_CONFIG_PATH) + + with pytest.raises(TransposeError, match="'UnknownEntry' does not exist"): + config.disable("UnknownEntry") + + config.disable(ENTRY_NAME) + assert config.entries[ENTRY_NAME].enabled is False + + +@setup_store() +def test_config_enable(): + config = TransposeConfig.load(TRANSPOSE_CONFIG_PATH) + + with pytest.raises(TransposeError, match="'UnknownEntry' does not exist"): + config.enable("UnknownEntry") + + config.enable(ENTRY_NAME) + assert config.entries[ENTRY_NAME].enabled is True + + @setup_store() def test_config_get(): config = TransposeConfig.load(TRANSPOSE_CONFIG_PATH) diff --git a/tests/utils.py b/tests/utils.py index 8d993c9..5026feb 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -26,11 +26,13 @@ TRANSPOSE_CONFIG = { "name": ENTRY_NAME, "path": str(TARGET_PATH), "created": "2023-01-21 01:02:03.1234567", + "enabled": True, }, SECOND_ENTRY_NAME: { "name": SECOND_ENTRY_NAME, "path": str(SECOND_TARGET_PATH), "created": "2023-02-23 01:02:03.1234567", + "enabled": True, }, }, }