Skip to content

Commit 65eec8e

Browse files
committed
ENH: Start testing a schema for pydantic
1 parent fdca470 commit 65eec8e

File tree

1 file changed

+162
-0
lines changed

1 file changed

+162
-0
lines changed

asv/schema.py

Lines changed: 162 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,162 @@
1+
from asv import util
2+
import rtoml
3+
import json
4+
5+
from pathlib import Path
6+
7+
from pydantic import (
8+
BaseModel,
9+
Field,
10+
HttpUrl,
11+
AnyUrl,
12+
ConfigDict,
13+
field_serializer,
14+
)
15+
from typing import Optional, Any, Union
16+
from typing_extensions import Literal
17+
18+
19+
class ASVConfig(BaseModel):
20+
model_config = ConfigDict(use_attribute_docstrings=False)
21+
22+
@field_serializer("project_url", "repo")
23+
def serialize_urls(self, _url: Optional[HttpUrl]):
24+
return str(_url)
25+
26+
project: str = Field(..., description="The name of the project being benchmarked.")
27+
project_url: Optional[HttpUrl] = Field(None)
28+
"""
29+
The URL to the homepage of the project.
30+
31+
This can point to anywhere, really, as it's only used for the link at the
32+
top of the benchmark results page back to your project.
33+
"""
34+
35+
repo: Union[AnyUrl, Path] = Field(...)
36+
"""
37+
The URL to the repository for the project.
38+
39+
The value can also be a path, relative to the location of the
40+
configuration file. For example, if the benchmarks are stored in the
41+
same repository as the project itself, and the configuration file is
42+
located at ``benchmarks/asv.conf.json`` inside the repository, you can
43+
set ``"repo": ".."`` to use the local repository.
44+
45+
Currently, only ``git`` and ``hg`` repositories are supported, so this must be
46+
a URL that ``git`` or ``hg`` know how to clone from, for example::
47+
48+
- [email protected]:airspeed-velocity/asv.git
49+
- https://github.com/airspeed-velocity/asv.git
50+
- ssh://[email protected]/yt_analysis/yt
51+
- hg+https://bitbucket.org/yt_analysis/yt
52+
53+
The repository may be readonly.
54+
"""
55+
repo_subdir: Optional[str] = Field(None)
56+
"""
57+
The relative path to your Python project inside the repository. This is
58+
where its ``setup.py`` file is located.
59+
60+
If empty or omitted, the project is assumed to be located at the root of
61+
the repository.
62+
"""
63+
64+
build_command: Optional[list[str]] = Field(
65+
default=[
66+
"python setup.py build",
67+
"python -mpip wheel --no-deps --no-index -w {build_cache_dir} {build_dir}",
68+
],
69+
description="Commands to rebuild the project.",
70+
)
71+
install_command: Optional[list[str]] = Field(
72+
default=["in-dir={env_dir} python -mpip install {wheel_file}"],
73+
description="Command to install the project.",
74+
)
75+
uninstall_command: Optional[list[str]] = Field(
76+
default=["return-code=any python -mpip uninstall -y {project}"],
77+
description="Command to uninstall the project.",
78+
)
79+
80+
branches: Optional[list[str]] = Field(
81+
None, description="List of branches to benchmark."
82+
)
83+
dvcs: Optional[str] = Field(None, description="The DVCS being used (e.g., 'git').")
84+
environment_type: Optional[str] = Field(
85+
None, description="The tool to use to create environments (e.g., 'virtualenv')."
86+
)
87+
install_timeout: Optional[int] = Field(
88+
600, description="Timeout in seconds for installing dependencies."
89+
)
90+
show_commit_url: Optional[str] = Field(
91+
None, description="Base URL to show a commit for the project."
92+
)
93+
pythons: Optional[list[str]] = Field(
94+
None, description="List of Python versions to test against."
95+
)
96+
conda_channels: Optional[list[str]] = Field(
97+
None, description="List of conda channels for dependency packages."
98+
)
99+
conda_environment_file: Optional[str] = Field(
100+
None, description="A conda environment file for environment creation."
101+
)
102+
103+
matrix: Optional[dict[str, dict[str, Union[list[Optional[str]], None]]]] = Field(
104+
None,
105+
description="Matrix of dependencies and environment variables to test.",
106+
)
107+
exclude: Optional[list[dict[str, Union[str, dict[str, Optional[str]]]]]] = Field(
108+
None,
109+
description="Combinations of libraries/python versions to exclude from testing.",
110+
)
111+
include: Optional[list[dict[str, Union[str, dict[str, Optional[str]]]]]] = Field(
112+
None,
113+
description="Combinations of libraries/python versions to include for testing.",
114+
)
115+
116+
benchmark_dir: Optional[str] = Field(
117+
None, description="Directory where benchmarks are stored."
118+
)
119+
env_dir: Optional[str] = Field(
120+
None, description="Directory to cache Python environments."
121+
)
122+
results_dir: Optional[str] = Field(
123+
None, description="Directory where raw benchmark results are stored."
124+
)
125+
html_dir: Optional[str] = Field(
126+
None, description="Directory where the HTML tree is written."
127+
)
128+
hash_length: Optional[int] = Field(
129+
8, description="Number of characters to retain in commit hashes."
130+
)
131+
build_cache_size: Optional[int] = Field(
132+
2, description="Number of builds to cache per environment."
133+
)
134+
regressions_first_commits: Optional[dict[str, Optional[str]]] = Field(
135+
None, description="Commits after which regression search starts."
136+
)
137+
regressions_thresholds: Optional[dict[str, float]] = Field(
138+
None,
139+
description="Thresholds for relative change in results to report regressions.",
140+
)
141+
142+
143+
# Example usage
144+
config = ASVConfig(
145+
project="MyProject",
146+
project_url="https://example.com",
147+
repo="https://github.com/example/repo",
148+
matrix={
149+
"req": {"numpy": ["1.6", "1.7"], "six": ["", None], "pip+emcee": [""]},
150+
"env": {"ENV_VAR_1": ["val1", "val2"]},
151+
"env_nobuild": {"ENV_VAR_2": ["val3", None]},
152+
},
153+
)
154+
155+
# Using model_dump with mode='json' to ensure proper serialization
156+
# print(rtoml.dumps(config.model_dump(mode="toml")))
157+
# print(json.dumps(config.model_dump(mode="toml"), indent=4))
158+
mkconf = ASVConfig.model_validate_json(
159+
Path("../asv_samples/asv.conf.json").open("rb").read()
160+
)
161+
# exclude_defaults=True to prevents "fat" outputs
162+
print(json.dumps(mkconf.model_dump(mode="toml", exclude_defaults=True), indent=4))

0 commit comments

Comments
 (0)