Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions CHANGES.rst
Original file line number Diff line number Diff line change
@@ -1,4 +1,13 @@

Release 1.4.3
=========================================

* **BUGFIX:** Fixed edge case error when deserializing ``ChartOptions`` using ``.from_dict()``
with a ``dict`` that had been serialized using ``.to_dict()`` which errored on ``.margin``
and ``.spacing`` (#124).

--------------------

Release 1.4.2
=========================================

Expand Down
2 changes: 1 addition & 1 deletion highcharts_core/__version__.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
__version__ = '1.4.2'
__version__ = '1.4.3'
62 changes: 48 additions & 14 deletions highcharts_core/options/chart/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -592,10 +592,22 @@ def margin(self, value):
f'or an iterable of four values. '
f'Received an iterable of {len(value)} '
f'values ({value})')
self.margin_top = value[0]
self.margin_right = value[1]
self.margin_bottom = value[2]
self.margin_left = value[3]
if value[0] == 'null':
self.margin_top = None
else:
self.margin_top = value[0]
if value[1] == 'null':
self.margin_right = None
else:
self.margin_right = value[1]
if value[2] == 'null':
self.margin_bottom = None
else:
self.margin_bottom = value[2]
if value[3] == 'null':
self.margin_left = None
else:
self.margin_left = value[3]
else:
self.margin_top = value
self.margin_right = value
Expand All @@ -620,7 +632,10 @@ def margin_bottom(self) -> Optional[int | float | Decimal]:

@margin_bottom.setter
def margin_bottom(self, value):
self._margin_bottom = validators.numeric(value, allow_empty = True)
if value is None or isinstance(value, constants.EnforcedNullType):
self._margin_bottom = None
else:
self._margin_bottom = validators.numeric(value)

@property
def margin_left(self) -> Optional[int | float | Decimal]:
Expand All @@ -640,7 +655,10 @@ def margin_left(self) -> Optional[int | float | Decimal]:

@margin_left.setter
def margin_left(self, value):
self._margin_left = validators.numeric(value, allow_empty = True)
if value is None or isinstance(value, constants.EnforcedNullType):
self._margin_left = None
else:
self._margin_left = validators.numeric(value)

@property
def margin_right(self) -> Optional[int | float | Decimal]:
Expand All @@ -660,7 +678,10 @@ def margin_right(self) -> Optional[int | float | Decimal]:

@margin_right.setter
def margin_right(self, value):
self._margin_right = validators.numeric(value, allow_empty = True)
if value is None or isinstance(value, constants.EnforcedNullType):
self._margin_right = None
else:
self._margin_right = validators.numeric(value)

@property
def margin_top(self) -> Optional[int | float | Decimal]:
Expand All @@ -680,7 +701,10 @@ def margin_top(self) -> Optional[int | float | Decimal]:

@margin_top.setter
def margin_top(self, value):
self._margin_top = validators.numeric(value, allow_empty = True)
if value is None or isinstance(value, constants.EnforcedNullType):
self._margin_top = None
else:
self._margin_top = validators.numeric(value)

@property
def number_formatter(self) -> Optional[CallbackFunction]:
Expand Down Expand Up @@ -1119,13 +1143,23 @@ def spacing(self, value):
f' or an iterable of four values. '
f'Received an iterable of {len(value)} '
f'values ({value})')
value = [validators.numeric(x) for x in value]
self.spacing_top = value[0]
self.spacing_right = value[1]
self.spacing_bottom = value[2]
self.spacing_left = value[3]
if value[0] == 'null':
self.spacing_top = None
else:
self.spacing_top = value[0]
if value[1] == 'null':
self.spacing_right = None
else:
self.spacing_right = value[1]
if value[2] == 'null':
self.spacing_bottom = None
else:
self.spacing_bottom = value[2]
if value[3] == 'null':
self.spacing_left = None
else:
self.spacing_left = value[3]
else:
value = validators.numeric(value, allow_empty = False)
self.spacing_top = value
self.spacing_right = value
self.spacing_bottom = value
Expand Down
163 changes: 163 additions & 0 deletions tests/options/chart/test_chart.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,3 +177,166 @@ def test_to_dict(kwargs, error):
])
def test_from_js_literal(input_files, filename, as_file, error):
Class_from_js_literal(cls, input_files, filename, as_file, error)


@pytest.mark.parametrize('as_dict, as_js_literal, error', [
({
'marginRight': 124
}, False, None),
({
'type': 'bar',
'marginRight': 124,
'marginTop': 421,
'marginBottom': 321,
'marginLeft': 789,
'scrollablePlotArea': {
'minHeight': 1000,
'opacity': 1
}
}, False, None),

({
'marginRight': 124
}, True, None),
({
'type': 'bar',
'marginRight': 124,
'marginTop': 421,
'marginBottom': 321,
'marginLeft': 789,
'scrollablePlotArea': {
'minHeight': 1000,
'opacity': 1
}
}, True, None),
])
def test_bug124_margin_right(as_dict, as_js_literal, error):
if not error:
if not as_js_literal:
result = cls.from_dict(as_dict)
else:
as_str = str(as_dict)
result = cls.from_js_literal(as_str)
assert isinstance(result, cls) is True
if 'marginRight' in as_dict or 'margin_right' in as_dict:
assert result.margin_right == as_dict.get('marginRight', None)
if 'marginTop' in as_dict or 'margin_top' in as_dict:
assert result.margin_top == as_dict.get('marginTop', None)
if 'marginBottom' in as_dict or 'margin_bottom' in as_dict:
assert result.margin_bottom == as_dict.get('marginBottom', None)
if 'marginLeft' in as_dict or 'margin_left' in as_dict:
assert result.margin_left == as_dict.get('marginLeft', None)
else:
with pytest.raises(error):
if not as_js_literal:
result = cls.from_dict(as_dict)
else:
as_str = str(as_dict)
result = cls.from_js_literal(as_str)


@pytest.mark.parametrize('as_str, error', [
("""{
marginRight: 124
}""", None),
("""{type: 'bar',
marginRight: 124,
marginTop: 421,
marginBottom: 321,
marginLeft: 789,
scrollablePlotArea: {
minHeight: 1000,
opacity: 1
}
}""", None),

("""{
marginRight: null
}""", None),
])
def test_bug124_margin_right_from_js_literal(as_str, error):
if not error:
result = cls.from_js_literal(as_str)
assert isinstance(result, cls) is True
if 'marginRight' in as_str or 'margin_right' in as_str:
if 'marginRight: null' not in as_str:
assert result.margin_right is not None
else:
assert result.margin_right is None
if 'marginTop' in as_str or 'margin_top' in as_str:
assert result.margin_top is not None
if 'marginBottom' in as_str or 'margin_bottom' in as_str:
assert result.margin_bottom is not None
if 'marginLeft' in as_str or 'margin_left' in as_str:
assert result.margin_left is not None
else:
with pytest.raises(error):
result = cls.from_js_literal(as_str)


@pytest.mark.parametrize('as_dict, error', [
({
'marginRight': 124
}, None),
({
'type': 'bar',
'marginRight': 124,
'marginTop': 421,
'marginBottom': 321,
'marginLeft': 789,
'scrollablePlotArea': {
'minHeight': 1000,
'opacity': 1
}
}, None),

])
def test_bug124_margin_right_to_dict_from_dict(as_dict, error):
if not error:
initial_result = cls.from_dict(as_dict)
as_new_dict = initial_result.to_dict()
result = cls.from_dict(as_new_dict)
assert isinstance(result, cls) is True
assert result.margin_right == initial_result.margin_right
assert result.margin_top == initial_result.margin_top
assert result.margin_bottom == initial_result.margin_bottom
assert result.margin_left == initial_result.margin_left
else:
with pytest.raises(error):
initial_result = cls.from_dict(as_dict)
as_new_dict = initial_result.to_dict()
result = cls.from_dict(as_new_dict)


@pytest.mark.parametrize('as_dict, error', [
({
'spacingRight': 124
}, None),
({
'type': 'bar',
'spacingRight': 124,
'spacingTop': 421,
'spacingBottom': 321,
'spacingLeft': 789,
'scrollablePlotArea': {
'minHeight': 1000,
'opacity': 1
}
}, None),

])
def test_bug124_spacing_right_to_dict_from_dict(as_dict, error):
if not error:
initial_result = cls.from_dict(as_dict)
as_new_dict = initial_result.to_dict()
result = cls.from_dict(as_new_dict)
assert isinstance(result, cls) is True
assert result.spacing_right == initial_result.spacing_right
assert result.spacing_top == initial_result.spacing_top
assert result.spacing_bottom == initial_result.spacing_bottom
assert result.spacing_left == initial_result.spacing_left
else:
with pytest.raises(error):
initial_result = cls.from_dict(as_dict)
as_new_dict = initial_result.to_dict()
result = cls.from_dict(as_new_dict)