Skip to content

Commit c6f5ee2

Browse files
authored
Allow signed numbers in literals (#46)
* Allow signed numbers in literals * Disallow floats & negative ints in array shapes * Fix wording
1 parent 4db30f8 commit c6f5ee2

File tree

2 files changed

+29
-8
lines changed

2 files changed

+29
-8
lines changed

src/docstub/doctype.lark

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -58,12 +58,12 @@ natlang_literal: "{" literal_item ("," literal_item)* "}"
5858

5959
// An single item in a literal expression (or `optional`). We must also allow
6060
// for qualified names, since a "class" or enum can be used as a literal too.
61-
?literal_item: ELLIPSES | STRING | NUMBER | qualname
61+
?literal_item: ELLIPSES | STRING | SIGNED_NUMBER | qualname
6262

6363

6464
// Natural language forms of the subscription expression for containers.
65-
// These forms allow nesting allow nesting in and with other expressions. But
66-
// it's discouraged to do so extensively to maintain readability.
65+
// These forms allow nesting with other expressions. But it's discouraged to do
66+
// so extensively to maintain readability.
6767
natlang_container: qualname "of" qualname _PLURAL_S?
6868
| qualname "of" "(" union ")"
6969
| _natlang_tuple
@@ -114,7 +114,7 @@ ARRAY_NAME: "array" | "ndarray" | "array-like" | "array_like"
114114
// NumPy arrays, this information is dropped during the transformation.
115115
shape: "(" dim ",)"
116116
| "(" leading_optional_dim? dim (("," dim | insert_optional_dim))* ")"
117-
| NUMBER "-"? "D"
117+
| INT "-"? "D"
118118

119119

120120
// Optional dimensions in a `shape` expression placed at the start,
@@ -129,7 +129,7 @@ shape: "(" dim ",)"
129129

130130
// Dimension can be a number, ellipses ('...') or a simple name. A simple name
131131
// can be bound to a specific number, e.g. `N=3`.
132-
?dim: NUMBER | ELLIPSES | NAME ("=" NUMBER)?
132+
?dim: INT | ELLIPSES | NAME ("=" INT)?
133133

134134

135135
// Optional information about a parameter has a default value, added after the
@@ -147,6 +147,6 @@ NAME: /[^\W\d][\w-]*/
147147

148148

149149
%import python (STRING)
150-
%import common (NUMBER, WS_INLINE)
150+
%import common (SIGNED_NUMBER, INT, WS_INLINE)
151151

152152
%ignore WS_INLINE

tests/test_docstrings.py

Lines changed: 23 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,12 +108,26 @@ def test_subscription_error(self, doctype):
108108
("doctype", "expected"),
109109
[
110110
("{0}", "Literal[0]"),
111-
("{'a', 1, None, False}", "Literal['a', 1, None, False]"),
112-
("dict[{'a', 'b'}, int]", "dict[Literal['a', 'b'], int]"),
111+
("{-1, 1}", "Literal[-1, 1]"),
112+
("{None}", "Literal[None]"),
113+
("{True, False}", "Literal[True, False]"),
114+
("""{'a', "bar"}""", """Literal['a', "bar"]"""),
115+
# Enum
113116
("{SomeEnum.FIRST}", "Literal[SomeEnum_FIRST]"),
114117
("{`SomeEnum.FIRST`, 1}", "Literal[SomeEnum_FIRST, 1]"),
115118
("{:ref:`SomeEnum.FIRST`, 2}", "Literal[SomeEnum_FIRST, 2]"),
116119
("{:py:ref:`SomeEnum.FIRST`, 3}", "Literal[SomeEnum_FIRST, 3]"),
120+
# Nesting
121+
("dict[{'a', 'b'}, int]", "dict[Literal['a', 'b'], int]"),
122+
# These aren't officially valid as an argument to `Literal` (yet)
123+
# https://typing.python.org/en/latest/spec/literal.html
124+
# TODO figure out how docstub should deal with these
125+
("{-2., 1.}", "Literal[-2., 1.]"),
126+
pytest.param(
127+
"{-inf, inf, nan}",
128+
"Literal[, 1.]",
129+
marks=pytest.mark.xfail(reason="unsure how to support"),
130+
),
117131
],
118132
)
119133
def test_literals(self, doctype, expected):
@@ -188,6 +202,13 @@ def escape(name: str) -> str:
188202
assert annotation.value == expected
189203
# fmt: on
190204

205+
@pytest.mark.parametrize("shape", ["(-1, 3)", "(1.0, 2)", "-3D", "-2-D"])
206+
def test_natlang_array_invalid_shape(self, shape):
207+
doctype = f"array of shape {shape}"
208+
transformer = DoctypeTransformer()
209+
with pytest.raises(lark.exceptions.UnexpectedInput):
210+
transformer.doctype_to_annotation(doctype)
211+
191212
def test_unknown_name(self):
192213
# Simple unknown name is aliased to typing.Any
193214
transformer = DoctypeTransformer()

0 commit comments

Comments
 (0)