66import sys
77import typing
88from pathlib import Path
9+ from types import NoneType
910from typing import TYPE_CHECKING
1011
1112import packaging .requirements
2425
2526@impl
2627def tox_add_option (parser : ToxParser ) -> None :
27- parser .add_command ("schema" , [], "Generate schema for tox configuration" , gen_schema )
28+ our = parser .add_command ("schema" , [], "Generate schema for tox configuration" , gen_schema )
29+ our .add_argument ("--strict" , action = "store_true" , help = "Disallow extra properties in configuration" )
2830
2931
30- def _process_type (of_type : typing .Any ) -> dict [str , typing .Any ]: # noqa: PLR0911
32+ def _process_type (of_type : typing .Any ) -> dict [str , typing .Any ]: # noqa: C901, PLR0911
3133 if of_type in {
3234 Path ,
3335 str ,
@@ -36,10 +38,18 @@ def _process_type(of_type: typing.Any) -> dict[str, typing.Any]: # noqa: PLR091
3638 tox .tox_env .python .pip .req_file .PythonDeps ,
3739 }:
3840 return {"type" : "string" }
41+ if typing .get_origin (of_type ) is typing .Union :
42+ types = [x for x in typing .get_args (of_type ) if x is not NoneType ]
43+ if len (types ) == 1 :
44+ return _process_type (types [0 ])
45+ msg = f"Union types are not supported: { of_type } "
46+ raise ValueError (msg )
3947 if of_type is bool :
4048 return {"type" : "boolean" }
4149 if of_type is float :
4250 return {"type" : "number" }
51+ if typing .get_origin (of_type ) is typing .Literal :
52+ return {"enum" : list (typing .get_args (of_type ))}
4353 if of_type in {tox .config .types .Command , tox .config .types .EnvList }:
4454 return {"type" : "array" , "items" : {"$ref" : "#/definitions/subs" }}
4555 if typing .get_origin (of_type ) in {list , set }:
@@ -82,22 +92,32 @@ def _get_schema(conf: ConfigSet, path: str) -> dict[str, dict[str, typing.Any]]:
8292
8393def gen_schema (state : State ) -> int :
8494 core = state .conf .core
95+ strict = state .conf .options .strict
8596
86- properties = _get_schema (core , path = "#/properties" )
87-
97+ # Accessing this adds extra stuff to core, so we need to do it first
8898 env_properties = _get_schema (state .envs ["3.13" ].conf , path = "#/properties/env_run_base/properties" )
8999
100+ properties = _get_schema (core , path = "#/properties" )
101+
90102 json_schema = {
91103 "$schema" : "http://json-schema.org/draft-07/schema" ,
92104 "$id" : "https://github.com/tox-dev/tox/blob/main/src/tox/util/tox.schema.json" ,
93105 "type" : "object" ,
94106 "properties" : {
95107 ** properties ,
96- "env_run_base" : {"type" : "object" , "properties" : env_properties },
97- "env_pkg_base" : {"$ref" : "#/properties/env_run_base" },
108+ "env_run_base" : {
109+ "type" : "object" ,
110+ "properties" : env_properties ,
111+ "additionalProperties" : not strict ,
112+ },
113+ "env_pkg_base" : {
114+ "$ref" : "#/properties/env_run_base" ,
115+ "additionalProperties" : not strict ,
116+ },
98117 "env" : {"type" : "object" , "patternProperties" : {"^.*$" : {"$ref" : "#/properties/env_run_base" }}},
99118 "legacy_tox_ini" : {"type" : "string" },
100119 },
120+ "additionalProperties" : not strict ,
101121 "definitions" : {
102122 "subs" : {
103123 "anyOf" : [
0 commit comments