Coverage for /usr/lib/python3/dist-packages/serial/serialutil.py: 50%

364 statements  

« prev     ^ index     » next       coverage.py v7.2.7, created at 2024-03-28 13:34 +0000

1#! python 

2# 

3# Base class and support functions used by various backends. 

4# 

5# This file is part of pySerial. https://github.com/pyserial/pyserial 

6# (C) 2001-2016 Chris Liechti <cliechti@gmx.net> 

7# 

8# SPDX-License-Identifier: BSD-3-Clause 

9 

10import io 

11import time 

12 

13# ``memoryview`` was introduced in Python 2.7 and ``bytes(some_memoryview)`` 

14# isn't returning the contents (very unfortunate). Therefore we need special 

15# cases and test for it. Ensure that there is a ``memoryview`` object for older 

16# Python versions. This is easier than making every test dependent on its 

17# existence. 

18try: 

19 memoryview 

20except (NameError, AttributeError): 

21 # implementation does not matter as we do not really use it. 

22 # it just must not inherit from something else we might care for. 

23 class memoryview(object): # pylint: disable=redefined-builtin,invalid-name 

24 pass 

25 

26try: 

27 unicode 

28except (NameError, AttributeError): 

29 unicode = str # for Python 3, pylint: disable=redefined-builtin,invalid-name 

30 

31try: 

32 basestring 

33except (NameError, AttributeError): 

34 basestring = (str,) # for Python 3, pylint: disable=redefined-builtin,invalid-name 

35 

36 

37# "for byte in data" fails for python3 as it returns ints instead of bytes 

38def iterbytes(b): 

39 """Iterate over bytes, returning bytes instead of ints (python3)""" 

40 if isinstance(b, memoryview): 

41 b = b.tobytes() 

42 i = 0 

43 while True: 

44 a = b[i:i + 1] 

45 i += 1 

46 if a: 

47 yield a 

48 else: 

49 break 

50 

51 

52# all Python versions prior 3.x convert ``str([17])`` to '[17]' instead of '\x11' 

53# so a simple ``bytes(sequence)`` doesn't work for all versions 

54def to_bytes(seq): 

55 """convert a sequence to a bytes type""" 

56 if isinstance(seq, bytes): 

57 return seq 

58 elif isinstance(seq, bytearray): 58 ↛ 59line 58 didn't jump to line 59, because the condition on line 58 was never true

59 return bytes(seq) 

60 elif isinstance(seq, memoryview): 60 ↛ 61line 60 didn't jump to line 61, because the condition on line 60 was never true

61 return seq.tobytes() 

62 elif isinstance(seq, unicode): 62 ↛ 63line 62 didn't jump to line 63, because the condition on line 62 was never true

63 raise TypeError('unicode strings are not supported, please encode to bytes: {!r}'.format(seq)) 

64 else: 

65 # handle list of integers and bytes (one or more items) for Python 2 and 3 

66 return bytes(bytearray(seq)) 

67 

68 

69# create control bytes 

70XON = to_bytes([17]) 

71XOFF = to_bytes([19]) 

72 

73CR = to_bytes([13]) 

74LF = to_bytes([10]) 

75 

76 

77PARITY_NONE, PARITY_EVEN, PARITY_ODD, PARITY_MARK, PARITY_SPACE = 'N', 'E', 'O', 'M', 'S' 

78STOPBITS_ONE, STOPBITS_ONE_POINT_FIVE, STOPBITS_TWO = (1, 1.5, 2) 

79FIVEBITS, SIXBITS, SEVENBITS, EIGHTBITS = (5, 6, 7, 8) 

80 

81PARITY_NAMES = { 

82 PARITY_NONE: 'None', 

83 PARITY_EVEN: 'Even', 

84 PARITY_ODD: 'Odd', 

85 PARITY_MARK: 'Mark', 

86 PARITY_SPACE: 'Space', 

87} 

88 

89 

90class SerialException(IOError): 

91 """Base class for serial port related exceptions.""" 

92 

93 

94class SerialTimeoutException(SerialException): 

95 """Write timeouts give an exception""" 

96 

97 

98writeTimeoutError = SerialTimeoutException('Write timeout') 

99portNotOpenError = SerialException('Attempting to use a port that is not open') 

100 

101 

102class Timeout(object): 

103 """\ 

104 Abstraction for timeout operations. Using time.monotonic() if available 

105 or time.time() in all other cases. 

106 

107 The class can also be initialized with 0 or None, in order to support 

108 non-blocking and fully blocking I/O operations. The attributes 

109 is_non_blocking and is_infinite are set accordingly. 

110 """ 

111 if hasattr(time, 'monotonic'): 111 ↛ 121line 111 didn't jump to line 121, because the condition on line 111 was never false

112 # Timeout implementation with time.monotonic(). This function is only 

113 # supported by Python 3.3 and above. It returns a time in seconds 

114 # (float) just as time.time(), but is not affected by system clock 

115 # adjustments. 

116 TIME = time.monotonic 

117 else: 

118 # Timeout implementation with time.time(). This is compatible with all 

119 # Python versions but has issues if the clock is adjusted while the 

120 # timeout is running. 

121 TIME = time.time 

122 

123 def __init__(self, duration): 

124 """Initialize a timeout with given duration""" 

125 self.is_infinite = (duration is None) 

126 self.is_non_blocking = (duration == 0) 

127 self.duration = duration 

128 if duration is not None: 

129 self.target_time = self.TIME() + duration 

130 else: 

131 self.target_time = None 

132 

133 def expired(self): 

134 """Return a boolean, telling if the timeout has expired""" 

135 return self.target_time is not None and self.time_left() <= 0 

136 

137 def time_left(self): 

138 """Return how many seconds are left until the timeout expires""" 

139 if self.is_non_blocking: 

140 return 0 

141 elif self.is_infinite: 141 ↛ 144line 141 didn't jump to line 144, because the condition on line 141 was never false

142 return None 

143 else: 

144 delta = self.target_time - self.TIME() 

145 if delta > self.duration: 

146 # clock jumped, recalculate 

147 self.target_time = self.TIME() + self.duration 

148 return self.duration 

149 else: 

150 return max(0, delta) 

151 

152 def restart(self, duration): 

153 """\ 

154 Restart a timeout, only supported if a timeout was already set up 

155 before. 

156 """ 

157 self.duration = duration 

158 self.target_time = self.TIME() + duration 

159 

160 

161class SerialBase(io.RawIOBase): 

162 """\ 

163 Serial port base class. Provides __init__ function and properties to 

164 get/set port settings. 

165 """ 

166 

167 # default values, may be overridden in subclasses that do not support all values 

168 BAUDRATES = (50, 75, 110, 134, 150, 200, 300, 600, 1200, 1800, 2400, 4800, 

169 9600, 19200, 38400, 57600, 115200, 230400, 460800, 500000, 

170 576000, 921600, 1000000, 1152000, 1500000, 2000000, 2500000, 

171 3000000, 3500000, 4000000) 

172 BYTESIZES = (FIVEBITS, SIXBITS, SEVENBITS, EIGHTBITS) 

173 PARITIES = (PARITY_NONE, PARITY_EVEN, PARITY_ODD, PARITY_MARK, PARITY_SPACE) 

174 STOPBITS = (STOPBITS_ONE, STOPBITS_ONE_POINT_FIVE, STOPBITS_TWO) 

175 

176 def __init__(self, 

177 port=None, 

178 baudrate=9600, 

179 bytesize=EIGHTBITS, 

180 parity=PARITY_NONE, 

181 stopbits=STOPBITS_ONE, 

182 timeout=None, 

183 xonxoff=False, 

184 rtscts=False, 

185 write_timeout=None, 

186 dsrdtr=False, 

187 inter_byte_timeout=None, 

188 exclusive=None, 

189 **kwargs): 

190 """\ 

191 Initialize comm port object. If a "port" is given, then the port will be 

192 opened immediately. Otherwise a Serial port object in closed state 

193 is returned. 

194 """ 

195 

196 self.is_open = False 

197 self.portstr = None 

198 self.name = None 

199 # correct values are assigned below through properties 

200 self._port = None 

201 self._baudrate = None 

202 self._bytesize = None 

203 self._parity = None 

204 self._stopbits = None 

205 self._timeout = None 

206 self._write_timeout = None 

207 self._xonxoff = None 

208 self._rtscts = None 

209 self._dsrdtr = None 

210 self._inter_byte_timeout = None 

211 self._rs485_mode = None # disabled by default 

212 self._rts_state = True 

213 self._dtr_state = True 

214 self._break_state = False 

215 self._exclusive = None 

216 

217 # assign values using get/set methods using the properties feature 

218 self.port = port 

219 self.baudrate = baudrate 

220 self.bytesize = bytesize 

221 self.parity = parity 

222 self.stopbits = stopbits 

223 self.timeout = timeout 

224 self.write_timeout = write_timeout 

225 self.xonxoff = xonxoff 

226 self.rtscts = rtscts 

227 self.dsrdtr = dsrdtr 

228 self.inter_byte_timeout = inter_byte_timeout 

229 self.exclusive = exclusive 

230 

231 # watch for backward compatible kwargs 

232 if 'writeTimeout' in kwargs: 232 ↛ 233line 232 didn't jump to line 233, because the condition on line 232 was never true

233 self.write_timeout = kwargs.pop('writeTimeout') 

234 if 'interCharTimeout' in kwargs: 234 ↛ 235line 234 didn't jump to line 235, because the condition on line 234 was never true

235 self.inter_byte_timeout = kwargs.pop('interCharTimeout') 

236 if kwargs: 236 ↛ 237line 236 didn't jump to line 237, because the condition on line 236 was never true

237 raise ValueError('unexpected keyword arguments: {!r}'.format(kwargs)) 

238 

239 if port is not None: 239 ↛ 240line 239 didn't jump to line 240, because the condition on line 239 was never true

240 self.open() 

241 

242 # - - - - - - - - - - - - - - - - - - - - - - - - 

243 

244 # to be implemented by subclasses: 

245 # def open(self): 

246 # def close(self): 

247 

248 # - - - - - - - - - - - - - - - - - - - - - - - - 

249 

250 @property 

251 def port(self): 

252 """\ 

253 Get the current port setting. The value that was passed on init or using 

254 setPort() is passed back. 

255 """ 

256 return self._port 

257 

258 @port.setter 

259 def port(self, port): 

260 """\ 

261 Change the port. 

262 """ 

263 if port is not None and not isinstance(port, basestring): 263 ↛ 264line 263 didn't jump to line 264, because the condition on line 263 was never true

264 raise ValueError('"port" must be None or a string, not {}'.format(type(port))) 

265 was_open = self.is_open 

266 if was_open: 266 ↛ 267line 266 didn't jump to line 267, because the condition on line 266 was never true

267 self.close() 

268 self.portstr = port 

269 self._port = port 

270 self.name = self.portstr 

271 if was_open: 271 ↛ 272line 271 didn't jump to line 272, because the condition on line 271 was never true

272 self.open() 

273 

274 @property 

275 def baudrate(self): 

276 """Get the current baud rate setting.""" 

277 return self._baudrate 

278 

279 @baudrate.setter 

280 def baudrate(self, baudrate): 

281 """\ 

282 Change baud rate. It raises a ValueError if the port is open and the 

283 baud rate is not possible. If the port is closed, then the value is 

284 accepted and the exception is raised when the port is opened. 

285 """ 

286 try: 

287 b = int(baudrate) 

288 except TypeError: 

289 raise ValueError("Not a valid baudrate: {!r}".format(baudrate)) 

290 else: 

291 if b < 0: 291 ↛ 292line 291 didn't jump to line 292, because the condition on line 291 was never true

292 raise ValueError("Not a valid baudrate: {!r}".format(baudrate)) 

293 self._baudrate = b 

294 if self.is_open: 294 ↛ 295line 294 didn't jump to line 295, because the condition on line 294 was never true

295 self._reconfigure_port() 

296 

297 @property 

298 def bytesize(self): 

299 """Get the current byte size setting.""" 

300 return self._bytesize 

301 

302 @bytesize.setter 

303 def bytesize(self, bytesize): 

304 """Change byte size.""" 

305 if bytesize not in self.BYTESIZES: 305 ↛ 306line 305 didn't jump to line 306, because the condition on line 305 was never true

306 raise ValueError("Not a valid byte size: {!r}".format(bytesize)) 

307 self._bytesize = bytesize 

308 if self.is_open: 308 ↛ 309line 308 didn't jump to line 309, because the condition on line 308 was never true

309 self._reconfigure_port() 

310 

311 @property 

312 def exclusive(self): 

313 """Get the current exclusive access setting.""" 

314 return self._exclusive 

315 

316 @exclusive.setter 

317 def exclusive(self, exclusive): 

318 """Change the exclusive access setting.""" 

319 self._exclusive = exclusive 

320 if self.is_open: 320 ↛ 321line 320 didn't jump to line 321, because the condition on line 320 was never true

321 self._reconfigure_port() 

322 

323 @property 

324 def parity(self): 

325 """Get the current parity setting.""" 

326 return self._parity 

327 

328 @parity.setter 

329 def parity(self, parity): 

330 """Change parity setting.""" 

331 if parity not in self.PARITIES: 331 ↛ 332line 331 didn't jump to line 332, because the condition on line 331 was never true

332 raise ValueError("Not a valid parity: {!r}".format(parity)) 

333 self._parity = parity 

334 if self.is_open: 334 ↛ 335line 334 didn't jump to line 335, because the condition on line 334 was never true

335 self._reconfigure_port() 

336 

337 @property 

338 def stopbits(self): 

339 """Get the current stop bits setting.""" 

340 return self._stopbits 

341 

342 @stopbits.setter 

343 def stopbits(self, stopbits): 

344 """Change stop bits size.""" 

345 if stopbits not in self.STOPBITS: 345 ↛ 346line 345 didn't jump to line 346, because the condition on line 345 was never true

346 raise ValueError("Not a valid stop bit size: {!r}".format(stopbits)) 

347 self._stopbits = stopbits 

348 if self.is_open: 348 ↛ 349line 348 didn't jump to line 349, because the condition on line 348 was never true

349 self._reconfigure_port() 

350 

351 @property 

352 def timeout(self): 

353 """Get the current timeout setting.""" 

354 return self._timeout 

355 

356 @timeout.setter 

357 def timeout(self, timeout): 

358 """Change timeout setting.""" 

359 if timeout is not None: 

360 try: 

361 timeout + 1 # test if it's a number, will throw a TypeError if not... 

362 except TypeError: 

363 raise ValueError("Not a valid timeout: {!r}".format(timeout)) 

364 if timeout < 0: 364 ↛ 365line 364 didn't jump to line 365, because the condition on line 364 was never true

365 raise ValueError("Not a valid timeout: {!r}".format(timeout)) 

366 self._timeout = timeout 

367 if self.is_open: 367 ↛ 368line 367 didn't jump to line 368, because the condition on line 367 was never true

368 self._reconfigure_port() 

369 

370 @property 

371 def write_timeout(self): 

372 """Get the current timeout setting.""" 

373 return self._write_timeout 

374 

375 @write_timeout.setter 

376 def write_timeout(self, timeout): 

377 """Change timeout setting.""" 

378 if timeout is not None: 378 ↛ 379line 378 didn't jump to line 379, because the condition on line 378 was never true

379 if timeout < 0: 

380 raise ValueError("Not a valid timeout: {!r}".format(timeout)) 

381 try: 

382 timeout + 1 # test if it's a number, will throw a TypeError if not... 

383 except TypeError: 

384 raise ValueError("Not a valid timeout: {!r}".format(timeout)) 

385 

386 self._write_timeout = timeout 

387 if self.is_open: 387 ↛ 388line 387 didn't jump to line 388, because the condition on line 387 was never true

388 self._reconfigure_port() 

389 

390 @property 

391 def inter_byte_timeout(self): 

392 """Get the current inter-character timeout setting.""" 

393 return self._inter_byte_timeout 

394 

395 @inter_byte_timeout.setter 

396 def inter_byte_timeout(self, ic_timeout): 

397 """Change inter-byte timeout setting.""" 

398 if ic_timeout is not None: 398 ↛ 399line 398 didn't jump to line 399, because the condition on line 398 was never true

399 if ic_timeout < 0: 

400 raise ValueError("Not a valid timeout: {!r}".format(ic_timeout)) 

401 try: 

402 ic_timeout + 1 # test if it's a number, will throw a TypeError if not... 

403 except TypeError: 

404 raise ValueError("Not a valid timeout: {!r}".format(ic_timeout)) 

405 

406 self._inter_byte_timeout = ic_timeout 

407 if self.is_open: 407 ↛ 408line 407 didn't jump to line 408, because the condition on line 407 was never true

408 self._reconfigure_port() 

409 

410 @property 

411 def xonxoff(self): 

412 """Get the current XON/XOFF setting.""" 

413 return self._xonxoff 

414 

415 @xonxoff.setter 

416 def xonxoff(self, xonxoff): 

417 """Change XON/XOFF setting.""" 

418 self._xonxoff = xonxoff 

419 if self.is_open: 419 ↛ 420line 419 didn't jump to line 420, because the condition on line 419 was never true

420 self._reconfigure_port() 

421 

422 @property 

423 def rtscts(self): 

424 """Get the current RTS/CTS flow control setting.""" 

425 return self._rtscts 

426 

427 @rtscts.setter 

428 def rtscts(self, rtscts): 

429 """Change RTS/CTS flow control setting.""" 

430 self._rtscts = rtscts 

431 if self.is_open: 431 ↛ 432line 431 didn't jump to line 432, because the condition on line 431 was never true

432 self._reconfigure_port() 

433 

434 @property 

435 def dsrdtr(self): 

436 """Get the current DSR/DTR flow control setting.""" 

437 return self._dsrdtr 

438 

439 @dsrdtr.setter 

440 def dsrdtr(self, dsrdtr=None): 

441 """Change DsrDtr flow control setting.""" 

442 if dsrdtr is None: 442 ↛ 444line 442 didn't jump to line 444, because the condition on line 442 was never true

443 # if not set, keep backwards compatibility and follow rtscts setting 

444 self._dsrdtr = self._rtscts 

445 else: 

446 # if defined independently, follow its value 

447 self._dsrdtr = dsrdtr 

448 if self.is_open: 448 ↛ 449line 448 didn't jump to line 449, because the condition on line 448 was never true

449 self._reconfigure_port() 

450 

451 @property 

452 def rts(self): 

453 return self._rts_state 

454 

455 @rts.setter 

456 def rts(self, value): 

457 self._rts_state = value 

458 if self.is_open: 

459 self._update_rts_state() 

460 

461 @property 

462 def dtr(self): 

463 return self._dtr_state 

464 

465 @dtr.setter 

466 def dtr(self, value): 

467 self._dtr_state = value 

468 if self.is_open: 

469 self._update_dtr_state() 

470 

471 @property 

472 def break_condition(self): 

473 return self._break_state 

474 

475 @break_condition.setter 

476 def break_condition(self, value): 

477 self._break_state = value 

478 if self.is_open: 

479 self._update_break_state() 

480 

481 # - - - - - - - - - - - - - - - - - - - - - - - - 

482 # functions useful for RS-485 adapters 

483 

484 @property 

485 def rs485_mode(self): 

486 """\ 

487 Enable RS485 mode and apply new settings, set to None to disable. 

488 See serial.rs485.RS485Settings for more info about the value. 

489 """ 

490 return self._rs485_mode 

491 

492 @rs485_mode.setter 

493 def rs485_mode(self, rs485_settings): 

494 self._rs485_mode = rs485_settings 

495 if self.is_open: 

496 self._reconfigure_port() 

497 

498 # - - - - - - - - - - - - - - - - - - - - - - - - 

499 

500 _SAVED_SETTINGS = ('baudrate', 'bytesize', 'parity', 'stopbits', 'xonxoff', 

501 'dsrdtr', 'rtscts', 'timeout', 'write_timeout', 

502 'inter_byte_timeout') 

503 

504 def get_settings(self): 

505 """\ 

506 Get current port settings as a dictionary. For use with 

507 apply_settings(). 

508 """ 

509 return dict([(key, getattr(self, '_' + key)) for key in self._SAVED_SETTINGS]) 

510 

511 def apply_settings(self, d): 

512 """\ 

513 Apply stored settings from a dictionary returned from 

514 get_settings(). It's allowed to delete keys from the dictionary. These 

515 values will simply left unchanged. 

516 """ 

517 for key in self._SAVED_SETTINGS: 

518 if key in d and d[key] != getattr(self, '_' + key): # check against internal "_" value 

519 setattr(self, key, d[key]) # set non "_" value to use properties write function 

520 

521 # - - - - - - - - - - - - - - - - - - - - - - - - 

522 

523 def __repr__(self): 

524 """String representation of the current port settings and its state.""" 

525 return '{name}<id=0x{id:x}, open={p.is_open}>(port={p.portstr!r}, ' \ 

526 'baudrate={p.baudrate!r}, bytesize={p.bytesize!r}, parity={p.parity!r}, ' \ 

527 'stopbits={p.stopbits!r}, timeout={p.timeout!r}, xonxoff={p.xonxoff!r}, ' \ 

528 'rtscts={p.rtscts!r}, dsrdtr={p.dsrdtr!r})'.format( 

529 name=self.__class__.__name__, id=id(self), p=self) 

530 

531 # - - - - - - - - - - - - - - - - - - - - - - - - 

532 # compatibility with io library 

533 # pylint: disable=invalid-name,missing-docstring 

534 

535 def readable(self): 

536 return True 

537 

538 def writable(self): 

539 return True 

540 

541 def seekable(self): 

542 return False 

543 

544 def readinto(self, b): 

545 data = self.read(len(b)) 

546 n = len(data) 

547 try: 

548 b[:n] = data 

549 except TypeError as err: 

550 import array 

551 if not isinstance(b, array.array): 

552 raise err 

553 b[:n] = array.array('b', data) 

554 return n 

555 

556 # - - - - - - - - - - - - - - - - - - - - - - - - 

557 # context manager 

558 

559 def __enter__(self): 

560 if not self.is_open: 

561 self.open() 

562 return self 

563 

564 def __exit__(self, *args, **kwargs): 

565 self.close() 

566 

567 # - - - - - - - - - - - - - - - - - - - - - - - - 

568 

569 def send_break(self, duration=0.25): 

570 """\ 

571 Send break condition. Timed, returns to idle state after given 

572 duration. 

573 """ 

574 if not self.is_open: 

575 raise portNotOpenError 

576 self.break_condition = True 

577 time.sleep(duration) 

578 self.break_condition = False 

579 

580 # - - - - - - - - - - - - - - - - - - - - - - - - 

581 # backwards compatibility / deprecated functions 

582 

583 def flushInput(self): 

584 self.reset_input_buffer() 

585 

586 def flushOutput(self): 

587 self.reset_output_buffer() 

588 

589 def inWaiting(self): 

590 return self.in_waiting 

591 

592 def sendBreak(self, duration=0.25): 

593 self.send_break(duration) 

594 

595 def setRTS(self, value=1): 

596 self.rts = value 

597 

598 def setDTR(self, value=1): 

599 self.dtr = value 

600 

601 def getCTS(self): 

602 return self.cts 

603 

604 def getDSR(self): 

605 return self.dsr 

606 

607 def getRI(self): 

608 return self.ri 

609 

610 def getCD(self): 

611 return self.cd 

612 

613 def setPort(self, port): 

614 self.port = port 

615 

616 @property 

617 def writeTimeout(self): 

618 return self.write_timeout 

619 

620 @writeTimeout.setter 

621 def writeTimeout(self, timeout): 

622 self.write_timeout = timeout 

623 

624 @property 

625 def interCharTimeout(self): 

626 return self.inter_byte_timeout 

627 

628 @interCharTimeout.setter 

629 def interCharTimeout(self, interCharTimeout): 

630 self.inter_byte_timeout = interCharTimeout 

631 

632 def getSettingsDict(self): 

633 return self.get_settings() 

634 

635 def applySettingsDict(self, d): 

636 self.apply_settings(d) 

637 

638 def isOpen(self): 

639 return self.is_open 

640 

641 # - - - - - - - - - - - - - - - - - - - - - - - - 

642 # additional functionality 

643 

644 def read_all(self): 

645 """\ 

646 Read all bytes currently available in the buffer of the OS. 

647 """ 

648 return self.read(self.in_waiting) 

649 

650 def read_until(self, terminator=LF, size=None): 

651 """\ 

652 Read until a termination sequence is found ('\n' by default), the size 

653 is exceeded or until timeout occurs. 

654 """ 

655 lenterm = len(terminator) 

656 line = bytearray() 

657 timeout = Timeout(self._timeout) 

658 while True: 

659 c = self.read(1) 

660 if c: 

661 line += c 

662 if line[-lenterm:] == terminator: 

663 break 

664 if size is not None and len(line) >= size: 

665 break 

666 else: 

667 break 

668 if timeout.expired(): 

669 break 

670 return bytes(line) 

671 

672 def iread_until(self, *args, **kwargs): 

673 """\ 

674 Read lines, implemented as generator. It will raise StopIteration on 

675 timeout (empty read). 

676 """ 

677 while True: 

678 line = self.read_until(*args, **kwargs) 

679 if not line: 

680 break 

681 yield line 

682 

683 

684# - - - - - - - - - - - - - - - - - - - - - - - - - 

685if __name__ == '__main__': 685 ↛ 686line 685 didn't jump to line 686, because the condition on line 685 was never true

686 import sys 

687 s = SerialBase() 

688 sys.stdout.write('port name: {}\n'.format(s.name)) 

689 sys.stdout.write('baud rates: {}\n'.format(s.BAUDRATES)) 

690 sys.stdout.write('byte sizes: {}\n'.format(s.BYTESIZES)) 

691 sys.stdout.write('parities: {}\n'.format(s.PARITIES)) 

692 sys.stdout.write('stop bits: {}\n'.format(s.STOPBITS)) 

693 sys.stdout.write('{}\n'.format(s))