9194ef5abd
Docs/Test Workflow / Test docs build (push) Failing after 0s
Check links & references / links-check (push) Failing after 1s
Pytest/Test Workflow / Import Test and Pytest Run (ubuntu-latest, 3.10) (push) Failing after 0s
Pytest/Test Workflow / Import Test and Pytest Run (ubuntu-latest, 3.11) (push) Failing after 0s
PR Conflict Labeler / main (push) Failing after 2s
Pytest/Test Workflow / Import Test and Pytest Run (ubuntu-latest, 3.12) (push) Failing after 2s
Pytest/Test Workflow / Import Test and Pytest Run (ubuntu-latest, 3.13) (push) Failing after 0s
Pytest/Test Workflow / Build this Package (push) Failing after 5s
Pytest/Test Workflow / Import Test and Pytest Run (macos-latest, 3.10) (push) Has been cancelled
Pytest/Test Workflow / Import Test and Pytest Run (macos-latest, 3.11) (push) Has been cancelled
Pytest/Test Workflow / Import Test and Pytest Run (macos-latest, 3.12) (push) Has been cancelled
Pytest/Test Workflow / Import Test and Pytest Run (macos-latest, 3.13) (push) Has been cancelled
Pytest/Test Workflow / Import Test and Pytest Run (windows-latest, 3.10) (push) Has been cancelled
Pytest/Test Workflow / Import Test and Pytest Run (windows-latest, 3.11) (push) Has been cancelled
Pytest/Test Workflow / Import Test and Pytest Run (windows-latest, 3.12) (push) Has been cancelled
Pytest/Test Workflow / Import Test and Pytest Run (windows-latest, 3.13) (push) Has been cancelled
Pytest/Test Workflow / testing-guardian (push) Has been cancelled
265 lines
6.7 KiB
Python
265 lines
6.7 KiB
Python
import json
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
import warnings
|
|
from contextlib import ExitStack as DoesNotRaise
|
|
from dataclasses import dataclass, field
|
|
from typing import Any
|
|
|
|
import numpy as np
|
|
import pytest
|
|
|
|
from supervision.detection.core import Detections
|
|
from supervision.utils.internal import (
|
|
SupervisionWarnings,
|
|
format_warning,
|
|
get_instance_variables,
|
|
)
|
|
|
|
|
|
class MockClass:
|
|
def __init__(self) -> None:
|
|
self.public = 0
|
|
self._protected = 1
|
|
self.__private = 2
|
|
|
|
def public_method(self) -> None:
|
|
pass
|
|
|
|
def _protected_method(self) -> None:
|
|
pass
|
|
|
|
def __private_method(self) -> None:
|
|
pass
|
|
|
|
@property
|
|
def public_property(self) -> int:
|
|
return 0
|
|
|
|
@property
|
|
def _protected_property(self) -> int:
|
|
return 1
|
|
|
|
@property
|
|
def __private_property(self) -> int:
|
|
return 2
|
|
|
|
|
|
@dataclass
|
|
class MockDataclass:
|
|
public: int = 0
|
|
_protected: int = 1
|
|
__private: int = 2
|
|
|
|
public_field: int = field(default=0)
|
|
_protected_field: int = field(default=1)
|
|
__private_field: int = field(default=2)
|
|
|
|
public_field_with_factory: dict = field(default_factory=dict)
|
|
_protected_field_with_factory: dict = field(default_factory=dict)
|
|
__private_field_with_factory: dict = field(default_factory=dict)
|
|
|
|
def public_method(self) -> None:
|
|
pass
|
|
|
|
def _protected_method(self) -> None:
|
|
pass
|
|
|
|
def __private_method(self) -> None:
|
|
pass
|
|
|
|
@property
|
|
def public_property(self) -> int:
|
|
return 0
|
|
|
|
@property
|
|
def _protected_property(self) -> int:
|
|
return 1
|
|
|
|
@property
|
|
def __private_property(self) -> int:
|
|
return 2
|
|
|
|
|
|
def _warning_messages_for_env(
|
|
*, new_env: str | None, legacy_env: str | None
|
|
) -> list[str]:
|
|
"""Run the deprecation-warning path under a controlled environment."""
|
|
env = os.environ.copy()
|
|
env.pop("SUPERVISION_DEPRECATION_WARNING", None)
|
|
env.pop("SUPERVISON_DEPRECATION_WARNING", None)
|
|
if new_env is not None:
|
|
env["SUPERVISION_DEPRECATION_WARNING"] = new_env
|
|
if legacy_env is not None:
|
|
env["SUPERVISON_DEPRECATION_WARNING"] = legacy_env
|
|
|
|
script = """
|
|
import json
|
|
import warnings
|
|
from supervision.utils.internal import warn_deprecated
|
|
|
|
with warnings.catch_warnings(record=True) as recorded:
|
|
warn_deprecated("deprecated")
|
|
|
|
print(json.dumps([str(item.message) for item in recorded]))
|
|
"""
|
|
completed = subprocess.run( # noqa: S603 - trusted fixed command in a test helper.
|
|
[sys.executable, "-c", script],
|
|
check=True,
|
|
capture_output=True,
|
|
text=True,
|
|
env=env,
|
|
)
|
|
return json.loads(completed.stdout)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("input_instance", "include_properties", "expected", "exception"),
|
|
[
|
|
(
|
|
MockClass,
|
|
False,
|
|
None,
|
|
pytest.raises(ValueError, match="Only class instances are supported"),
|
|
),
|
|
(
|
|
MockClass(),
|
|
False,
|
|
{"public"},
|
|
DoesNotRaise(),
|
|
),
|
|
(
|
|
MockClass(),
|
|
True,
|
|
{"public", "public_property"},
|
|
DoesNotRaise(),
|
|
),
|
|
(
|
|
MockDataclass(),
|
|
False,
|
|
{"public", "public_field", "public_field_with_factory"},
|
|
DoesNotRaise(),
|
|
),
|
|
(
|
|
MockDataclass(),
|
|
True,
|
|
{"public", "public_field", "public_field_with_factory", "public_property"},
|
|
DoesNotRaise(),
|
|
),
|
|
(
|
|
Detections,
|
|
False,
|
|
None,
|
|
pytest.raises(ValueError, match="Only class instances are supported"),
|
|
),
|
|
(
|
|
Detections,
|
|
True,
|
|
None,
|
|
pytest.raises(ValueError, match="Only class instances are supported"),
|
|
),
|
|
(
|
|
Detections.empty(),
|
|
False,
|
|
{
|
|
"xyxy",
|
|
"class_id",
|
|
"confidence",
|
|
"mask",
|
|
"tracker_id",
|
|
"data",
|
|
"metadata",
|
|
},
|
|
DoesNotRaise(),
|
|
),
|
|
(
|
|
Detections.empty(),
|
|
True,
|
|
{
|
|
"xyxy",
|
|
"class_id",
|
|
"confidence",
|
|
"mask",
|
|
"tracker_id",
|
|
"data",
|
|
"metadata",
|
|
"area",
|
|
"box_area",
|
|
"box_aspect_ratio",
|
|
},
|
|
DoesNotRaise(),
|
|
),
|
|
(
|
|
Detections(xyxy=np.array([[1, 2, 3, 4]])),
|
|
False,
|
|
{
|
|
"xyxy",
|
|
"class_id",
|
|
"confidence",
|
|
"mask",
|
|
"tracker_id",
|
|
"data",
|
|
"metadata",
|
|
},
|
|
DoesNotRaise(),
|
|
),
|
|
(
|
|
Detections(
|
|
xyxy=np.array([[1, 2, 3, 4], [5, 6, 7, 8]]),
|
|
class_id=np.array([1, 2]),
|
|
confidence=np.array([0.1, 0.2]),
|
|
mask=np.array([[[1]], [[2]]], dtype=bool),
|
|
tracker_id=np.array([1, 2]),
|
|
data={"key_1": [1, 2], "key_2": [3, 4]},
|
|
),
|
|
False,
|
|
{
|
|
"xyxy",
|
|
"class_id",
|
|
"confidence",
|
|
"mask",
|
|
"tracker_id",
|
|
"data",
|
|
"metadata",
|
|
},
|
|
DoesNotRaise(),
|
|
),
|
|
],
|
|
)
|
|
def test_get_instance_variables(
|
|
input_instance: Any,
|
|
include_properties: bool,
|
|
expected: set[str],
|
|
exception: Exception,
|
|
) -> None:
|
|
with exception:
|
|
result = get_instance_variables(
|
|
input_instance, include_properties=include_properties
|
|
)
|
|
assert result == expected
|
|
|
|
|
|
def test_supervision_warning_formatter_is_not_global() -> None:
|
|
"""Supervision warning formatting is opt-in, not global warnings state."""
|
|
assert warnings.formatwarning is not format_warning
|
|
assert format_warning("message", SupervisionWarnings, "file.py", 1) == (
|
|
"SupervisionWarnings: message\n"
|
|
)
|
|
|
|
|
|
@pytest.mark.parametrize(
|
|
("new_env", "legacy_env", "expected_count"),
|
|
[
|
|
pytest.param("0", "1", 0, id="prefer-new-env"),
|
|
pytest.param(None, "0", 0, id="legacy-fallback"),
|
|
pytest.param("1", "0", 1, id="new-env-enables-warning"),
|
|
],
|
|
)
|
|
def test_supervision_warning_env_var_controls_deprecation_warnings(
|
|
new_env: str | None, legacy_env: str | None, expected_count: int
|
|
) -> None:
|
|
"""SUPERVISION_DEPRECATION_WARNING keeps precedence over the legacy alias."""
|
|
messages = _warning_messages_for_env(new_env=new_env, legacy_env=legacy_env)
|
|
assert len(messages) == expected_count
|