Skip to content

Commit 530c10e

Browse files
committed
refactor(url): reversible SSH-style URL patch
why: - Previously, enabling SSH-style URL detection mutated libvcs’s DEFAULT_RULES on import and lost the rule’s original metadata. - Users couldn’t cleanly disable the patch or restore upstream defaults if libvcs changed. - Implicit module-import side-effects are harder to reason about. what: - Introduce private `_orig_rule_meta` to snapshot rule’s original metadata. - Store original metadata on first enable and apply `(True, 100)`. - Restore saved metadata (or safe defaults) on disable, clearing snapshot. - Remove auto-patch on import; require explicit call. - Add `ssh_style_url_detection` context manager. - Call `enable_ssh_style_url_detection()` in `update_repo()` to maintain behavior. - Add pytest tests for enable/disable roundtrip and context manager.
1 parent b07516f commit 530c10e

File tree

3 files changed

+92
-10
lines changed

3 files changed

+92
-10
lines changed

src/vcspull/cli/sync.py

+3
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@
1212

1313
from vcspull import exc
1414
from vcspull.config import filter_repos, find_config_files, load_configs
15+
from vcspull.url import enable_ssh_style_url_detection
1516

1617
if t.TYPE_CHECKING:
1718
import argparse
@@ -147,6 +148,8 @@ def update_repo(
147148
# repo_dict: Dict[str, Union[str, Dict[str, GitRemote], pathlib.Path]]
148149
) -> GitSync:
149150
"""Synchronize a single repository."""
151+
# Ensure SSH-style URLs are recognized as explicit Git URLs
152+
enable_ssh_style_url_detection()
150153
repo_dict = deepcopy(repo_dict)
151154
if "pip_url" not in repo_dict:
152155
repo_dict["pip_url"] = repo_dict.pop("url")

src/vcspull/url.py

+47-8
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,12 @@
22

33
from __future__ import annotations
44

5+
from typing import Any
6+
57
from libvcs.url.git import DEFAULT_RULES
68

9+
_orig_rule_meta: dict[str, tuple[bool, int]] = {}
10+
711

812
def enable_ssh_style_url_detection() -> None:
913
"""Enable detection of SSH-style URLs as explicit Git URLs.
@@ -23,12 +27,14 @@ def enable_ssh_style_url_detection() -> None:
2327
>>> GitURL.is_valid('user@hostname:path/to/repo.git', is_explicit=True)
2428
True
2529
"""
30+
# Patch the core-git-scp rule, storing its original state if not already stored
2631
for rule in DEFAULT_RULES:
2732
if rule.label == "core-git-scp":
28-
# Make the rule explicit so it can be detected with is_explicit=True
33+
if rule.label not in _orig_rule_meta:
34+
_orig_rule_meta[rule.label] = (rule.is_explicit, rule.weight)
2935
rule.is_explicit = True
30-
# Increase the weight to ensure it takes precedence
3136
rule.weight = 100
37+
break
3238

3339

3440
def disable_ssh_style_url_detection() -> None:
@@ -39,7 +45,8 @@ def disable_ssh_style_url_detection() -> None:
3945
4046
Examples
4147
--------
42-
>>> from vcspull.url import enable_ssh_style_url_detection, disable_ssh_style_url_detection
48+
>>> from vcspull.url import enable_ssh_style_url_detection
49+
>>> from vcspull.url import disable_ssh_style_url_detection
4350
>>> from libvcs.url.git import GitURL
4451
>>> # Enable the patch
4552
>>> enable_ssh_style_url_detection()
@@ -50,11 +57,18 @@ def disable_ssh_style_url_detection() -> None:
5057
>>> GitURL.is_valid('user@hostname:path/to/repo.git', is_explicit=True)
5158
False
5259
"""
60+
# Restore the core-git-scp rule to its original state, if known
5361
for rule in DEFAULT_RULES:
5462
if rule.label == "core-git-scp":
55-
# Revert to original state
56-
rule.is_explicit = False
57-
rule.weight = 0
63+
orig = _orig_rule_meta.get(rule.label)
64+
if orig:
65+
rule.is_explicit, rule.weight = orig
66+
_orig_rule_meta.pop(rule.label, None)
67+
else:
68+
# Fallback to safe defaults
69+
rule.is_explicit = False
70+
rule.weight = 0
71+
break
5872

5973

6074
def is_ssh_style_url_detection_enabled() -> bool:
@@ -70,5 +84,30 @@ def is_ssh_style_url_detection_enabled() -> bool:
7084
return False
7185

7286

73-
# Enable SSH-style URL detection by default
74-
enable_ssh_style_url_detection()
87+
"""
88+
Context manager and utility for SSH-style URL detection.
89+
"""
90+
91+
92+
class ssh_style_url_detection:
93+
"""Context manager to enable/disable SSH-style URL detection."""
94+
95+
def __init__(self, enabled: bool = True) -> None:
96+
self.enabled = enabled
97+
98+
def __enter__(self) -> None:
99+
"""Enable or disable SSH-style URL detection on context enter."""
100+
if self.enabled:
101+
enable_ssh_style_url_detection()
102+
else:
103+
disable_ssh_style_url_detection()
104+
105+
def __exit__(
106+
self,
107+
exc_type: type[BaseException] | None,
108+
exc_val: BaseException | None,
109+
exc_tb: Any,
110+
) -> None:
111+
"""Restore original SSH-style URL detection state on context exit."""
112+
# Always restore to disabled after context
113+
disable_ssh_style_url_detection()

tests/test_url.py

+42-2
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,13 @@
33
from __future__ import annotations
44

55
import pytest
6-
from libvcs.url.git import GitURL
6+
from libvcs.url.git import DEFAULT_RULES, GitURL
77

8-
from vcspull.url import disable_ssh_style_url_detection, enable_ssh_style_url_detection
8+
from vcspull.url import (
9+
disable_ssh_style_url_detection,
10+
enable_ssh_style_url_detection,
11+
ssh_style_url_detection,
12+
)
913

1014

1115
def test_ssh_style_url_detection_toggle() -> None:
@@ -99,3 +103,39 @@ def test_ssh_style_url_parsing(
99103
assert git_url.hostname == expected_hostname
100104
assert git_url.path == expected_path
101105
assert git_url.suffix == ".git"
106+
107+
108+
def test_enable_disable_restores_original_state() -> None:
109+
"""Original rule metadata is preserved and restored after enable/disable."""
110+
# Ensure any prior patch is cleared
111+
disable_ssh_style_url_detection()
112+
# Find the core-git-scp rule and capture its original state
113+
rule = next(r for r in DEFAULT_RULES if r.label == "core-git-scp")
114+
orig_state = (rule.is_explicit, rule.weight)
115+
116+
# Disabling without prior enable should leave original state
117+
disable_ssh_style_url_detection()
118+
assert (rule.is_explicit, rule.weight) == orig_state
119+
120+
# Enable should patch
121+
enable_ssh_style_url_detection()
122+
assert rule.is_explicit is True
123+
assert rule.weight == 100
124+
125+
# Disable should restore to original
126+
disable_ssh_style_url_detection()
127+
assert (rule.is_explicit, rule.weight) == orig_state
128+
129+
130+
def test_context_manager_restores_original_state() -> None:
131+
"""Context manager enables then restores original rule state."""
132+
rule = next(r for r in DEFAULT_RULES if r.label == "core-git-scp")
133+
orig_state = (rule.is_explicit, rule.weight)
134+
135+
# Use context manager
136+
with ssh_style_url_detection():
137+
assert rule.is_explicit is True
138+
assert rule.weight == 100
139+
140+
# After context, state should be back to original
141+
assert (rule.is_explicit, rule.weight) == orig_state

0 commit comments

Comments
 (0)