Skip to content

Commit 5e93a68

Browse files
Merge pull request #22 from CASParser/release-please--branches--main--changes--next
release: 1.11.0
2 parents a7ed6fe + 3babe67 commit 5e93a68

11 files changed

Lines changed: 175 additions & 24 deletions

File tree

.github/workflows/ci.yml

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@ jobs:
2121
runs-on: ${{ github.repository == 'stainless-sdks/cas-parser-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }}
2222
if: (github.event_name == 'push' || github.event.pull_request.head.repo.fork) && (github.event_name != 'push' || github.event.head_commit.message != 'codegen metadata')
2323
steps:
24-
- uses: actions/checkout@v6
24+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
2525

2626
- name: Install uv
27-
uses: astral-sh/setup-uv@v5
27+
uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2
2828
with:
2929
version: '0.10.2'
3030

@@ -43,10 +43,10 @@ jobs:
4343
id-token: write
4444
runs-on: ${{ github.repository == 'stainless-sdks/cas-parser-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }}
4545
steps:
46-
- uses: actions/checkout@v6
46+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
4747

4848
- name: Install uv
49-
uses: astral-sh/setup-uv@v5
49+
uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2
5050
with:
5151
version: '0.10.2'
5252

@@ -61,7 +61,7 @@ jobs:
6161
github.repository == 'stainless-sdks/cas-parser-python' &&
6262
!startsWith(github.ref, 'refs/heads/stl/')
6363
id: github-oidc
64-
uses: actions/github-script@v8
64+
uses: actions/github-script@ed597411d8f924073f98dfc5c65a23a2325f34cd # v8.0.0
6565
with:
6666
script: core.setOutput('github_token', await core.getIDToken());
6767

@@ -81,10 +81,10 @@ jobs:
8181
runs-on: ${{ github.repository == 'stainless-sdks/cas-parser-python' && 'depot-ubuntu-24.04' || 'ubuntu-latest' }}
8282
if: github.event_name == 'push' || github.event.pull_request.head.repo.fork
8383
steps:
84-
- uses: actions/checkout@v6
84+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
8585

8686
- name: Install uv
87-
uses: astral-sh/setup-uv@v5
87+
uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2
8888
with:
8989
version: '0.10.2'
9090

.github/workflows/publish-pypi.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,10 +17,10 @@ jobs:
1717
id-token: write
1818

1919
steps:
20-
- uses: actions/checkout@v6
20+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
2121

2222
- name: Install uv
23-
uses: astral-sh/setup-uv@v5
23+
uses: astral-sh/setup-uv@d4b2f3b6ecc6e67c4457f6d3e41ec42d3d0fcb86 # v5.4.2
2424
with:
2525
version: '0.9.13'
2626

.github/workflows/release-doctor.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ jobs:
1212
if: github.repository == 'CASParser/cas-parser-python' && (github.event_name == 'push' || github.event_name == 'workflow_dispatch' || startsWith(github.head_ref, 'release-please') || github.head_ref == 'next')
1313

1414
steps:
15-
- uses: actions/checkout@v6
15+
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
1616

1717
- name: Check release environment
1818
run: |

.release-please-manifest.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
2-
".": "1.10.0"
2+
".": "1.11.0"
33
}

.stats.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
configured_endpoints: 21
2-
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cas-parser/cas-parser-e572d88c2af6e4d7bc4f7e119357fd3f68b1e67d612fd1d3a657d916cde0087c.yml
3-
openapi_spec_hash: a9fc7d947111bffa9184f8ca8be4a579
2+
openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/cas-parser/cas-parser-904e3aa8081755d046016db9d84d13d140a4235c724e18e1cd7f8ebb7712883e.yml
3+
openapi_spec_hash: 453b8e667c364b064e04352ad4deccfa
44
config_hash: 5509bb7a961ae2e79114b24c381606d4

CHANGELOG.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,18 @@
11
# Changelog
22

3+
## 1.11.0 (2026-06-18)
4+
5+
Full Changelog: [v1.10.0...v1.11.0](https://github.com/CASParser/cas-parser-python/compare/v1.10.0...v1.11.0)
6+
7+
### Features
8+
9+
* **internal/types:** support eagerly validating pydantic iterators ([47b37f5](https://github.com/CASParser/cas-parser-python/commit/47b37f58341741936a5b3e9954fecd63b4a0ccaf))
10+
11+
12+
### Bug Fixes
13+
14+
* **auth:** prioritize first auth header ([b0e3a92](https://github.com/CASParser/cas-parser-python/commit/b0e3a923509d1c47c31bf6a65884dce31d11ea54))
15+
316
## 1.10.0 (2026-05-09)
417

518
Full Changelog: [v1.9.0...v1.10.0](https://github.com/CASParser/cas-parser-python/compare/v1.9.0...v1.10.0)

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "cas-parser-python"
3-
version = "1.10.0"
3+
version = "1.11.0"
44
description = "The official Python library for the cas-parser API"
55
dynamic = ["readme"]
66
license = "Apache-2.0"

src/cas_parser/_client.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -290,9 +290,11 @@ def qs(self) -> Querystring:
290290

291291
@override
292292
def _auth_headers(self, security: SecurityOptions) -> dict[str, str]:
293-
return {
294-
**(self._api_key_auth if security.get("api_key_auth", False) else {}),
295-
}
293+
headers: dict[str, str] = {}
294+
if security.get("api_key_auth", False):
295+
for key, value in self._api_key_auth.items():
296+
headers.setdefault(key, value)
297+
return headers
296298

297299
@property
298300
def _api_key_auth(self) -> dict[str, str]:
@@ -608,9 +610,11 @@ def qs(self) -> Querystring:
608610

609611
@override
610612
def _auth_headers(self, security: SecurityOptions) -> dict[str, str]:
611-
return {
612-
**(self._api_key_auth if security.get("api_key_auth", False) else {}),
613-
}
613+
headers: dict[str, str] = {}
614+
if security.get("api_key_auth", False):
615+
for key, value in self._api_key_auth.items():
616+
headers.setdefault(key, value)
617+
return headers
614618

615619
@property
616620
def _api_key_auth(self) -> dict[str, str]:

src/cas_parser/_models.py

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,9 @@
2525
ClassVar,
2626
Protocol,
2727
Required,
28+
Annotated,
2829
ParamSpec,
30+
TypeAlias,
2931
TypedDict,
3032
TypeGuard,
3133
final,
@@ -79,7 +81,15 @@
7981
from ._constants import RAW_RESPONSE_HEADER
8082

8183
if TYPE_CHECKING:
84+
from pydantic import GetCoreSchemaHandler, ValidatorFunctionWrapHandler
85+
from pydantic_core import CoreSchema, core_schema
8286
from pydantic_core.core_schema import ModelField, ModelSchema, LiteralSchema, ModelFieldsSchema
87+
else:
88+
try:
89+
from pydantic_core import CoreSchema, core_schema
90+
except ImportError:
91+
CoreSchema = None
92+
core_schema = None
8393

8494
__all__ = ["BaseModel", "GenericModel"]
8595

@@ -396,6 +406,76 @@ def model_dump_json(
396406
)
397407

398408

409+
class _EagerIterable(list[_T], Generic[_T]):
410+
"""
411+
Accepts any Iterable[T] input (including generators), consumes it
412+
eagerly, and validates all items upfront.
413+
414+
Validation preserves the original container type where possible
415+
(e.g. a set[T] stays a set[T]). Serialization (model_dump / JSON)
416+
always emits a list — round-tripping through model_dump() will not
417+
restore the original container type.
418+
"""
419+
420+
@classmethod
421+
def __get_pydantic_core_schema__(
422+
cls,
423+
source_type: Any,
424+
handler: GetCoreSchemaHandler,
425+
) -> CoreSchema:
426+
(item_type,) = get_args(source_type) or (Any,)
427+
item_schema: CoreSchema = handler.generate_schema(item_type)
428+
list_of_items_schema: CoreSchema = core_schema.list_schema(item_schema)
429+
430+
return core_schema.no_info_wrap_validator_function(
431+
cls._validate,
432+
list_of_items_schema,
433+
serialization=core_schema.plain_serializer_function_ser_schema(
434+
cls._serialize,
435+
info_arg=False,
436+
),
437+
)
438+
439+
@staticmethod
440+
def _validate(v: Iterable[_T], handler: "ValidatorFunctionWrapHandler") -> Any:
441+
original_type: type[Any] = type(v)
442+
443+
# Normalize to list so list_schema can validate each item
444+
if isinstance(v, list):
445+
items: list[_T] = v
446+
else:
447+
try:
448+
items = list(v)
449+
except TypeError as e:
450+
raise TypeError("Value is not iterable") from e
451+
452+
# Validate items against the inner schema
453+
validated: list[_T] = handler(items)
454+
455+
# Reconstruct original container type
456+
if original_type is list:
457+
return validated
458+
# str(list) produces the list's repr, not a string built from items,
459+
# so skip reconstruction for str and its subclasses.
460+
if issubclass(original_type, str):
461+
return validated
462+
try:
463+
return original_type(validated)
464+
except (TypeError, ValueError):
465+
# If the type cannot be reconstructed, just return the validated list
466+
return validated
467+
468+
@staticmethod
469+
def _serialize(v: Iterable[_T]) -> list[_T]:
470+
"""Always serialize as a list so Pydantic's JSON encoder is happy."""
471+
if isinstance(v, list):
472+
return v
473+
return list(v)
474+
475+
476+
EagerIterable: TypeAlias = Annotated[Iterable[_T], _EagerIterable]
477+
478+
399479
def _construct_field(value: object, field: FieldInfo, key: str) -> object:
400480
if value is None:
401481
return field_get_default(field)

src/cas_parser/_version.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
# File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
22

33
__title__ = "cas_parser"
4-
__version__ = "1.10.0" # x-release-please-version
4+
__version__ = "1.11.0" # x-release-please-version

0 commit comments

Comments
 (0)