Skip to content

Commit 86f69c7

Browse files
committed
fix: Defer tomlkit import until uv management is confirmed
1 parent 24ef1bd commit 86f69c7

2 files changed

Lines changed: 40 additions & 7 deletions

File tree

src/mxdev/uv.py

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -24,23 +24,30 @@ def read(self, state: State) -> None:
2424
pass
2525

2626
def write(self, state: State) -> None:
27-
try:
28-
import tomlkit
29-
except ImportError:
30-
raise RuntimeError("tomlkit is required for the uv hook. Install it with: pip install mxdev[uv]")
31-
3227
pyproject_path = Path(state.configuration.settings.get("directory", ".")) / "pyproject.toml"
3328
if not pyproject_path.exists():
3429
logger.debug("[%s] pyproject.toml not found, skipping.", self.namespace)
3530
return
3631

3732
try:
38-
with pyproject_path.open("r", encoding="utf-8") as f:
39-
doc = tomlkit.load(f)
33+
content = pyproject_path.read_text(encoding="utf-8")
4034
except OSError as e:
4135
logger.error("[%s] Failed to read pyproject.toml: %s", self.namespace, e)
4236
return
4337

38+
if "[tool.uv]" not in content:
39+
logger.debug(
40+
"[%s] Project not explicitly managed by uv ([tool.uv] managed=true missing), skipping.", self.namespace
41+
)
42+
return
43+
44+
try:
45+
import tomlkit
46+
except ImportError:
47+
raise RuntimeError("tomlkit is required for the uv hook. Install it with: pip install mxdev[uv]")
48+
49+
doc = tomlkit.loads(content)
50+
4451
# Check for the UV managed signal
4552
tool_uv = doc.get("tool", {}).get("uv", {})
4653
if tool_uv.get("managed") is not True:

tests/test_uv.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -310,6 +310,32 @@ def fake_import(name, *args, **kw):
310310
assert "tomlkit is required for the uv hook" in str(excinfo.value)
311311

312312

313+
def test_hook_does_not_require_tomlkit_if_not_uv_managed(mocker, tmp_path, monkeypatch):
314+
monkeypatch.chdir(tmp_path)
315+
hook = UvPyprojectUpdater()
316+
317+
(tmp_path / "mx.ini").write_text("[settings]")
318+
config = Configuration("mx.ini")
319+
state = State(config)
320+
321+
(tmp_path / "pyproject.toml").write_text("[project]\\nname = 'test'\\n")
322+
323+
mocker.patch.dict(sys.modules, {"tomlkit": None})
324+
import builtins
325+
326+
orig_import = builtins.__import__
327+
328+
def fake_import(name, *args, **kw):
329+
if name == "tomlkit":
330+
raise ImportError("No module named 'tomlkit'")
331+
return orig_import(name, *args, **kw)
332+
333+
mocker.patch("builtins.__import__", side_effect=fake_import)
334+
335+
# Should not raise any error, even though tomlkit import is mocked to fail
336+
hook.write(state)
337+
338+
313339
def test_hook_resolves_path_relative_to_config(mocker, tmp_path, monkeypatch):
314340
monkeypatch.chdir(tmp_path)
315341

0 commit comments

Comments
 (0)