Skip to content

Commit 8e1e609

Browse files
authored
Fix #113: support server role aliases and improve validation error reporting
* Added an internal alias mapping to support strings like devel, dev, prod, p, stage, and s. * Implemented the _missing_ class method in the ServerRole enum. * Added case-insensitive lookup logic to handle uppercase input (for example, PRODUCTION). Signed-off-by: sushant-suse <[email protected]>
1 parent dc7627e commit 8e1e609

5 files changed

Lines changed: 62 additions & 44 deletions

File tree

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -228,4 +228,4 @@ jobs:
228228
source .venv/bin/activate
229229
pytest --cov=src -vv
230230
env:
231-
PYTHONSTARTMETHOD: spawn
231+
PYTHONSTARTMETHOD: spawn

changelog.d/113.feature.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Implement a "smart" ``ServerRole`` enum that accepts case-insensitive or abbreviated aliases like ``p``, ``prod``, or ``PRODUCTION``. The new value ``devel`` is an alias for ``testing``.
2+
Refactor CLI error handling to display Pydantic validation failures in a clean, structured format.

src/docbuild/constants.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,15 +20,16 @@
2020
DEFAULT_DELIVERABLES = "*/@supported/en-us"
2121
"""The default deliverables when no specific doctype is provided."""
2222

23-
# SERVER_ROLES = (
24-
# "production", "prod", "p",
25-
# "testing", "test", "t",
26-
# "staging", "stage", "s",
27-
# )
23+
# The primary, unique values of the Enum ('production', 'staging', 'testing')
2824
SERVER_ROLES = tuple(
29-
[role.value for role in ServerRole] # type: ignore[call-arg]
25+
[role.value for role in ServerRole]
3026
)
31-
"""The different server roles, including long and short spelling."""
27+
"""The unique primary server role values."""
28+
29+
# Every single valid name and alias defined in the Enum
30+
# ('PRODUCTION', 'PROD', 'P', 'production', 'prod', 'p', 'devel', etc.)
31+
SERVER_ROLES_ALIASES = tuple(ServerRole.__members__.keys())
32+
"""All valid server role names and aliases for validation and testing."""
3233

3334
DEFAULT_LIFECYCLE = "supported"
3435
"""The default lifecycle state for a docset."""

src/docbuild/models/serverroles.py

Lines changed: 48 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,58 @@
11
"""Server roles for the docbuild application."""
22

33
from enum import StrEnum
4-
5-
# from typing import Literal, Type
6-
# from pydantic import BaseModel, field_validator, Field
7-
# from ...constants import SERVER_ROLES
8-
#
9-
# ServerRole = StrEnum(
10-
# "ServerRole",
11-
# # Allow lowercase and uppercase names
12-
# {name: name for name in SERVER_ROLES}
13-
# | {name.upper(): name for name in SERVER_ROLES},
14-
# )
4+
from typing import Self
155

166

177
class ServerRole(StrEnum):
18-
"""The server role."""
8+
"""The server role.
9+
10+
This Enum supports various aliases and case variations for each role.
11+
"""
1912

20-
# production
13+
# Primary Members
2114
PRODUCTION = "production"
22-
"""Server is in production mode, serving live traffic."""
23-
PROD = PRODUCTION
24-
P = PRODUCTION
25-
production = PRODUCTION
26-
prod = PRODUCTION
27-
p = PRODUCTION
28-
# staging
2915
STAGING = "staging"
30-
"""Server is in staging mode, used for testing before production."""
31-
STAGE = STAGING
32-
S = STAGING
33-
staging = STAGING
34-
stage = STAGING
35-
s = STAGING
36-
# testing
3716
TESTING = "testing"
38-
"""Server is in testing mode, used for development and QA."""
39-
TEST = TESTING
40-
T = TESTING
41-
testing = TESTING
42-
test = TESTING
43-
t = TESTING
17+
18+
# Aliases for PRODUCTION
19+
PROD = "production"
20+
P = "production"
21+
prod = "production"
22+
p = "production"
23+
24+
# Aliases for STAGING
25+
STAGE = "staging"
26+
S = "staging"
27+
stage = "staging"
28+
s = "staging"
29+
30+
# Aliases for TESTING
31+
TEST = "testing"
32+
T = "testing"
33+
test = "testing"
34+
t = "testing"
35+
DEVEL = "testing"
36+
devel = "testing"
37+
DEV = "testing"
38+
dev = "testing"
39+
40+
@classmethod
41+
def _missing_(cls: type[Self], value: object) -> "ServerRole | None":
42+
"""Handle aliases and case-insensitive lookups using class members.
43+
44+
If the value passed isn't a valid value (for example, 'production'),
45+
check if it matches one of the alias names (for example, 'p').
46+
"""
47+
# Convert the input to a string to check against member keys
48+
name = str(value)
49+
member = cls.__members__.get(name)
50+
51+
if member is not None:
52+
return member
53+
54+
# If no match is found, raise ValueError with all valid names/aliases
55+
valid = ", ".join(cls.__members__.keys())
56+
raise ValueError(
57+
f"{name!r} is not a valid {cls.__name__}; valid names/aliases: {valid}"
58+
)

tests/models/test_serverroles.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import pytest
22

3-
from docbuild.constants import SERVER_ROLES
3+
from docbuild.constants import SERVER_ROLES, SERVER_ROLES_ALIASES
44
from docbuild.models.serverroles import ServerRole
55

66

@@ -9,9 +9,9 @@ def test_serverrole_with_call(role):
99
assert ServerRole(role)
1010

1111

12-
@pytest.mark.parametrize("role", SERVER_ROLES)
12+
@pytest.mark.parametrize("role", SERVER_ROLES_ALIASES)
1313
def test_serverrole_with_predicate(role):
14-
assert ServerRole[role]
14+
assert ServerRole(role)
1515

1616

1717
@pytest.mark.parametrize("role", SERVER_ROLES)

0 commit comments

Comments
 (0)