Non puoi selezionare più di 25 argomenti Gli argomenti devono iniziare con una lettera o un numero, possono includere trattini ('-') e possono essere lunghi fino a 35 caratteri.
 
 
 
 

140 righe
4.2 KiB

  1. from io import SEEK_SET, UnsupportedOperation
  2. from os import PathLike
  3. from pathlib import Path
  4. from typing import Any, BinaryIO, Callable, Dict, Mapping, Union, cast
  5. from .. import (
  6. BrokenResourceError, ClosedResourceError, EndOfStream, TypedAttributeSet, to_thread,
  7. typed_attribute)
  8. from ..abc import ByteReceiveStream, ByteSendStream
  9. class FileStreamAttribute(TypedAttributeSet):
  10. #: the open file descriptor
  11. file: BinaryIO = typed_attribute()
  12. #: the path of the file on the file system, if available (file must be a real file)
  13. path: Path = typed_attribute()
  14. #: the file number, if available (file must be a real file or a TTY)
  15. fileno: int = typed_attribute()
  16. class _BaseFileStream:
  17. def __init__(self, file: BinaryIO):
  18. self._file = file
  19. async def aclose(self) -> None:
  20. await to_thread.run_sync(self._file.close)
  21. @property
  22. def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]:
  23. attributes: Dict[Any, Callable[[], Any]] = {
  24. FileStreamAttribute.file: lambda: self._file,
  25. }
  26. if hasattr(self._file, 'name'):
  27. attributes[FileStreamAttribute.path] = lambda: Path(self._file.name)
  28. try:
  29. self._file.fileno()
  30. except UnsupportedOperation:
  31. pass
  32. else:
  33. attributes[FileStreamAttribute.fileno] = lambda: self._file.fileno()
  34. return attributes
  35. class FileReadStream(_BaseFileStream, ByteReceiveStream):
  36. """
  37. A byte stream that reads from a file in the file system.
  38. :param file: a file that has been opened for reading in binary mode
  39. .. versionadded:: 3.0
  40. """
  41. @classmethod
  42. async def from_path(cls, path: Union[str, 'PathLike[str]']) -> 'FileReadStream':
  43. """
  44. Create a file read stream by opening the given file.
  45. :param path: path of the file to read from
  46. """
  47. file = await to_thread.run_sync(Path(path).open, 'rb')
  48. return cls(cast(BinaryIO, file))
  49. async def receive(self, max_bytes: int = 65536) -> bytes:
  50. try:
  51. data = await to_thread.run_sync(self._file.read, max_bytes)
  52. except ValueError:
  53. raise ClosedResourceError from None
  54. except OSError as exc:
  55. raise BrokenResourceError from exc
  56. if data:
  57. return data
  58. else:
  59. raise EndOfStream
  60. async def seek(self, position: int, whence: int = SEEK_SET) -> int:
  61. """
  62. Seek the file to the given position.
  63. .. seealso:: :meth:`io.IOBase.seek`
  64. .. note:: Not all file descriptors are seekable.
  65. :param position: position to seek the file to
  66. :param whence: controls how ``position`` is interpreted
  67. :return: the new absolute position
  68. :raises OSError: if the file is not seekable
  69. """
  70. return await to_thread.run_sync(self._file.seek, position, whence)
  71. async def tell(self) -> int:
  72. """
  73. Return the current stream position.
  74. .. note:: Not all file descriptors are seekable.
  75. :return: the current absolute position
  76. :raises OSError: if the file is not seekable
  77. """
  78. return await to_thread.run_sync(self._file.tell)
  79. class FileWriteStream(_BaseFileStream, ByteSendStream):
  80. """
  81. A byte stream that writes to a file in the file system.
  82. :param file: a file that has been opened for writing in binary mode
  83. .. versionadded:: 3.0
  84. """
  85. @classmethod
  86. async def from_path(cls, path: Union[str, 'PathLike[str]'],
  87. append: bool = False) -> 'FileWriteStream':
  88. """
  89. Create a file write stream by opening the given file for writing.
  90. :param path: path of the file to write to
  91. :param append: if ``True``, open the file for appending; if ``False``, any existing file
  92. at the given path will be truncated
  93. """
  94. mode = 'ab' if append else 'wb'
  95. file = await to_thread.run_sync(Path(path).open, mode)
  96. return cls(cast(BinaryIO, file))
  97. async def send(self, item: bytes) -> None:
  98. try:
  99. await to_thread.run_sync(self._file.write, item)
  100. except ValueError:
  101. raise ClosedResourceError from None
  102. except OSError as exc:
  103. raise BrokenResourceError from exc