1616# Copyright (c) OWASP Foundation. All Rights Reserved.
1717
1818
19- __all__ = ['XmlValidator' ]
19+ __all__ = ['XmlValidator' , 'XmlValidationError' ]
2020
2121from abc import ABC
2222from collections .abc import Iterable
3737 XMLSchema ,
3838 fromstring as xml_fromstring ,
3939 )
40+
41+ if TYPE_CHECKING : # pragma: no cover
42+ from lxml .etree import _LogEntry as _XmlLogEntry
4043except ImportError as err :
4144 _missing_deps_error = MissingOptionalDependencyException (
4245 'This functionality requires optional dependencies.\n '
4346 'Please install `cyclonedx-python-lib` with the extra "xml-validation".\n '
4447 ), err
4548
4649
50+ class XmlValidationError (ValidationError ):
51+ @classmethod
52+ def __make_from_xle (cls , e : '_XmlLogEntry' ) -> 'XmlValidationError' :
53+ """⚠️ This is an internal API. It is not part of the public interface and may change without notice."""
54+ # in preparation for https://github.com/CycloneDX/cyclonedx-python-lib/pull/836
55+ return cls (e )
56+
57+
4758class _BaseXmlValidator (BaseSchemabasedValidator , ABC ):
4859
4960 @property
@@ -57,16 +68,16 @@ def __init__(self, schema_version: 'SchemaVersion') -> None:
5768 # region typing-relevant copy from parent class - needed for mypy and doc tools
5869
5970 @overload
60- def validate_str (self , data : str , * , all_errors : Literal [False ] = ...) -> Optional [ValidationError ]:
71+ def validate_str (self , data : str , * , all_errors : Literal [False ] = ...) -> Optional [XmlValidationError ]:
6172 ... # pragma: no cover
6273
6374 @overload
64- def validate_str (self , data : str , * , all_errors : Literal [True ]) -> Optional [Iterable [ValidationError ]]:
75+ def validate_str (self , data : str , * , all_errors : Literal [True ]) -> Optional [Iterable [XmlValidationError ]]:
6576 ... # pragma: no cover
6677
6778 def validate_str (
6879 self , data : str , * , all_errors : bool = False
69- ) -> Union [None , ValidationError , Iterable [ValidationError ]]:
80+ ) -> Union [None , XmlValidationError , Iterable [XmlValidationError ]]:
7081 ... # pragma: no cover
7182
7283 # endregion typing-relevant
@@ -76,13 +87,13 @@ def validate_str(
7687
7788 def validate_str ( # type:ignore[no-redef] # noqa:F811 # typing-relevant headers go first
7889 self , data : str , * , all_errors : bool = False
79- ) -> Union [None , ValidationError , Iterable [ValidationError ]]:
90+ ) -> Union [None , XmlValidationError , Iterable [XmlValidationError ]]:
8091 raise self .__MDERROR [0 ] from self .__MDERROR [1 ]
8192
8293 else :
8394 def validate_str ( # type:ignore[no-redef] # noqa:F811 # typing-relevant headers go first
8495 self , data : str , * , all_errors : bool = False
85- ) -> Union [None , ValidationError , Iterable [ValidationError ]]:
96+ ) -> Union [None , XmlValidationError , Iterable [XmlValidationError ]]:
8697 validator = self ._validator # may throw on error that MUST NOT be caught
8798 valid = validator .validate (
8899 xml_fromstring ( # nosec B320 -- we use a custom prepared safe parser
@@ -91,9 +102,9 @@ def validate_str( # type:ignore[no-redef] # noqa:F811 # typing-relevant headers
91102 if valid :
92103 return None
93104 errors = validator .error_log
94- return map (ValidationError , errors ) \
105+ return map (XmlValidationError . _make_from_xle , errors ) \
95106 if all_errors \
96- else ValidationError (errors .last_error )
107+ else XmlValidationError . _make_from_xle (errors .last_error )
97108
98109 __validator : Optional ['XMLSchema' ] = None
99110
0 commit comments