f20080305d
Определение специфических рекомендаций (матрица профилей §8):
1. Ж/д слой (закрыт мёртвый railway ×2.5 у РАС):
- /api/v1/water/{case_id} отдаёт railway=rail как LineString
(без service/industrial/military веток), кэш общий v2;
- railway_warning «перекрыть/проверить немедленно» по профилям;
- SearchMap: Polyline слой ж/д (тёмно-красный), счётчики 💧/🚂.
2. cant_swim → профиль не_умеет_плавать (water ×3.0, без изменения
радиуса, critical_warning «обследовать водоёмы НЕМЕДЛЕННО»):
- раньше чекбокс влиял только на текст, в скоринге был пробел;
- derive в analyze._derive_profiles — работает и для closed_cases.
3. unmodeled_profiles: ДЦП/слабое зрение/слух — честная пометка
«вне поведенческой модели» с пояснением (vector_tasks B12:
профили без аналога не выдавать за учтённые); блок на фронте
в карточке здоровья.
Площадь воды: сферический эксцесс, проверен на квадрате 53° (744017 м²
vs 743272 точного). Тесты: 202 passed (новый test_cant_swim_profile).
158 lines
4.4 KiB
Python
158 lines
4.4 KiB
Python
from __future__ import annotations
|
|
|
|
__all__ = (
|
|
"FileReadStream",
|
|
"FileStreamAttribute",
|
|
"FileWriteStream",
|
|
)
|
|
|
|
from collections.abc import Callable, Mapping
|
|
from io import SEEK_SET, UnsupportedOperation
|
|
from os import PathLike
|
|
from pathlib import Path
|
|
from typing import IO, Any
|
|
|
|
from .. import (
|
|
BrokenResourceError,
|
|
ClosedResourceError,
|
|
EndOfStream,
|
|
TypedAttributeSet,
|
|
to_thread,
|
|
typed_attribute,
|
|
)
|
|
from ..abc import ByteReceiveStream, ByteSendStream
|
|
|
|
|
|
class FileStreamAttribute(TypedAttributeSet):
|
|
#: the open file descriptor
|
|
file: IO[bytes] = typed_attribute()
|
|
#: the path of the file on the file system, if available (file must be a real file)
|
|
path: Path = typed_attribute()
|
|
#: the file number, if available (file must be a real file or a TTY)
|
|
fileno: int = typed_attribute()
|
|
|
|
|
|
class _BaseFileStream:
|
|
def __init__(self, file: IO[bytes]):
|
|
self._file = file
|
|
|
|
async def aclose(self) -> None:
|
|
await to_thread.run_sync(self._file.close)
|
|
|
|
@property
|
|
def extra_attributes(self) -> Mapping[Any, Callable[[], Any]]:
|
|
attributes: dict[Any, Callable[[], Any]] = {
|
|
FileStreamAttribute.file: lambda: self._file,
|
|
}
|
|
|
|
if hasattr(self._file, "name"):
|
|
attributes[FileStreamAttribute.path] = lambda: Path(self._file.name)
|
|
|
|
try:
|
|
self._file.fileno()
|
|
except UnsupportedOperation:
|
|
pass
|
|
else:
|
|
attributes[FileStreamAttribute.fileno] = lambda: self._file.fileno()
|
|
|
|
return attributes
|
|
|
|
|
|
class FileReadStream(_BaseFileStream, ByteReceiveStream):
|
|
"""
|
|
A byte stream that reads from a file in the file system.
|
|
|
|
:param file: a file that has been opened for reading in binary mode
|
|
|
|
.. versionadded:: 3.0
|
|
"""
|
|
|
|
@classmethod
|
|
async def from_path(cls, path: str | PathLike[str]) -> FileReadStream:
|
|
"""
|
|
Create a file read stream by opening the given file.
|
|
|
|
:param path: path of the file to read from
|
|
|
|
"""
|
|
file = await to_thread.run_sync(Path(path).open, "rb")
|
|
return cls(file)
|
|
|
|
async def receive(self, max_bytes: int = 65536) -> bytes:
|
|
if max_bytes < 1:
|
|
raise ValueError("max_bytes must be a positive integer")
|
|
|
|
try:
|
|
data = await to_thread.run_sync(self._file.read, max_bytes)
|
|
except ValueError:
|
|
raise ClosedResourceError from None
|
|
except OSError as exc:
|
|
raise BrokenResourceError from exc
|
|
|
|
if data:
|
|
return data
|
|
else:
|
|
raise EndOfStream
|
|
|
|
async def seek(self, position: int, whence: int = SEEK_SET) -> int:
|
|
"""
|
|
Seek the file to the given position.
|
|
|
|
.. seealso:: :meth:`io.IOBase.seek`
|
|
|
|
.. note:: Not all file descriptors are seekable.
|
|
|
|
:param position: position to seek the file to
|
|
:param whence: controls how ``position`` is interpreted
|
|
:return: the new absolute position
|
|
:raises OSError: if the file is not seekable
|
|
|
|
"""
|
|
return await to_thread.run_sync(self._file.seek, position, whence)
|
|
|
|
async def tell(self) -> int:
|
|
"""
|
|
Return the current stream position.
|
|
|
|
.. note:: Not all file descriptors are seekable.
|
|
|
|
:return: the current absolute position
|
|
:raises OSError: if the file is not seekable
|
|
|
|
"""
|
|
return await to_thread.run_sync(self._file.tell)
|
|
|
|
|
|
class FileWriteStream(_BaseFileStream, ByteSendStream):
|
|
"""
|
|
A byte stream that writes to a file in the file system.
|
|
|
|
:param file: a file that has been opened for writing in binary mode
|
|
|
|
.. versionadded:: 3.0
|
|
"""
|
|
|
|
@classmethod
|
|
async def from_path(
|
|
cls, path: str | PathLike[str], append: bool = False
|
|
) -> FileWriteStream:
|
|
"""
|
|
Create a file write stream by opening the given file for writing.
|
|
|
|
:param path: path of the file to write to
|
|
:param append: if ``True``, open the file for appending; if ``False``, any
|
|
existing file at the given path will be truncated
|
|
|
|
"""
|
|
mode = "ab" if append else "wb"
|
|
file = await to_thread.run_sync(Path(path).open, mode)
|
|
return cls(file)
|
|
|
|
async def send(self, item: bytes) -> None:
|
|
try:
|
|
await to_thread.run_sync(self._file.write, item)
|
|
except ValueError:
|
|
raise ClosedResourceError from None
|
|
except OSError as exc:
|
|
raise BrokenResourceError from exc
|