Browse Source

v2.2.0 - Enable, Disable Entries and Rework Config Updates

master v2.2.0
Ryan Reed 1 year ago
parent
commit
d62840f5fb
7 changed files with 159 additions and 17 deletions
  1. +3
    -0
      README.md
  2. +1
    -1
      pyproject.toml
  3. +40
    -5
      src/transpose/console.py
  4. +51
    -7
      src/transpose/transpose.py
  5. +23
    -2
      tests/test_console.py
  6. +39
    -2
      tests/test_transpose.py
  7. +2
    -0
      tests/utils.py

+ 3
- 0
README.md View File

@ -114,8 +114,11 @@ It's possible to modify the transpose configuration file, `STORE_PATH/transpose.
``` ```
transpose config add "NewEntry" "/path/to/location" transpose config add "NewEntry" "/path/to/location"
transpose config get "NewEntry" transpose config get "NewEntry"
transpose config disable "NewEntry"
transpose config enable "NewEntry"
transpose config list transpose config list
transpose config remove "NewEntry" transpose config remove "NewEntry"
transpose config update "NewEntry" "path" "/path/to/new/location"
``` ```


+ 1
- 1
pyproject.toml View File

@ -1,6 +1,6 @@
[tool.poetry] [tool.poetry]
name = "transpose" name = "transpose"
version = "2.1.0"
version = "2.2.0"
description = "Move and symlink a path to a central location" description = "Move and symlink a path to a central location"
authors = ["Ryan Reed"] authors = ["Ryan Reed"]
license = "GPLv3" license = "GPLv3"


+ 40
- 5
src/transpose/console.py View File

@ -34,6 +34,12 @@ def run(args, config_path) -> None:
if args.config_action == "add": if args.config_action == "add":
t.config.add(args.name, args.path) t.config.add(args.name, args.path)
t.config.save(config_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": elif args.config_action == "get":
print(t.config.get(args.name)) print(t.config.get(args.name))
elif args.config_action == "list": elif args.config_action == "list":
@ -43,7 +49,7 @@ def run(args, config_path) -> None:
t.config.remove(args.name) t.config.remove(args.name)
t.config.save(config_path) t.config.save(config_path)
elif args.config_action == "update": elif args.config_action == "update":
t.config.update(args.name, args.path)
t.config.update(args.name, args.field_key, args.field_value)
t.config.save(config_path) t.config.save(config_path)
@ -115,7 +121,7 @@ def parse_arguments(args=None):
apply_all_parser.add_argument( apply_all_parser.add_argument(
"--force", "--force",
dest="force", dest="force",
help="If original path already exists, existing path to <path>.backup and continue",
help="Continue with apply even if original path already exists or entry is disabled in config",
action="store_true", action="store_true",
) )
@ -128,7 +134,12 @@ def parse_arguments(args=None):
"name", "name",
help="The name of the stored entity to restore", 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_parser = subparsers.add_parser(
"store", "store",
@ -169,6 +180,26 @@ def parse_arguments(args=None):
help="The path of the directory that should be symlinked to the store", 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( config_get_parser = config_subparsers.add_parser(
"get", "get",
help="Retrieve the settings of a specific entity, such as the path", help="Retrieve the settings of a specific entity, such as the path",
@ -205,8 +236,12 @@ def parse_arguments(args=None):
help="The name of the entry in the store path", help="The name of the entry in the store path",
) )
config_update_parser.add_argument( config_update_parser.add_argument(
"path",
help="The path of the directory that should be symlinked to the store",
"field_key",
help="The config key to be updated",
)
config_update_parser.add_argument(
"field_value",
help="The value to updated in the config",
) )
return parser.parse_args(args) return parser.parse_args(args)


+ 51
- 7
src/transpose/transpose.py View File

@ -1,5 +1,6 @@
from dataclasses import asdict, dataclass, field from dataclasses import asdict, dataclass, field
from pathlib import Path from pathlib import Path
from typing import Any
# from typing import Self # from typing import Self
@ -16,6 +17,7 @@ class TransposeEntry:
name: str name: str
path: str path: str
created: str # Should be datetime.datetime but not really necessary here created: str # Should be datetime.datetime but not really necessary here
enabled: bool = True
@dataclass @dataclass
@ -47,6 +49,36 @@ class TransposeConfig:
created=created, 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: def get(self, name: str) -> TransposeEntry:
""" """
Get an entry by the name Get an entry by the name
@ -77,19 +109,20 @@ class TransposeConfig:
except KeyError: except KeyError:
raise TransposeError(f"'{name}' does not exist in Transpose config entries") raise TransposeError(f"'{name}' does not exist in Transpose config entries")
def update(self, name: str, path: str) -> None:
def update(self, name: str, field_key: str, field_value: Any) -> None:
""" """
Update an entry by name Update an entry by name
Args: Args:
name: The name of the entry (must exist) name: The name of the entry (must exist)
path: The path where the entry originally exists
field_key: The key to update
field_value: The value to update
Returns: Returns:
None None
""" """
try: try:
self.entries[name].path = path
setattr(self.entries[name], field_key, field_value)
except KeyError: except KeyError:
raise TransposeError(f"'{name}' does not exist in Transpose config entries") raise TransposeError(f"'{name}' does not exist in Transpose config entries")
@ -103,11 +136,14 @@ class TransposeConfig:
config = TransposeConfig() config = TransposeConfig()
try: try:
for name in in_config["entries"]: for name in in_config["entries"]:
entry = in_config["entries"][name]
config.add( config.add(
name, 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: except (KeyError, TypeError) as e:
raise TransposeError(f"Unrecognized Transpose config file format: {e}") raise TransposeError(f"Unrecognized Transpose config file format: {e}")
@ -160,7 +196,11 @@ class Transpose:
if not self.config.entries.get(name): if not self.config.entries.get(name):
raise TransposeError(f"Entry does not exist: '{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 entry_path.exists():
if force: # Backup the existing path if force: # Backup the existing path
move(entry_path, entry_path.with_suffix(".backup")) move(entry_path, entry_path.with_suffix(".backup"))
@ -188,7 +228,11 @@ class Transpose:
if not self.config.entries.get(name): if not self.config.entries.get(name):
raise TransposeError(f"Could not locate entry by name: '{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 entry_path.exists():
if force: # Backup the existing path if force: # Backup the existing path
move(entry_path, entry_path.with_suffix(".backup")) move(entry_path, entry_path.with_suffix(".backup"))


+ 23
- 2
tests/test_console.py View File

@ -184,6 +184,26 @@ def test_run_config_add():
assert config.entries.get(args.name) 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() @setup_restore()
def test_run_config_get(capsys): def test_run_config_get(capsys):
args = RunConfigArgs("get") args = RunConfigArgs("get")
@ -217,9 +237,10 @@ def test_run_config_remove():
@setup_restore() @setup_restore()
def test_run_config_update(): def test_run_config_update():
args = RunConfigArgs("update") args = RunConfigArgs("update")
args.path = "/var/tmp/something"
args.field_key = "path"
args.field_value = "/var/tmp/something"
run_console(args, TRANSPOSE_CONFIG_PATH) run_console(args, TRANSPOSE_CONFIG_PATH)
config = TransposeConfig().load(TRANSPOSE_CONFIG_PATH) config = TransposeConfig().load(TRANSPOSE_CONFIG_PATH)
assert config.entries[args.name].path == args.path
assert config.entries[args.name].path == args.field_value

+ 39
- 2
tests/test_transpose.py View File

@ -52,11 +52,26 @@ def test_apply():
assert TARGET_PATH.is_symlink() assert TARGET_PATH.is_symlink()
assert ENTRY_STORE_PATH.is_dir() 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() @setup_restore()
def test_restore(): def test_restore():
t = Transpose(config_path=TRANSPOSE_CONFIG_PATH) 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 # Success
t.restore(ENTRY_NAME) t.restore(ENTRY_NAME)
assert TARGET_PATH.is_dir() assert TARGET_PATH.is_dir()
@ -125,6 +140,28 @@ def test_config_add():
assert config.entries["NewEntry"].path == str(TARGET_PATH) 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() @setup_store()
def test_config_get(): def test_config_get():
config = TransposeConfig.load(TRANSPOSE_CONFIG_PATH) config = TransposeConfig.load(TRANSPOSE_CONFIG_PATH)
@ -157,9 +194,9 @@ def test_config_update():
with pytest.raises( with pytest.raises(
TransposeError, match="does not exist in Transpose config entries" TransposeError, match="does not exist in Transpose config entries"
): ):
config.update("UnknownEntry", "/some/new/path")
config.update("UnknownEntry", "path", "/some/new/path")
config.update(ENTRY_NAME, "/some/new/path")
config.update(ENTRY_NAME, "path", "/some/new/path")
assert config.entries[ENTRY_NAME].path == "/some/new/path" assert config.entries[ENTRY_NAME].path == "/some/new/path"


+ 2
- 0
tests/utils.py View File

@ -26,11 +26,13 @@ TRANSPOSE_CONFIG = {
"name": ENTRY_NAME, "name": ENTRY_NAME,
"path": str(TARGET_PATH), "path": str(TARGET_PATH),
"created": "2023-01-21 01:02:03.1234567", "created": "2023-01-21 01:02:03.1234567",
"enabled": True,
}, },
SECOND_ENTRY_NAME: { SECOND_ENTRY_NAME: {
"name": SECOND_ENTRY_NAME, "name": SECOND_ENTRY_NAME,
"path": str(SECOND_TARGET_PATH), "path": str(SECOND_TARGET_PATH),
"created": "2023-02-23 01:02:03.1234567", "created": "2023-02-23 01:02:03.1234567",
"enabled": True,
}, },
}, },
} }


Loading…
Cancel
Save