From 44765e854bd74081b35d471e2238bdad30e5df23 Mon Sep 17 00:00:00 2001 From: Benjamin Poldrack Date: Thu, 17 Apr 2025 12:53:39 +0200 Subject: [PATCH] TMP: Rewrite new --- onyo/cli/new.py | 6 +- onyo/cli/tests/test_new.py | 1 + onyo/lib/command_utils.py | 27 -- onyo/lib/commands.py | 166 +++++++----- onyo/lib/inventory.py | 32 +-- onyo/lib/items.py | 2 +- onyo/lib/tests/test_commands_new.py | 51 ++-- onyo/lib/tests/test_inventory_operations.py | 273 +++++++++++--------- 8 files changed, 301 insertions(+), 257 deletions(-) diff --git a/onyo/cli/new.py b/onyo/cli/new.py index 86c92c2b..dac9c347 100644 --- a/onyo/cli/new.py +++ b/onyo/cli/new.py @@ -174,9 +174,9 @@ def new(args: argparse.Namespace) -> None: else: template = None onyo_new(inventory=inventory, - directory=Path(args.directory).resolve() if args.directory else None, - template=template, - clone=Path(args.clone).resolve() if args.clone else None, + base=Path(args.directory).resolve() if args.directory else None, + template=[template] if template else None, #FIXME: Actually allow multiple + clone=[Path(args.clone).resolve()] if args.clone else None, #FIXME: Actually allow multiple keys=args.keys, edit=args.edit, message='\n\n'.join(m for m in args.message) if args.message else None, diff --git a/onyo/cli/tests/test_new.py b/onyo/cli/tests/test_new.py index cf3d368c..2b2ddf39 100644 --- a/onyo/cli/tests/test_new.py +++ b/onyo/cli/tests/test_new.py @@ -368,6 +368,7 @@ def test_new_with_flags_edit_keys_template(repo: OnyoRepo, assert ret.returncode == 1 # create asset with --edit, --template and --keys + ret = subprocess.run(['onyo', '--yes', 'new', '--edit', '--template', template, '--directory', directory, '--keys'] + key_values, capture_output=True, text=True) diff --git a/onyo/lib/command_utils.py b/onyo/lib/command_utils.py index 96576f43..e4e7a0db 100644 --- a/onyo/lib/command_utils.py +++ b/onyo/lib/command_utils.py @@ -296,30 +296,3 @@ def inventory_path_to_yaml(inventory: Inventory, # dump YAML return ''.join([i.yaml(exclude=[]) for i in items]) - -def iamyourfather(inventory: Inventory, - yaml_stream: str, - base: Path | None = None) -> list[Item]: - - from onyo.lib.utils import yaml_to_dict_multi - from onyo.lib.filters import Filter - from onyo.lib.items import Item - - specs = [d for d in yaml_to_dict_multi(yaml_stream)] - for spec in specs: - if spec["onyo.is.asset"]: - spec["onyo.path.name"] = inventory.generate_asset_name(spec) - for spec in specs: - if not spec["onyo.path.parent"].startswith(""): - spec["onyo.path.parent"] = Path(spec["onyo.path.parent"]) - spec["onyo.path.relative"] = spec["onyo.path.parent"] / spec["onyo.path.name"] - for spec in specs: - if isinstance(spec["onyo.path.parent"], str) and spec["onyo.path.parent"].startswith(""): - filter_ = Filter(spec["onyo.path.parent"][2:-1]) - matches = [s for s in specs if filter_.match(s)] - assert len(matches) == 1 - spec["onyo.path.parent"] = matches[0]["onyo.path.relative"] - spec["onyo.path.relative"] = spec["onyo.path.parent"] / spec["onyo.path.name"] - - # maybe put Filter.match in here and search for callables when resolving. - return [Item(spec) for spec in specs] diff --git a/onyo/lib/commands.py b/onyo/lib/commands.py index d0d65811..783996cb 100644 --- a/onyo/lib/commands.py +++ b/onyo/lib/commands.py @@ -2,6 +2,7 @@ import logging import subprocess +from copy import deepcopy from pathlib import Path from typing import ( ParamSpec, @@ -11,7 +12,7 @@ from functools import wraps from rich import box -from rich.table import Table # pyre-ignore[21] for some reason pyre doesn't find Table +from rich.table import Table from onyo.lib.command_utils import ( inline_path_diff, @@ -42,11 +43,13 @@ ) from onyo.lib.inventory import Inventory, OPERATIONS_MAPPING from onyo.lib.onyo import OnyoRepo -from onyo.lib.pseudokeys import PSEUDO_KEYS +from onyo.lib.pseudokeys import ( + PSEUDOKEY_ALIASES, + PSEUDO_KEYS, +) from onyo.lib.ui import ui from onyo.lib.utils import ( deduplicate, - write_asset_to_file, ) if TYPE_CHECKING: @@ -194,9 +197,9 @@ def onyo_config(inventory: Inventory, def _edit_asset(inventory: Inventory, - asset: Item, + asset: ItemSpec, operation: Callable, - editor: str | None) -> Item: + editor: str | None) -> ItemSpec: r"""Edit an ``asset`` (as a temporary file) with ``editor``. A helper for ``onyo_edit()`` and ``onyo_new(edit=True)``. @@ -240,7 +243,10 @@ def _edit_asset(inventory: Inventory, disallowed_keys.remove('onyo.path.parent') tmp_path = get_temp_file() - write_asset_to_file(asset, path=tmp_path) + # stringify Paths ?? We need to deal with ItemSpec (via new) and Item (via edit) here. + # -> different .yaml() defaults WRT exclude= + # -> Do we want to dump settable pseudokeys here? + tmp_path.write_text(asset.yaml(exclude=list(reserved_keys.keys()))) # store operations queue length in case we need to roll-back queue_length = len(inventory.operations) @@ -836,10 +842,11 @@ def onyo_mv(inventory: Inventory, @raise_on_inventory_state def onyo_new(inventory: Inventory, - directory: Path | None = None, - template: Path | str | None = None, - clone: Path | None = None, + base: Path | None = None, + template: list[Path] | None = None, + clone: list[Path] | None = None, keys: list[Dict | UserDict] | None = None, + recursive: bool = False, edit: bool = False, message: str | None = None, auto_message: bool | None = None) -> None: @@ -861,19 +868,20 @@ def onyo_new(inventory: Inventory, ---------- inventory The Inventory in which to create new assets. - directory - The directory to create new asset(s) in. This cannot be used with the - ``directory`` Reserved Key. - - If `None` and the ``directory`` Reserved Key is not found, it defaults - to CWD. + base + The directory to create new asset(s) in. All relative path specifications + are interpreted relative to this base. + Defaults to CWD. template - Path to a template to populate the contents of new assets. - + List of Paths to template(s) to populate the contents of new assets. Relative paths are resolved relative to ``.onyo/templates``. + + TODO: - mention layering + - mention 1-or-N rule + - mention directory templates and ``recursive`` + - mention multidoc YAML clone - Path of an asset to clone. Cannot be used with the ``template`` argument - nor the ``template`` Reserved Key. + List of inventory paths to clone. Cannot be used with the ``template`` argument. keys List of dictionaries with key/value pairs to set in the new assets. @@ -898,62 +906,104 @@ def onyo_new(inventory: Inventory, If information is invalid, missing, or contradictory. """ - from copy import deepcopy + from onyo.lib.filters import Filter + from onyo.lib.utils import yaml_to_dict_multi + from onyo.lib.items import ItemSpec auto_message = inventory.repo.auto_message if auto_message is None else auto_message - keys = keys or [] + template = template or [] + clone = clone or [] + if not any([keys, edit, template, clone]): raise ValueError("Key-value pairs or a template/clone-target must be given.") if template and clone: raise ValueError("'template' and 'clone' options are mutually exclusive.") + base = base or Path.cwd() + base = base.resolve() + if not base.is_relative_to(inventory.root): + raise ValueError(f"'base' ({str(base)}) outside inventory.") + # get editor early in case it fails editor = inventory.repo.get_editor() if edit else "" - # Note that `keys` can be empty. - specs = deepcopy(keys) - - # TODO: These validations could probably be more efficient and neat. - # For ex., only first dict is actually relevant. It came from --key, - # where everything after the first one comes from repetition (However, what about python interface where one - # could pass an arbitrary list of dicts? -> requires consistency check - if any('directory' in d.keys() for d in specs): - if directory: - raise ValueError("Can't use '--directory' option and specify 'directory' key.") + specs = [] + for t in template: + for spec in inventory.get_templates(t, recursive=recursive): + specs.append(spec) + for c in clone: + yaml_docs = inventory_path_to_yaml(inventory=inventory, + path=c, + recursive=recursive) + specs.extend([ItemSpec(d, alias_map=PSEUDOKEY_ALIASES) for d in yaml_to_dict_multi(yaml_docs)]) + + # Merge `specs` and `keys`, applying 1-or-N rule across options: + num_specs = len(specs) + num_keys = len(keys) + if num_keys > 1 and num_specs > 1 and num_specs != num_keys: + raise InvalidArgumentError(f"Number of items mismatch. 'keys' option wants to update" + f" {len(keys)} items, but {len(specs)} are given.") + if num_specs == 0 and num_keys > 0: + # 'keys' option is the only one providing any content. + # Create ItemSpec(s) and annotate necessary pseudokeys. + for item in keys: + # keys may come in as ItemSpec already. + # Instantiate new ItemSpecs with (repo-specific) alias mapping: + spec = ItemSpec(alias_map=PSEUDOKEY_ALIASES) + spec.update(item) + if "onyo.is.asset" not in spec.keys(): + spec["onyo.is.asset"] = any(not k.startswith("onyo.") for k in spec.keys()) + if "onyo.path.parent" not in spec.keys(): + spec["onyo.path.parent"] = "." + specs.append(spec) + elif num_specs == 1 and num_keys > 1: + # one `spec` that is updated by different `keys`: + updated_specs = [] + for k in keys: + spec = deepcopy(specs[0]) + spec.update(k) + updated_specs.append(spec) + specs = updated_specs + elif num_specs > 1 and num_keys == 1: + # multiple specs that need to be updated form the same dict in `keys` + for spec in specs: + spec.update(keys[0]) else: - # default - directory = directory or Path.cwd() - if template and any('template' in d.keys() for d in specs): - raise ValueError("Can't use 'template' key and 'template' option.") - if clone and any('template' in d.keys() for d in specs): - raise ValueError("Can't use 'clone' key and 'template' option.") - - # Generate actual assets: + # same number of `specs` and `keys`; update pair-wise + for spec, k in zip(specs, keys): + spec.update(k) + + # Resolve dynamic parts of specs: + for spec in specs: + if spec["onyo.is.asset"]: # TODO: Is that guaranteed to be a bool? Check yaml_to_dict_multi + spec["onyo.path.name"] = inventory.generate_asset_name(spec) + for spec in specs: + if (((isinstance(spec["onyo.path.parent"], str) and + (not spec["onyo.path.parent"].startswith("")))) or + isinstance(spec["onyo.path.parent"], Path)): + spec["onyo.path.parent"] = (base / spec["onyo.path.parent"]).relative_to(inventory.root) + spec["onyo.path.relative"] = spec["onyo.path.parent"] / spec["onyo.path.name"] + for spec in specs: + if (isinstance(spec["onyo.path.parent"], str) and + (spec["onyo.path.parent"].startswith(""))): + filter_ = Filter(spec["onyo.path.parent"][2:-1]) + matches = [s for s in specs if filter_.match(s)] + assert len(matches) == 1 + spec["onyo.path.parent"] = matches[0]["onyo.path.relative"] + spec["onyo.path.relative"] = spec["onyo.path.parent"] / spec["onyo.path.name"] + if edit and not specs: - # Special case: No asset specification defined via `keys`, but we have `edit`. - # This implies a single asset, starting with a (possibly empty) template. - specs = [{}] + # Nothing but `edit` was given. Create one empty ItemSpec to edit. + spec = ItemSpec(alias_map=PSEUDOKEY_ALIASES) + spec["onyo.path.parent"] = base + specs.append(spec) for spec in specs: - # 1. Unify directory specification - directory = Path(spec.get('directory', directory)) - if not directory.is_absolute(): - directory = inventory.root / directory - spec['directory'] = directory - # 2. start from template - if clone: - asset = inventory.get_item(clone) - else: - t = spec.pop('template', None) or template - asset = inventory.get_templates(Path(t) if t else None).__next__() - # 3. fill in asset specification - asset.update(spec) - # 4. (try to) add to inventory if edit: - _edit_asset(inventory, asset, inventory.add_asset, editor) + _edit_asset(inventory, spec, inventory.add_asset, editor) else: - inventory.add_asset(asset) + inventory.add_asset(spec) if inventory.operations_pending(): if not edit: diff --git a/onyo/lib/inventory.py b/onyo/lib/inventory.py index a1eafe87..11ef31b5 100644 --- a/onyo/lib/inventory.py +++ b/onyo/lib/inventory.py @@ -42,7 +42,10 @@ exec_rename_directory, generic_executor, ) -from onyo.lib.items import Item +from onyo.lib.items import ( + Item, + ItemSpec, +) from onyo.lib.onyo import OnyoRepo from onyo.lib.pseudokeys import PSEUDO_KEYS from onyo.lib.recorders import ( @@ -347,7 +350,7 @@ def _add_operation(self, return op def add_asset(self, - asset: Item) -> list[InventoryOperation]: + asset: ItemSpec) -> list[InventoryOperation]: r"""Create an asset. Parameters @@ -367,6 +370,8 @@ def add_asset(self, operations = [] path = None + asset = Item(asset, repo=self.repo) + self.raise_empty_keys(asset) # ### generate stuff - TODO: function - reuse in modify_asset if asset.get('serial') == 'faux': @@ -381,7 +386,7 @@ def add_asset(self, if path is None: # Otherwise, a 'onyo.path.parent' to create the asset in is expected as with # any other asset. - path = asset['onyo.path.absolute'] = asset['onyo.path.parent'] / self.generate_asset_name(asset) + path = asset['onyo.path.absolute'] = self.root / asset['onyo.path.parent'] / self.generate_asset_name(asset) if not path: raise ValueError("Unable to determine asset path") assert isinstance(asset, Item) @@ -400,7 +405,10 @@ def add_asset(self, if asset.get('onyo.is.directory', False): if self.repo.is_inventory_dir(path): # We want to turn an existing dir into an asset dir. - operations.extend(self.rename_directory(asset, self.generate_asset_name(asset))) + operations.extend(self.rename_directory( + self.get_item(path), # get the existing dir, rather than the to-be-asset + self.generate_asset_name(asset)) + ) # Temporary hack: Adjust the asset's path to the renamed one. # TODO: Actual solution: This entire method must not be based on the dict's 'onyo.path.absolute', but # 'onyo.path.parent' + generated name. This ties in with pulling parts of `onyo_new` in here. @@ -864,7 +872,7 @@ def get_items(self, def get_templates(self, template: Path | None, - recursive: bool = False) -> Generator[Item, None, None]: + recursive: bool = False) -> Generator[ItemSpec, None, None]: r"""Get templates as Items. template: @@ -873,16 +881,10 @@ def get_templates(self, recursive: Recursive into template directories. """ - # TODO: This function should pass on ItemSpecs, but `new` can't deal with that yet. - for d in self.repo.get_templates(template, recursive=recursive): - # TODO: The following is currently necessary, b/c `Item(ItemSpec)` has a bug - # that kills pseudokeys. - item = Item(repo=self.repo) - item.update(d) - yield item + yield from self.repo.get_templates(template, recursive=recursive) def generate_asset_name(self, - asset: Item) -> str: + asset: ItemSpec) -> str: r"""Generate an ``asset``'s file or directory name. The asset name format is defined by the configuration @@ -964,7 +966,7 @@ def get_faux_serials(self, return faux_serials def raise_required_key_empty_value(self, - asset: Item) -> None: + asset: ItemSpec) -> None: r"""Raise if ``asset`` has an empty value for a required key. A validation helper. This checks only asset name keys. @@ -986,7 +988,7 @@ def raise_required_key_empty_value(self, f" must not have empty values.") def raise_empty_keys(self, - asset: Item) -> None: + asset: ItemSpec) -> None: r"""Raise if ``asset`` has empty keys. A validation helper. diff --git a/onyo/lib/items.py b/onyo/lib/items.py index ed61cb32..ccb6e256 100644 --- a/onyo/lib/items.py +++ b/onyo/lib/items.py @@ -626,7 +626,7 @@ def yaml(self, excluded. """ - exclude = exclude or RESERVED_KEYS + exclude = exclude if exclude is not None else RESERVED_KEYS return super().yaml(exclude) # TODO/Notes for next PR(s): diff --git a/onyo/lib/tests/test_commands_new.py b/onyo/lib/tests/test_commands_new.py index 4161206b..782856b6 100644 --- a/onyo/lib/tests/test_commands_new.py +++ b/onyo/lib/tests/test_commands_new.py @@ -1,4 +1,5 @@ import subprocess +from pathlib import Path import pytest @@ -12,11 +13,6 @@ from ..commands import onyo_new -# TODO: Asset dirs! -# TODO: Commit message! -# TODO: Changed name scheme config - - def test_onyo_new_invalid(inventory: Inventory) -> None: # no arguments is insufficient pytest.raises(ValueError, onyo_new, inventory) @@ -27,8 +23,8 @@ def test_onyo_new_invalid(inventory: Inventory) -> None: # clone and template are mutually exclusive pytest.raises(ValueError, onyo_new, inventory, keys=[{'serial': 'faux'}], - clone=inventory.root / "somewhere" / "nested" / "TYPE_MAKE_MODEL.SERIAL", - template='laptop.example') + clone=[inventory.root / "somewhere" / "nested" / "TYPE_MAKE_MODEL.SERIAL"], + template=[Path('laptop.example')]) assert inventory.repo.git.is_clean_worktree() @@ -49,7 +45,7 @@ def test_onyo_new_keys(inventory: Inventory) -> None: ] old_hexsha = inventory.repo.git.get_hexsha() onyo_new(inventory, - directory=inventory.root / "empty", + base=inventory.root / "empty", keys=specs) # pyre-ignore[6] # exactly one commit added assert inventory.repo.git.get_hexsha('HEAD~1') == old_hexsha @@ -62,7 +58,7 @@ def test_onyo_new_keys(inventory: Inventory) -> None: assert new_asset.get("onyo.path.absolute") == p assert all(new_asset[k] == s[k] for k in s.keys()) - # faux serial and 'directory' key + # faux serial and 'directory' key (alias to onyo.path.parent) specs = [ItemSpec({'type': 'A', 'make': 'faux', 'model': {'name': 'serial'}, @@ -74,13 +70,6 @@ def test_onyo_new_keys(inventory: Inventory) -> None: 'directory': 'completely/elsewhere', 'serial': 'faux'}) ] - # 'directory' is in conflict with `directory` being given: - pytest.raises(ValueError, - onyo_new, - inventory, - directory=inventory.root / "empty", - keys=specs) - # w/o `directory` everything is fine: onyo_new(inventory, keys=specs) # pyre-ignore[6] # another commit added @@ -114,9 +103,10 @@ def test_onyo_new_keys(inventory: Inventory) -> None: specs = [ItemSpec({'type': 'flavor', 'make': 'manufacturer', 'model': {'name': 'exquisite'}, - 'template': 'laptop.example', 'serial': '1234'})] + onyo_new(inventory, + template=[Path('laptop.example')], keys=specs) # pyre-ignore[6] # another commit added assert inventory.repo.git.get_hexsha('HEAD~3') == old_hexsha @@ -147,7 +137,7 @@ def test_onyo_new_creates_directories(inventory: Inventory) -> None: old_hexsha = inventory.repo.git.get_hexsha() onyo_new(inventory, - directory=new_directory, + base=new_directory, keys=specs) # pyre-ignore[6] # exactly one commit added @@ -171,18 +161,18 @@ def test_onyo_new_creates_directories(inventory: Inventory) -> None: @pytest.mark.ui({'yes': True}) def test_onyo_new_edit(inventory: Inventory, monkeypatch) -> None: - directory = inventory.root / "edited" + base = inventory.root / "edited" monkeypatch.setenv('EDITOR', "printf 'key: value #w/ comment' >>") specs = [{'model': {'name': 'MODEL'}, - 'make': 'MAKER', + 'make': 'MAKE', 'type': 'TYPE', 'serial': 'totally_random'}] onyo_new(inventory, keys=specs, # pyre-ignore[6] - directory=directory, + base=base, edit=True) - expected_path = directory / "TYPE_MAKER_MODEL.totally_random" + expected_path = base / "TYPE_MAKE_MODEL.totally_random" assert inventory.repo.is_asset_path(expected_path) assert expected_path in inventory.repo.git.files asset_content = inventory.get_item(expected_path) @@ -190,24 +180,25 @@ def test_onyo_new_edit(inventory: Inventory, monkeypatch) -> None: assert 'key: value #w/ comment' in expected_path.read_text() assert 'None' not in list(inventory.get_history(expected_path, n=1))[0]['message'] # file already exists: - edit_str = "model:\n name: MODEL\nmake: MAKER\ntype: TYPE\n" + edit_str = "model:\n name: MODEL\nmake: MAKE\ntype: TYPE\n" monkeypatch.setenv('EDITOR', f"printf '{edit_str}' >>") specs = [{'serial': 'totally_random'}] - pytest.raises(ValueError, onyo_new, inventory, keys=specs, directory=directory, edit=True) + pytest.raises(ValueError, onyo_new, inventory, keys=specs, base=base, edit=True) + # missing required fields: monkeypatch.setenv('EDITOR', "printf 'key: value' >>") - pytest.raises(ValueError, onyo_new, inventory, directory=directory, edit=True) + pytest.raises(ValueError, onyo_new, inventory, base=base, edit=True) # content should be exactly as expected - edit_str = "model:\n name: MODEL\nmake: MAKER\ntype: TYPE\nserial: 8675309\n" + edit_str = "model:\n name: MODEL\nmake: MAKE\ntype: TYPE\nserial: 8675309\n" monkeypatch.setenv('EDITOR', f"printf '{edit_str}' >>") onyo_new(inventory, - directory=directory, + base=base, edit=True) expected_content = '---\n' + edit_str - expected_path = directory / "TYPE_MAKER_MODEL.8675309" + expected_path = base / "TYPE_MAKE_MODEL.8675309" assert expected_content == expected_path.read_text() assert 'None' not in list(inventory.get_history(expected_path, n=1))[0]['message'] @@ -222,8 +213,8 @@ def test_onyo_new_clones(inventory: Inventory) -> None: onyo_new(inventory, keys=[{'serial': 'ANOTHER'}, {'serial': 'whatever'}], - clone=existing_asset_path, - directory=asset_dir) + clone=[existing_asset_path], + base=asset_dir) # exactly one commit added assert inventory.repo.git.get_hexsha('HEAD~1') == old_hexsha diff --git a/onyo/lib/tests/test_inventory_operations.py b/onyo/lib/tests/test_inventory_operations.py index 4a3c3ee8..1f3af85f 100644 --- a/onyo/lib/tests/test_inventory_operations.py +++ b/onyo/lib/tests/test_inventory_operations.py @@ -1,11 +1,15 @@ import pytest +from copy import deepcopy from onyo.lib.consts import ( ANCHOR_FILE_NAME, ASSET_DIR_FILE_NAME, RESERVED_KEYS, ) -from onyo.lib.pseudokeys import PSEUDO_KEYS +from onyo.lib.pseudokeys import ( + PSEUDO_KEYS, + PSEUDOKEY_ALIASES, +) from onyo.lib.exceptions import ( InvalidInventoryOperationError, NoopError, @@ -14,7 +18,10 @@ ) from onyo.lib.inventory import Inventory, OPERATIONS_MAPPING from onyo.lib.onyo import OnyoRepo -from onyo.lib.items import Item +from onyo.lib.items import ( + Item, + ItemSpec, +) # TODO: - Inventory fixture(s) @@ -49,13 +56,14 @@ def test_add_asset(repo: OnyoRepo) -> None: newdir1 = inventory.root / "somewhere" newdir2 = newdir1 / "new" asset_file = newdir2 / "test_I_mk1.123" - asset = Item(some_key="some_value", - other='1', - directory=newdir2, - type="test", - make="I", - model=dict(name="mk1"), - serial="123") + asset = ItemSpec(alias_map=PSEUDOKEY_ALIASES, + some_key="some_value", + other='1', + directory=newdir2, + type="test", + make="I", + model=dict(name="mk1"), + serial="123") assert num_operations(inventory, 'new_assets') == 0 assert num_operations(inventory, 'new_directories') == 0 @@ -66,10 +74,15 @@ def test_add_asset(repo: OnyoRepo) -> None: assert num_operations(inventory, 'new_directories') == 2 operands = [op.operands for op in inventory.operations] assert all(isinstance(o, tuple) for o in operands) - assert (asset,) in operands assert (newdir1,) in operands assert (newdir2,) in operands + # assertions on the item in registered operation: + item_operands = [op[0] for op in operands if isinstance(op[0], Item)] + assert len(item_operands) == 1 + assert item_operands[0].equal_content(asset) + assert item_operands[0]["onyo.path.absolute"] == asset_file + # nothing done on disc yet: assert not asset_file.exists() assert not newdir2.exists() @@ -182,14 +195,14 @@ def test_move_asset(repo: OnyoRepo) -> None: newdir1 = repo.git.root / "somewhere" newdir2 = newdir1 / "new" asset_file = newdir2 / "test_I_mk1.123" - asset = Item( - some_key="some_value", - other='1', - directory=newdir2, - type="test", - make="I", - model=dict(name="mk1"), - serial="123") + asset = ItemSpec(alias_map=PSEUDOKEY_ALIASES, + some_key="some_value", + other='1', + directory=newdir2, + type="test", + make="I", + model=dict(name="mk1"), + serial="123") inventory.add_asset(asset) inventory.commit("First asset added") @@ -239,16 +252,17 @@ def test_rename_asset(repo: OnyoRepo) -> None: inventory = Inventory(repo) newdir1 = repo.git.root / "somewhere" newdir2 = newdir1 / "new" - asset = Item( - some_key="some_value", - type="TYPE", - make="MAKER", - model=dict(name="MODEL"), - serial="SERIAL", - other='1', - directory=newdir2) - inventory.add_asset(asset) + spec = ItemSpec(alias_map=PSEUDOKEY_ALIASES, + some_key="some_value", + type="TYPE", + make="MAKER", + model=dict(name="MODEL"), + serial="SERIAL", + other='1', + directory=newdir2) + inventory.add_asset(spec) inventory.commit("First asset added") + asset = inventory.get_item(newdir2 / inventory.generate_asset_name(spec)) # rename to itself raises: pytest.raises(NoopError, inventory.rename_asset, asset) @@ -266,21 +280,22 @@ def test_modify_asset(repo: OnyoRepo) -> None: newdir1 = repo.git.root / "somewhere" newdir2 = newdir1 / "new" asset_file = newdir2 / "TYPE_MAKER_MODEL.SERIAL" - asset = Item( - some_key="some_value", - type="TYPE", - make="MAKER", - model=dict(name="MODEL"), - serial="SERIAL", - other='1', - directory=newdir2) - inventory.add_asset(asset) + spec = ItemSpec(alias_map=PSEUDOKEY_ALIASES, + some_key="some_value", + type="TYPE", + make="MAKER", + model=dict(name="MODEL"), + serial="SERIAL", + other='1', + directory=newdir2) + inventory.add_asset(spec) inventory.commit("First asset added") + asset = inventory.get_item(newdir2 / inventory.generate_asset_name(spec)) asset_changes = dict(some_key="new_value", # arbitrary content change model=dict(name="") # empty required key ) - new_asset = asset.copy() + new_asset = deepcopy(asset) new_asset.update(asset_changes) # required keys must not be empty @@ -404,14 +419,14 @@ def test_remove_directory(repo: OnyoRepo) -> None: emptydir = newdir1 / "empty" does_not_exist = repo.git.root / 'does' / 'not' / 'exist' asset_file = newdir2 / "TYPE_MAKE_MODEL.SERIAL" - asset = Item( - some_key="some_value", - type="TYPE", - make="MAKE", - model=dict(name="MODEL"), - serial="SERIAL", - other='1', - directory=newdir2) + asset = ItemSpec(alias_map=PSEUDOKEY_ALIASES, + some_key="some_value", + type="TYPE", + make="MAKE", + model=dict(name="MODEL"), + serial="SERIAL", + other='1', + directory=newdir2) inventory.add_asset(asset) inventory.add_directory(Item(emptydir, repo=repo)) inventory.commit("First asset added") @@ -465,15 +480,15 @@ def test_move_directory(repo: OnyoRepo) -> None: newdir2 = newdir1 / "new" emptydir = newdir1 / "empty" asset_file = newdir2 / "asset_file" - asset = Item( - some_key="some_value", - type="TYPE", - make="MAKER", - model=dict(name="MODEL"), - serial="SERIAL", - other=1, - directory=newdir2) - inventory.add_asset(asset) + spec = ItemSpec(alias_map=PSEUDOKEY_ALIASES, + some_key="some_value", + type="TYPE", + make="MAKER", + model=dict(name="MODEL"), + serial="SERIAL", + other=1, + directory=newdir2) + inventory.add_asset(spec) inventory.add_directory(Item(emptydir, repo=repo)) inventory.commit("First asset added") asset = inventory.get_item(asset_file) @@ -528,14 +543,14 @@ def test_rename_directory(repo: OnyoRepo) -> None: newdir2 = newdir1 / "new" emptydir = newdir1 / "empty" asset_file = newdir2 / "asset_file" - asset = Item( - some_key="some_value", - type="TYPE", - make="MAKER", - model=dict(name="MODEL"), - serial="SERIAL", - other=1, - directory=newdir2) + asset = ItemSpec(alias_map=PSEUDOKEY_ALIASES, + some_key="some_value", + type="TYPE", + make="MAKER", + model=dict(name="MODEL"), + serial="SERIAL", + other=1, + directory=newdir2) inventory.add_asset(asset) inventory.add_directory(Item(emptydir, repo=repo)) inventory.commit("First asset added") @@ -587,13 +602,13 @@ def test_add_asset_dir(repo: OnyoRepo) -> None: inventory = Inventory(repo) asset_dir_path = inventory.root / "TYPE_MAKE_MODEL.SERIAL" - asset = Item( - some_key="some_value", - other=1, - type="TYPE", - make="MAKE", - model=dict(name="MODEL"), - serial="SERIAL") + asset = ItemSpec(alias_map=PSEUDOKEY_ALIASES, + some_key="some_value", + other=1, + type="TYPE", + make="MAKE", + model=dict(name="MODEL"), + serial="SERIAL") asset["onyo.path.absolute"] = asset_dir_path asset["onyo.is.directory"] = True inventory.add_asset(asset) @@ -644,12 +659,13 @@ def test_add_asset_dir(repo: OnyoRepo) -> None: inventory.add_directory(Item(dir_path, repo=repo)) inventory.commit("New inventory dir") - asset = Item(some_key="some_value", - other=1, - type="TYPE1", - make="MAKE1", - model=dict(name="MODEL1"), - serial="1X2") + asset = ItemSpec(alias_map=PSEUDOKEY_ALIASES, + some_key="some_value", + other=1, + type="TYPE1", + make="MAKE1", + model=dict(name="MODEL1"), + serial="1X2") asset["onyo.path.absolute"] = dir_path asset["onyo.is.directory"] = True expected_name = inventory.generate_asset_name(asset) @@ -663,7 +679,12 @@ def test_add_asset_dir(repo: OnyoRepo) -> None: assert num_operations(inventory, 'rename_directories') == 1 operands = [op.operands for op in inventory.operations] assert all(isinstance(o, tuple) for o in operands) - assert (asset,) in operands + + item_operands = [op[0] for op in operands if isinstance(op[0], Item)] + assert len(item_operands) == 1 + assert item_operands[0].equal_content(asset) + assert item_operands[0]["onyo.path.absolute"] == expected_path + assert (dir_path, expected_path) in operands # nothing done on disc yet @@ -709,17 +730,18 @@ def test_add_asset_dir(repo: OnyoRepo) -> None: def test_add_dir_asset(repo: OnyoRepo) -> None: inventory = Inventory(repo) - asset = Item( - some_key="some_value", - other=1, - type="TYPE1", - make="MAKE1", - model=dict(name="MODEL1"), - serial="1X2", - directory=inventory.root) - inventory.add_asset(asset) + spec = ItemSpec(alias_map=PSEUDOKEY_ALIASES, + some_key="some_value", + other=1, + type="TYPE1", + make="MAKE1", + model=dict(name="MODEL1"), + serial="1X2", + directory=inventory.root) + inventory.add_asset(spec) inventory.commit("Add an asset.") asset_path = inventory.root / "TYPE1_MAKE1_MODEL1.1X2" + asset = inventory.get_item(asset_path) # Add directory aspect to existing asset: inventory.add_directory(asset) @@ -760,21 +782,22 @@ def test_add_dir_asset(repo: OnyoRepo) -> None: def test_remove_asset_dir_directory(repo: OnyoRepo) -> None: inventory = Inventory(repo) asset_dir_path = inventory.root / "TYPE_MAKE_MODEL.SERIAL" - asset = Item( - some_key="some_value", - other=1, - type="TYPE", - make="MAKE", - model=dict(name="MODEL"), - serial="SERIAL") + asset = ItemSpec(alias_map=PSEUDOKEY_ALIASES, + some_key="some_value", + other=1, + type="TYPE", + make="MAKE", + model=dict(name="MODEL"), + serial="SERIAL") asset["onyo.path.absolute"] = asset_dir_path asset["onyo.is.directory"] = True inventory.add_asset(asset) - asset_within = Item(type="a", - make="b", - model=dict(name="c"), - serial="1A", - directory=asset_dir_path) + asset_within = ItemSpec(alias_map=PSEUDOKEY_ALIASES, + type="a", + make="b", + model=dict(name="c"), + serial="1A", + directory=asset_dir_path) inventory.add_asset(asset_within) inventory.commit("Whatever") @@ -823,21 +846,22 @@ def test_remove_asset_dir_directory(repo: OnyoRepo) -> None: def test_remove_asset_dir_asset(repo: OnyoRepo) -> None: inventory = Inventory(repo) asset_dir_path = inventory.root / "TYPE_MAKE_MODEL.SERIAL" - asset = Item( - some_key="some_value", - other=1, - type="TYPE", - make="MAKE", - model=dict(name="MODEL"), - serial="SERIAL") + asset = ItemSpec(alias_map=PSEUDOKEY_ALIASES, + some_key="some_value", + other=1, + type="TYPE", + make="MAKE", + model=dict(name="MODEL"), + serial="SERIAL") asset["onyo.path.absolute"] = asset_dir_path asset["onyo.is.directory"] = True inventory.add_asset(asset) - asset_within = Item(type="a", - make="b", - model=dict(name="c"), - serial="1A", - directory=asset_dir_path) + asset_within = ItemSpec(alias_map=PSEUDOKEY_ALIASES, + type="a", + make="b", + model=dict(name="c"), + serial="1A", + directory=asset_dir_path) inventory.add_asset(asset_within) inventory.commit("Whatever") inventory.remove_asset(asset) @@ -887,12 +911,13 @@ def test_move_asset_dir(repo: OnyoRepo) -> None: inventory = Inventory(repo) asset_dir_path = inventory.root / "TYPE_MAKE_MODEL.SERIAL" dir_path = inventory.root / "destination" - asset = Item(some_key="some_value", - other=1, - type="TYPE", - make="MAKE", - model=dict(name="MODEL"), - serial="SERIAL") + asset = ItemSpec(alias_map=PSEUDOKEY_ALIASES, + some_key="some_value", + other=1, + type="TYPE", + make="MAKE", + model=dict(name="MODEL"), + serial="SERIAL") asset["onyo.path.absolute"] = asset_dir_path asset["onyo.is.directory"] = True inventory.add_asset(asset) @@ -974,12 +999,13 @@ def test_rename_asset_dir(repo: OnyoRepo) -> None: inventory = Inventory(repo) asset_dir_path = inventory.root / "TYPE_MAKE_MODEL.SERIAL" - asset = Item(some_key="some_value", - other=1, - type="TYPE", - make="MAKE", - model=dict(name="MODEL"), - serial="SERIAL") + asset = ItemSpec(alias_map=PSEUDOKEY_ALIASES, + some_key="some_value", + other=1, + type="TYPE", + make="MAKE", + model=dict(name="MODEL"), + serial="SERIAL") asset["onyo.path.absolute"] = asset_dir_path asset["onyo.is.directory"] = True inventory.add_asset(asset) @@ -1043,7 +1069,7 @@ def test_modify_asset_dir(repo: OnyoRepo) -> None: newdir1 = repo.git.root / "somewhere" newdir2 = newdir1 / "new" asset_path = newdir2 / "TYPE_MAKE_MODEL.SERIAL" - asset = Item( + asset = ItemSpec( {"some_key": "some_value", "type": "TYPE", "make": "MAKE", @@ -1053,7 +1079,8 @@ def test_modify_asset_dir(repo: OnyoRepo) -> None: "onyo": {"is": {"asset": True, "directory": True}, "path": {"absolute": asset_path}} - } + }, + alias_map=PSEUDOKEY_ALIASES, ) inventory.add_asset(asset) inventory.commit("asset dir added")