55import errno
66import shelve
77import signal
8+
89from functools import wraps
9- from typing import NamedTuple , List , Any
10+
11+ from typing import NamedTuple
12+ from typing import List
13+ from typing import Any
14+ from typing import Optional
15+ from typing import Callable
16+ from typing import IO
17+ from typing import Iterable
1018
1119from . import constants
1220from . import global_
@@ -35,7 +43,7 @@ class DataRef(NamedTuple):
3543 datas : List [Any ]
3644
3745
38- def read_txt_file (fname ) :
46+ def read_txt_file (fname : str ) -> str :
3947 """Reads a txt file, regardless of its encoding
4048 """
4149 encodings = ['utf-8-sig' , 'cp1252' ]
@@ -54,90 +62,104 @@ def read_txt_file(fname):
5462 return ''
5563
5664
57- def open_file (fname , mode = 'rb' , encoding = 'utf-8' ):
65+ def open_file (fname : str , mode : str = 'rb' , encoding : str = 'utf-8' ) -> IO [ Any ] :
5866 """ An open() wrapper for PY2 and PY3 which allows encoding
5967 :param fname: file name (string)
6068 :param mode: file mode (string) optional
6169 :param encoding: optional encoding (string). Ignored in python2 or if not in text mode
6270 :return: an open file handle
6371 """
64- if 't' not in mode :
65- kwargs = {}
66- else :
67- kwargs = {'encoding' : encoding }
72+ if 't' not in mode or not encoding :
73+ return open (fname , mode )
6874
69- return open (fname , mode , ** kwargs )
75+ return open (fname , mode , encoding = encoding )
7076
7177
72- def sanitize_filename (fname ) :
78+ def sanitize_filename (fname : str ) -> str :
7379 """ Given a file name (string) returns it with back-slashes reversed.
7480 This is to make all BASIC programs compatible in all OSes
7581 """
7682 return fname .replace ('\\ ' , '/' )
7783
7884
79- def current_data_label ():
85+ def current_data_label () -> str :
8086 """ Returns a data label to which all labels must point to, until
8187 a new DATA line is declared
8288 """
8389 return '__DATA__{0}' .format (len (global_ .DATAS ))
8490
8591
86- def flatten_list (x ):
92+ def flatten_list (x : Iterable [Any ], iterables = (list , )) -> List [Any ]:
93+ """ Flattens a nested iterable and returns it as a List.
94+ Nested iterables will be flattened recursively (default only nested lists)
95+ """
8796 result = []
8897
89- for l in x :
90- if not isinstance (l , list ):
91- result .append (l )
98+ for elem in x :
99+ if not isinstance (elem , iterables ):
100+ result .append (elem )
92101 else :
93- result .extend (flatten_list (l ))
102+ result .extend (flatten_list (elem ))
94103
95104 return result
96105
97106
98- def parse_int (str_num ) :
107+ def parse_int (num : Optional [ str ]) -> Optional [ int ] :
99108 """ Given an integer number, return its value,
100109 or None if it could not be parsed.
101110 Allowed formats: DECIMAL, HEXA (0xnnn, $nnnn or nnnnh)
102- :param str_num: (string) the number to be parsed
103- :return: an integer number or None if it could not be parsedd
111+ An hexadecimal number is ambiguous if it starts with a letter (i.e. A0h can be a label),
112+ and won't be parsed. Such numbers must be prefixed with 0 digit (i.e. 0A0h)
113+ :param num: (string) the number to be parsed
114+ :return: an integer number or None if it could not be parsed
104115 """
105- str_num = (str_num or "" ).strip ().upper ()
106- if not str_num :
116+ num = (num or "" ).strip ().upper ()
117+ if not num :
107118 return None
108119
109120 base = 10
110- if str_num . startswith ( '0X' ) :
121+ if num [: 2 ] == '0X' :
111122 base = 16
112- str_num = str_num [2 :]
113- if str_num .endswith ('H' ):
123+ elif num [- 1 ] == 'H' :
124+ if num [0 ] not in '0123456789' :
125+ return None
114126 base = 16
115- str_num = str_num [:- 1 ]
116- if str_num . startswith ( '$' ) :
127+ num = num [:- 1 ]
128+ elif num [ 0 ] == '$' :
117129 base = 16
118- str_num = str_num [1 :]
130+ num = num [1 :]
131+ elif num [0 ] == '%' :
132+ base = 2
133+ num = num [1 :]
134+ elif num [- 1 ] == 'B' :
135+ if num [0 ] not in '01' :
136+ return None
137+ base = 2
138+ num = num [:- 1 ]
119139
120140 try :
121- return int (str_num , base )
141+ return int (num , base )
122142 except ValueError :
123- return None
143+ pass
144+
145+ return None
124146
125147
126- def load_object (key ) :
148+ def load_object (key : str ) -> Any :
127149 return SHELVE [key ] if key in SHELVE else None
128150
129151
130- def save_object (key , obj ) :
152+ def save_object (key : str , obj : Any ) -> Any :
131153 SHELVE [key ] = obj
132154 SHELVE .sync ()
133155 return obj
134156
135157
136- def get_or_create (key , fn ) :
158+ def get_or_create (key : str , fn : Callable [[], Any ]) -> Any :
137159 return load_object (key ) or save_object (key , fn ())
138160
139161
140- def get_final_value (symbol : symbols .SYMBOL ):
162+ def get_final_value (symbol : symbols .SYMBOL ) -> Any :
141163 assert check .is_static (symbol )
142164 result = symbol
143165 while hasattr (result , 'value' ):
0 commit comments