12
12
from .io import PushbackReader , set_nonblock
13
13
from .six import PY32 , string_types
14
14
15
+ if False :
16
+ from typing import Any , IO , MutableSequence , Optional , Sequence , Tuple , Union , overload # noqa: E501, F401
17
+ if True :
18
+ def overload (f ): # type: ignore # noqa: D103, F811
19
+ return None
20
+
15
21
16
22
class FormLink (object ):
17
23
"""Connection to a FORM process."""
@@ -25,18 +31,20 @@ class FormLink(object):
25
31
_PROMPT = '\n __READY__\n '
26
32
27
33
def __init__ (self , args = None , keep_log = False ):
34
+ # type: (Optional[Union[str, Sequence[str]]], Union[bool, int]) -> None
28
35
"""Open a connection to a FORM process."""
29
36
self ._closed = True
30
- self ._head = None
31
- self ._log = None
32
- self ._childpid = None
33
- self ._formpid = None
34
- self ._parentin = None
35
- self ._parentout = None
36
- self ._loggingin = None
37
+ self ._head = None # type: Optional[str]
38
+ self ._log = None # type: Optional[MutableSequence[str]]
39
+ self ._childpid = None # type: Optional[int]
40
+ self ._formpid = None # type: Optional[int]
41
+ self ._parentin = None # type: Optional[PushbackReader]
42
+ self ._parentout = None # type: Optional[IO[str]]
43
+ self ._loggingin = None # type: Optional[PushbackReader]
37
44
self .open (args , keep_log )
38
45
39
46
def __del__ (self ):
47
+ # type: () -> None
40
48
"""Destructor.
41
49
42
50
Free the connection to the FORM process if it still exists. Since in
@@ -51,14 +59,17 @@ def __del__(self):
51
59
pass
52
60
53
61
def __enter__ (self ):
62
+ # type: () -> FormLink
54
63
"""Enter the runtime context."""
55
64
return self
56
65
57
66
def __exit__ (self , exc_type , exc_value , traceback ):
67
+ # type: (Any, Any, Any) -> None
58
68
"""Exit the runtime context."""
59
69
self .close ()
60
70
61
71
def open (self , args = None , keep_log = False ):
72
+ # type: (Optional[Union[str, Sequence[str]]], Union[bool, int]) -> None
62
73
"""Open a connection to FORM.
63
74
64
75
Open a connection to a new FORM process. The opened connection should
@@ -94,11 +105,12 @@ def open(self, args=None, keep_log=False):
94
105
args = os .environ ['FORM' ]
95
106
else :
96
107
args = 'form'
97
-
98
108
if isinstance (args , string_types ):
99
109
args = shlex .split (args ) # Split the arguments.
100
110
elif isinstance (args , (list , tuple )):
101
111
args = list (args ) # As a modifiable mutable object.
112
+ else :
113
+ raise TypeError ("invalid args = {0}" .format (args ))
102
114
103
115
self .close ()
104
116
@@ -146,8 +158,6 @@ def open(self, args=None, keep_log=False):
146
158
147
159
set_nonblock (fd_parentin )
148
160
set_nonblock (fd_loggingin )
149
- parentin = PushbackReader (parentin )
150
- loggingin = PushbackReader (loggingin )
151
161
152
162
self ._closed = False
153
163
self ._head = head
@@ -164,9 +174,9 @@ def open(self, args=None, keep_log=False):
164
174
parentout .write ('#-\n ' )
165
175
self ._childpid = pid
166
176
self ._formpid = formpid
167
- self ._parentin = parentin
177
+ self ._parentin = PushbackReader ( parentin )
168
178
self ._parentout = parentout
169
- self ._loggingin = loggingin
179
+ self ._loggingin = PushbackReader ( loggingin )
170
180
else :
171
181
# child process
172
182
@@ -202,6 +212,7 @@ def open(self, args=None, keep_log=False):
202
212
os ._exit (0 )
203
213
204
214
def close (self ):
215
+ # type: () -> None
205
216
"""Close the connection to FORM.
206
217
207
218
Close the connection to the FORM process established by :meth:`open`.
@@ -212,7 +223,13 @@ def close(self):
212
223
self ._close ()
213
224
214
225
def _close (self , term = False , kill = False ):
226
+ # type: (float, float) -> None
215
227
if not self ._closed :
228
+ assert self ._childpid is not None
229
+ assert self ._formpid is not None
230
+ assert self ._parentin is not None
231
+ assert self ._parentout is not None
232
+ assert self ._loggingin is not None
216
233
try :
217
234
# We ignore broken pipes.
218
235
try :
@@ -236,7 +253,9 @@ def _close(self, term=False, kill=False):
236
253
# finish shortly.
237
254
238
255
def wait (timeout ): # timeout <= 0 means no wait
256
+ # type: (float) -> bool
239
257
# Wait for the child to finish.
258
+ assert self ._childpid is not None
240
259
t = 0.0
241
260
dt = 0.01
242
261
if timeout > 0 :
@@ -281,11 +300,13 @@ def wait(timeout): # timeout <= 0 means no wait
281
300
self ._loggingin = None
282
301
283
302
def kill (self ):
303
+ # type: () -> None
284
304
"""Kill the FORM process and close the connection."""
285
305
self ._close (kill = - 1 ) # Kill it immediately.
286
306
# self._close(term=-1, kill=1)
287
307
288
308
def write (self , script ):
309
+ # type: (str) -> None
289
310
"""Send a script to FORM.
290
311
291
312
Write the given script to the communication channel to FORM. It could
@@ -296,10 +317,12 @@ def write(self, script):
296
317
raise IOError ('tried to write to closed connection' )
297
318
script = script .strip ()
298
319
if script :
320
+ assert self ._parentout is not None
299
321
self ._parentout .write (script )
300
322
self ._parentout .write ('\n ' )
301
323
302
324
def flush (self ):
325
+ # type: () -> None
303
326
"""Flush the channel to FORM.
304
327
305
328
Flush the communication channel to FORM. Because :meth:`write` is
@@ -308,9 +331,71 @@ def flush(self):
308
331
"""
309
332
if self ._closed :
310
333
raise IOError ('tried to flush closed connection' )
334
+ assert self ._parentout is not None
311
335
self ._parentout .flush ()
312
336
313
- def read (self , * names ):
337
+ @overload
338
+ def read (self , name ): # noqa: D102
339
+ # type: (str) -> str
340
+ pass
341
+
342
+ @overload # noqa: F811
343
+ def read (self , name1 , name2 ): # noqa: D102
344
+ # type: (str, str) -> Tuple[str, str]
345
+ pass
346
+
347
+ @overload # noqa: F811
348
+ def read (self , name1 , name2 , name3 ): # noqa: D102
349
+ # type: (str, str, str) -> Tuple[str, str, str]
350
+ pass
351
+
352
+ @overload # noqa: F811
353
+ def read (self , name1 , name2 , name3 , name4 ): # noqa: D102
354
+ # type: (str, str, str, str) -> Tuple[str, str, str, str]
355
+ pass
356
+
357
+ @overload # noqa: F811
358
+ def read (self , name1 , name2 , name3 , name4 , name5 ): # noqa: D102
359
+ # type: (str, str, str, str, str) -> Tuple[str, str, str, str, str]
360
+ pass
361
+
362
+ @overload # noqa: F811
363
+ def read (self , name1 , name2 , name3 , name4 , name5 , name6 ): # noqa: D102
364
+ # type: (str, str, str, str, str, str) -> Tuple[str, str, str, str, str, str] # noqa: E501
365
+ pass
366
+
367
+ @overload # noqa: F811
368
+ def read (self , name1 , name2 , name3 , name4 , name5 , name6 , name7 ): # noqa: D102, E501
369
+ # type: (str, str, str, str, str, str, str) -> Tuple[str, str, str, str, str, str, str] # noqa: E501
370
+ pass
371
+
372
+ @overload # noqa: F811
373
+ def read (self , name1 , name2 , name3 , name4 , name5 , name6 , name7 , name8 ): # noqa: D102, E501
374
+ # type: (str, str, str, str, str, str, str, str) -> Tuple[str, str, str, str, str, str, str, str] # noqa: E501
375
+ pass
376
+
377
+ @overload # noqa: F811
378
+ def read (self , name1 , name2 , name3 , name4 , name5 , name6 , name7 , name8 , name9 ): # noqa: D102, E501
379
+ # type: (str, str, str, str, str, str, str, str, str) -> Tuple[str, str, str, str, str, str, str, str, str] # noqa: E501
380
+ pass
381
+
382
+ @overload # noqa: F811
383
+ def read (self , names ): # noqa: D102
384
+ # type: (Sequence[str]) -> Sequence[str]
385
+ pass
386
+
387
+ @overload # noqa: F811
388
+ def read (self , * names ): # noqa: D102
389
+ # type: (str) -> Sequence[str]
390
+ pass
391
+
392
+ @overload # noqa: F811
393
+ def read (self , * names ): # noqa: D102
394
+ # type: (Any) -> Any
395
+ pass
396
+
397
+ def read (self , * names ): # type: ignore # noqa: F811
398
+ # type: (Any) -> Any
314
399
r"""Read results from FORM.
315
400
316
401
Wait for a response of FORM to obtain the results specified by
@@ -373,6 +458,10 @@ def read(self, *names):
373
458
if any (not isinstance (x , string_types ) for x in names ):
374
459
return [self .read (x ) for x in names ]
375
460
461
+ assert self ._parentin is not None
462
+ assert self ._parentout is not None
463
+ assert self ._loggingin is not None
464
+
376
465
for e in names :
377
466
if len (e ) >= 2 and e [0 ] == '`' and e [- 1 ] == "'" :
378
467
self ._parentout .write (
@@ -461,22 +550,26 @@ def read(self, *names):
461
550
462
551
@property
463
552
def closed (self ):
553
+ # type: () -> bool
464
554
"""Return True if the connection is closed."""
465
555
return self ._closed
466
556
467
557
@property
468
558
def head (self ):
559
+ # type: () -> str
469
560
"""Return the first line of the FORM output."""
561
+ assert self ._head is not None
470
562
return self ._head
471
563
472
564
@property
473
565
def _dateversion (self ):
566
+ # type: () -> int
474
567
"""Return the build/revision date as an integer "yyyymmdd"."""
475
568
import re
476
569
if self ._head :
477
- m = re .search (r'(?<=\()(.*)(?=\))' , self ._head )
478
- if m :
479
- s = re .split (r'[, ]+' , m .group (0 ))
570
+ ma = re .search (r'(?<=\()(.*)(?=\))' , self ._head )
571
+ if ma :
572
+ s = re .split (r'[, ]+' , ma .group (0 ))
480
573
if len (s ) >= 3 :
481
574
# month
482
575
month_names = ('Jan' , 'Feb' , 'Mar' , 'Apr' , 'May' , 'Jun' ,
0 commit comments