From ae19957db0e01a2650dd0389dee9370a2918c869 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 21 Nov 2025 20:42:07 +0000 Subject: [PATCH 1/7] Support all known instances in short URL format Co-authored-by: yarikoptic <39889+yarikoptic@users.noreply.github.com> --- dandi/dandiarchive.py | 4 +-- dandi/tests/test_dandiarchive.py | 49 ++++++++++++++++++++++++++++++++ 2 files changed, 51 insertions(+), 2 deletions(-) diff --git a/dandi/dandiarchive.py b/dandi/dandiarchive.py index 9da715faa..ae16913ec 100644 --- a/dandi/dandiarchive.py +++ b/dandi/dandiarchive.py @@ -589,13 +589,13 @@ class _dandi_url_parser: # for not only "dandiarchive.org" URLs ( re.compile( - rf"(?PDANDI):" + rf"(?P[-\w._]+):" rf"{dandiset_id_grp}" rf"(/(?P{VERSION_REGEX}))?", flags=re.I, ), {}, - "DANDI:[/]", + ":[/]", ), ( re.compile(r"https?://gui\.dandiarchive\.org/.*"), diff --git a/dandi/tests/test_dandiarchive.py b/dandi/tests/test_dandiarchive.py index 2cbf65752..dc4bfa0ee 100644 --- a/dandi/tests/test_dandiarchive.py +++ b/dandi/tests/test_dandiarchive.py @@ -117,6 +117,55 @@ version_id="0.210831.2033", ), ), + # Test other instances with short format + ( + "DANDI-SANDBOX:000029", + DandisetURL( + instance=known_instances["dandi-sandbox"], + dandiset_id="000029", + version_id=None, + ), + ), + ( + "dandi-sandbox:000029/draft", + DandisetURL( + instance=known_instances["dandi-sandbox"], + dandiset_id="000029", + version_id="draft", + ), + ), + ( + "LINC:000029", + DandisetURL( + instance=known_instances["linc"], + dandiset_id="000029", + version_id=None, + ), + ), + ( + "linc:000029/0.210831.2033", + DandisetURL( + instance=known_instances["linc"], + dandiset_id="000029", + version_id="0.210831.2033", + ), + ), + ( + "EMBER:000029", + DandisetURL( + instance=known_instances["ember"], + dandiset_id="000029", + version_id=None, + ), + ), + ( + "ember-sandbox:000029/draft", + DandisetURL( + instance=known_instances["ember-sandbox"], + dandiset_id="000029", + version_id="draft", + ), + ), ( "http://localhost:8000/api/dandisets/000002/", DandisetURL( From 1bc12f8d056375b7973e0c9460b3e91a51ff92be Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Fri, 21 Nov 2025 21:54:39 +0000 Subject: [PATCH 2/7] Build instance name pattern from known_instances - Changed regex from generic `[-\w._]+` to pattern composed from known instance names - Prevents false matches like `http:000001` or other invalid patterns - Pattern is built dynamically: dandi|dandi-staging|dandi-sandbox|...|ember-sandbox - All existing tests pass, invalid patterns now correctly rejected Co-authored-by: yarikoptic <39889+yarikoptic@users.noreply.github.com> --- dandi/dandiarchive.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/dandi/dandiarchive.py b/dandi/dandiarchive.py index ae16913ec..59dbfe34b 100644 --- a/dandi/dandiarchive.py +++ b/dandi/dandiarchive.py @@ -573,6 +573,8 @@ class _dandi_url_parser: dandiset_id_grp = f"(?P{DANDISET_ID_REGEX})" # Should absorb port and "api/": server_grp = "(?P(?Phttps?)://(?P[^/]+)/(api/)?)" + # Build instance name pattern from known instances to avoid matching unknown patterns + instance_name_pattern = "|".join(re.escape(name) for name in known_instances.keys()) known_urls: list[tuple[re.Pattern[str], dict[str, Any], str]] = [ # List of (regex, settings, display string) triples # @@ -589,7 +591,7 @@ class _dandi_url_parser: # for not only "dandiarchive.org" URLs ( re.compile( - rf"(?P[-\w._]+):" + rf"(?P{instance_name_pattern}):" rf"{dandiset_id_grp}" rf"(/(?P{VERSION_REGEX}))?", flags=re.I, From bee26552a27c4fca37927b5ac7bb0f71ee866013 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Thu, 27 Nov 2025 17:17:22 +0000 Subject: [PATCH 3/7] Update documentation to reflect all known instances support in short URL format Co-authored-by: yarikoptic <39889+yarikoptic@users.noreply.github.com> --- docs/source/ref/urls.rst | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/source/ref/urls.rst b/docs/source/ref/urls.rst index 8c6811008..d314d6d6a 100644 --- a/docs/source/ref/urls.rst +++ b/docs/source/ref/urls.rst @@ -17,8 +17,10 @@ has one, and its draft version will be used otherwise. (case insensitive; ``version`` cannot be "draft") when it redirects to one of the other URL formats -- :samp:`DANDI:{dandiset-id}[/{version}]` (case insensitive) - — Refers to a Dandiset on the main DANDI Archive instance named "dandi". +- :samp:`{instance-name}:{dandiset-id}[/{version}]` (case insensitive, + where ``instance-name`` is a known DANDI instance such as ``DANDI``, + ``DANDI-SANDBOX``, ``LINC``, ``EMBER``, etc.) + — Refers to a Dandiset on the specified DANDI Archive instance. `parse_dandi_url()` converts this format to a `DandisetURL`. - Any ``https://gui.dandiarchive.org/`` or From 6bd8500bbb839c68b88c49137faf5f4b61dcd58d Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 1 Dec 2025 19:38:22 +0000 Subject: [PATCH 4/7] Fix test_helptext to expect : instead of DANDI: Co-authored-by: yarikoptic <39889+yarikoptic@users.noreply.github.com> --- dandi/tests/test_helptext.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/dandi/tests/test_helptext.py b/dandi/tests/test_helptext.py index b72062112..7495f319b 100644 --- a/dandi/tests/test_helptext.py +++ b/dandi/tests/test_helptext.py @@ -13,7 +13,7 @@ def get_helptext(command): def test_resource_identifier_helptext(): # The \n chars must be included for correct rendering - correct = "Accepted resource identifier patterns:\n - DANDI:[/]\n" + correct = "Accepted resource identifier patterns:\n - :[/]\n" ls_helptext = get_helptext(['dandi', 'ls']) assert correct in ls_helptext From 6d1bc0630613bb31f04df6ff84cbc1afa4f189f7 Mon Sep 17 00:00:00 2001 From: Yaroslav Halchenko Date: Wed, 3 Dec 2025 15:48:22 -0500 Subject: [PATCH 5/7] rf: make "INSTANCE-NAME:ID" URI pattern more stringent - no arbitrary case, only capital So it would avoid confusion with dandi:// URL. Also fixing up tests for prior commit renames --- dandi/dandiarchive.py | 5 +++-- dandi/tests/test_dandiapi.py | 2 +- dandi/tests/test_dandiarchive.py | 22 +++++++--------------- 3 files changed, 11 insertions(+), 18 deletions(-) diff --git a/dandi/dandiarchive.py b/dandi/dandiarchive.py index 59dbfe34b..1cc7ac5d7 100644 --- a/dandi/dandiarchive.py +++ b/dandi/dandiarchive.py @@ -574,7 +574,9 @@ class _dandi_url_parser: # Should absorb port and "api/": server_grp = "(?P(?Phttps?)://(?P[^/]+)/(api/)?)" # Build instance name pattern from known instances to avoid matching unknown patterns - instance_name_pattern = "|".join(re.escape(name) for name in known_instances.keys()) + instance_name_pattern = "|".join( + re.escape(name.upper()) for name in known_instances + ) known_urls: list[tuple[re.Pattern[str], dict[str, Any], str]] = [ # List of (regex, settings, display string) triples # @@ -594,7 +596,6 @@ class _dandi_url_parser: rf"(?P{instance_name_pattern}):" rf"{dandiset_id_grp}" rf"(/(?P{VERSION_REGEX}))?", - flags=re.I, ), {}, ":[/]", diff --git a/dandi/tests/test_dandiapi.py b/dandi/tests/test_dandiapi.py index 29f8119b1..cab2df0ec 100644 --- a/dandi/tests/test_dandiapi.py +++ b/dandi/tests/test_dandiapi.py @@ -869,7 +869,7 @@ def test_asset_as_readable_open(new_dandiset: SampleDandiset, tmp_path: Path) -> ("dandi", "DANDI_API_KEY"), ("dandi-api-local-docker-tests", "DANDI_API_LOCAL_DOCKER_TESTS_API_KEY"), ("dandi-sandbox", "DANDI_SANDBOX_API_KEY"), - ("ember-sandbox", "EMBER_SANDBOX_API_KEY"), + ("ember-dandi-sandbox", "EMBER_SANDBOX_API_KEY"), ], ) def test_get_api_key_env_var(instance_name: str, expected_env_var_name: str) -> None: diff --git a/dandi/tests/test_dandiarchive.py b/dandi/tests/test_dandiarchive.py index dc4bfa0ee..cc5d988a4 100644 --- a/dandi/tests/test_dandiarchive.py +++ b/dandi/tests/test_dandiarchive.py @@ -108,9 +108,9 @@ version_id="draft", ), ), - # lower cased + # numeric version ( - "dandi:000027/0.210831.2033", + "DANDI:000027/0.210831.2033", DandisetURL( instance=known_instances["dandi"], dandiset_id="000027", @@ -126,14 +126,6 @@ version_id=None, ), ), - ( - "dandi-sandbox:000029/draft", - DandisetURL( - instance=known_instances["dandi-sandbox"], - dandiset_id="000029", - version_id="draft", - ), - ), ( "LINC:000029", DandisetURL( @@ -143,7 +135,7 @@ ), ), ( - "linc:000029/0.210831.2033", + "LINC:000029/0.210831.2033", DandisetURL( instance=known_instances["linc"], dandiset_id="000029", @@ -151,17 +143,17 @@ ), ), ( - "EMBER:000029", + "EMBER-DANDI:000029", DandisetURL( - instance=known_instances["ember"], + instance=known_instances["ember-dandi"], dandiset_id="000029", version_id=None, ), ), ( - "ember-sandbox:000029/draft", + "EMBER-DANDI-SANDBOX:000029/draft", DandisetURL( - instance=known_instances["ember-sandbox"], + instance=known_instances["ember-dandi-sandbox"], dandiset_id="000029", version_id="draft", ), From c463d6793a6598dbf533bae55d099c948d6a3536 Mon Sep 17 00:00:00 2001 From: Yaroslav Halchenko Date: Wed, 3 Dec 2025 15:27:51 -0500 Subject: [PATCH 6/7] Make naming of instances consistent with deployed and remove dandi-staging --- DEVELOPMENT.md | 2 +- dandi/cli/tests/test_instances.py | 7 ++----- dandi/consts.py | 15 ++++----------- dandi/tests/test_dandiapi.py | 2 +- dandi/utils.py | 4 ++-- docs/source/cmdline/instances.rst | 7 ++----- 6 files changed, 12 insertions(+), 25 deletions(-) diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index a5d36df0a..cd874f259 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -61,7 +61,7 @@ development command line options. variable providing the key for a specific known DANDI instance corresponds to the name of the instance. For example, the environment variable `DANDI_API_KEY` provides the key for the known instance named `dandi` and the environment variable - `EMBER_SANDBOX_API_KEY` provides the key for the known instance named `ember-sandbox`. + `EMBER_DANDI_SANDBOX_API_KEY` provides the key for the known instance named `ember-sandbox`. I.e., the environment variable name is the capitalized version of the instance's name with "-" replaced by "_" suffixed by "_API_KEY". Providing API keys through environment variables avoids using keyrings, thus making it possible to "temporarily" use another diff --git a/dandi/cli/tests/test_instances.py b/dandi/cli/tests/test_instances.py index 4a9ca5b67..d0df778b9 100644 --- a/dandi/cli/tests/test_instances.py +++ b/dandi/cli/tests/test_instances.py @@ -19,13 +19,10 @@ def test_cmd_instances(monkeypatch): "dandi-sandbox:\n" " api: https://api.sandbox.dandiarchive.org/api\n" " gui: https://sandbox.dandiarchive.org\n" - "dandi-staging:\n" - " api: https://api.sandbox.dandiarchive.org/api\n" - " gui: https://sandbox.dandiarchive.org\n" - "ember:\n" + "ember-dandi:\n" " api: https://api-dandi.emberarchive.org/api\n" " gui: https://dandi.emberarchive.org\n" - "ember-sandbox:\n" + "ember-dandi-sandbox:\n" " api: https://api-dandi-sandbox.emberarchive.org/api\n" " gui: https://apl-setup--ember-dandi-archive.netlify.app/\n" "linc:\n" diff --git a/dandi/consts.py b/dandi/consts.py index 5518d8d38..01723d364 100644 --- a/dandi/consts.py +++ b/dandi/consts.py @@ -123,13 +123,6 @@ def urls(self) -> Iterator[str]: "https://dandiarchive.org", "https://api.dandiarchive.org/api", ), - # Deprecated. Remove early 2026. - # Should come before dandi-sandbox so _rev map does map to sandbox - "dandi-staging": DandiInstance( - "dandi-staging", - "https://sandbox.dandiarchive.org", - "https://api.sandbox.dandiarchive.org/api", - ), "dandi-sandbox": DandiInstance( "dandi-sandbox", "https://sandbox.dandiarchive.org", @@ -150,13 +143,13 @@ def urls(self) -> Iterator[str]: "https://staging.lincbrain.org", "https://staging-api.lincbrain.org/api", ), - "ember": DandiInstance( - "ember", + "ember-dandi": DandiInstance( + "ember-dandi", "https://dandi.emberarchive.org", "https://api-dandi.emberarchive.org/api", ), - "ember-sandbox": DandiInstance( - "ember-sandbox", + "ember-dandi-sandbox": DandiInstance( + "ember-dandi-sandbox", "https://apl-setup--ember-dandi-archive.netlify.app/", "https://api-dandi-sandbox.emberarchive.org/api", ), diff --git a/dandi/tests/test_dandiapi.py b/dandi/tests/test_dandiapi.py index cab2df0ec..b50640b12 100644 --- a/dandi/tests/test_dandiapi.py +++ b/dandi/tests/test_dandiapi.py @@ -869,7 +869,7 @@ def test_asset_as_readable_open(new_dandiset: SampleDandiset, tmp_path: Path) -> ("dandi", "DANDI_API_KEY"), ("dandi-api-local-docker-tests", "DANDI_API_LOCAL_DOCKER_TESTS_API_KEY"), ("dandi-sandbox", "DANDI_SANDBOX_API_KEY"), - ("ember-dandi-sandbox", "EMBER_SANDBOX_API_KEY"), + ("ember-dandi-sandbox", "EMBER_DANDI_SANDBOX_API_KEY"), ], ) def test_get_api_key_env_var(instance_name: str, expected_env_var_name: str) -> None: diff --git a/dandi/utils.py b/dandi/utils.py index 93a0e138c..9d77f33cc 100644 --- a/dandi/utils.py +++ b/dandi/utils.py @@ -577,8 +577,8 @@ def get_instance(dandi_instance_id: str | DandiInstance) -> DandiInstance: dandi_id = dandi_instance_id instance = known_instances[dandi_id] if dandi_id == "dandi-staging": - lgr.warning( - "'dandi-staging' DANDI instance identifier is deprecated. " + raise ValueError( + "'dandi-staging' DANDI instance identifier was removed. " "The instance was renamed into 'dandi-sandbox', please use that identifier instead." ) if redirector_url is None: diff --git a/docs/source/cmdline/instances.rst b/docs/source/cmdline/instances.rst index c7c3fd636..69958e731 100644 --- a/docs/source/cmdline/instances.rst +++ b/docs/source/cmdline/instances.rst @@ -22,18 +22,15 @@ Example output: dandi-sandbox: api: https://api.sandbox.dandiarchive.org/api gui: https://sandbox.dandiarchive.org - dandi-staging: - api: https://api.sandbox.dandiarchive.org/api - gui: https://sandbox.dandiarchive.org linc-staging: api: https://staging-api.lincbrain.org/api gui: https://staging.lincbrain.org linc: api: https://api.lincbrain.org/api gui: https://lincbrain.org - ember-sandbox: + ember-dandi-sandbox: api: https://api-dandi-sandbox.emberarchive.org/api gui: https://apl-setup--ember-dandi-archive.netlify.app/ - ember: + ember-dandi: api: https://api-dandi.emberarchive.org/api gui: https://dandi.emberarchive.org From 5f60b9bbc9f1dcdb04186c27613b19551a9f6d67 Mon Sep 17 00:00:00 2001 From: Yaroslav Halchenko Date: Thu, 4 Dec 2025 16:16:08 -0500 Subject: [PATCH 7/7] Fix instance name in the doc --- DEVELOPMENT.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DEVELOPMENT.md b/DEVELOPMENT.md index cd874f259..9a737842e 100644 --- a/DEVELOPMENT.md +++ b/DEVELOPMENT.md @@ -61,7 +61,7 @@ development command line options. variable providing the key for a specific known DANDI instance corresponds to the name of the instance. For example, the environment variable `DANDI_API_KEY` provides the key for the known instance named `dandi` and the environment variable - `EMBER_DANDI_SANDBOX_API_KEY` provides the key for the known instance named `ember-sandbox`. + `EMBER_DANDI_SANDBOX_API_KEY` provides the key for the known instance named `ember-dandi-sandbox`. I.e., the environment variable name is the capitalized version of the instance's name with "-" replaced by "_" suffixed by "_API_KEY". Providing API keys through environment variables avoids using keyrings, thus making it possible to "temporarily" use another