Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 71fd224

Browse files
Issue #21859: Added Python implementation of io.FileIO.
1 parent cd092ef commit 71fd224

4 files changed

Lines changed: 500 additions & 66 deletions

File tree

Lib/_pyio.py

Lines changed: 344 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,16 @@
77
import codecs
88
import errno
99
import array
10+
import stat
1011
# Import _thread instead of threading to reduce startup cost
1112
try:
1213
from _thread import allocate_lock as Lock
1314
except ImportError:
1415
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
1520

1621
import io
1722
from io import (__all__, SEEK_SET, SEEK_CUR, SEEK_END)
@@ -1378,6 +1383,345 @@ def write(self, b):
13781383
return BufferedWriter.write(self, b)
13791384

13801385

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+
13811725
class TextIOBase(IOBase):
13821726

13831727
"""Base class for text I/O.

0 commit comments

Comments
 (0)