chore: import upstream snapshot with attribution
CPU tests Workflow / Testing (ubuntu-latest, 3.12) (push) Failing after 1s
CPU tests Workflow / Testing (ubuntu-latest, 3.13) (push) Failing after 0s
Mypy Type Check / Type Check (push) Failing after 0s
Docs/Test WorkFlow / Test docs build (push) Failing after 1s
PR Conflict Labeler / labeling (push) Failing after 1s
Dependency resolution / Resolve [tflite] extra — Python 3.12 (push) Failing after 0s
Smoke Tests / try-all-models (ubuntu-latest, 3.10) (push) Failing after 0s
Smoke Tests / try-all-models (ubuntu-latest, 3.13) (push) Failing after 1s
CPU tests Workflow / build-pkg (push) Failing after 1s
CPU tests Workflow / Testing (ubuntu-latest, 3.10) (push) Failing after 0s
CPU tests Workflow / Testing (ubuntu-latest, 3.11) (push) Failing after 0s
Smoke Tests / try-all-models (macos-latest, 3.10) (push) Has been cancelled
Smoke Tests / try-all-models (macos-latest, 3.13) (push) Has been cancelled
Smoke Tests / try-all-models (windows-latest, 3.10) (push) Has been cancelled
Smoke Tests / try-all-models (windows-latest, 3.13) (push) Has been cancelled
CPU tests Workflow / Testing (macos-latest, 3.10) (push) Has been cancelled
CPU tests Workflow / Testing (macos-latest, 3.13) (push) Has been cancelled
CPU tests Workflow / Testing (windows-latest, 3.10) (push) Has been cancelled
CPU tests Workflow / Testing (windows-latest, 3.13) (push) Has been cancelled
CPU tests Workflow / testing-guardian (push) Has been cancelled
GPU tests Workflow / Testing (push) Has been cancelled
CPU tests Workflow / Testing (ubuntu-latest, 3.12) (push) Failing after 1s
CPU tests Workflow / Testing (ubuntu-latest, 3.13) (push) Failing after 0s
Mypy Type Check / Type Check (push) Failing after 0s
Docs/Test WorkFlow / Test docs build (push) Failing after 1s
PR Conflict Labeler / labeling (push) Failing after 1s
Dependency resolution / Resolve [tflite] extra — Python 3.12 (push) Failing after 0s
Smoke Tests / try-all-models (ubuntu-latest, 3.10) (push) Failing after 0s
Smoke Tests / try-all-models (ubuntu-latest, 3.13) (push) Failing after 1s
CPU tests Workflow / build-pkg (push) Failing after 1s
CPU tests Workflow / Testing (ubuntu-latest, 3.10) (push) Failing after 0s
CPU tests Workflow / Testing (ubuntu-latest, 3.11) (push) Failing after 0s
Smoke Tests / try-all-models (macos-latest, 3.10) (push) Has been cancelled
Smoke Tests / try-all-models (macos-latest, 3.13) (push) Has been cancelled
Smoke Tests / try-all-models (windows-latest, 3.10) (push) Has been cancelled
Smoke Tests / try-all-models (windows-latest, 3.13) (push) Has been cancelled
CPU tests Workflow / Testing (macos-latest, 3.10) (push) Has been cancelled
CPU tests Workflow / Testing (macos-latest, 3.13) (push) Has been cancelled
CPU tests Workflow / Testing (windows-latest, 3.10) (push) Has been cancelled
CPU tests Workflow / Testing (windows-latest, 3.13) (push) Has been cancelled
CPU tests Workflow / testing-guardian (push) Has been cancelled
GPU tests Workflow / Testing (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,269 @@
|
||||
# ------------------------------------------------------------------------
|
||||
# RF-DETR
|
||||
# Copyright (c) 2025 Roboflow. All Rights Reserved.
|
||||
# Licensed under the Apache License, Version 2.0 [see LICENSE for details]
|
||||
# ------------------------------------------------------------------------
|
||||
"""Tests for package metadata helpers and structural import paths."""
|
||||
|
||||
import subprocess
|
||||
import sys
|
||||
from unittest.mock import patch
|
||||
|
||||
import pytest
|
||||
|
||||
from rfdetr.utilities.package import get_sha
|
||||
|
||||
|
||||
def test_get_sha_marks_dirty_worktree_when_diff_command_returns_exit_code_1() -> None:
|
||||
"""A diff exit code of 1 should report uncommitted changes, not unknown."""
|
||||
|
||||
def _fake_check_output(command: list[str], cwd: str | None = None) -> bytes:
|
||||
if command[:3] == ["git", "rev-parse", "HEAD"]:
|
||||
return b"abc123\n"
|
||||
if command[:4] == ["git", "rev-parse", "--abbrev-ref", "HEAD"]:
|
||||
return b"feature/test\n"
|
||||
raise AssertionError(f"Unexpected command: {command!r}")
|
||||
|
||||
class _RunResult:
|
||||
def __init__(self, returncode: int) -> None:
|
||||
self.returncode = returncode
|
||||
|
||||
with (
|
||||
patch("rfdetr.utilities.package.subprocess.check_output", side_effect=_fake_check_output),
|
||||
patch("rfdetr.utilities.package.subprocess.run", return_value=_RunResult(returncode=1)),
|
||||
):
|
||||
sha = get_sha()
|
||||
|
||||
assert sha == "sha: abc123, status: has uncommitted changes, branch: feature/test"
|
||||
|
||||
|
||||
def test_peft_not_imported_eagerly_on_backbone_import_characterization() -> None:
|
||||
"""Importing backbone.backbone must NOT pull peft into sys.modules (peft is optional).
|
||||
|
||||
This characterization test captures the invariant introduced in PR 1 (chore/packaging-peft-lora): after the lazy-
|
||||
import refactor, importing backbone at module-load time must not trigger a top-level ``from peft import PeftModel``.
|
||||
"""
|
||||
result = subprocess.run(
|
||||
[
|
||||
sys.executable,
|
||||
"-c",
|
||||
(
|
||||
"import sys; "
|
||||
"import rfdetr.models.backbone.backbone; "
|
||||
"assert 'peft' not in sys.modules, "
|
||||
"'peft was eagerly imported by backbone.backbone'"
|
||||
),
|
||||
],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=False,
|
||||
)
|
||||
assert result.returncode == 0, (
|
||||
"Subprocess for backbone import failed:\n"
|
||||
f"return code: {result.returncode}\n"
|
||||
f"stdout:\n{result.stdout}\n"
|
||||
f"stderr:\n{result.stderr}"
|
||||
)
|
||||
|
||||
|
||||
class TestImportPaths:
|
||||
"""Structural tests for the detr.py → inference.py + variants.py split (PR 6).
|
||||
|
||||
After the split:
|
||||
- ``rfdetr.inference`` exports ``ModelContext`` and ``_build_model_context``
|
||||
- ``rfdetr.variants`` exports all 15 concrete model classes
|
||||
- ``rfdetr.detr`` re-exports both for backward compatibility
|
||||
- ``rfdetr`` (top-level) continues to export public names unchanged
|
||||
"""
|
||||
|
||||
def test_model_context_importable_from_inference(self) -> None:
|
||||
"""ModelContext must be importable from the new rfdetr.inference module."""
|
||||
from rfdetr.inference import ModelContext
|
||||
|
||||
assert ModelContext is not None
|
||||
|
||||
def test_build_model_context_importable_from_inference(self) -> None:
|
||||
"""_build_model_context must be importable from rfdetr.inference."""
|
||||
from rfdetr.inference import _build_model_context
|
||||
|
||||
assert callable(_build_model_context)
|
||||
|
||||
def test_rfdetr_large_importable_from_variants(self) -> None:
|
||||
"""RFDETRLarge must be importable from the new rfdetr.variants module."""
|
||||
from rfdetr.variants import RFDETRLarge
|
||||
|
||||
assert RFDETRLarge is not None
|
||||
|
||||
def test_variants_import_first_does_not_trigger_circular_import(self) -> None:
|
||||
"""Importing variants before detr must still preserve shared class identity."""
|
||||
result = subprocess.run(
|
||||
[
|
||||
sys.executable,
|
||||
"-c",
|
||||
(
|
||||
"from rfdetr.variants import RFDETRLarge; "
|
||||
"from rfdetr.detr import RFDETRLarge as FromDetr; "
|
||||
"assert RFDETRLarge is FromDetr"
|
||||
),
|
||||
],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=False,
|
||||
)
|
||||
assert result.returncode == 0, (
|
||||
"Subprocess for variants-first import failed:\n"
|
||||
f"return code: {result.returncode}\n"
|
||||
f"stdout:\n{result.stdout}\n"
|
||||
f"stderr:\n{result.stderr}"
|
||||
)
|
||||
|
||||
@pytest.mark.parametrize(
|
||||
"class_name",
|
||||
[
|
||||
pytest.param("RFDETRBase", id="base"),
|
||||
pytest.param("RFDETRKeypointPreview", id="keypoint-preview"),
|
||||
pytest.param("RFDETRNano", id="nano"),
|
||||
pytest.param("RFDETRSmall", id="small"),
|
||||
pytest.param("RFDETRMedium", id="medium"),
|
||||
pytest.param("RFDETRLargeDeprecated", id="large-deprecated"),
|
||||
pytest.param("RFDETRLarge", id="large"),
|
||||
pytest.param("RFDETRSeg", id="seg-base"),
|
||||
pytest.param("RFDETRSegPreview", id="seg-preview"),
|
||||
pytest.param("RFDETRSegNano", id="seg-nano"),
|
||||
pytest.param("RFDETRSegSmall", id="seg-small"),
|
||||
pytest.param("RFDETRSegMedium", id="seg-medium"),
|
||||
pytest.param("RFDETRSegLarge", id="seg-large"),
|
||||
pytest.param("RFDETRSegXLarge", id="seg-xlarge"),
|
||||
pytest.param("RFDETRSeg2XLarge", id="seg-2xlarge"),
|
||||
],
|
||||
)
|
||||
def test_all_variant_classes_importable_from_variants(self, class_name: str) -> None:
|
||||
"""Every concrete variant class must be importable from rfdetr.variants."""
|
||||
import rfdetr.variants as variants_mod
|
||||
|
||||
cls = getattr(variants_mod, class_name, None)
|
||||
assert cls is not None, f"{class_name} not found in rfdetr.variants"
|
||||
|
||||
def test_model_context_backward_compat_from_detr(self) -> None:
|
||||
"""ModelContext must remain importable from rfdetr.detr (backward compat)."""
|
||||
from rfdetr.detr import ModelContext
|
||||
|
||||
assert ModelContext is not None
|
||||
|
||||
def test_rfdetr_large_backward_compat_from_detr(self) -> None:
|
||||
"""RFDETRLarge must remain importable from rfdetr.detr (backward compat)."""
|
||||
from rfdetr.detr import RFDETRLarge
|
||||
|
||||
assert RFDETRLarge is not None
|
||||
|
||||
def test_rfdetr_large_importable_from_top_level(self) -> None:
|
||||
"""RFDETRLarge must remain importable from rfdetr (top-level package)."""
|
||||
from rfdetr import RFDETRLarge
|
||||
|
||||
assert RFDETRLarge is not None
|
||||
|
||||
def test_model_context_importable_from_top_level(self) -> None:
|
||||
"""ModelContext must remain importable from rfdetr (top-level package)."""
|
||||
from rfdetr import ModelContext
|
||||
|
||||
assert ModelContext is not None
|
||||
|
||||
def test_top_level_import_sets_numpy_complex_alias(self) -> None:
|
||||
"""Verify rfdetr shim sets np.complex_ in a fresh interpreter with complex_ absent.
|
||||
|
||||
Runs in a subprocess so the shim code path is actually executed — importing rfdetr inside pytest is a no-op
|
||||
because rfdetr is already cached in sys.modules.
|
||||
"""
|
||||
result = subprocess.run(
|
||||
[
|
||||
sys.executable,
|
||||
"-c",
|
||||
(
|
||||
"import numpy\n"
|
||||
"if hasattr(numpy, 'complex_'):\n"
|
||||
" del numpy.complex_\n"
|
||||
"import rfdetr\n"
|
||||
"assert hasattr(numpy, 'complex_'), 'shim did not set numpy.complex_'\n"
|
||||
"assert numpy.complex_ is numpy.complex128, 'complex_ must alias complex128'\n"
|
||||
),
|
||||
],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=False,
|
||||
)
|
||||
assert result.returncode == 0, (
|
||||
"Subprocess for NumPy complex_ shim failed:\n"
|
||||
f"return code: {result.returncode}\n"
|
||||
f"stdout:\n{result.stdout}\n"
|
||||
f"stderr:\n{result.stderr}"
|
||||
)
|
||||
|
||||
def test_identity_across_import_paths(self) -> None:
|
||||
"""The same class object must be returned regardless of import path.
|
||||
|
||||
This ensures re-exports are true re-exports (not copies) so that isinstance() checks work across all import
|
||||
paths.
|
||||
"""
|
||||
import rfdetr
|
||||
from rfdetr.detr import ModelContext as FromDetr
|
||||
from rfdetr.detr import RFDETRLarge as LargeFromDetr
|
||||
from rfdetr.inference import ModelContext as FromInference
|
||||
from rfdetr.variants import RFDETRLarge as LargeFromVariants
|
||||
|
||||
assert FromDetr is FromInference, (
|
||||
"rfdetr.detr.ModelContext and rfdetr.inference.ModelContext must be the same object"
|
||||
)
|
||||
assert LargeFromDetr is LargeFromVariants, (
|
||||
"rfdetr.detr.RFDETRLarge and rfdetr.variants.RFDETRLarge must be the same object"
|
||||
)
|
||||
assert rfdetr.RFDETRLarge is LargeFromVariants, (
|
||||
"top-level rfdetr.RFDETRLarge and rfdetr.variants.RFDETRLarge must be the same object"
|
||||
)
|
||||
assert rfdetr.ModelContext is FromInference, (
|
||||
"top-level rfdetr.ModelContext and rfdetr.inference.ModelContext must be the same object"
|
||||
)
|
||||
|
||||
def test_build_model_context_backward_compat_from_detr(self) -> None:
|
||||
"""_build_model_context must remain importable from rfdetr.detr (backward compat)."""
|
||||
from rfdetr.detr import _build_model_context
|
||||
|
||||
assert callable(_build_model_context)
|
||||
|
||||
def test_getattr_raises_for_unknown_names(self) -> None:
|
||||
"""Accessing an unknown name via rfdetr.detr must raise AttributeError."""
|
||||
import rfdetr.detr as detr_mod
|
||||
|
||||
with pytest.raises(AttributeError, match="has no attribute"):
|
||||
_ = detr_mod._this_name_does_not_exist_12345
|
||||
|
||||
def test_dir_includes_lazy_exports(self) -> None:
|
||||
"""dir(rfdetr.detr) must include all lazy re-export names."""
|
||||
import rfdetr.detr as detr_mod
|
||||
|
||||
names = dir(detr_mod)
|
||||
assert "ModelContext" in names, "ModelContext missing from dir(rfdetr.detr)"
|
||||
assert "RFDETRLarge" in names, "RFDETRLarge missing from dir(rfdetr.detr)"
|
||||
assert "RFDETRBase" in names, "RFDETRBase missing from dir(rfdetr.detr)"
|
||||
|
||||
def test_detr_first_import_order_preserves_identity(self) -> None:
|
||||
"""Importing detr before variants must preserve object identity for variant classes."""
|
||||
result = subprocess.run(
|
||||
[
|
||||
sys.executable,
|
||||
"-c",
|
||||
(
|
||||
"from rfdetr.detr import RFDETRLarge; "
|
||||
"from rfdetr.variants import RFDETRLarge as FromVariants; "
|
||||
"assert RFDETRLarge is FromVariants"
|
||||
),
|
||||
],
|
||||
capture_output=True,
|
||||
text=True,
|
||||
check=False,
|
||||
)
|
||||
assert result.returncode == 0, (
|
||||
"Subprocess for detr-first import failed:\n"
|
||||
f"return code: {result.returncode}\n"
|
||||
f"stdout:\n{result.stdout}\n"
|
||||
f"stderr:\n{result.stderr}"
|
||||
)
|
||||
Reference in New Issue
Block a user