Skip to content

Commit 4d6a8fd

Browse files
author
CometAPI
committed
fix: cover Unicode configuration boundaries
1 parent 4cea9df commit 4d6a8fd

4 files changed

Lines changed: 38 additions & 13 deletions

File tree

scripts/check_workflows.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@
2121
RELEASE_PLEASE_LOCK_JSONPATH = "$.package[?(@.name.value == 'cometapi')].version"
2222
RELEASE_PLEASE_ACTION_SHA = "5c625bfb5d1ff62eadeeb3772007f7f66fdcf071"
2323
RELEASE_PLEASE_BRIDGE_VERSION = "0.1.0-alpha.1"
24-
RELEASE_PLEASE_STABLE_VERSION_PATTERN = re.compile(r"0\.1\.(?:0|[1-9]\d*)")
24+
RELEASE_PLEASE_STABLE_VERSION_PATTERN = re.compile(r"0\.1\.(?:0|[1-9][0-9]*)")
2525
RELEASE_PLEASE_VERIFY_COMMAND = """\
2626
test -n "$EXPECTED_TAG"
2727
test -n "$EXPECTED_SHA"

src/cometapi/_config.py

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,23 @@
1212
_T = TypeVar("_T")
1313

1414

15+
def _strip_configuration_whitespace(value: str) -> str:
16+
start = 0
17+
end = len(value)
18+
while start < end and (value[start].isspace() or value[start] == "\ufeff"):
19+
start += 1
20+
while end > start and (value[end - 1].isspace() or value[end - 1] == "\ufeff"):
21+
end -= 1
22+
return value[start:end]
23+
24+
1525
def resolve_api_key(api_key: _T | None) -> _T | str:
1626
"""Resolve an explicit API key before consulting ``COMETAPI_KEY``."""
1727
if api_key is not None and not isinstance(api_key, str):
1828
return api_key
1929

2030
resolved = api_key if api_key is not None else os.environ.get("COMETAPI_KEY")
21-
normalized = resolved.strip() if resolved is not None else ""
31+
normalized = _strip_configuration_whitespace(resolved) if resolved is not None else ""
2232
if not normalized:
2333
raise OpenAIError(
2434
"The CometAPI API key must be provided with the api_key client option "
@@ -31,12 +41,14 @@ def resolve_base_url(base_url: _T | None) -> _T | str:
3141
"""Resolve an explicit base URL before environment and default values."""
3242
if base_url is not None:
3343
if isinstance(base_url, str):
34-
normalized = base_url.strip()
44+
normalized = _strip_configuration_whitespace(base_url)
3545
if not normalized:
3646
raise OpenAIError("The CometAPI base_url client option must not be empty.")
3747
return normalized
3848
return base_url
3949

4050
environment_url = os.environ.get("COMETAPI_BASE_URL")
41-
normalized = environment_url.strip() if environment_url is not None else ""
51+
normalized = (
52+
_strip_configuration_whitespace(environment_url) if environment_url is not None else ""
53+
)
4254
return normalized or DEFAULT_BASE_URL

tests/test_client.py

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ def test_missing_key_raises_official_error_without_secret(
4646

4747

4848
@pytest.mark.parametrize("client_type", [CometAPI, AsyncCometAPI])
49-
@pytest.mark.parametrize("api_key", ["", " "])
49+
@pytest.mark.parametrize("api_key", ["", " ", "\ufeff", " \ufeff\t"])
5050
def test_explicit_blank_key_does_not_fall_back_to_environment(
5151
client_type: type[CometAPI] | type[AsyncCometAPI],
5252
api_key: str,
@@ -61,11 +61,13 @@ def test_explicit_blank_key_does_not_fall_back_to_environment(
6161

6262

6363
@pytest.mark.parametrize("client_type", [CometAPI, AsyncCometAPI])
64+
@pytest.mark.parametrize("environment_key", [" \t ", "\ufeff", " \ufeff\t"])
6465
def test_whitespace_environment_key_is_treated_as_missing(
6566
client_type: type[CometAPI] | type[AsyncCometAPI],
67+
environment_key: str,
6668
monkeypatch: pytest.MonkeyPatch,
6769
) -> None:
68-
monkeypatch.setenv("COMETAPI_KEY", " \t ")
70+
monkeypatch.setenv("COMETAPI_KEY", environment_key)
6971
monkeypatch.setenv("OPENAI_API_KEY", "upstream-environment-key")
7072

7173
with pytest.raises(OpenAIError) as caught:
@@ -93,7 +95,11 @@ def test_explicit_sync_key_is_trimmed() -> None:
9395
router = ContractRouter()
9496
http_client = sync_http_client(router)
9597

96-
with CometAPI(api_key=f" {API_KEY}\t", base_url=BASE_URL, http_client=http_client) as client:
98+
with CometAPI(
99+
api_key=f" \ufeff{API_KEY}\ufeff\t",
100+
base_url=BASE_URL,
101+
http_client=http_client,
102+
) as client:
97103
client.models.list()
98104

99105
assert router.requests[0].headers["authorization"] == f"Bearer {API_KEY}"
@@ -123,7 +129,7 @@ async def test_explicit_async_key_is_trimmed() -> None:
123129
http_client = async_http_client(router)
124130

125131
async with AsyncCometAPI(
126-
api_key=f" {API_KEY}\t",
132+
api_key=f" \ufeff{API_KEY}\ufeff\t",
127133
base_url=BASE_URL,
128134
http_client=http_client,
129135
) as client:
@@ -171,7 +177,7 @@ def test_explicit_base_url_takes_precedence_over_environment(
171177

172178

173179
@pytest.mark.parametrize("client_type", [CometAPI, AsyncCometAPI])
174-
@pytest.mark.parametrize("base_url", ["", " "])
180+
@pytest.mark.parametrize("base_url", ["", " ", "\ufeff", " \ufeff\t"])
175181
def test_explicit_blank_base_url_does_not_fall_back_to_environment(
176182
client_type: type[CometAPI] | type[AsyncCometAPI],
177183
base_url: str,
@@ -184,7 +190,7 @@ def test_explicit_blank_base_url_does_not_fall_back_to_environment(
184190

185191

186192
def test_explicit_sync_string_base_url_is_trimmed() -> None:
187-
client = CometAPI(api_key=API_KEY, base_url=f" {BASE_URL}\t")
193+
client = CometAPI(api_key=API_KEY, base_url=f" \ufeff{BASE_URL}\ufeff\t")
188194
try:
189195
assert str(client.base_url) == f"{BASE_URL}/"
190196
finally:
@@ -248,10 +254,12 @@ def test_default_base_url_is_used_when_no_override_exists(
248254
client.close()
249255

250256

257+
@pytest.mark.parametrize("environment_url", [" \t ", "\ufeff", " \ufeff\t"])
251258
def test_whitespace_environment_base_url_uses_cometapi_default(
259+
environment_url: str,
252260
monkeypatch: pytest.MonkeyPatch,
253261
) -> None:
254-
monkeypatch.setenv("COMETAPI_BASE_URL", " \t ")
262+
monkeypatch.setenv("COMETAPI_BASE_URL", environment_url)
255263
monkeypatch.setenv("OPENAI_BASE_URL", "https://upstream.example.test/v1")
256264

257265
client = CometAPI(api_key=API_KEY)
@@ -262,10 +270,12 @@ def test_whitespace_environment_base_url_uses_cometapi_default(
262270

263271

264272
@pytest.mark.asyncio
273+
@pytest.mark.parametrize("environment_url", [" \t ", "\ufeff", " \ufeff\t"])
265274
async def test_async_whitespace_environment_base_url_uses_cometapi_default(
275+
environment_url: str,
266276
monkeypatch: pytest.MonkeyPatch,
267277
) -> None:
268-
monkeypatch.setenv("COMETAPI_BASE_URL", " \t ")
278+
monkeypatch.setenv("COMETAPI_BASE_URL", environment_url)
269279
monkeypatch.setenv("OPENAI_BASE_URL", "https://upstream.example.test/v1")
270280

271281
client = AsyncCometAPI(api_key=API_KEY)

tests/test_release_workflow.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1017,7 +1017,10 @@ def test_release_please_config_accepts_stable_maintenance_cleanup(version: str)
10171017
)
10181018

10191019

1020-
@pytest.mark.parametrize("version", ["0.1.01", "0.1.1-alpha.1", "0.2.0", "1.0.0"])
1020+
@pytest.mark.parametrize(
1021+
"version",
1022+
["0.1.01", "0.1.1-alpha.1", "0.1.1\u0662", "0.1.1\uff12", "0.2.0", "1.0.0"],
1023+
)
10211024
def test_release_please_config_rejects_versions_outside_stable_maintenance(
10221025
version: str,
10231026
) -> None:

0 commit comments

Comments
 (0)