Skip to content
Open
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion CHANGES.rst
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,8 @@ Internals
Bugs
++++

# ``0`` with a given precision (like in ```0`3```) is now parsed as ``0``, an integer number.
#. ``0`` with a given precision (like in ```0`3```) is now parsed as ``0``, an integer number.
#. `RowBox` now have an `elements` attribute, in a way that expressions like `Part[RowBox[{"a"}],1]` does not raises an unhandled exception.


Enhancements
Expand Down
11 changes: 9 additions & 2 deletions mathics/builtin/box/layout.py
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,13 @@ def apply_list(self, boxes, evaluation):
result = RowBox(*items)
return result

@property
def elements(self):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It feels like something in the code or the API is wrong here.

I get nervous about "properties" doing possibly unbounded calculations such as self.to_expressions().

If I understand correctly, the caller somehow knows this is a compound Expression/Element and assumes that therefore it has an .element attribute which is of type Expression?

It feels like what should be happening that the caller should be asking instead for a conversion, that is, should be calling to_expression().elements

I need to think about this more.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It feels like something in the code or the API is wrong here.

Probably it is, but I tried to build over the existing API. At least, this is "localized" in this class.

I get nervous about "properties" doing possibly unbounded calculations such as self.to_expressions().

If I understand correctly, the caller somehow knows this is a compound Expression/Element and assumes that therefore it has an .element attribute which is of type Expression?

The problem is that Part assumes that an Expression has a property elements. This just provides the property by creating an Expression. A different possibility would be to implement a different rule for working with RowBoxes. I thought this change was simpler, but maybe there is a better approach.

It feels like what should be happening that the caller should be asking instead for a conversion, that is, should be calling to_expression().elements

I need to think about this more.

Sure.

return self.to_expression().elements

def get_head(self):
return SymbolRowBox

def init(self, *items, **kwargs):
# TODO: check that each element is an string or a BoxElementMixin
self.box_options = {}
Expand Down Expand Up @@ -352,8 +359,8 @@ def to_expression(self) -> Expression:
for item in self.items
)

self._elements = Expression(SymbolRowBox, ListExpression(*items))
return self._elements
self._elements = ListExpression(*items)
return Expression(SymbolRowBox, self._elements)


class ShowStringCharacters(Builtin):
Expand Down
34 changes: 34 additions & 0 deletions test/builtin/test_makeboxes.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,40 @@
else:
skip_or_fail = pytest.mark.skip


@pytest.mark.parametrize(
("str_expr", "str_expected", "fail_msg", "msgs"),
[
('rb=RowBox[{"a", "b"}]; rb[[1]]', "{a, b}", None, []),
("rb[[0]]", "RowBox", None, []),
(
"rb[[2]]",
"RowBox[{a, b}][[2]]",
None,
["Part 2 of RowBox[{a, b}] does not exist."],
),
('fb=FractionBox["1", "2"]; fb[[0]]', "FractionBox", None, []),
("fb[[1]]", "1", None, []),
('sb=StyleBox["string", "Section"]; sb[[0]]', "StyleBox", None, []),
("sb[[1]]", "string", None, []),
# FIXME: <<RowBox object does not have the attribute "restructure">>
# ('rb[[All, 1]]', "{a, b}", "\"a\"", []),
# ('fb[[All]][[1]]','1', None, []),
# ('sb[[All]][[1]]','string', None, []),
],
)
def test_part_boxes(str_expr, str_expected, fail_msg, msgs):
check_evaluation(
str_expr,
str_expected,
to_string_expr=True,
to_string_expected=True,
hold_expected=True,
failure_message=fail_msg,
expected_messages=msgs,
)


# 15 tests
@pytest.mark.parametrize(
("str_expr", "str_expected", "msg"),
Expand Down