Skip to content

Commit 98d9a0b

Browse files
committed
reverted debatable improvements on error class
Signed-off-by: Jan Kowalleck <[email protected]>
1 parent c2471ed commit 98d9a0b

File tree

5 files changed

+5
-241
lines changed

5 files changed

+5
-241
lines changed

cyclonedx/validation/__init__.py

Lines changed: 2 additions & 63 deletions
Original file line numberDiff line numberDiff line change
@@ -28,75 +28,14 @@
2828
from .xml import XmlValidator
2929

3030

31-
def squeeze(text: str, size: int, replacement: str = ' ... ') -> str:
32-
"""Replaces the middle of ``text`` with ``replacement``.
33-
34-
:param size: the length of the output, -1 to make no squeezing.
35-
:return: potentially shorter text
36-
:retval: ``text`` if ``size`` is -1 (for easy pass-through)
37-
:retval: ``text`` if it is shorter than ``size``
38-
:retval: ``text`` with the middle of it replaced with ``replacement``,
39-
if ``text`` is longer, than ``size``
40-
41-
Raises error if ``replacement`` is longer than ``size``, and replacement
42-
would happen.
43-
"""
44-
if size == -1:
45-
return text
46-
47-
if size < len(replacement):
48-
raise ValueError(f'squeeze: {size = } < {len(replacement) = }')
49-
50-
if len(text) <= size:
51-
return text
52-
53-
left_size = (size - len(replacement)) // 2
54-
right_size = size - len(replacement) - left_size
55-
right_offset = len(text) - right_size
56-
57-
return f'{text[:left_size]}{replacement}{text[right_offset:]}'
58-
59-
6031
class ValidationError:
6132
"""Validation failed with this specific error.
6233
63-
You can use :attr:`~data` to access the raw error object, but prefer
64-
other properties and functions, if possible.
34+
Use :attr:`~data` to access the content.
6535
"""
6636

6737
data: Any
68-
"""Raw error data from one of the validation libraries."""
69-
70-
@property
71-
def message(self) -> str:
72-
"""The error message."""
73-
return str(getattr(self.data, 'message', self))
74-
75-
@property
76-
def path(self) -> str:
77-
"""Path to the location of the problem in the document.
78-
79-
An XPath/JSONPath string.
80-
"""
81-
# only subclasses know how to extract this info
82-
return str(getattr(self.data, 'path', ''))
83-
84-
def get_squeezed_message(self, *, context_limit: int = -1, max_size: int = -1, replacement: str = ' ... ') -> str:
85-
"""Extracts, and sanitizes the error message.
86-
87-
Messages can be quite big from underlying libraries, as they sometimes
88-
add context to the error message: both the input or the rule can be big.
89-
90-
This can be amended both in a generic and library specific ways.
91-
92-
:param max_size: squeeze message to this size.
93-
:param context_limit: limit of tolerated context length.
94-
:param replacement: to mark place of dropped text bit[s]
95-
96-
With the defaults, no squeezing happens.
97-
"""
98-
# subclasses may know how to do it better
99-
return squeeze(self.message, max_size, replacement)
38+
"""Raw error data from one of the underlying validation methods."""
10039

10140
def __init__(self, data: Any) -> None:
10241
self.data = data

cyclonedx/validation/json.py

Lines changed: 2 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -49,51 +49,6 @@
4949
), err
5050

5151

52-
def _get_message_with_squeezed_context(error: 'JsonSchemaValidationError', context_limit: int, replacement: str) -> str:
53-
# The below code depends on jsonschema internals, that messages are created
54-
# like `yield ValidationError(f"{instance!r} has non-unique elements")`
55-
# and tries to replace `{instance!r}` with a shortened version, if needed
56-
message: str = error.message
57-
if context_limit <= 0 or len(message) <= context_limit:
58-
return message
59-
60-
repr_context = repr(error.instance)
61-
if len(repr_context) <= context_limit:
62-
return message
63-
64-
return message.replace(repr_context, squeeze(repr_context, context_limit, replacement))
65-
66-
67-
class _JsonValidationError(ValidationError):
68-
def get_squeezed_message(self, *, context_limit: int = -1, max_size: int = -1, replacement: str = ' ... ') -> str:
69-
"""Extracts, and sanitizes the error message.
70-
71-
Messages can be quite big from underlying libraries, as they sometimes
72-
add context to the error message..
73-
74-
This is amended both in a generic and library specific ways here.
75-
76-
:param max_size: squeeze message to this size.
77-
:param context_limit: jsonschema messages most of the time include the
78-
instance repr as context, which can be very big
79-
(in the megabytes range), so an attempt is made to
80-
shorten context to this size.
81-
:param replacement: to mark place of dropped text bit[s]
82-
83-
With the defaults, no squeezing happens.
84-
"""
85-
message = _get_message_with_squeezed_context(self.data, context_limit, replacement)
86-
return squeeze(message, max_size, replacement)
87-
88-
@property
89-
def path(self) -> str:
90-
"""Path to the location of the problem in the document.
91-
92-
An XPath/JSONPath string.
93-
"""
94-
return str(getattr(self.data, 'json_path', ''))
95-
96-
9752
class _BaseJsonValidator(BaseSchemabasedValidator, ABC):
9853
@property
9954
def output_format(self) -> Literal[OutputFormat.JSON]:
@@ -139,8 +94,8 @@ def validate_str( # type:ignore[no-redef] # noqa:F811 # typing-relevant headers
13994
first_error = next(errors, None)
14095
if first_error is None:
14196
return None
142-
first_error = _JsonValidationError(first_error)
143-
return chain((first_error,), map(_JsonValidationError, errors)) \
97+
first_error = ValidationError(first_error)
98+
return chain((first_error,), map(ValidationError, errors)) \
14499
if all_errors \
145100
else first_error
146101

tests/test_validation.py

Lines changed: 1 addition & 119 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@
2222
from ddt import data, ddt, named_data, unpack
2323

2424
from cyclonedx.schema import OutputFormat, SchemaVersion
25-
from cyclonedx.validation import make_schemabased_validator, squeeze
25+
from cyclonedx.validation import make_schemabased_validator
2626

2727
UNDEFINED_FORMAT_VERSION = {
2828
(OutputFormat.JSON, SchemaVersion.V1_1),
@@ -51,121 +51,3 @@ def test_as_expected(self, of: OutputFormat, sv: SchemaVersion) -> None:
5151
def test_fails_on_wrong_args(self, of: OutputFormat, sv: SchemaVersion, raises_regex: tuple) -> None:
5252
with self.assertRaisesRegex(*raises_regex):
5353
make_schemabased_validator(of, sv)
54-
55-
56-
class TestSqueeze(unittest.TestCase):
57-
58-
def test_squeeze_size_minus_one_returns_original_text(self) -> None:
59-
"""Test that size=-1 returns original text unchanged."""
60-
self.assertEqual(squeeze('hello world', -1), 'hello world')
61-
self.assertEqual(squeeze('', -1), '')
62-
self.assertEqual(squeeze('a', -1), 'a')
63-
self.assertEqual(squeeze('very long text that would normally be squeezed', -1),
64-
'very long text that would normally be squeezed')
65-
66-
def test_squeeze_size_zero_returns_empty_text(self) -> None:
67-
"""Test that size=-1 returns original text unchanged."""
68-
self.assertEqual(squeeze('hello world', 0, ''), '')
69-
self.assertEqual(squeeze('', 0, ''), '')
70-
71-
def test_squeeze_text_shorter_than_or_equal_size_returns_original(self) -> None:
72-
"""Test that text shorter than or equal to size returns original text."""
73-
self.assertEqual(squeeze('hello', 10), 'hello')
74-
self.assertEqual(squeeze('hello', 5), 'hello')
75-
self.assertEqual(squeeze('', 5), '')
76-
self.assertEqual(squeeze('a', 5), 'a')
77-
self.assertEqual(squeeze('ab', 10), 'ab')
78-
79-
def test_squeeze_with_default_replacement(self) -> None:
80-
"""Test squeezing with default ' ... ' replacement."""
81-
self.assertEqual(squeeze('hello world', 8), 'h ... ld')
82-
self.assertEqual(squeeze('hello world', 7), 'h ... d')
83-
self.assertEqual(squeeze('hello world', 9), 'he ... ld')
84-
self.assertEqual(squeeze('hello world', 10), 'he ... rld')
85-
self.assertEqual(squeeze('hello world', 11), 'hello world')
86-
87-
def test_squeeze_with_custom_replacement(self) -> None:
88-
"""Test squeezing with custom replacement strings."""
89-
self.assertEqual(squeeze('hello world', 8, '..'), 'hel..rld')
90-
self.assertEqual(squeeze('hello world', 7, '..'), 'he..rld')
91-
self.assertEqual(squeeze('hello world', 9, '---'), 'hel---rld')
92-
self.assertEqual(squeeze('hello world', 10, 'XX'), 'hellXXorld')
93-
94-
def test_squeeze_with_single_character_replacement(self) -> None:
95-
"""Test squeezing with single character replacement."""
96-
self.assertEqual(squeeze('hello world', 5, '*'), 'he*ld')
97-
self.assertEqual(squeeze('hello world', 6, '*'), 'he*rld')
98-
self.assertEqual(squeeze('hello world', 7, '*'), 'hel*rld')
99-
100-
def test_squeeze_with_empty_replacement(self) -> None:
101-
"""Test squeezing with empty replacement string."""
102-
self.assertEqual(squeeze('hello world', 5, ''), 'herld')
103-
self.assertEqual(squeeze('hello world', 6, ''), 'helrld')
104-
self.assertEqual(squeeze('hello world', 7, ''), 'helorld')
105-
106-
def test_squeeze_replacement_equals_target_size(self) -> None:
107-
"""Test when replacement string equals the target size."""
108-
self.assertEqual(squeeze('hello world', 4, '....'), '....')
109-
self.assertEqual(squeeze('hello world', 3, '***'), '***')
110-
111-
def test_squeeze_very_short_target_sizes(self) -> None:
112-
"""Test edge cases with very short target sizes."""
113-
self.assertEqual(squeeze('hello world', 5, '.'), 'he.ld')
114-
self.assertEqual(squeeze('hello world', 6, '.'), 'he.rld')
115-
self.assertEqual(squeeze('hello world', 1, 'X'), 'X')
116-
117-
def test_squeeze_with_long_text(self) -> None:
118-
"""Test squeezing with very long text."""
119-
long_text = 'a' * 100
120-
result = squeeze(long_text, 10, '...')
121-
self.assertEqual(len(result), 10)
122-
self.assertEqual(result, 'aaa...aaaa')
123-
124-
# Test with different replacement
125-
result2 = squeeze(long_text, 8, '--')
126-
self.assertEqual(len(result2), 8)
127-
self.assertEqual(result2, 'aaa--aaa')
128-
129-
def test_squeeze_size_distribution_even(self) -> None:
130-
"""Test size distribution when remaining space is even."""
131-
# size=8, replacement="--" (len=2), remaining=6, left=3, right=3
132-
self.assertEqual(squeeze('abcdefghijk', 8, '--'), 'abc--ijk')
133-
# size=10, replacement="...." (len=4), remaining=6, left=3, right=3
134-
self.assertEqual(squeeze('abcdefghijk', 10, '....'), 'abc....ijk')
135-
136-
def test_squeeze_size_distribution_odd(self) -> None:
137-
"""Test size distribution when remaining space is odd."""
138-
# size=9, replacement="--" (len=2), remaining=7, left=3, right=4
139-
self.assertEqual(squeeze('abcdefghijk', 9, '--'), 'abc--hijk')
140-
# size=11, replacement="..." (len=3), remaining=8, left=4, right=4
141-
self.assertEqual(squeeze('abcdefghijk', 11, '...'), 'abcdefghijk')
142-
143-
def test_squeeze_raises_error_when_replacement_too_long(self) -> None:
144-
"""Test that ValueError is raised when replacement is longer than target size."""
145-
with self.assertRaises(ValueError) as context:
146-
squeeze('hello world', 3, ' ... ')
147-
self.assertIn('size = 3 < len(replacement) = 5', str(context.exception))
148-
149-
with self.assertRaises(ValueError) as context:
150-
squeeze('hello world', 2, 'abc')
151-
self.assertIn('size = 2 < len(replacement) = 3', str(context.exception))
152-
153-
with self.assertRaises(ValueError) as context:
154-
squeeze('hello world', 1, 'ab')
155-
self.assertIn('size = 1 < len(replacement) = 2', str(context.exception))
156-
157-
def test_squeeze_error_when_replacement_long_but_no_squeeze_needed(self) -> None:
158-
"""Test that no error is raised when replacement is long but text doesn't need squeezing."""
159-
# Text is shorter than size, so no squeezing would occur,
160-
# yet, the replacement is longer than the requested size, so error is raised
161-
with self.assertRaises(ValueError) as context:
162-
self.assertEqual(squeeze('abc', 10, 'very long replacement'), 'abc')
163-
self.assertIn('size = 10 < len(replacement) = 21', str(context.exception))
164-
165-
with self.assertRaises(ValueError) as context:
166-
self.assertEqual(squeeze('', 3, 'abcd'), '')
167-
self.assertIn('size = 3 < len(replacement) = 4', str(context.exception))
168-
169-
170-
if __name__ == '__main__':
171-
unittest.main()

tests/test_validation_json.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -151,12 +151,6 @@ def test_validate_expected_error_one(self, schema_version: SchemaVersion, test_d
151151
self.skipTest('MissingOptionalDependencyException')
152152
self.assertIsNotNone(validation_error)
153153
self.assertIsNotNone(validation_error.data)
154-
self.assertTrue(bool(validation_error.message))
155-
self.assertTrue(bool(validation_error.get_squeezed_message(context_limit=22)))
156-
self.assertTrue(bool(validation_error.path))
157-
158-
squeezed_message = validation_error.get_squeezed_message(max_size=100)
159-
self.assertLessEqual(len(squeezed_message), 100, squeezed_message)
160154

161155
@idata(chain(
162156
_dp_sv_tf(False),

tests/test_validation_xml.py

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -92,12 +92,6 @@ def test_validate_expected_error_one(self, schema_version: SchemaVersion, test_d
9292
self.skipTest('MissingOptionalDependencyException')
9393
self.assertIsNotNone(validation_error)
9494
self.assertIsNotNone(validation_error.data)
95-
self.assertTrue(bool(validation_error.message))
96-
self.assertTrue(bool(validation_error.get_squeezed_message()))
97-
self.assertTrue(bool(validation_error.path))
98-
99-
squeezed_message = validation_error.get_squeezed_message(max_size=100)
100-
self.assertLessEqual(len(squeezed_message), 100, squeezed_message)
10195

10296
@idata(chain(
10397
_dp_sv_tf(False),

0 commit comments

Comments
 (0)