Update python dependencies and ruff/black versions

This commit is contained in:
Donkie
2025-03-16 21:33:08 +01:00
parent 92065fd8df
commit 70ea7ad414
9 changed files with 295 additions and 273 deletions

View File

@@ -60,7 +60,6 @@ select = [
"ALL", "ALL",
] ]
ignore = [ ignore = [
"ANN101",
"A003", "A003",
"D101", "D101",
"D104", "D104",
@@ -85,6 +84,7 @@ ignore = [
[tool.ruff.lint.per-file-ignores] [tool.ruff.lint.per-file-ignores]
"tests*/*" = [ "tests*/*" = [
"ANN201", "ANN201",
"ASYNC210",
"S101", "S101",
"PLR2004", "PLR2004",
"D103", "D103",

View File

@@ -121,14 +121,13 @@ class FilamentParameters(BaseModel):
@field_validator("color_hex") @field_validator("color_hex")
@classmethod @classmethod
def color_hex_validator(cls, v: Optional[str]) -> Optional[str]: # noqa: ANN102 def color_hex_validator(cls, v: Optional[str]) -> Optional[str]:
"""Validate the color_hex field.""" """Validate the color_hex field."""
if not v: if not v:
return None return None
clr = v.upper() clr = v.upper()
if clr.startswith("#"): clr = clr.removeprefix("#")
clr = clr[1:]
for c in clr: for c in clr:
if c not in "0123456789ABCDEF": if c not in "0123456789ABCDEF":
@@ -141,14 +140,13 @@ class FilamentParameters(BaseModel):
@field_validator("multi_color_hexes") @field_validator("multi_color_hexes")
@classmethod @classmethod
def multi_color_hexes_validator(cls, v: Optional[str]) -> Optional[str]: # noqa: ANN102 def multi_color_hexes_validator(cls, v: Optional[str]) -> Optional[str]:
"""Validate the multi_color_hexes field.""" """Validate the multi_color_hexes field."""
if not v: if not v:
return None return None
for clr_raw in v.split(","): for clr_raw in v.split(","):
clr = clr_raw.upper() clr = clr_raw.upper()
if clr.startswith("#"): clr = clr.removeprefix("#")
clr = clr[1:]
for c in clr: for c in clr:
if c not in "0123456789ABCDEF": if c not in "0123456789ABCDEF":
@@ -204,111 +202,123 @@ class FilamentUpdateParameters(FilamentParameters):
async def find( async def find(
*, *,
db: Annotated[AsyncSession, Depends(get_db_session)], db: Annotated[AsyncSession, Depends(get_db_session)],
vendor_name_old: Optional[str] = Query( vendor_name_old: Annotated[
alias="vendor_name", Optional[str],
default=None, Query(alias="vendor_name", title="Vendor Name", description="See vendor.name.", deprecated=True),
title="Vendor Name", ] = None,
description="See vendor.name.", vendor_id_old: Annotated[
deprecated=True, Optional[str],
), Query(
vendor_id_old: Optional[str] = Query( alias="vendor_id",
alias="vendor_id", title="Vendor ID",
default=None, description="See vendor.id.",
title="Vendor ID", deprecated=True,
description="See vendor.id.", pattern=r"^-?\d+(,-?\d+)*$",
deprecated=True,
pattern=r"^-?\d+(,-?\d+)*$",
),
vendor_name: Optional[str] = Query(
alias="vendor.name",
default=None,
title="Vendor Name",
description=(
"Partial case-insensitive search term for the filament vendor name. "
"Separate multiple terms with a comma. Specify an empty string to match filaments with no vendor name. "
"Surround a term with quotes to search for the exact term."
), ),
), ] = None,
vendor_id: Optional[str] = Query( vendor_name: Annotated[
alias="vendor.id", Optional[str],
default=None, Query(
title="Vendor ID", alias="vendor.name",
description=( title="Vendor Name",
"Match an exact vendor ID. Separate multiple IDs with a comma. " description=(
"Specify -1 to match filaments with no vendor." "Partial case-insensitive search term for the filament vendor name. "
"Separate multiple terms with a comma. Specify an empty string to match filaments with no vendor name. "
"Surround a term with quotes to search for the exact term."
),
), ),
pattern=r"^-?\d+(,-?\d+)*$", ] = None,
examples=["1", "1,2"], vendor_id: Annotated[
), Optional[str],
name: Optional[str] = Query( Query(
default=None, alias="vendor.id",
title="Filament Name", title="Vendor ID",
description=( description=(
"Partial case-insensitive search term for the filament name. Separate multiple terms with a comma. " "Match an exact vendor ID. Separate multiple IDs with a comma. "
"Specify an empty string to match filaments with no name. " "Specify -1 to match filaments with no vendor."
"Surround a term with quotes to search for the exact term." ),
pattern=r"^-?\d+(,-?\d+)*$",
examples=["1", "1,2"],
), ),
), ] = None,
material: Optional[str] = Query( name: Annotated[
default=None, Optional[str],
title="Filament Material", Query(
description=( title="Filament Name",
"Partial case-insensitive search term for the filament material. Separate multiple terms with a comma. " description=(
"Specify an empty string to match filaments with no material. " "Partial case-insensitive search term for the filament name. Separate multiple terms with a comma. "
"Surround a term with quotes to search for the exact term." "Specify an empty string to match filaments with no name. "
"Surround a term with quotes to search for the exact term."
),
), ),
), ] = None,
article_number: Optional[str] = Query( material: Annotated[
default=None, Optional[str],
title="Filament Article Number", Query(
description=( title="Filament Material",
"Partial case-insensitive search term for the filament article number. " description=(
"Separate multiple terms with a comma. " "Partial case-insensitive search term for the filament material. Separate multiple terms with a comma. "
"Specify an empty string to match filaments with no article number. " "Specify an empty string to match filaments with no material. "
"Surround a term with quotes to search for the exact term." "Surround a term with quotes to search for the exact term."
),
), ),
), ] = None,
color_hex: Optional[str] = Query( article_number: Annotated[
default=None, Optional[str],
title="Filament Color", Query(
description="Match filament by similar color. Slow operation!", title="Filament Article Number",
), description=(
color_similarity_threshold: float = Query( "Partial case-insensitive search term for the filament article number. "
default=20.0, "Separate multiple terms with a comma. "
description=( "Specify an empty string to match filaments with no article number. "
"The similarity threshold for color matching. " "Surround a term with quotes to search for the exact term."
"A value between 0.0-100.0, where 0 means match only exactly the same color." ),
), ),
example=20.0, ] = None,
), color_hex: Annotated[
external_id: Optional[str] = Query( Optional[str],
default=None, Query(
description=( title="Filament Color",
"Find filaments imported by the given external ID. " description="Match filament by similar color. Slow operation!",
"Separate multiple IDs with a comma. "
"Specify empty string to match filaments with no external ID. "
"Surround a term with quotes to search for the exact term."
), ),
example="polymaker_pla_polysonicblack_1000_175", ] = None,
), color_similarity_threshold: Annotated[
sort: Optional[str] = Query( float,
default=None, Query(
title="Sort", description=(
description=( "The similarity threshold for color matching. "
'Sort the results by the given field. Should be a comma-separate string with "field:direction" items.' "A value between 0.0-100.0, where 0 means match only exactly the same color."
),
example=20.0,
), ),
example="vendor.name:asc,spool_weight:desc", ] = 20.0,
), external_id: Annotated[
limit: Optional[int] = Query( Optional[str],
default=None, Query(
title="Limit", description=(
description="Maximum number of items in the response.", "Find filaments imported by the given external ID. "
), "Separate multiple IDs with a comma. "
offset: int = Query( "Specify empty string to match filaments with no external ID. "
default=0, "Surround a term with quotes to search for the exact term."
title="Offset", ),
description="Offset in the full result set if a limit is set.", example="polymaker_pla_polysonicblack_1000_175",
), ),
] = None,
sort: Annotated[
Optional[str],
Query(
title="Sort",
description=(
'Sort the results by the given field. Should be a comma-separate string with "field:direction" items.'
),
example="vendor.name:asc,spool_weight:desc",
),
] = None,
limit: Annotated[
Optional[int],
Query(title="Limit", description="Maximum number of items in the response."),
] = None,
offset: Annotated[int, Query(title="Offset", description="Offset in the full result set if a limit is set.")] = 0,
) -> JSONResponse: ) -> JSONResponse:
sort_by: dict[str, SortOrder] = {} sort_by: dict[str, SortOrder] = {}
if sort is not None: if sort is not None:

View File

@@ -128,133 +128,144 @@ class SpoolMeasureParameters(BaseModel):
async def find( async def find(
*, *,
db: Annotated[AsyncSession, Depends(get_db_session)], db: Annotated[AsyncSession, Depends(get_db_session)],
filament_name_old: Optional[str] = Query( filament_name_old: Annotated[
alias="filament_name", Optional[str],
default=None, Query(alias="filament_name", title="Filament Name", description="See filament.name.", deprecated=True),
title="Filament Name", ] = None,
description="See filament.name.", filament_id_old: Annotated[
deprecated=True, Optional[str],
), Query(
filament_id_old: Optional[str] = Query( alias="filament_id",
alias="filament_id", title="Filament ID",
default=None, description="See filament.id.",
title="Filament ID", deprecated=True,
description="See filament.id.", pattern=r"^-?\d+(,-?\d+)*$",
deprecated=True,
pattern=r"^-?\d+(,-?\d+)*$",
),
filament_material_old: Optional[str] = Query(
alias="filament_material",
default=None,
title="Filament Material",
description="See filament.material.",
deprecated=True,
),
vendor_name_old: Optional[str] = Query(
alias="vendor_name",
default=None,
title="Vendor Name",
description="See filament.vendor.name.",
deprecated=True,
),
vendor_id_old: Optional[str] = Query(
alias="vendor_id",
default=None,
title="Vendor ID",
description="See filament.vendor.id.",
deprecated=True,
pattern=r"^-?\d+(,-?\d+)*$",
),
filament_name: Optional[str] = Query(
alias="filament.name",
default=None,
title="Filament Name",
description=(
"Partial case-insensitive search term for the filament name. Separate multiple terms with a comma. "
"Specify an empty string to match spools with no filament name. "
"Surround a term with quotes to search for the exact term."
), ),
), ] = None,
filament_id: Optional[str] = Query( filament_material_old: Annotated[
alias="filament.id", Optional[str],
default=None, Query(
title="Filament ID", alias="filament_material",
description="Match an exact filament ID. Separate multiple IDs with a comma.", title="Filament Material",
examples=["1", "1,2"], description="See filament.material.",
pattern=r"^-?\d+(,-?\d+)*$", deprecated=True,
),
filament_material: Optional[str] = Query(
alias="filament.material",
default=None,
title="Filament Material",
description=(
"Partial case-insensitive search term for the filament material. Separate multiple terms with a comma. "
"Specify an empty string to match spools with no filament material. "
"Surround a term with quotes to search for the exact term."
), ),
), ] = None,
filament_vendor_name: Optional[str] = Query( vendor_name_old: Annotated[
alias="filament.vendor.name", Optional[str],
default=None, Query(alias="vendor_name", title="Vendor Name", description="See filament.vendor.name.", deprecated=True),
title="Vendor Name", ] = None,
description=( vendor_id_old: Annotated[
"Partial case-insensitive search term for the filament vendor name. Separate multiple terms with a comma. " Optional[str],
"Specify an empty string to match spools with no vendor name. " Query(
"Surround a term with quotes to search for the exact term." alias="vendor_id",
title="Vendor ID",
description="See filament.vendor.id.",
deprecated=True,
pattern=r"^-?\d+(,-?\d+)*$",
), ),
), ] = None,
filament_vendor_id: Optional[str] = Query( filament_name: Annotated[
alias="filament.vendor.id", Optional[str],
default=None, Query(
title="Vendor ID", alias="filament.name",
description=( title="Filament Name",
"Match an exact vendor ID. Separate multiple IDs with a comma. " description=(
"Set it to -1 to match spools with filaments with no vendor." "Partial case-insensitive search term for the filament name. Separate multiple terms with a comma. "
"Specify an empty string to match spools with no filament name. "
"Surround a term with quotes to search for the exact term."
),
), ),
examples=["1", "1,2"], ] = None,
pattern=r"^-?\d+(,-?\d+)*$", filament_id: Annotated[
), Optional[str],
location: Optional[str] = Query( Query(
default=None, alias="filament.id",
title="Location", title="Filament ID",
description=( description="Match an exact filament ID. Separate multiple IDs with a comma.",
"Partial case-insensitive search term for the spool location. Separate multiple terms with a comma. " examples=["1", "1,2"],
"Specify an empty string to match spools with no location. " pattern=r"^-?\d+(,-?\d+)*$",
"Surround a term with quotes to search for the exact term."
), ),
), ] = None,
lot_nr: Optional[str] = Query( filament_material: Annotated[
default=None, Optional[str],
title="Lot/Batch Number", Query(
description=( alias="filament.material",
"Partial case-insensitive search term for the spool lot number. Separate multiple terms with a comma. " title="Filament Material",
"Specify an empty string to match spools with no lot nr. " description=(
"Surround a term with quotes to search for the exact term." "Partial case-insensitive search term for the filament material. Separate multiple terms with a comma. "
"Specify an empty string to match spools with no filament material. "
"Surround a term with quotes to search for the exact term."
),
), ),
), ] = None,
allow_archived: bool = Query( filament_vendor_name: Annotated[
default=False, Optional[str],
title="Allow Archived", Query(
description="Whether to include archived spools in the search results.", alias="filament.vendor.name",
), title="Vendor Name",
sort: Optional[str] = Query( description=(
default=None, "Partial case-insensitive search term for the filament vendor name. "
title="Sort", "Separate multiple terms with a comma. "
description=( "Specify an empty string to match spools with no vendor name. "
'Sort the results by the given field. Should be a comma-separate string with "field:direction" items.' "Surround a term with quotes to search for the exact term."
),
), ),
example="filament.name:asc,filament.vendor.id:asc,location:desc", ] = None,
), filament_vendor_id: Annotated[
limit: Optional[int] = Query( Optional[str],
default=None, Query(
title="Limit", alias="filament.vendor.id",
description="Maximum number of items in the response.", title="Vendor ID",
), description=(
offset: int = Query( "Match an exact vendor ID. Separate multiple IDs with a comma. "
default=0, "Set it to -1 to match spools with filaments with no vendor."
title="Offset", ),
description="Offset in the full result set if a limit is set.", examples=["1", "1,2"],
), pattern=r"^-?\d+(,-?\d+)*$",
),
] = None,
location: Annotated[
Optional[str],
Query(
title="Location",
description=(
"Partial case-insensitive search term for the spool location. Separate multiple terms with a comma. "
"Specify an empty string to match spools with no location. "
"Surround a term with quotes to search for the exact term."
),
),
] = None,
lot_nr: Annotated[
Optional[str],
Query(
title="Lot/Batch Number",
description=(
"Partial case-insensitive search term for the spool lot number. Separate multiple terms with a comma. "
"Specify an empty string to match spools with no lot nr. "
"Surround a term with quotes to search for the exact term."
),
),
] = None,
allow_archived: Annotated[
bool,
Query(title="Allow Archived", description="Whether to include archived spools in the search results."),
] = False,
sort: Annotated[
Optional[str],
Query(
title="Sort",
description=(
'Sort the results by the given field. Should be a comma-separate string with "field:direction" items.'
),
example="filament.name:asc,filament.vendor.id:asc,location:desc",
),
] = None,
limit: Annotated[
Optional[int],
Query(title="Limit", description="Maximum number of items in the response."),
] = None,
offset: Annotated[int, Query(title="Offset", description="Offset in the full result set if a limit is set.")] = 0,
) -> JSONResponse: ) -> JSONResponse:
sort_by: dict[str, SortOrder] = {} sort_by: dict[str, SortOrder] = {}
if sort is not None: if sort is not None:

View File

@@ -80,42 +80,43 @@ class VendorUpdateParameters(VendorParameters):
) )
async def find( async def find(
db: Annotated[AsyncSession, Depends(get_db_session)], db: Annotated[AsyncSession, Depends(get_db_session)],
name: Optional[str] = Query( name: Annotated[
default=None, Optional[str],
title="Vendor Name", Query(
description=( title="Vendor Name",
"Partial case-insensitive search term for the vendor name. Separate multiple terms with a comma. " description=(
"Surround a term with quotes to search for the exact term." "Partial case-insensitive search term for the vendor name. Separate multiple terms with a comma. "
"Surround a term with quotes to search for the exact term."
),
), ),
), ] = None,
external_id: Optional[str] = Query( external_id: Annotated[
default=None, Optional[str],
title="Vendor External ID", Query(
description=( title="Vendor External ID",
"Exact match for the vendor external ID. " description=(
"Separate multiple IDs with a comma. " "Exact match for the vendor external ID. "
"Specify empty string to match filaments with no external ID. " "Separate multiple IDs with a comma. "
"Surround a term with quotes to search for the exact term." "Specify empty string to match filaments with no external ID. "
"Surround a term with quotes to search for the exact term."
),
), ),
), ] = None,
sort: Optional[str] = Query( sort: Annotated[
default=None, Optional[str],
title="Sort", Query(
description=( title="Sort",
'Sort the results by the given field. Should be a comma-separate string with "field:direction" items.' description=(
'Sort the results by the given field. Should be a comma-separate string with "field:direction" items.'
),
example="name:asc,id:desc",
), ),
example="name:asc,id:desc", ] = None,
), limit: Annotated[
limit: Optional[int] = Query( Optional[int],
default=None, Query(title="Limit", description="Maximum number of items in the response."),
title="Limit", ] = None,
description="Maximum number of items in the response.", offset: Annotated[int, Query(title="Offset", description="Offset in the full result set if a limit is set.")] = 0,
),
offset: int = Query(
default=0,
title="Offset",
description="Offset in the full result set if a limit is set.",
),
) -> JSONResponse: ) -> JSONResponse:
sort_by: dict[str, SortOrder] = {} sort_by: dict[str, SortOrder] = {}
if sort is not None: if sort is not None:

View File

@@ -59,11 +59,11 @@ class Database:
engine: Optional[AsyncEngine] engine: Optional[AsyncEngine]
session_maker: Optional[async_sessionmaker[AsyncSession]] session_maker: Optional[async_sessionmaker[AsyncSession]]
def __init__(self: "Database", connection_url: URL) -> None: def __init__(self, connection_url: URL) -> None:
"""Construct the Database wrapper and set config parameters.""" """Construct the Database wrapper and set config parameters."""
self.connection_url = connection_url self.connection_url = connection_url
def is_file_based_sqlite(self: "Database") -> bool: def is_file_based_sqlite(self) -> bool:
"""Return True if the database is file based.""" """Return True if the database is file based."""
return ( return (
self.connection_url.drivername[:6] == "sqlite" self.connection_url.drivername[:6] == "sqlite"
@@ -71,7 +71,7 @@ class Database:
and self.connection_url.database != ":memory:" and self.connection_url.database != ":memory:"
) )
def connect(self: "Database") -> None: def connect(self) -> None:
"""Connect to the database.""" """Connect to the database."""
if env.get_logging_level() == logging.DEBUG: if env.get_logging_level() == logging.DEBUG:
logging.getLogger("sqlalchemy.engine").setLevel(logging.INFO) logging.getLogger("sqlalchemy.engine").setLevel(logging.INFO)
@@ -87,7 +87,7 @@ class Database:
) )
self.session_maker = async_sessionmaker(self.engine, autocommit=False, autoflush=True, expire_on_commit=False) self.session_maker = async_sessionmaker(self.engine, autocommit=False, autoflush=True, expire_on_commit=False)
def backup(self: "Database", target_path: Union[str, PathLike[str]]) -> None: def backup(self, target_path: Union[str, PathLike[str]]) -> None:
"""Backup the database.""" """Backup the database."""
if not self.is_file_based_sqlite() or self.connection_url.database is None: if not self.is_file_based_sqlite() or self.connection_url.database is None:
return return
@@ -108,7 +108,7 @@ class Database:
logger.info("Backup complete.") logger.info("Backup complete.")
def backup_and_rotate( def backup_and_rotate(
self: "Database", self,
backup_folder: Union[str, PathLike[str]], backup_folder: Union[str, PathLike[str]],
num_backups: int = 5, num_backups: int = 5,
) -> Optional[Path]: ) -> Optional[Path]:

View File

@@ -23,7 +23,7 @@ class DatabaseType(Enum):
SQLITE = "sqlite" SQLITE = "sqlite"
COCKROACHDB = "cockroachdb" COCKROACHDB = "cockroachdb"
def to_drivername(self: "DatabaseType") -> str: def to_drivername(self) -> str:
"""Get the drivername for the database type. """Get the drivername for the database type.
Returns: Returns:

View File

@@ -153,7 +153,7 @@ async def startup() -> None:
# There is some issue with the uvicorn worker that causes the process to hang when running alembic directly. # There is some issue with the uvicorn worker that causes the process to hang when running alembic directly.
# See: https://github.com/sqlalchemy/alembic/discussions/1155 # See: https://github.com/sqlalchemy/alembic/discussions/1155
project_root = Path(__file__).parent.parent project_root = Path(__file__).parent.parent
subprocess.run(["alembic", "upgrade", "head"], check=True, cwd=project_root) # noqa: S603, S607, ASYNC101 subprocess.run(["alembic", "upgrade", "head"], check=True, cwd=project_root) # noqa: S603, S607, ASYNC221
# Setup scheduler # Setup scheduler
schedule = Scheduler() schedule = Scheduler()

View File

@@ -162,35 +162,35 @@ def random_empty_filament_empty_vendor_impl():
httpx.delete(f"{URL}/api/v1/filament/{filament['id']}").raise_for_status() httpx.delete(f"{URL}/api/v1/filament/{filament['id']}").raise_for_status()
@pytest.fixture() @pytest.fixture
def random_vendor(): def random_vendor():
"""Return a random vendor.""" """Return a random vendor."""
with random_vendor_impl() as random_vendor: with random_vendor_impl() as random_vendor:
yield random_vendor yield random_vendor
@pytest.fixture() @pytest.fixture
def random_empty_vendor(): def random_empty_vendor():
"""Return a random vendor with only required fields specified.""" """Return a random vendor with only required fields specified."""
with random_empty_vendor_impl() as random_empty_vendor: with random_empty_vendor_impl() as random_empty_vendor:
yield random_empty_vendor yield random_empty_vendor
@pytest.fixture() @pytest.fixture
def random_filament(): def random_filament():
"""Return a random filament.""" """Return a random filament."""
with random_filament_impl() as random_filament: with random_filament_impl() as random_filament:
yield random_filament yield random_filament
@pytest.fixture() @pytest.fixture
def random_empty_filament(): def random_empty_filament():
"""Return a random filament with only required fields specified.""" """Return a random filament with only required fields specified."""
with random_empty_filament_impl() as random_empty_filament: with random_empty_filament_impl() as random_empty_filament:
yield random_empty_filament yield random_empty_filament
@pytest.fixture() @pytest.fixture
def random_empty_filament_empty_vendor(): def random_empty_filament_empty_vendor():
"""Return a random filament with only required fields specified and a vendor with only required fields specified.""" """Return a random filament with only required fields specified and a vendor with only required fields specified."""
with random_empty_filament_empty_vendor_impl() as random_empty_filament_empty_vendor: with random_empty_filament_empty_vendor_impl() as random_empty_filament_empty_vendor:

View File

@@ -138,12 +138,12 @@ def test_use_spool_not_found():
assert "123456789" in message assert "123456789" in message
@pytest.mark.asyncio() @pytest.mark.asyncio
async def test_use_spool_concurrent(random_filament: dict[str, Any]): async def test_use_spool_concurrent(random_filament: dict[str, Any]):
"""Test using a spool with many concurrent requests.""" """Test using a spool with many concurrent requests."""
# Setup # Setup
start_weight = 1000 start_weight = 1000
result = httpx.post( # noqa: ASYNC100 result = httpx.post(
f"{URL}/api/v1/spool", f"{URL}/api/v1/spool",
json={ json={
"filament_id": random_filament["id"], "filament_id": random_filament["id"],
@@ -171,10 +171,10 @@ async def test_use_spool_concurrent(random_filament: dict[str, Any]):
) )
# Verify # Verify
result = httpx.get(f"{URL}/api/v1/spool/{spool['id']}") # noqa: ASYNC100 result = httpx.get(f"{URL}/api/v1/spool/{spool['id']}")
result.raise_for_status() result.raise_for_status()
spool = result.json() spool = result.json()
assert spool["remaining_weight"] == pytest.approx(start_weight - (used_weight * requests)) assert spool["remaining_weight"] == pytest.approx(start_weight - (used_weight * requests))
# Clean up # Clean up
httpx.delete(f"{URL}/api/v1/spool/{spool['id']}").raise_for_status() # noqa: ASYNC100 httpx.delete(f"{URL}/api/v1/spool/{spool['id']}").raise_for_status()