Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
e689678
update: add yaml dep and config
synchon Dec 17, 2025
c023dce
update: add h5io, tqdm as deps and _utils.py
synchon Dec 17, 2025
c51aab6
feat(cli): introduce add command
synchon Dec 17, 2025
32b2e8d
feat(functions): introduce add command
synchon Dec 17, 2025
4c6a294
chore(functions): update create
synchon Dec 17, 2025
ff90bd8
update(utils): improve feature yaml generation
synchon Mar 2, 2026
6e06fa0
update(utils): improve feature meta yaml generation
synchon Mar 2, 2026
7b2f8ce
update(utils): improve storage hdf5 processing
synchon Mar 2, 2026
13f688a
update(functions): add dataset_display_name to add
synchon Mar 2, 2026
8b1b019
update(functions): copy default registry-config.yml in create
synchon Mar 2, 2026
eab51df
update(cli): add dataset_display_name in create
synchon Mar 2, 2026
35ef2e4
chore(utils): code clean up
synchon Mar 2, 2026
decd53e
update(utils): add dataset_display_name in process_features
synchon Mar 2, 2026
c67881f
update(tests): add test for add
synchon Mar 2, 2026
2043da4
chore: add workdir section in tests/feature.yml
synchon Mar 2, 2026
5b928fc
fix(tests): make add test work
synchon Mar 3, 2026
fba5788
chore(functions): fix statements
synchon Mar 3, 2026
d94634f
chore(functions): add missing shutil import
synchon Mar 3, 2026
e212482
chore: add missing package import
synchon Mar 3, 2026
c99073b
chore(functions): add no cover pragma for add
synchon Mar 10, 2026
33187c8
tests(functions): improve test for add
synchon Mar 10, 2026
424f5e1
update: improve tests for add
synchon Mar 11, 2026
0b2e020
chore: add and update no cover pragma
synchon Mar 11, 2026
97cbc18
chore: update tox.ini
synchon Mar 11, 2026
bbf525a
chore: fix no cover pragma
synchon Mar 11, 2026
f288050
chore: update valid_extra.yml
synchon Mar 11, 2026
355e173
update: improve tests for add
synchon Mar 11, 2026
a8784cc
chore: lint
synchon Mar 11, 2026
5d2018c
chore: add changelog
synchon Mar 11, 2026
bf010ab
chore: remove ruamel.yaml and use junifer's yaml config
synchon May 29, 2026
3d0930e
update: use junifer to parse and generate yaml
synchon May 29, 2026
817e050
chore: improve datetime imports
synchon May 29, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/3.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Introduce feature files addition to registry
3 changes: 2 additions & 1 deletion julio/__init__.pyi
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
__all__ = [
"add",
"create",
]

from ._functions import create
from ._functions import add, create
51 changes: 51 additions & 0 deletions julio/_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,14 @@
import logging.config
import pathlib
import sys
from pathlib import Path

import click
import datalad
import structlog

from . import _functions as cli_func
from . import _utils as cli_utils


__all__ = ["cli", "create"]
Expand Down Expand Up @@ -152,3 +154,52 @@ def create(
click.echo(f"{err}", err=True)
else:
click.echo("Success")


@cli.command
@click.argument(
"yaml_path",
type=click.Path(
exists=True,
readable=True,
writable=True,
dir_okay=False,
path_type=pathlib.Path,
),
metavar="<yaml>",
)
@click.option(
"-r",
"--registry",
default=None,
type=cli_utils.PathOrURL,
metavar="<registry>",
help="Path to registry; if not passed, use current directory",
)
@click.option(
"-d",
"--dataset-display-name",
default=None,
type=str,
metavar="<dataset-display-name>",
help="Dataset display name; if not passed, will use the name from YAML",
)
@click.option("-v", "--verbose", count=True, type=int)
def add(
yaml_path: click.Path,
registry: click.Path,
dataset_display_name: str,
verbose: int,
) -> None:
"""Add feature(s) registry."""
_set_log_config(verbose)
try:
cli_func.add(
yaml_path=yaml_path,
registry_path=registry if registry is not None else Path("."),
dataset_display_name=dataset_display_name,
)
except RuntimeError as err:
click.echo(f"{err}", err=True)
else:
click.echo("Success")
85 changes: 81 additions & 4 deletions julio/_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,20 +3,24 @@
# Authors: Synchon Mandal <s.mandal@fz-juelich.de>
# License: AGPL

import shutil
import tempfile
from pathlib import Path

import datalad.api as dl
import structlog
from datalad.support.exceptions import IncompleteResultsError

from ._utils import is_julio_registry, process_features

__all__ = ["create"]

__all__ = ["add", "create"]


logger = structlog.get_logger()


def create(registry_path: Path):
def create(registry_path: Path) -> None:
"""Create a registry at `registry_path`.

Parameters
Expand Down Expand Up @@ -46,11 +50,84 @@ def create(registry_path: Path):
path=str(registry_path.resolve()),
)
# Add config file
conf_path = Path(ds.path) / "registry-config.yml"
conf_path.touch()
conf_path = ds.pathobj / "registry-config.yml"
shutil.copy(
src=Path(__file__).parent / "registry-config.yml",
dst=conf_path,
)
ds.save(
conf_path,
message="[julio] add registry configuration",
on_failure="stop",
result_renderer="disabled",
)


def add(
yaml_path: Path,
registry_path: str | Path,
dataset_display_name: str | None,
) -> None:
"""Add feature(s) from `yaml_path` to the registry at `registry_path`.

Parameters
----------
yaml_path : pathlib.Path
Path to the junifer YAML.
registry_path : str or pathlib.Path
Path to the existing julio registry.
dataset_display_name : str or None
Dataset display name.

Raises
------
RuntimeError
If there is a problem cloning a remote registry or
if the dataset is not a julio registry.

"""
log = logger.bind(
cmd="add",
path=registry_path if str else str(registry_path.resolve()),
)
if isinstance(registry_path, str): # pragma: no cover
log.debug("Cloning remote registry")
with tempfile.TemporaryDirectory() as tmpdir:
log.debug(f"Temporary directory created at {tmpdir}")
# Clone the remote registry
try:
ds = dl.clone(
source=registry_path,
path=tmpdir,
on_failure="stop",
result_renderer="disabled",
)
except IncompleteResultsError as e:
raise RuntimeError(
f"Failed to clone dataset: {e.failed}"
) from e
else:
log.debug("Remote registry cloned successfully")
if not is_julio_registry(ds):
raise RuntimeError(
f"Dataset at {ds.path} is not a julio registry"
)
# Add features
process_features(yaml_path, ds, dataset_display_name)
# Push changes to remote registry
try:
ds = dl.push(
on_failure="stop",
result_renderer="disabled",
)
except IncompleteResultsError as e:
raise RuntimeError(
f"Failed to push dataset: {e.failed}"
) from e
else:
log.debug("Pushed changes to remote registry successfully")
else:
ds = dl.Dataset(registry_path)
if not is_julio_registry(ds):
raise RuntimeError(f"Dataset at {ds.path} is not a julio registry")
process_features(yaml_path, ds, dataset_display_name)
Loading
Loading