From 14b88bfdb5178c760c45a0c00d3489027400eddf Mon Sep 17 00:00:00 2001 From: Cong <72737794+robolearning123@users.noreply.github.com> Date: Mon, 30 Mar 2026 19:11:49 -0400 Subject: [PATCH 1/5] fix: resolve pytest warnings - Add pytest-timeout dependency to fix UnknownMarkWarning - Add filterwarnings config to ignore nmrglue NumPy 2.0 deprecation Tests now run with 0 warnings (1165 passed, 14 skipped) --- pyproject.toml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index cea7995..a31d216 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -42,6 +42,7 @@ dev = [ "pytest>=8.0", "pytest-asyncio>=0.23", "pytest-cov>=5.0", + "pytest-timeout>=2.0", ] [project.scripts] @@ -76,6 +77,10 @@ markers = [ "network: tests that require network access (deselect with -m 'not network')", "skipif: conditional skip", ] +filterwarnings = [ + # nmrglue uses deprecated NumPy 2.0 dtype aliases (fixed in their repo, pending release) + "ignore::DeprecationWarning:nmrglue.*", +] [tool.coverage.run] source = ["device_use"] From 4cf88756acbde017fb4feb3d3bf427fba9431972 Mon Sep 17 00:00:00 2001 From: Cong <72737794+robolearning123@users.noreply.github.com> Date: Mon, 30 Mar 2026 19:32:33 -0400 Subject: [PATCH 2/5] fix(cli): extract vendor from device name in scaffold command The scaffold command was generating adapters with vendor="TODO" hardcoded. Now it extracts the vendor from the first part of the device name (e.g., "zeiss" from "zeiss-zen", "biotek" from "biotek-gen5"). - Parse vendor from device_name.split('-')[0] - Capitalize vendor name properly - Add tests for vendor extraction Fixes: TODO comment in cli.py line 340 --- src/device_use/cli.py | 4 +++- tests/test_cli.py | 21 +++++++++++++++++++++ 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/src/device_use/cli.py b/src/device_use/cli.py index f4dcb2a..47d78c5 100644 --- a/src/device_use/cli.py +++ b/src/device_use/cli.py @@ -255,6 +255,8 @@ def _scaffold(device_name: str, output_dir: str): class_name = "".join( w.capitalize() for w in device_name.replace("-", " ").replace("_", " ").split() ) + # Extract vendor from device name (first part before dash/underscore) + vendor = device_name.split("-")[0].split("_")[0].capitalize() root = os.path.join(output_dir, pkg_name) if os.path.exists(root): @@ -337,7 +339,7 @@ def __init__(self, mode: ControlMode = ControlMode.OFFLINE): def info(self) -> InstrumentInfo: return InstrumentInfo( name="{class_name}", - vendor="TODO", + vendor="{vendor}", instrument_type="{slug}", supported_modes=[ControlMode.OFFLINE, ControlMode.API, ControlMode.GUI], version="0.1.0", diff --git a/tests/test_cli.py b/tests/test_cli.py index c4d9c80..f1864ae 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -257,6 +257,27 @@ def test_scaffold_existing_dir(self, tmp_path, capsys): out = capsys.readouterr().out assert "already exists" in out + def test_scaffold_vendor_extraction(self, tmp_path): + """Vendor should be extracted from device name (first part before dash).""" + cli._scaffold("biotek-gen5", str(tmp_path)) + adapter_file = ( + tmp_path / "device_use_biotek_gen5" / "src" / "device_use_biotek_gen5" / "adapter.py" + ) + content = adapter_file.read_text() + assert 'vendor="Biotek"' in content + # Ensure vendor field doesn't have the placeholder + assert 'vendor="TODO"' not in content + + def test_scaffold_vendor_single_word(self, tmp_path): + """Vendor should be capitalized for single-word device names.""" + cli._scaffold("mydevice", str(tmp_path)) + adapter_file = ( + tmp_path / "device_use_mydevice" / "src" / "device_use_mydevice" / "adapter.py" + ) + content = adapter_file.read_text() + assert 'vendor="Mydevice"' in content + assert 'vendor="TODO"' not in content + # --------------------------------------------------------------------------- # _write From 138ebc5100d91a2cffe3a5a98d8fc204df62cc1f Mon Sep 17 00:00:00 2001 From: Cong <72737794+robolearning123@users.noreply.github.com> Date: Mon, 30 Mar 2026 21:55:02 -0400 Subject: [PATCH 3/5] chore: add .claudeignore for agent context optimization Co-Authored-By: Claude Opus 4.6 (1M context) --- .claudeignore | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 .claudeignore diff --git a/.claudeignore b/.claudeignore new file mode 100644 index 0000000..375aed6 --- /dev/null +++ b/.claudeignore @@ -0,0 +1,39 @@ +# Python-specific ignores +__pycache__/ +*.pyc +*.pyo +*.pyd +.Python +*.so +*.egg +*.egg-info/ +dist/ +build/ +.eggs/ + +# Virtual environments +.venv/ +venv/ +ENV/ +env/ + +# Testing & coverage +.pytest_cache/ +.mypy_cache/ +.ruff_cache/ +htmlcov/ +.coverage +.coverage.* +.tox/ +noxfile.py +.nox/ + +# IDE +.vscode/ +.idea/ +*.swp +*.swo +*~ + +# Git +.git/ From 664b92f3bc628f51cadcafda2ce01b7ffcd4523b Mon Sep 17 00:00:00 2001 From: Cong <72737794+robolearning123@users.noreply.github.com> Date: Mon, 30 Mar 2026 23:30:46 -0400 Subject: [PATCH 4/5] fix(lint): resolve 17 ruff errors in test files - N802: rename test functions with non-lowercase names (enUS, REFRESH) - N806: rename MockAsyncOpenAI_class -> mock_openai_cls, MockTool -> mock_tool_cls - F841: remove unused variable assignments (original, captured, mock_processor) - E501: break long JSON string literal in test_openai_compat --- tests/test_converter_coverage.py | 9 ++++----- tests/test_openai_compat.py | 31 ++++++++++++++++--------------- tests/test_spectral_library.py | 2 +- tests/test_web.py | 8 ++++---- 4 files changed, 25 insertions(+), 25 deletions(-) diff --git a/tests/test_converter_coverage.py b/tests/test_converter_coverage.py index c0b8fd0..75289b2 100644 --- a/tests/test_converter_coverage.py +++ b/tests/test_converter_coverage.py @@ -28,7 +28,7 @@ class TestResolveRedirect: - def test_url_without_enUS_pattern_returns_none(self, tmp_path): + def test_url_without_en_us_pattern_returns_none(self, tmp_path): """Line 182: redirect URL that doesn't match the en-US regex.""" stub = tmp_path / "stub.html" stub.write_text('') @@ -247,7 +247,7 @@ def test_description_fallback_summary(self, tmp_path): assert result["summary"] == "This is a detailed description for the command page." assert result["commands"] == [] - def test_convert_missing_enUS_dir(self, tmp_path): + def test_convert_missing_en_us_dir(self, tmp_path): """convert_topspin_command returns None when en-US dir is missing.""" stub = tmp_path / "missing.html" stub.write_text( @@ -291,7 +291,6 @@ def test_exception_in_convert_is_caught(self, tmp_path, capsys): pass # More direct approach: patch convert_topspin_command itself - original = convert_topspin_command with patch( "device_use.knowledge.converter.convert_topspin_command", side_effect=RuntimeError("boom"), @@ -589,7 +588,7 @@ def test_single_word_no_dash(self): class TestRedirectParserEdgeCases: - def test_uppercase_REFRESH(self): + def test_uppercase_refresh(self): """http-equiv='REFRESH' (uppercase) is matched case-insensitively.""" html = ( ' Date: Mon, 30 Mar 2026 23:32:03 -0400 Subject: [PATCH 5/5] style: ruff format test_openai_compat.py --- tests/test_openai_compat.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/test_openai_compat.py b/tests/test_openai_compat.py index d2d16dc..bc3383a 100644 --- a/tests/test_openai_compat.py +++ b/tests/test_openai_compat.py @@ -66,9 +66,7 @@ def test_initialization(self, mock_async_openai, model, expected_native_cu): assert backend.system_prompt == "" assert backend._previous_response_id is None - mock_openai_cls.assert_called_once_with( - api_key="test_key", base_url="http://test.url" - ) + mock_openai_cls.assert_called_once_with(api_key="test_key", base_url="http://test.url") def test_default_values(self, mock_async_openai): mock_openai_cls, mock_async_openai_instance = mock_async_openai