Skip to content

Commit 5924373

Browse files
committed
syntax: Add type hints
1 parent 4c38856 commit 5924373

File tree

7 files changed

+218
-162
lines changed

7 files changed

+218
-162
lines changed

fluent.syntax/fluent/syntax/__init__.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,17 @@
1+
from typing import Any
2+
from .ast import Resource
13
from .parser import FluentParser
24
from .serializer import FluentSerializer
35

46

5-
def parse(source, **kwargs):
7+
def parse(source: str, **kwargs: Any) -> Resource:
68
"""Create an ast.Resource from a Fluent Syntax source.
79
"""
810
parser = FluentParser(**kwargs)
911
return parser.parse(source)
1012

1113

12-
def serialize(resource, **kwargs):
14+
def serialize(resource: Resource, **kwargs: Any) -> str:
1315
"""Serialize an ast.Resource to a unicode string.
1416
"""
1517
serializer = FluentSerializer(**kwargs)

fluent.syntax/fluent/syntax/ast.py

Lines changed: 73 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
import re
22
import sys
33
import json
4+
from typing import Any, Callable, Dict, List, TypeVar, Union, cast
45

6+
Node = TypeVar('Node', bound='BaseNode')
7+
ToJsonFn = Callable[[Dict[str, Any]], Any]
58

6-
def to_json(value, fn=None):
9+
10+
def to_json(value: Any, fn: Union[ToJsonFn, None] = None) -> Any:
711
if isinstance(value, BaseNode):
812
return value.to_json(fn)
913
if isinstance(value, list):
@@ -14,7 +18,7 @@ def to_json(value, fn=None):
1418
return value
1519

1620

17-
def from_json(value):
21+
def from_json(value: Any) -> Any:
1822
if isinstance(value, dict):
1923
cls = getattr(sys.modules[__name__], value['type'])
2024
args = {
@@ -29,7 +33,7 @@ def from_json(value):
2933
return value
3034

3135

32-
def scalars_equal(node1, node2, ignored_fields):
36+
def scalars_equal(node1: Any, node2: Any, ignored_fields: List[str]) -> bool:
3337
"""Compare two nodes which are not lists."""
3438

3539
if type(node1) != type(node2):
@@ -38,7 +42,7 @@ def scalars_equal(node1, node2, ignored_fields):
3842
if isinstance(node1, BaseNode):
3943
return node1.equals(node2, ignored_fields)
4044

41-
return node1 == node2
45+
return cast(bool, node1 == node2)
4246

4347

4448
class BaseNode:
@@ -48,9 +52,9 @@ class BaseNode:
4852
Annotation. Implements __str__, to_json and traverse.
4953
"""
5054

51-
def clone(self):
55+
def clone(self: Node) -> Node:
5256
"""Create a deep clone of the current node."""
53-
def visit(value):
57+
def visit(value: Any) -> Any:
5458
"""Clone node and its descendants."""
5559
if isinstance(value, BaseNode):
5660
return value.clone()
@@ -65,7 +69,7 @@ def visit(value):
6569
**{name: visit(value) for name, value in vars(self).items()}
6670
)
6771

68-
def equals(self, other, ignored_fields=['span']):
72+
def equals(self, other: 'BaseNode', ignored_fields: List[str] = ['span']) -> bool:
6973
"""Compare two nodes.
7074
7175
Nodes are deeply compared on a field by field basis. If possible, False
@@ -104,7 +108,7 @@ def equals(self, other, ignored_fields=['span']):
104108

105109
return True
106110

107-
def to_json(self, fn=None):
111+
def to_json(self, fn: Union[ToJsonFn, None] = None) -> Any:
108112
obj = {
109113
name: to_json(value, fn)
110114
for name, value in vars(self).items()
@@ -114,23 +118,23 @@ def to_json(self, fn=None):
114118
)
115119
return fn(obj) if fn else obj
116120

117-
def __str__(self):
121+
def __str__(self) -> str:
118122
return json.dumps(self.to_json())
119123

120124

121125
class SyntaxNode(BaseNode):
122126
"""Base class for AST nodes which can have Spans."""
123127

124-
def __init__(self, span=None, **kwargs):
128+
def __init__(self, span: Union['Span', None] = None, **kwargs: Any):
125129
super().__init__(**kwargs)
126130
self.span = span
127131

128-
def add_span(self, start, end):
132+
def add_span(self, start: int, end: int) -> None:
129133
self.span = Span(start, end)
130134

131135

132136
class Resource(SyntaxNode):
133-
def __init__(self, body=None, **kwargs):
137+
def __init__(self, body: Union[List['EntryType'], None] = None, **kwargs: Any):
134138
super().__init__(**kwargs)
135139
self.body = body or []
136140

@@ -140,8 +144,12 @@ class Entry(SyntaxNode):
140144

141145

142146
class Message(Entry):
143-
def __init__(self, id, value=None, attributes=None,
144-
comment=None, **kwargs):
147+
def __init__(self,
148+
id: 'Identifier',
149+
value: Union['Pattern', None] = None,
150+
attributes: Union[List['Attribute'], None] = None,
151+
comment: Union['Comment', None] = None,
152+
**kwargs: Any):
145153
super().__init__(**kwargs)
146154
self.id = id
147155
self.value = value
@@ -150,8 +158,8 @@ def __init__(self, id, value=None, attributes=None,
150158

151159

152160
class Term(Entry):
153-
def __init__(self, id, value, attributes=None,
154-
comment=None, **kwargs):
161+
def __init__(self, id: 'Identifier', value: 'Pattern', attributes: Union[List['Attribute'], None] = None,
162+
comment: Union['Comment', None] = None, **kwargs: Any):
155163
super().__init__(**kwargs)
156164
self.id = id
157165
self.value = value
@@ -160,7 +168,7 @@ def __init__(self, id, value, attributes=None,
160168

161169

162170
class Pattern(SyntaxNode):
163-
def __init__(self, elements, **kwargs):
171+
def __init__(self, elements: List[Union['TextElement', 'Placeable']], **kwargs: Any):
164172
super().__init__(**kwargs)
165173
self.elements = elements
166174

@@ -170,13 +178,15 @@ class PatternElement(SyntaxNode):
170178

171179

172180
class TextElement(PatternElement):
173-
def __init__(self, value, **kwargs):
181+
def __init__(self, value: str, **kwargs: Any):
174182
super().__init__(**kwargs)
175183
self.value = value
176184

177185

178186
class Placeable(PatternElement):
179-
def __init__(self, expression, **kwargs):
187+
def __init__(self,
188+
expression: Union['InlineExpression', 'Placeable', 'SelectExpression'],
189+
**kwargs: Any):
180190
super().__init__(**kwargs)
181191
self.expression = expression
182192

@@ -187,20 +197,21 @@ class Expression(SyntaxNode):
187197

188198
class Literal(Expression):
189199
"""An abstract base class for literals."""
190-
def __init__(self, value, **kwargs):
200+
201+
def __init__(self, value: str, **kwargs: Any):
191202
super().__init__(**kwargs)
192203
self.value = value
193204

194-
def parse(self):
205+
def parse(self) -> Dict[str, Any]:
195206
return {'value': self.value}
196207

197208

198209
class StringLiteral(Literal):
199-
def parse(self):
200-
def from_escape_sequence(matchobj):
210+
def parse(self) -> Dict[str, str]:
211+
def from_escape_sequence(matchobj: Any) -> str:
201212
c, codepoint4, codepoint6 = matchobj.groups()
202213
if c:
203-
return c
214+
return cast(str, c)
204215
codepoint = int(codepoint4 or codepoint6, 16)
205216
if codepoint <= 0xD7FF or 0xE000 <= codepoint:
206217
return chr(codepoint)
@@ -218,7 +229,7 @@ def from_escape_sequence(matchobj):
218229

219230

220231
class NumberLiteral(Literal):
221-
def parse(self):
232+
def parse(self) -> Dict[str, Union[float, int]]:
222233
value = float(self.value)
223234
decimal_position = self.value.find('.')
224235
precision = 0
@@ -231,116 +242,135 @@ def parse(self):
231242

232243

233244
class MessageReference(Expression):
234-
def __init__(self, id, attribute=None, **kwargs):
245+
def __init__(self, id: 'Identifier', attribute: Union['Identifier', None] = None, **kwargs: Any):
235246
super().__init__(**kwargs)
236247
self.id = id
237248
self.attribute = attribute
238249

239250

240251
class TermReference(Expression):
241-
def __init__(self, id, attribute=None, arguments=None, **kwargs):
252+
def __init__(self,
253+
id: 'Identifier',
254+
attribute: Union['Identifier', None] = None,
255+
arguments: Union['CallArguments', None] = None,
256+
**kwargs: Any):
242257
super().__init__(**kwargs)
243258
self.id = id
244259
self.attribute = attribute
245260
self.arguments = arguments
246261

247262

248263
class VariableReference(Expression):
249-
def __init__(self, id, **kwargs):
264+
def __init__(self, id: 'Identifier', **kwargs: Any):
250265
super().__init__(**kwargs)
251266
self.id = id
252267

253268

254269
class FunctionReference(Expression):
255-
def __init__(self, id, arguments, **kwargs):
270+
def __init__(self, id: 'Identifier', arguments: 'CallArguments', **kwargs: Any):
256271
super().__init__(**kwargs)
257272
self.id = id
258273
self.arguments = arguments
259274

260275

261276
class SelectExpression(Expression):
262-
def __init__(self, selector, variants, **kwargs):
277+
def __init__(self, selector: 'InlineExpression', variants: List['Variant'], **kwargs: Any):
263278
super().__init__(**kwargs)
264279
self.selector = selector
265280
self.variants = variants
266281

267282

268283
class CallArguments(SyntaxNode):
269-
def __init__(self, positional=None, named=None, **kwargs):
284+
def __init__(self,
285+
positional: Union[List[Union['InlineExpression', Placeable]], None] = None,
286+
named: Union[List['NamedArgument'], None] = None,
287+
**kwargs: Any):
270288
super().__init__(**kwargs)
271289
self.positional = [] if positional is None else positional
272290
self.named = [] if named is None else named
273291

274292

275293
class Attribute(SyntaxNode):
276-
def __init__(self, id, value, **kwargs):
294+
def __init__(self, id: 'Identifier', value: Pattern, **kwargs: Any):
277295
super().__init__(**kwargs)
278296
self.id = id
279297
self.value = value
280298

281299

282300
class Variant(SyntaxNode):
283-
def __init__(self, key, value, default=False, **kwargs):
301+
def __init__(self, key: Union['Identifier', NumberLiteral], value: Pattern, default: bool = False, **kwargs: Any):
284302
super().__init__(**kwargs)
285303
self.key = key
286304
self.value = value
287305
self.default = default
288306

289307

290308
class NamedArgument(SyntaxNode):
291-
def __init__(self, name, value, **kwargs):
309+
def __init__(self, name: 'Identifier', value: Union[NumberLiteral, StringLiteral], **kwargs: Any):
292310
super().__init__(**kwargs)
293311
self.name = name
294312
self.value = value
295313

296314

297315
class Identifier(SyntaxNode):
298-
def __init__(self, name, **kwargs):
316+
def __init__(self, name: str, **kwargs: Any):
299317
super().__init__(**kwargs)
300318
self.name = name
301319

302320

303321
class BaseComment(Entry):
304-
def __init__(self, content=None, **kwargs):
322+
def __init__(self, content: Union[str, None] = None, **kwargs: Any):
305323
super().__init__(**kwargs)
306324
self.content = content
307325

308326

309327
class Comment(BaseComment):
310-
def __init__(self, content=None, **kwargs):
328+
def __init__(self, content: Union[str, None] = None, **kwargs: Any):
311329
super().__init__(content, **kwargs)
312330

313331

314332
class GroupComment(BaseComment):
315-
def __init__(self, content=None, **kwargs):
333+
def __init__(self, content: Union[str, None] = None, **kwargs: Any):
316334
super().__init__(content, **kwargs)
317335

318336

319337
class ResourceComment(BaseComment):
320-
def __init__(self, content=None, **kwargs):
338+
def __init__(self, content: Union[str, None] = None, **kwargs: Any):
321339
super().__init__(content, **kwargs)
322340

323341

324342
class Junk(SyntaxNode):
325-
def __init__(self, content=None, annotations=None, **kwargs):
343+
def __init__(self,
344+
content: Union[str, None] = None,
345+
annotations: Union[List['Annotation'], None] = None,
346+
**kwargs: Any):
326347
super().__init__(**kwargs)
327348
self.content = content
328349
self.annotations = annotations or []
329350

330-
def add_annotation(self, annot):
351+
def add_annotation(self, annot: 'Annotation') -> None:
331352
self.annotations.append(annot)
332353

333354

334355
class Span(BaseNode):
335-
def __init__(self, start, end, **kwargs):
356+
def __init__(self, start: int, end: int, **kwargs: Any):
336357
super().__init__(**kwargs)
337358
self.start = start
338359
self.end = end
339360

340361

341362
class Annotation(SyntaxNode):
342-
def __init__(self, code, arguments=None, message=None, **kwargs):
363+
def __init__(self,
364+
code: str,
365+
arguments: Union[List[Any], None] = None,
366+
message: Union[str, None] = None,
367+
**kwargs: Any):
343368
super().__init__(**kwargs)
344369
self.code = code
345370
self.arguments = arguments or []
346371
self.message = message
372+
373+
374+
EntryType = Union[Message, Term, Comment, GroupComment, ResourceComment, Junk]
375+
InlineExpression = Union[NumberLiteral, StringLiteral, MessageReference,
376+
TermReference, VariableReference, FunctionReference]

fluent.syntax/fluent/syntax/errors.py

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
1+
from typing import Tuple, Union
2+
3+
14
class ParseError(Exception):
2-
def __init__(self, code, *args):
5+
def __init__(self, code: str, *args: Union[str, None]):
36
self.code = code
47
self.args = args
58
self.message = get_error_message(code, args)
69

710

8-
def get_error_message(code, args):
11+
def get_error_message(code: str, args: Tuple[Union[str, None], ...]) -> str:
912
if code == 'E00001':
1013
return 'Generic error'
1114
if code == 'E0002':

0 commit comments

Comments
 (0)