11import logging
2+ import os
23import sys
34from argparse import ArgumentParser
45from collections .abc import Sequence
910from openapi_spec_validator import __version__
1011from openapi_spec_validator .readers import read_from_filename
1112from openapi_spec_validator .readers import read_from_stdin
13+ from openapi_spec_validator .shortcuts import get_validator_cls
1214from openapi_spec_validator .shortcuts import validate
1315from openapi_spec_validator .validation import OpenAPIV2SpecValidator
1416from openapi_spec_validator .validation import OpenAPIV30SpecValidator
1517from openapi_spec_validator .validation import OpenAPIV31SpecValidator
18+ from openapi_spec_validator .validation import SpecValidator
1619
1720logger = logging .getLogger (__name__ )
1821logging .basicConfig (
@@ -30,27 +33,42 @@ def print_error(filename: str, exc: Exception) -> None:
3033
3134
3235def print_validationerror (
33- filename : str , exc : ValidationError , errors : str = "best-match"
36+ filename : str ,
37+ exc : ValidationError ,
38+ subschema_errors : str = "best-match" ,
39+ index : int | None = None ,
3440) -> None :
35- print (f"{ filename } : Validation Error: { exc } " )
41+ if index is None :
42+ print (f"{ filename } : Validation Error: { exc } " )
43+ else :
44+ print (f"{ filename } : Validation Error: [{ index } ] { exc } " )
3645 if exc .cause :
3746 print ("\n # Cause\n " )
3847 print (exc .cause )
3948 if not exc .context :
4049 return
41- if errors == "all" :
50+ if subschema_errors == "all" :
4251 print ("\n \n # Due to one of those errors\n " )
4352 print ("\n \n \n " .join ("## " + str (e ) for e in exc .context ))
44- elif errors == "best-match" :
53+ elif subschema_errors == "best-match" :
4554 print ("\n \n # Probably due to this subschema error\n " )
4655 print ("## " + str (best_match (exc .context )))
4756 if len (exc .context ) > 1 :
4857 print (
4958 f"\n ({ len (exc .context ) - 1 } more subschemas errors," ,
50- "use --errors=all to see them.)" ,
59+ "use --subschema- errors=all to see them.)" ,
5160 )
5261
5362
63+ def should_warn_deprecated () -> bool :
64+ return os .getenv ("OPENAPI_SPEC_VALIDATOR_WARN_DEPRECATED" , "1" ) != "0"
65+
66+
67+ def warn_deprecated (message : str ) -> None :
68+ if should_warn_deprecated ():
69+ print (f"DeprecationWarning: { message } " , file = sys .stderr )
70+
71+
5472def main (args : Sequence [str ] | None = None ) -> None :
5573 parser = ArgumentParser (prog = "openapi-spec-validator" )
5674 parser .add_argument (
@@ -59,12 +77,27 @@ def main(args: Sequence[str] | None = None) -> None:
5977 help = "Validate specified file(s)." ,
6078 )
6179 parser .add_argument (
62- "--errors" ,
80+ "--subschema- errors" ,
6381 choices = ("best-match" , "all" ),
64- default = "best-match" ,
65- help = """Control error reporting . Defaults to "best-match", """
82+ default = None ,
83+ help = """Control subschema error details . Defaults to "best-match", """
6684 """use "all" to get all subschema errors.""" ,
6785 )
86+ parser .add_argument (
87+ "--validation-errors" ,
88+ choices = ("first" , "all" ),
89+ default = "first" ,
90+ help = """Control validation errors count. Defaults to "first", """
91+ """use "all" to get all validation errors.""" ,
92+ )
93+ parser .add_argument (
94+ "--errors" ,
95+ "--error" ,
96+ dest = "deprecated_subschema_errors" ,
97+ choices = ("best-match" , "all" ),
98+ default = None ,
99+ help = "Deprecated alias for --subschema-errors." ,
100+ )
68101 parser .add_argument (
69102 "--schema" ,
70103 type = str ,
@@ -80,6 +113,22 @@ def main(args: Sequence[str] | None = None) -> None:
80113 )
81114 args_parsed = parser .parse_args (args )
82115
116+ subschema_errors = args_parsed .subschema_errors
117+ if args_parsed .deprecated_subschema_errors is not None :
118+ if args_parsed .subschema_errors is None :
119+ subschema_errors = args_parsed .deprecated_subschema_errors
120+ warn_deprecated (
121+ "--errors/--error is deprecated. "
122+ "Use --subschema-errors instead."
123+ )
124+ else :
125+ warn_deprecated (
126+ "--errors/--error is deprecated and ignored when "
127+ "--subschema-errors is provided."
128+ )
129+ if subschema_errors is None :
130+ subschema_errors = "best-match"
131+
83132 for filename in args_parsed .file :
84133 # choose source
85134 reader = read_from_filename
@@ -95,7 +144,7 @@ def main(args: Sequence[str] | None = None) -> None:
95144 sys .exit (1 )
96145
97146 # choose the validator
98- validators = {
147+ validators : dict [ str , type [ SpecValidator ] | None ] = {
99148 "detect" : None ,
100149 "2.0" : OpenAPIV2SpecValidator ,
101150 "3.0" : OpenAPIV30SpecValidator ,
@@ -108,9 +157,27 @@ def main(args: Sequence[str] | None = None) -> None:
108157
109158 # validate
110159 try :
160+ if args_parsed .validation_errors == "all" :
161+ if validator_cls is None :
162+ validator_cls = get_validator_cls (spec )
163+ validator = validator_cls (spec , base_uri = base_uri )
164+ errors = list (validator .iter_errors ())
165+ if errors :
166+ for idx , err in enumerate (errors , start = 1 ):
167+ print_validationerror (
168+ filename ,
169+ err ,
170+ subschema_errors ,
171+ index = idx ,
172+ )
173+ print (f"{ filename } : { len (errors )} validation errors found" )
174+ sys .exit (1 )
175+ print_ok (filename )
176+ continue
177+
111178 validate (spec , base_uri = base_uri , cls = validator_cls )
112179 except ValidationError as exc :
113- print_validationerror (filename , exc , args_parsed . errors )
180+ print_validationerror (filename , exc , subschema_errors )
114181 sys .exit (1 )
115182 except Exception as exc :
116183 print_error (filename , exc )
0 commit comments