Skip to content

Commit

Permalink
TMP: to be enhanced and broken into different commits
Browse files Browse the repository at this point in the history
  • Loading branch information
bpoldrack committed Dec 16, 2024
1 parent 5d26295 commit 8a58406
Show file tree
Hide file tree
Showing 7 changed files with 264 additions and 56 deletions.
2 changes: 1 addition & 1 deletion onyo/lib/git.py
Original file line number Diff line number Diff line change
Expand Up @@ -418,7 +418,7 @@ def _parse_log_output(self, lines: list[str]) -> dict:
commit = dict()
for line in lines:
if line.startswith('commit '):
commit['commit'] = line.split()[1]
commit['hexsha'] = line.split()[1]
continue
elif line.startswith('Author:'):
try:
Expand Down
74 changes: 62 additions & 12 deletions onyo/lib/items.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

from copy import deepcopy
from pathlib import Path
from typing import TYPE_CHECKING

Expand Down Expand Up @@ -51,7 +52,10 @@ def __init__(self,
self.update(onyo.lib.pseudokeys.PSEUDO_KEYS)
self._path = None

if isinstance(item, Path):
if isinstance(item, Item):
self._path = item._path
self.data = deepcopy(item.data)
elif isinstance(item, Path):
assert item.is_absolute() # currently no support for relative. This is how all existing code should work ATM.
self.update_from_path(item)
elif item is not None:
Expand Down Expand Up @@ -95,15 +99,51 @@ def update_from_path(self, path: Path):
loader = self.repo.get_asset_content if self.repo else get_asset_content
self.update(loader(path))

# TODO: git metadata:
# def fill_created(self, what: str | None):
# # self.repo.git.xxx (self.get('onyo.path.file'))
# # fill in all created keys
# # switch `what` -> return whatever part was requesting this
# pass#raise NotImplementedError
#
# def fill_modified(self, what: str | None):
# pass#raise NotImplementedError
def fill_created(self, what: str | None):
"""Initializer for the 'onyo.was.modified' pseudo-keys.
Fills in the entire sub-dict and returns the value specified by ``what``.
Note/TODO:
----------
This is currently based on ``git log --follow <path>``. Looking back, the first appearance
of a 'new_assets'/'new_directories' operation should be it, assuming the history
was created by onyo commands.
However, if the history was created using the python interface, that assumption wouldn't hold
and we'd have to trace back moves and renames in order to know what path or file name we are looking
to match against these operations.
"""
if self.repo and self['onyo.path.absolute']:
for commit in self.repo.get_history(self['onyo.path.file']): # pyre-ignore[16]
if 'operations' in commit:
if (self['onyo.is.asset'] and commit['operations']['new_assets']) or \
(self['onyo.is.directory'] and commit['operations']['new_directories']):
self['onyo.was.created'] = commit.data
return commit[what] if what else None
return None

def fill_modified(self, what: str | None):
"""Initializer for the 'onyo.was.modified' pseudo-keys.
Fills in the entire sub-dict and returns the value specified by ``what``.
Note/TODO:
----------
See ``fill_created``.
"""
if self.repo and self['onyo.path.absolute']:
for commit in self.repo.get_history(self['onyo.path.file']): # pyre-ignore[16]
if 'operations' in commit:
if (self['onyo.is.asset'] and
(commit['operations']['modify_assets'] or
commit['operations']['new_assets'])) or \
(self['onyo.is.directory'] and
(commit['operations']['new_directories'] or
commit['operations']['move_directories'] or
commit['operations']['rename_directories'])):
self['onyo.was.modified'] = commit.data
return commit[what] if what else None
return None

def get_path_absolute(self):
"""Initializer for the 'onyo.path.absolute' pseudo-key."""
Expand All @@ -125,7 +165,14 @@ def get_path_parent(self):

def get_path_file(self):
"""Initializer for the 'onyo.path.file' pseudo-key."""
return self._path.relative_to(self.repo.git.root) if self.repo and self['onyo.is.asset'] else None
if self.repo and self['onyo.path.absolute']:
if self['onyo.is.asset'] and not self['onyo.is.directory']:
return self['onyo.path.relative']
if self['onyo.is.asset'] and self['onyo.is.directory']:
return self['onyo.path.relative'] / onyo.lib.onyo.OnyoRepo.ASSET_DIR_FILE_NAME
if self['onyo.is.directory'] and not self['onyo.is.asset']:
return self['onyo.path.relative'] / onyo.lib.onyo.OnyoRepo.ANCHOR_FILE_NAME
return None

def is_asset(self) -> bool | None:
"""Initializer for the 'onyo.is.asset' pseudo-key."""
Expand All @@ -147,7 +194,10 @@ def is_template(self) -> bool | None:

def is_empty(self) -> bool | None:
"""Initializer for the 'onyo.is.empty' pseudo-key."""
return None # TODO: Unclear what exactly this needs to consider. Probably child items? Or just assets?
if self['onyo.is.directory'] and self.repo and self._path:
# TODO: This likely can be faster when redoing/enhancing caching of repo paths.
return not any(p.parent == self._path for p in self.repo.get_asset_paths())
return None

# TODO/Notes for next PR(s):
# - Bug/Missing feature: pseudo-keys that are supposed to be settable by commands, are not yet
Expand Down
3 changes: 2 additions & 1 deletion onyo/lib/onyo.py
Original file line number Diff line number Diff line change
Expand Up @@ -746,6 +746,7 @@ def get_history(self, path: Path | None = None, n: int | None = None):
# or have sort of a proxy in OnyoRepo.
# -> May be: get_history(Item) in Inventory and get_history(path) in OnyoRepo.
from onyo.lib.decoder import parse_operations_record
from onyo.lib.utils import DotNotationWrapper
for commit in self.git.follow(path, n):
record = []
start = False
Expand All @@ -757,4 +758,4 @@ def get_history(self, path: Path | None = None, n: int | None = None):
if record:

commit['operations'] = parse_operations_record(record)
yield commit
yield DotNotationWrapper(commit)
75 changes: 48 additions & 27 deletions onyo/lib/pseudokeys.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,12 @@ class PseudoKey:
This Callable is expected to have a single parameter of type `Item`.
"""

def __eq__(self, other):
if not isinstance(other, PseudoKey):
return False

# TODO: This isn't clean yet, since it relies on `implementation` being a `partial`:
return self.description == other.description and self.implementation.func == other.implementation.func

def delegate(self: Item, attribute: str, *args, **kwargs):
# This is to avoid circular imports ATM.
Expand Down Expand Up @@ -56,6 +62,48 @@ def delegate(self: Item, attribute: str, *args, **kwargs):
'onyo.is.empty': PseudoKey(description="Is the directory empty. <unset> if the item is not a directory.",
implementation=partial(delegate, attribute='is_empty')
),
'onyo.was.modified.hexsha': PseudoKey(description="SHA of the last commit that modified the item.",
implementation=partial(delegate, attribute='fill_modified', what='hexsha')
),
'onyo.was.modified.time': PseudoKey(description="Time of the last commit that modified the item.",
implementation=partial(delegate, attribute='fill_modified', what='time')),
'onyo.was.modified.author.name': PseudoKey(
description="Name of the author of the last commit that modified the item.",
implementation=partial(delegate, attribute='fill_modified', what='author.time')
),
'onyo.was.modified.author.email': PseudoKey(
description="Email of the author of the last commit that modified the item.",
implementation=partial(delegate, attribute='fill_modified', what='author.email')
),
'onyo.was.modified.committer.name': PseudoKey(
description="Name of the committer of the last commit that modified the item.",
implementation=partial(delegate, attribute='fill_modified', what='committer.name')
),
'onyo.was.modified.committer.email': PseudoKey(
description="Email of the committer of the last commit that modified the item.",
implementation=partial(delegate, attribute='fill_modified', what='committer.email')
),
'onyo.was.created.hexsha': PseudoKey(description="SHA of the last commit that created the item.",
implementation=partial(delegate, attribute='fill_created', what='hexsha')
),
'onyo.was.created.time': PseudoKey(description="Time of the last commit that created the item.",
implementation=partial(delegate, attribute='fill_created', what='time')),
'onyo.was.created.author.name': PseudoKey(
description="Name of the author of the last commit that created the item.",
implementation=partial(delegate, attribute='fill_created', what='author.time')
),
'onyo.was.created.author.email': PseudoKey(
description="Email of the author of the last commit that created the item.",
implementation=partial(delegate, attribute='fill_created', what='author.email')
),
'onyo.was.created.committer.name': PseudoKey(
description="Name of the committer of the last commit that created the item.",
implementation=partial(delegate, attribute='fill_created', what='committer.name')
),
'onyo.was.created.committer.email': PseudoKey(
description="Email of the committer of the last commit that created the item.",
implementation=partial(delegate, attribute='fill_created', what='committer.email')
),
}
r"""Pseudo-Keys are key names that are addressable but not written to disk in asset YAML.
Expand All @@ -66,33 +114,6 @@ def delegate(self: Item, attribute: str, *args, **kwargs):
RESERVED_KEYS
"""

# 'onyo.git.created.time': PseudoKey(description="Datetime the inventory item was created.",
# implementation=partial(delegate,
# attribute='fill_created',
# what='time')
# # or onyo.git.created.time for an "return self.get(what)" in implementation?
# ),
# 'onyo.git.created.commit': PseudoKey(description="Commit SHA of the commit the object was created in",
# implementation=partial(delegate,
# attribute='fill_created',
# what='SHA')
# ),

# 'onyo.git.created.committer.name': None,
# 'onyo.git.created.committer.email': None,
# 'onyo.git.created.author.name': None,
# 'onyo.git.created.author.email': None,
# 'onyo.git.modified.time': None,
# 'onyo.git.modified.commit': None,
# 'onyo.git.modified.committer.name': None,
# 'onyo.git.modified.committer.email': None,
# 'onyo.git.modified.author.name': None,
# 'onyo.git.modified.author.email': None,
#
# },
# }
# }

# Hardcode aliases for now:
# Introduction of proper aliases requires config cache first.
PSEUDOKEY_ALIASES: Dict[str, str] = {
Expand Down
8 changes: 6 additions & 2 deletions onyo/lib/tests/test_commands_new.py
Original file line number Diff line number Diff line change
Expand Up @@ -265,8 +265,12 @@ def test_onyo_new_clones(inventory: Inventory) -> None:
assert inventory.repo.is_asset_path(new_asset_path1)
new_asset = inventory.get_asset(new_asset_path1)
# equals existing asset except for path-pseudo-keys and serial:
# Actually: history differs as well. onyo.is. doesn't, though
for k, v in existing_asset.items():
if k != "serial" and not k.startswith('onyo.path.') and not k.startswith('onyo.was.'):
assert v == new_asset[k], f"{k}: {v} != {new_asset[k]}"
assert all(v == new_asset[k] for k, v in existing_asset.items()
if k != "serial" and not k.startswith('onyo.path'))
if k != "serial" and not k.startswith('onyo.path') and not k.startswith('onyo.was.'))
assert new_asset['serial'] == 'ANOTHER'

# second new asset
Expand All @@ -275,7 +279,7 @@ def test_onyo_new_clones(inventory: Inventory) -> None:
new_asset = inventory.get_asset(new_asset_path2)
# equals existing asset except for path-pseudo-keys and serial:
assert all(v == new_asset[k] for k, v in existing_asset.items()
if k != "serial" and not k.startswith('onyo.path'))
if k != "serial" and not k.startswith('onyo.path') and not k.startswith('onyo.was.'))
assert new_asset['serial'] == 'whatever'
assert inventory.repo.git.is_clean_worktree()

Expand Down
Loading

0 comments on commit 8a58406

Please sign in to comment.