Skip to content

Commit 0bbd761

Browse files
committed
Expose resolved cache
1 parent 023da95 commit 0bbd761

File tree

11 files changed

+259
-25
lines changed

11 files changed

+259
-25
lines changed

README.rst

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,21 @@ Python package
116116
117117
For more details, read about `Python package <https://openapi-spec-validator.readthedocs.io/en/latest/python.html>`__.
118118

119+
Performance tuning
120+
******************
121+
122+
You can tune resolved-path caching with an environment variable:
123+
124+
.. code-block:: bash
125+
126+
OPENAPI_SPEC_VALIDATOR_RESOLVED_CACHE_MAXSIZE=2048
127+
128+
Rules:
129+
130+
* Default is ``128``.
131+
* Set ``0`` to disable the resolved cache.
132+
* Invalid values (non-integer or negative) fall back to ``128``.
133+
119134
Related projects
120135
################
121136

docs/cli.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,3 +68,8 @@ CLI (Command Line Interface)
6868
Legacy note:
6969
``--errors`` / ``--error`` are deprecated and emit warnings by default.
7070
Set ``OPENAPI_SPEC_VALIDATOR_WARN_DEPRECATED=0`` to silence warnings.
71+
72+
Performance note:
73+
You can tune resolved-path caching with
74+
``OPENAPI_SPEC_VALIDATOR_RESOLVED_CACHE_MAXSIZE``.
75+
Default is ``128``; set ``0`` to disable.

docs/python.rst

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,3 +59,19 @@ If you want to iterate through validation errors:
5959
from openapi_spec_validator import OpenAPIV32SpecValidator
6060
6161
errors_iterator = OpenAPIV32SpecValidator(spec).iter_errors()
62+
63+
Resolved path cache
64+
-------------------
65+
66+
``openapi-spec-validator`` can configure the ``jsonschema-path`` resolved
67+
path cache through an environment variable:
68+
69+
.. code-block:: bash
70+
71+
OPENAPI_SPEC_VALIDATOR_RESOLVED_CACHE_MAXSIZE=2048
72+
73+
Rules:
74+
75+
* Default is ``128``.
76+
* Set ``0`` to disable the resolved cache.
77+
* Invalid values (non-integer or negative) fall back to ``128``.

openapi_spec_validator/settings.py

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
from pydantic import field_validator
2+
from pydantic_settings import BaseSettings
3+
from pydantic_settings import SettingsConfigDict
4+
5+
ENV_PREFIX = "OPENAPI_SPEC_VALIDATOR_"
6+
RESOLVED_CACHE_MAXSIZE_DEFAULT = 128
7+
8+
9+
class OpenAPISpecValidatorSettings(BaseSettings):
10+
model_config = SettingsConfigDict(
11+
env_prefix=ENV_PREFIX,
12+
extra="ignore",
13+
)
14+
15+
resolved_cache_maxsize: int = RESOLVED_CACHE_MAXSIZE_DEFAULT
16+
17+
@field_validator("resolved_cache_maxsize", mode="before")
18+
@classmethod
19+
def normalize_resolved_cache_maxsize(
20+
cls, value: int | str | None
21+
) -> int:
22+
if value is None:
23+
return RESOLVED_CACHE_MAXSIZE_DEFAULT
24+
25+
if isinstance(value, int):
26+
parsed_value = value
27+
elif isinstance(value, str):
28+
try:
29+
parsed_value = int(value)
30+
except ValueError:
31+
return RESOLVED_CACHE_MAXSIZE_DEFAULT
32+
else:
33+
return RESOLVED_CACHE_MAXSIZE_DEFAULT
34+
35+
if parsed_value < 0:
36+
return RESOLVED_CACHE_MAXSIZE_DEFAULT
37+
38+
return parsed_value
39+
40+
41+
def get_resolved_cache_maxsize() -> int:
42+
settings = OpenAPISpecValidatorSettings()
43+
return settings.resolved_cache_maxsize

openapi_spec_validator/shortcuts.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
from jsonschema_path.handlers import all_urls_handler
88
from jsonschema_path.typing import Schema
99

10+
from openapi_spec_validator.settings import OpenAPISpecValidatorSettings
1011
from openapi_spec_validator.validation import OpenAPIV2SpecValidator
1112
from openapi_spec_validator.validation import OpenAPIV30SpecValidator
1213
from openapi_spec_validator.validation import OpenAPIV31SpecValidator
@@ -44,7 +45,12 @@ def validate(
4445
) -> None:
4546
if cls is None:
4647
cls = get_validator_cls(spec)
47-
sp = SchemaPath.from_dict(spec, base_uri=base_uri)
48+
settings = OpenAPISpecValidatorSettings()
49+
sp = SchemaPath.from_dict(
50+
spec,
51+
base_uri=base_uri,
52+
resolved_cache_maxsize=settings.resolved_cache_maxsize,
53+
)
4854
v = cls(sp)
4955
return v.validate()
5056

openapi_spec_validator/validation/validators.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
from openapi_spec_validator.schemas import openapi_v31_schema_validator
1818
from openapi_spec_validator.schemas import openapi_v32_schema_validator
1919
from openapi_spec_validator.schemas.types import AnySchema
20+
from openapi_spec_validator.settings import OpenAPISpecValidatorSettings
2021
from openapi_spec_validator.validation import keywords
2122
from openapi_spec_validator.validation.decorators import unwraps_iter
2223
from openapi_spec_validator.validation.decorators import wraps_cached_iter
@@ -54,11 +55,13 @@ def __init__(
5455
self.schema_path = schema
5556
self.schema = schema.read_value()
5657
else:
58+
settings = OpenAPISpecValidatorSettings()
5759
self.schema = schema
5860
self.schema_path = SchemaPath.from_dict(
5961
self.schema,
6062
base_uri=self.base_uri,
6163
handlers=self.resolver_handlers,
64+
resolved_cache_maxsize=settings.resolved_cache_maxsize,
6265
)
6366

6467
self.keyword_validators_registry = KeywordValidatorRegistry(

poetry.lock

Lines changed: 53 additions & 14 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@ dependencies = [
2828
"openapi-schema-validator >=0.7.3,<0.9.0",
2929
"jsonschema-path >=0.4.3,<0.5.0",
3030
"lazy-object-proxy >=1.7.1,<2.0",
31+
"pydantic-settings (>=2.0.0,<3.0.0)",
32+
"pydantic (>=2.0.0,<3.0.0)",
3133
]
3234

3335
[project.urls]

tests/bench/runner.py

Lines changed: 7 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,11 @@
2121
from pathlib import Path
2222
from typing import Any
2323

24-
from jsonschema_path import SchemaPath
2524
from jsonschema_path.typing import Schema
2625

2726
from openapi_spec_validator import schemas
2827
from openapi_spec_validator import validate
2928
from openapi_spec_validator.readers import read_from_filename
30-
from openapi_spec_validator.shortcuts import get_validator_cls
3129

3230

3331
@dataclass
@@ -110,11 +108,7 @@ def get_spec_version(spec: Schema) -> str:
110108
def run_once(spec: Schema) -> float:
111109
"""Run validation once and return elapsed time."""
112110
t0 = time.perf_counter()
113-
cls = get_validator_cls(spec)
114-
sp = SchemaPath.from_dict(spec)
115-
v = cls(sp)
116-
v.validate()
117-
# validate(spec)
111+
validate(spec)
118112
return time.perf_counter() - t0
119113

120114

@@ -271,8 +265,8 @@ def get_synthetic_specs_iterator(
271265
configs: list[tuple[int, int, str]],
272266
) -> Iterator[tuple[dict[str, Any], str, float]]:
273267
"""Iterator over synthetic specs based on provided configurations."""
274-
for paths, schemas, size in configs:
275-
spec = generate_synthetic_spec(paths, schemas)
268+
for paths, schema_count, size in configs:
269+
spec = generate_synthetic_spec(paths, schema_count)
276270
yield spec, f"synthetic_{size}", 0
277271

278272

@@ -348,7 +342,10 @@ def main():
348342
results.append(result.as_dict())
349343
if result.success:
350344
print(
351-
f" ✅ {result.median_s:.4f}s, {result.validations_per_sec:.2f} val/s"
345+
" ✅ {:.4f}s, {:.2f} val/s".format(
346+
result.median_s,
347+
result.validations_per_sec,
348+
)
352349
)
353350
else:
354351
print(f" ❌ Error: {result.error}")

0 commit comments

Comments
 (0)