|
7 | 7 | import codecs |
8 | 8 | import errno |
9 | 9 | import array |
| 10 | +import stat |
10 | 11 | # Import _thread instead of threading to reduce startup cost |
11 | 12 | try: |
12 | 13 | from _thread import allocate_lock as Lock |
13 | 14 | except ImportError: |
14 | 15 | from _dummy_thread import allocate_lock as Lock |
| 16 | +if os.name == 'win32': |
| 17 | + from msvcrt import setmode as _setmode |
| 18 | +else: |
| 19 | + _setmode = None |
15 | 20 |
|
16 | 21 | import io |
17 | 22 | from io import (__all__, SEEK_SET, SEEK_CUR, SEEK_END) |
@@ -1378,6 +1383,345 @@ def write(self, b): |
1378 | 1383 | return BufferedWriter.write(self, b) |
1379 | 1384 |
|
1380 | 1385 |
|
| 1386 | +class FileIO(RawIOBase): |
| 1387 | + _fd = -1 |
| 1388 | + _created = False |
| 1389 | + _readable = False |
| 1390 | + _writable = False |
| 1391 | + _appending = False |
| 1392 | + _seekable = None |
| 1393 | + _closefd = True |
| 1394 | + |
| 1395 | + def __init__(self, file, mode='r', closefd=True, opener=None): |
| 1396 | + """Open a file. The mode can be 'r' (default), 'w', 'x' or 'a' for reading, |
| 1397 | + writing, exclusive creation or appending. The file will be created if it |
| 1398 | + doesn't exist when opened for writing or appending; it will be truncated |
| 1399 | + when opened for writing. A FileExistsError will be raised if it already |
| 1400 | + exists when opened for creating. Opening a file for creating implies |
| 1401 | + writing so this mode behaves in a similar way to 'w'. Add a '+' to the mode |
| 1402 | + to allow simultaneous reading and writing. A custom opener can be used by |
| 1403 | + passing a callable as *opener*. The underlying file descriptor for the file |
| 1404 | + object is then obtained by calling opener with (*name*, *flags*). |
| 1405 | + *opener* must return an open file descriptor (passing os.open as *opener* |
| 1406 | + results in functionality similar to passing None). |
| 1407 | + """ |
| 1408 | + if self._fd >= 0: |
| 1409 | + # Have to close the existing file first. |
| 1410 | + try: |
| 1411 | + if self._closefd: |
| 1412 | + os.close(self._fd) |
| 1413 | + finally: |
| 1414 | + self._fd = -1 |
| 1415 | + |
| 1416 | + if isinstance(file, float): |
| 1417 | + raise TypeError('integer argument expected, got float') |
| 1418 | + if isinstance(file, int): |
| 1419 | + fd = file |
| 1420 | + if fd < 0: |
| 1421 | + raise ValueError('negative file descriptor') |
| 1422 | + else: |
| 1423 | + fd = -1 |
| 1424 | + |
| 1425 | + if not isinstance(mode, str): |
| 1426 | + raise TypeError('invalid mode: %s' % (mode,)) |
| 1427 | + if not set(mode) <= set('xrwab+'): |
| 1428 | + raise ValueError('invalid mode: %s' % (mode,)) |
| 1429 | + if sum(c in 'rwax' for c in mode) != 1 or mode.count('+') > 1: |
| 1430 | + raise ValueError('Must have exactly one of create/read/write/append ' |
| 1431 | + 'mode and at most one plus') |
| 1432 | + |
| 1433 | + if 'x' in mode: |
| 1434 | + self._created = True |
| 1435 | + self._writable = True |
| 1436 | + flags = os.O_EXCL | os.O_CREAT |
| 1437 | + elif 'r' in mode: |
| 1438 | + self._readable = True |
| 1439 | + flags = 0 |
| 1440 | + elif 'w' in mode: |
| 1441 | + self._writable = True |
| 1442 | + flags = os.O_CREAT | os.O_TRUNC |
| 1443 | + elif 'a' in mode: |
| 1444 | + self._writable = True |
| 1445 | + self._appending = True |
| 1446 | + flags = os.O_APPEND | os.O_CREAT |
| 1447 | + |
| 1448 | + if '+' in mode: |
| 1449 | + self._readable = True |
| 1450 | + self._writable = True |
| 1451 | + |
| 1452 | + if self._readable and self._writable: |
| 1453 | + flags |= os.O_RDWR |
| 1454 | + elif self._readable: |
| 1455 | + flags |= os.O_RDONLY |
| 1456 | + else: |
| 1457 | + flags |= os.O_WRONLY |
| 1458 | + |
| 1459 | + flags |= getattr(os, 'O_BINARY', 0) |
| 1460 | + |
| 1461 | + noinherit_flag = (getattr(os, 'O_NOINHERIT', 0) or |
| 1462 | + getattr(os, 'O_CLOEXEC', 0)) |
| 1463 | + flags |= noinherit_flag |
| 1464 | + |
| 1465 | + owned_fd = None |
| 1466 | + try: |
| 1467 | + if fd < 0: |
| 1468 | + if not closefd: |
| 1469 | + raise ValueError('Cannot use closefd=False with file name') |
| 1470 | + if opener is None: |
| 1471 | + fd = os.open(file, flags, 0o666) |
| 1472 | + else: |
| 1473 | + fd = opener(file, flags) |
| 1474 | + if not isinstance(fd, int): |
| 1475 | + raise TypeError('expected integer from opener') |
| 1476 | + if fd < 0: |
| 1477 | + raise OSError('Negative file descriptor') |
| 1478 | + owned_fd = fd |
| 1479 | + if not noinherit_flag: |
| 1480 | + os.set_inheritable(fd, False) |
| 1481 | + |
| 1482 | + self._closefd = closefd |
| 1483 | + fdfstat = os.fstat(fd) |
| 1484 | + try: |
| 1485 | + if stat.S_ISDIR(fdfstat.st_mode): |
| 1486 | + raise IsADirectoryError(errno.EISDIR, |
| 1487 | + os.strerror(errno.EISDIR), file) |
| 1488 | + except AttributeError: |
| 1489 | + # Ignore the AttribueError if stat.S_ISDIR or errno.EISDIR |
| 1490 | + # don't exist. |
| 1491 | + pass |
| 1492 | + self._blksize = getattr(fdfstat, 'st_blksize', 0) |
| 1493 | + if self._blksize <= 1: |
| 1494 | + self._blksize = DEFAULT_BUFFER_SIZE |
| 1495 | + |
| 1496 | + if _setmode: |
| 1497 | + # don't translate newlines (\r\n <=> \n) |
| 1498 | + _setmode(fd, os.O_BINARY) |
| 1499 | + |
| 1500 | + self.name = file |
| 1501 | + if self._appending: |
| 1502 | + # For consistent behaviour, we explicitly seek to the |
| 1503 | + # end of file (otherwise, it might be done only on the |
| 1504 | + # first write()). |
| 1505 | + os.lseek(fd, 0, SEEK_END) |
| 1506 | + except: |
| 1507 | + if owned_fd is not None: |
| 1508 | + os.close(owned_fd) |
| 1509 | + raise |
| 1510 | + self._fd = fd |
| 1511 | + |
| 1512 | + def __del__(self): |
| 1513 | + if self._fd >= 0 and self._closefd and not self.closed: |
| 1514 | + import warnings |
| 1515 | + warnings.warn('unclosed file %r' % (self,), ResourceWarning, |
| 1516 | + stacklevel=2) |
| 1517 | + self.close() |
| 1518 | + |
| 1519 | + def __getstate__(self): |
| 1520 | + raise TypeError("cannot serialize '%s' object", self.__class__.__name__) |
| 1521 | + |
| 1522 | + def __repr__(self): |
| 1523 | + class_name = '%s.%s' % (self.__class__.__module__, |
| 1524 | + self.__class__.__qualname__) |
| 1525 | + if self.closed: |
| 1526 | + return '<%s [closed]>' % class_name |
| 1527 | + try: |
| 1528 | + name = self.name |
| 1529 | + except AttributeError: |
| 1530 | + return ('<%s fd=%d mode=%r closefd=%r>' % |
| 1531 | + (class_name, self._fd, self.mode, self._closefd)) |
| 1532 | + else: |
| 1533 | + return ('<%s name=%r mode=%r closefd=%r>' % |
| 1534 | + (class_name, name, self.mode, self._closefd)) |
| 1535 | + |
| 1536 | + def _checkReadable(self): |
| 1537 | + if not self._readable: |
| 1538 | + raise UnsupportedOperation('File not open for reading') |
| 1539 | + |
| 1540 | + def _checkWritable(self, msg=None): |
| 1541 | + if not self._writable: |
| 1542 | + raise UnsupportedOperation('File not open for writing') |
| 1543 | + |
| 1544 | + def read(self, size=None): |
| 1545 | + """Read at most size bytes, returned as bytes. |
| 1546 | +
|
| 1547 | + Only makes one system call, so less data may be returned than requested |
| 1548 | + In non-blocking mode, returns None if no data is available. |
| 1549 | + Return an empty bytes object at EOF. |
| 1550 | + """ |
| 1551 | + self._checkClosed() |
| 1552 | + self._checkReadable() |
| 1553 | + if size is None or size < 0: |
| 1554 | + return self.readall() |
| 1555 | + try: |
| 1556 | + return os.read(self._fd, size) |
| 1557 | + except BlockingIOError: |
| 1558 | + return None |
| 1559 | + |
| 1560 | + def readall(self): |
| 1561 | + """Read all data from the file, returned as bytes. |
| 1562 | +
|
| 1563 | + In non-blocking mode, returns as much as is immediately available, |
| 1564 | + or None if no data is available. Return an empty bytes object at EOF. |
| 1565 | + """ |
| 1566 | + self._checkClosed() |
| 1567 | + self._checkReadable() |
| 1568 | + bufsize = DEFAULT_BUFFER_SIZE |
| 1569 | + try: |
| 1570 | + pos = os.lseek(self._fd, 0, SEEK_CUR) |
| 1571 | + end = os.fstat(self._fd).st_size |
| 1572 | + if end >= pos: |
| 1573 | + bufsize = end - pos + 1 |
| 1574 | + except OSError: |
| 1575 | + pass |
| 1576 | + |
| 1577 | + result = bytearray() |
| 1578 | + while True: |
| 1579 | + if len(result) >= bufsize: |
| 1580 | + bufsize = len(result) |
| 1581 | + bufsize += max(bufsize, DEFAULT_BUFFER_SIZE) |
| 1582 | + n = bufsize - len(result) |
| 1583 | + try: |
| 1584 | + chunk = os.read(self._fd, n) |
| 1585 | + except BlockingIOError: |
| 1586 | + if result: |
| 1587 | + break |
| 1588 | + return None |
| 1589 | + if not chunk: # reached the end of the file |
| 1590 | + break |
| 1591 | + result += chunk |
| 1592 | + |
| 1593 | + return bytes(result) |
| 1594 | + |
| 1595 | + def readinto(self, b): |
| 1596 | + """Same as RawIOBase.readinto().""" |
| 1597 | + m = memoryview(b).cast('B') |
| 1598 | + data = self.read(len(m)) |
| 1599 | + n = len(data) |
| 1600 | + m[:n] = data |
| 1601 | + return n |
| 1602 | + |
| 1603 | + def write(self, b): |
| 1604 | + """Write bytes b to file, return number written. |
| 1605 | +
|
| 1606 | + Only makes one system call, so not all of the data may be written. |
| 1607 | + The number of bytes actually written is returned. In non-blocking mode, |
| 1608 | + returns None if the write would block. |
| 1609 | + """ |
| 1610 | + self._checkClosed() |
| 1611 | + self._checkWritable() |
| 1612 | + try: |
| 1613 | + return os.write(self._fd, b) |
| 1614 | + except BlockingIOError: |
| 1615 | + return None |
| 1616 | + |
| 1617 | + def seek(self, pos, whence=SEEK_SET): |
| 1618 | + """Move to new file position. |
| 1619 | +
|
| 1620 | + Argument offset is a byte count. Optional argument whence defaults to |
| 1621 | + SEEK_SET or 0 (offset from start of file, offset should be >= 0); other values |
| 1622 | + are SEEK_CUR or 1 (move relative to current position, positive or negative), |
| 1623 | + and SEEK_END or 2 (move relative to end of file, usually negative, although |
| 1624 | + many platforms allow seeking beyond the end of a file). |
| 1625 | +
|
| 1626 | + Note that not all file objects are seekable. |
| 1627 | + """ |
| 1628 | + if isinstance(pos, float): |
| 1629 | + raise TypeError('an integer is required') |
| 1630 | + self._checkClosed() |
| 1631 | + return os.lseek(self._fd, pos, whence) |
| 1632 | + |
| 1633 | + def tell(self): |
| 1634 | + """tell() -> int. Current file position. |
| 1635 | +
|
| 1636 | + Can raise OSError for non seekable files.""" |
| 1637 | + self._checkClosed() |
| 1638 | + return os.lseek(self._fd, 0, SEEK_CUR) |
| 1639 | + |
| 1640 | + def truncate(self, size=None): |
| 1641 | + """Truncate the file to at most size bytes. |
| 1642 | +
|
| 1643 | + Size defaults to the current file position, as returned by tell(). |
| 1644 | + The current file position is changed to the value of size. |
| 1645 | + """ |
| 1646 | + self._checkClosed() |
| 1647 | + self._checkWritable() |
| 1648 | + if size is None: |
| 1649 | + size = self.tell() |
| 1650 | + os.ftruncate(self._fd, size) |
| 1651 | + return size |
| 1652 | + |
| 1653 | + def close(self): |
| 1654 | + """Close the file. |
| 1655 | +
|
| 1656 | + A closed file cannot be used for further I/O operations. close() may be |
| 1657 | + called more than once without error. |
| 1658 | + """ |
| 1659 | + if not self.closed: |
| 1660 | + try: |
| 1661 | + if self._closefd: |
| 1662 | + os.close(self._fd) |
| 1663 | + finally: |
| 1664 | + super().close() |
| 1665 | + |
| 1666 | + def seekable(self): |
| 1667 | + """True if file supports random-access.""" |
| 1668 | + self._checkClosed() |
| 1669 | + if self._seekable is None: |
| 1670 | + try: |
| 1671 | + self.tell() |
| 1672 | + except OSError: |
| 1673 | + self._seekable = False |
| 1674 | + else: |
| 1675 | + self._seekable = True |
| 1676 | + return self._seekable |
| 1677 | + |
| 1678 | + def readable(self): |
| 1679 | + """True if file was opened in a read mode.""" |
| 1680 | + self._checkClosed() |
| 1681 | + return self._readable |
| 1682 | + |
| 1683 | + def writable(self): |
| 1684 | + """True if file was opened in a write mode.""" |
| 1685 | + self._checkClosed() |
| 1686 | + return self._writable |
| 1687 | + |
| 1688 | + def fileno(self): |
| 1689 | + """Return the underlying file descriptor (an integer).""" |
| 1690 | + self._checkClosed() |
| 1691 | + return self._fd |
| 1692 | + |
| 1693 | + def isatty(self): |
| 1694 | + """True if the file is connected to a TTY device.""" |
| 1695 | + self._checkClosed() |
| 1696 | + return os.isatty(self._fd) |
| 1697 | + |
| 1698 | + @property |
| 1699 | + def closefd(self): |
| 1700 | + """True if the file descriptor will be closed by close().""" |
| 1701 | + return self._closefd |
| 1702 | + |
| 1703 | + @property |
| 1704 | + def mode(self): |
| 1705 | + """String giving the file mode""" |
| 1706 | + if self._created: |
| 1707 | + if self._readable: |
| 1708 | + return 'xb+' |
| 1709 | + else: |
| 1710 | + return 'xb' |
| 1711 | + elif self._appending: |
| 1712 | + if self._readable: |
| 1713 | + return 'ab+' |
| 1714 | + else: |
| 1715 | + return 'ab' |
| 1716 | + elif self._readable: |
| 1717 | + if self._writable: |
| 1718 | + return 'rb+' |
| 1719 | + else: |
| 1720 | + return 'rb' |
| 1721 | + else: |
| 1722 | + return 'wb' |
| 1723 | + |
| 1724 | + |
1381 | 1725 | class TextIOBase(IOBase): |
1382 | 1726 |
|
1383 | 1727 | """Base class for text I/O. |
|
0 commit comments