16
16
import json
17
17
import re
18
18
from ast import literal_eval
19
- from typing import List , Optional
19
+ from typing import Any , Dict , List , Optional
20
20
21
21
import yaml
22
22
@@ -126,7 +126,7 @@ def __init__(
126
126
self .current_params_indentation = 1
127
127
128
128
# The current element i.e. user, bot, event, if ...
129
- self .current_element = None
129
+ self .current_element : Optional [ Dict [ str , Any ]] = None
130
130
131
131
# The flows that have been parsed
132
132
self .flows = {}
@@ -264,7 +264,7 @@ def _normalize_line_text(self):
264
264
265
265
flow_hash = string_hash (flow_text )
266
266
267
- self .text += " anonymous-" + flow_hash
267
+ self .text += " anonymous-" + str ( flow_hash )
268
268
269
269
# Below are some more advanced normalizations
270
270
@@ -313,8 +313,9 @@ def _create_namespace(self, namespace):
313
313
# Now, append the new one
314
314
self .current_namespaces .append (namespace )
315
315
self .current_namespace = "." .join (self .current_namespaces )
316
- self .current_indentation = self .next_line ["indentation" ]
317
- self .current_indentations .append (self .next_line ["indentation" ])
316
+ next_indentation = self .next_line ["indentation" ] if self .next_line else 0
317
+ self .current_indentation = next_indentation
318
+ self .current_indentations .append (next_indentation )
318
319
319
320
# Reset the branches and the ifs on a new flow
320
321
self .branches = []
@@ -335,7 +336,11 @@ def _ignore_block_body(self):
335
336
def _include_source_mappings (self ):
336
337
# Include the source mapping information if required
337
338
if self .include_source_mapping :
338
- if self .current_element and "_source_mapping" not in self .current_element :
339
+ if (
340
+ self .current_element is not None
341
+ and isinstance (self .current_element , dict )
342
+ and "_source_mapping" not in self .current_element
343
+ ):
339
344
self .current_element ["_source_mapping" ] = {
340
345
"filename" : self .filename ,
341
346
"line_number" : self .current_line ["number" ],
@@ -790,7 +795,7 @@ def _process_define(self):
790
795
791
796
# If we're dealing with a topic, then we expand the flow definition
792
797
if define_token == "topic" :
793
- self . _insert_topic_flow_definition ()
798
+ # TODO: Implement topic flow definition insertion
794
799
return
795
800
796
801
# Compute the symbol type
@@ -957,14 +962,18 @@ def _extract_params(self, param_lines: Optional[List] = None):
957
962
if isinstance (yaml_value , str ):
958
963
yaml_value = {"$0" : yaml_value }
959
964
960
- # self.current_element.update(yaml_value)
961
- for k in yaml_value .keys ():
962
- # if the key tarts with $, we remove it
963
- param_name = k
964
- if param_name [0 ] == "$" :
965
- param_name = param_name [1 :]
965
+ if (
966
+ self .current_element is not None
967
+ and isinstance (self .current_element , dict )
968
+ and yaml_value is not None
969
+ ):
970
+ for k in yaml_value .keys ():
971
+ # if the key tarts with $, we remove it
972
+ param_name = k
973
+ if param_name [0 ] == "$" :
974
+ param_name = param_name [1 :]
966
975
967
- self .current_element [param_name ] = yaml_value [k ]
976
+ self .current_element [param_name ] = yaml_value [k ]
968
977
969
978
def _is_test_flow (self ):
970
979
"""Returns true if the current flow is a test one.
@@ -1005,11 +1014,13 @@ def _is_sample_flow(self):
1005
1014
def _parse_when (self ):
1006
1015
# TODO: deal with "when" after "else when"
1007
1016
assert (
1008
- self .next_line ["indentation" ] > self .current_line ["indentation" ]
1017
+ self .next_line is not None
1018
+ and self .next_line ["indentation" ] > self .current_line ["indentation" ]
1009
1019
), "Expected indented block after 'when' statement."
1010
1020
1011
1021
# Create the new branch
1012
- new_branch = {"elements" : [], "indentation" : self .next_line ["indentation" ]}
1022
+ next_indentation = self .next_line ["indentation" ] if self .next_line else 0
1023
+ new_branch = {"elements" : [], "indentation" : next_indentation }
1013
1024
1014
1025
# # on else, we need to pop the previous branch
1015
1026
# if self.main_token == "else when":
@@ -1040,13 +1051,16 @@ def _parse_when(self):
1040
1051
# continue
1041
1052
# else
1042
1053
# ...
1054
+ next_indentation = (
1055
+ self .next_line ["indentation" ] if self .next_line else 0
1056
+ )
1043
1057
self .lines .insert (
1044
1058
self .current_line_idx + 1 ,
1045
1059
{
1046
1060
"text" : f"continue" ,
1047
1061
# We keep the line mapping the same
1048
1062
"number" : self .current_line ["number" ],
1049
- "indentation" : self . next_line [ "indentation" ] ,
1063
+ "indentation" : next_indentation ,
1050
1064
},
1051
1065
)
1052
1066
self .lines .insert (
@@ -1320,9 +1334,11 @@ def _parse_bot(self):
1320
1334
"text" : f"{ utterance_text } " ,
1321
1335
# We keep the line mapping the same
1322
1336
"number" : self .current_line ["number" ],
1323
- "indentation" : self .current_indentation + 2
1324
- if i == len (indented_lines )
1325
- else indented_lines [i ]["indentation" ],
1337
+ "indentation" : (
1338
+ self .current_indentation + 2
1339
+ if i == len (indented_lines )
1340
+ else indented_lines [i ]["indentation" ]
1341
+ ),
1326
1342
},
1327
1343
)
1328
1344
@@ -1343,7 +1359,9 @@ def _parse_bot(self):
1343
1359
if utterance_id is None :
1344
1360
self .current_element ["bot" ] = {
1345
1361
"_type" : "element" ,
1346
- "text" : utterance_text [1 :- 1 ],
1362
+ "text" : (
1363
+ utterance_text [1 :- 1 ] if utterance_text is not None else ""
1364
+ ),
1347
1365
}
1348
1366
1349
1367
# if we have quick_replies, we move them in the element
@@ -1361,7 +1379,13 @@ def _parse_bot(self):
1361
1379
# If there was a bot message with a snippet, we also add an expect
1362
1380
# TODO: can this be handled better?
1363
1381
try :
1364
- if "snippet" in self .current_element ["bot" ]:
1382
+ if (
1383
+ self .current_element is not None
1384
+ and isinstance (self .current_element , dict )
1385
+ and "bot" in self .current_element
1386
+ and isinstance (self .current_element ["bot" ], dict )
1387
+ and "snippet" in self .current_element ["bot" ]
1388
+ ):
1365
1389
self .branches [- 1 ]["elements" ].append (
1366
1390
{
1367
1391
"expect" : "snippet" ,
@@ -1425,7 +1449,8 @@ def _parse_do(self):
1425
1449
1426
1450
# if we need to save the return values, we store the info
1427
1451
if "=" in flow_name :
1428
- return_vars , flow_name = get_stripped_tokens (split_max (flow_name , "=" , 1 ))
1452
+ stripped_tokens = get_stripped_tokens (split_max (flow_name , "=" , 1 ))
1453
+ return_vars , flow_name = stripped_tokens [0 ], stripped_tokens [1 ]
1429
1454
else :
1430
1455
return_vars = None
1431
1456
@@ -1475,8 +1500,9 @@ def _parse_meta(self):
1475
1500
branch_elements .insert (0 , {"meta" : {}})
1476
1501
1477
1502
# Update the elements coming from the parameters
1478
- for k in self .current_element .keys ():
1479
- branch_elements [0 ]["meta" ][k ] = self .current_element [k ]
1503
+ if self .current_element is not None :
1504
+ for k in self .current_element .keys ():
1505
+ branch_elements [0 ]["meta" ][k ] = self .current_element [k ]
1480
1506
1481
1507
def _parse_generic (self ):
1482
1508
value = split_max (self .text , " " , 1 )[1 ].strip ()
@@ -1545,7 +1571,9 @@ def _parse_if_branch(self, if_condition):
1545
1571
self .ifs .append (
1546
1572
{
1547
1573
"element" : self .current_element ,
1548
- "indentation" : self .next_line ["indentation" ],
1574
+ "indentation" : (
1575
+ self .next_line ["indentation" ] if self .next_line is not None else 0
1576
+ ),
1549
1577
# We also record this to match it with the else
1550
1578
"keyword_indentation" : self .current_indentation ,
1551
1579
}
@@ -1588,7 +1616,9 @@ def _parse_while(self):
1588
1616
self .branches .append (
1589
1617
{
1590
1618
"elements" : self .current_element ["do" ],
1591
- "indentation" : self .next_line ["indentation" ],
1619
+ "indentation" : (
1620
+ self .next_line ["indentation" ] if self .next_line is not None else 0
1621
+ ),
1592
1622
}
1593
1623
)
1594
1624
@@ -1602,7 +1632,9 @@ def _parse_any(self):
1602
1632
self .branches .append (
1603
1633
{
1604
1634
"elements" : self .current_element ["any" ],
1605
- "indentation" : self .next_line ["indentation" ],
1635
+ "indentation" : (
1636
+ self .next_line ["indentation" ] if self .next_line is not None else 0
1637
+ ),
1606
1638
}
1607
1639
)
1608
1640
@@ -1631,7 +1663,9 @@ def _parse_infer(self):
1631
1663
self .branches .append (
1632
1664
{
1633
1665
"elements" : self .current_element ["infer" ],
1634
- "indentation" : self .next_line ["indentation" ],
1666
+ "indentation" : (
1667
+ self .next_line ["indentation" ] if self .next_line is not None else 0
1668
+ ),
1635
1669
}
1636
1670
)
1637
1671
@@ -1767,15 +1801,15 @@ def parse(self):
1767
1801
exception = Exception (error )
1768
1802
1769
1803
# Decorate the exception with where the parsing failed
1770
- exception . filename = self .filename
1771
- exception . line = self .current_line ["number" ]
1772
- exception . error = str (ex )
1804
+ setattr ( exception , " filename" , self .filename )
1805
+ setattr ( exception , " line" , self .current_line ["number" ])
1806
+ setattr ( exception , " error" , str (ex ) )
1773
1807
1774
1808
raise exception
1775
1809
1776
1810
self .current_line_idx += 1
1777
1811
1778
- result = {"flows" : self .flows }
1812
+ result : Dict [ str , Any ] = {"flows" : self .flows }
1779
1813
1780
1814
if self .imports :
1781
1815
result ["imports" ] = self .imports
@@ -1818,7 +1852,7 @@ def parse_snippets_and_imports(self):
1818
1852
"""
1819
1853
snippets = {}
1820
1854
imports = []
1821
- snippet = None
1855
+ snippet : Optional [ Dict [ str , Any ]] = None
1822
1856
1823
1857
while self .current_line_idx < len (self .lines ):
1824
1858
self ._fetch_current_line ()
@@ -1833,6 +1867,7 @@ def parse_snippets_and_imports(self):
1833
1867
for k in self .current_line .keys ():
1834
1868
d [k ] = self .current_line [k ]
1835
1869
d ["filename" ] = self .filename
1870
+ assert snippet is not None # Type checker hint
1836
1871
snippet ["lines" ].append (d )
1837
1872
1838
1873
self .current_line_idx += 1
0 commit comments