Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
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,108 changes: 1,108 additions & 0 deletions .specify/specs/validate-machine-readable-output.md

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dandi/bids_validator_deno/_validator.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
from pydantic import DirectoryPath, validate_call

from dandi.utils import find_parent_directory_containing
from dandi.validate_types import (
from dandi.validate.types import (
Origin,
OriginType,
Scope,
Expand Down
11 changes: 9 additions & 2 deletions dandi/cli/cmd_upload.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,15 +96,21 @@ def upload(
can point to specific files you would like to validate and have uploaded.
"""
# Avoid heavy imports by importing with function:
from ..upload import upload
from ..upload import upload as upload_
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is better of course than clobbering the namespace, but in the long term a better name that distinguishes API vs CLI functions (with CLI usually marked private since it is odd to expose them to library) is still preferred

Which would be a breaking change, not saying do it here, just thinking out loud

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah -- typically we just clobbered click's interfaces (sorry didn't interleave... but at least manually pruned some unrelated)

❯ grep -l 'from \.\.\(.*\) import \1' dandi/cli/cmd_* | xargs grep '^def '
dandi/cli/cmd_delete.py:def delete(paths, skip_missing, dandi_instance, force, devel_debug=False):
dandi/cli/cmd_download.py:def download(
dandi/cli/cmd_move.py:def move(
dandi/cli/cmd_organize.py:def organize(
dandi/cli/cmd_upload.py:def upload(
dandi/cli/cmd_validate.py:def validate_bids(
dandi/cli/cmd_validate.py:def validate(
❯ grep 'from \.\.\(.*\) import \1' dandi/cli/cmd_*
dandi/cli/cmd_delete.py:    from ..delete import delete
dandi/cli/cmd_download.py:    from .. import download
dandi/cli/cmd_move.py:    from .. import move as move_mod
dandi/cli/cmd_organize.py:    from ..organize import organize
dandi/cli/cmd_upload.py:    from ..upload import upload
dandi/cli/cmd_validate.py:from ..validate import validate as validate_

but frankly and unfortunately here it doesn't matter much since those click functions are not usable as python interfaces! I wish it was otherwise. So, no point of giving them any special names really.

Copy link
Copy Markdown
Contributor

@CodyCBakerPhD CodyCBakerPhD Mar 26, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

but frankly and unfortunately here it doesn't matter much since those click functions are not usable as python interfaces!

How do you mean? Any other library can import them and manipulate the click groups; sometimes that might even be intentional, but I don't think so here

from dandi.cli.command import main
from dandi.cli.cmd_upload import upload  # Implying it is not private and intended to be imported and customized

import click

@main.command("upload2")  # Or even re-register under the same name if attempting some nasty injection
@click.pass_context
def wrapped_original(ctx):
    click.echo("Before original")  # Inject custom code here
    ctx.invoke(upload)
    click.echo("After original")  # Inject more custom code here

from ..validate.io import validation_sidecar_path

if jobs_pair is None:
jobs = None
jobs_per_file = None
else:
jobs, jobs_per_file = jobs_pair

upload(
sidecar = None
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

IDK about referring to this as a 'sidecar' since that could get confusing with BIDS language

What is meant is, specifically, might be 'persistent file recording the validation results of this Dandiset'?

Even referring to it as a 'log' could get confusing with our own log file terminology (as in, the ones that contain runtime errors rather than validations)

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

indeed there is a clash with "sidecar" in BIDS but overall it is the same meaning, just rarely used outside of BIDS. ... but here we could just call it validation_log_path for the variable.

as for the helper , ATM no critically better name instead of current validation_sidecar_path comes to mind.

note that the validation log is not necessarily of a dandiset -- could be of nwb files AFAIK, or potentially multiple dandisets... in this case what matters is that it is associated with the same run for which there is a log.

ctx = click.get_current_context()
if ctx.obj is not None:
sidecar = validation_sidecar_path(ctx.obj.logfile)

upload_(
paths,
existing=existing,
validation=validation,
Expand All @@ -115,4 +121,5 @@ def upload(
jobs=jobs,
jobs_per_file=jobs_per_file,
sync=sync,
validation_log_path=sidecar,
)
Loading
Loading