Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
66 changes: 46 additions & 20 deletions packages/timenet-connectors/src/timenet_connectors/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,56 +24,82 @@ def build( # ruff: ignore[too-many-arguments]
fetch_from: "str | BaseRegistry | None" = None,
build_parents: bool = False,
) -> Path:
# ruff: ignore[docstring-extraneous-exception] (raised by build_release, not directly here)
# ruff: ignore[docstring-extraneous-exception] (raised by the plan and its run, not directly here)
"""Build a dataset release in a local registry.

The build reuses an existing release unless you set ``force``. The connector runs in an environment
with its required packages, or in this interpreter if ``TIMENET_ISOLATION=off``.
The build reuses an existing release unless ``force`` is set. The connector runs in an environment
from its ``requirements.txt``, or in this interpreter if ``TIMENET_ISOLATION=off``.

Args:
dataset_id: The connector's dataset ID (``org/name``).
version: The version to build, or ``None`` for the connector's declared version.
out: The output directory, or ``None`` to use the default local registry.
force: Rebuild the release even if it exists in the registry.
keep_cache: Keep downloaded source files after a successful build.
fetch_from: The registry, or its URI, to copy missing parent releases from.
build_parents: Build missing parent releases from their installed connectors.
dataset_id: The ``org/name`` ID declared in the connector's card.
version: The required card version, or ``None`` to accept any version.
out: The local registry directory, or ``None`` to use the default local registry.
force: Rebuild an existing release.
keep_cache: Keep the raw download cache after a successful build.
fetch_from: The registry or URI to copy missing parents from before trying builds.
build_parents: Build missing parents with installed connectors.

Returns:
The release's version directory.
The version directory of the release.

Raises:
TimeNetDatasetNotFoundError: If no installed connector builds ``dataset_id``.
TimeNetVersionUnavailableError: If ``version`` differs from the connector's declared version.
TimeNetMissingParentError: If a required parent release is still missing.
TimeNetVersionUnavailableError: If the card's version differs from ``version``.
TimeNetMissingParentError: If a required release cannot be copied or built.
"""
from timenet.builders import BuildOptions, declared # ruff: ignore[import-outside-top-level]
from timenet.coordinator.build import build_release # ruff: ignore[import-outside-top-level]
from timenet.coordinator.build import plan_release_build, run_plan # ruff: ignore[import-outside-top-level]
from timenet.errors import TimeNetMissingParentError # ruff: ignore[import-outside-top-level]
from timenet.registry import ( # ruff: ignore[import-outside-top-level]
LocalRegistry,
default_registry_path,
open_registry,
)
from timenet.registry._paths import release_dir # ruff: ignore[import-outside-top-level, import-private-name]
from timenet.sources import InstalledConnectors # ruff: ignore[import-outside-top-level]

release = declared(dataset_id).release
root = Path(out).expanduser() if out is not None else default_registry_path()
source = open_registry(fetch_from) if isinstance(fetch_from, str) else fetch_from
store = LocalRegistry(root)
try:
build_release(
plan = plan_release_build(
f"{dataset_id}@{version}" if version else dataset_id,
store=LocalRegistry(root),
fetch_from=open_registry(fetch_from) if isinstance(fetch_from, str) else fetch_from,
build_parents=build_parents,
store=store,
# TODO(adrian): Remove temporary glue which maps fetch_from and build_parents onto a
Comment thread
karhu marked this conversation as resolved.
Resolved
# list of sources until the producer API replaces this function.
parents_from=[
*([source] if source is not None else []),
*([InstalledConnectors()] if build_parents else []),
],
force=force,
options=BuildOptions(keep_cache=keep_cache),
)
run_plan(plan, store, options=BuildOptions(keep_cache=keep_cache))
except TimeNetMissingParentError as error:
error.hint = "Copy it with fetch_from=<registry> or build it with build_parents=True."
error.hint = _missing_hint(fetch_from=fetch_from, build_parents=build_parents)
raise
return release_dir(root, release)


def _missing_hint(*, fetch_from: "str | BaseRegistry | None", build_parents: bool) -> str | None:
Comment thread
karhu marked this conversation as resolved.
Resolved
"""Suggest options to obtain a missing release.

Args:
fetch_from: The selected registry or URI, or ``None``.
build_parents: Whether parent builds are enabled.

Returns:
A sentence naming unused options, or ``None`` if both are enabled.
"""
if fetch_from is None and not build_parents:
return "Copy it with fetch_from=<registry> or build it with build_parents=True."
if fetch_from is None:
return "Copy it with fetch_from=<registry>."
if not build_parents:
return "Build it with build_parents=True."
return None


def load(
dataset_id: str,
version: str | None = None,
Expand Down
44 changes: 35 additions & 9 deletions packages/timenet-connectors/src/timenet_connectors/builder/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from timenet.cli.runner import run_cli
from timenet.cli.ui import console, progress_renderer
from timenet.config import settings
from timenet.coordinator.build import build_release
from timenet.coordinator.build import plan_release_build, run_plan
from timenet.errors import (
TimeNetAccessError,
TimeNetFileError,
Expand All @@ -33,6 +33,7 @@
open_writable_registry,
)
from timenet.registry._paths import release_dir # ruff: ignore[import-private-name]
from timenet.sources import InstalledConnectors
from timenet.types.references import DatasetRef, is_latest, split_ref
from timenet_connectors.builder.backend import ConnectorBuilder
from timenet_connectors.discovery import known_ids
Expand Down Expand Up @@ -160,7 +161,7 @@ def build(
),
)
except TimeNetMissingParentError as error:
error.hint = "Fetch it with --fetch-from <registry> or build it with --build-parents."
error.hint = _missing_hint(fetch_from=fetch_from, build_parents=build_parents)
raise
except TimeNetFileError as error:
error.hint = (
Expand All @@ -175,6 +176,27 @@ def build(
typer.echo(result)


def _missing_hint(*, fetch_from: str | None, build_parents: bool) -> str | None:
"""Suggest flags to obtain a missing release.

Args:
fetch_from: The value of ``--fetch-from``, or ``None``.
build_parents: Whether ``--build-parents`` is set.

Returns:
A sentence naming unused flags, or ``None`` if both are set.
"""
ways = []
if fetch_from is None:
ways.append("fetch it with --fetch-from <registry>")
if not build_parents:
ways.append("build it with --build-parents")
if not ways:
return None
hint = " or ".join(ways)
return f"{hint[0].upper()}{hint[1:]}."


def _build_to( # ruff: ignore[too-many-arguments]
dataset_id: str,
target: WritableRegistry | Path,
Expand All @@ -192,12 +214,12 @@ def _build_to( # ruff: ignore[too-many-arguments]
dataset_id: The dataset ID.
target: The local directory or remote registry to write to.
fetch_from: The registry to copy missing parents from. ``None`` uses a remote output, if any.
build_parents: Build missing parents with their installed connectors.
force: Rebuild the release even if the local registry holds it.
build_parents: Build missing parents with installed connectors.
force: Rebuild an existing local release.
options: Settings for each build.

Returns:
The version directory for a local output, or the version string for a remote output.
The local version directory or the remote version string.

Raises:
BadParameter: If ``force`` is set and the remote output already holds the version.
Expand All @@ -221,15 +243,19 @@ def _build_to( # ruff: ignore[too-many-arguments]
fetch_from = fetch_from if fetch_from is not None else remote
# A remote registry holds only open releases. So for a remote output, the build refuses another
# release before any download, as a build into that registry would.
plan = build_release(
plan = plan_release_build(
dataset_id,
store=store,
fetch_from=fetch_from,
build_parents=build_parents,
# TODO(adrian): Remove temporary glue which maps --fetch-from and --build-parents onto a
# list of sources until the flags of the defaults replace them.
parents_from=[
*([fetch_from] if fetch_from is not None else []),
*([InstalledConnectors()] if build_parents else []),
],
force=force,
open_access_only=remote is not None,
options=options,
)
run_plan(plan, store, options=options)
if remote is not None:
copy_release(release, store, remote, progress=options.progress)
return str(release.version)
Expand Down
Loading
Loading