| 34 | |
| 35 | |
| 36 | class TextLogStream(io.TextIOWrapper): |
| 37 | def __init__(self, prio, tag, original=None, **kwargs): |
| 38 | # Respect the -u option. |
| 39 | if original: |
| 40 | kwargs.setdefault("write_through", original.write_through) |
| 41 | fileno = original.fileno() |
| 42 | else: |
| 43 | fileno = None |
| 44 | |
| 45 | # The default is surrogateescape for stdout and backslashreplace for |
| 46 | # stderr, but in the context of an Android log, readability is more |
| 47 | # important than reversibility. |
| 48 | kwargs.setdefault("encoding", "UTF-8") |
| 49 | kwargs.setdefault("errors", "backslashreplace") |
| 50 | |
| 51 | super().__init__(BinaryLogStream(prio, tag, fileno), **kwargs) |
| 52 | self._lock = RLock() |
| 53 | self._pending_bytes = [] |
| 54 | self._pending_bytes_count = 0 |
| 55 | |
| 56 | def __repr__(self): |
| 57 | return f"<TextLogStream {self.buffer.tag!r}>" |
| 58 | |
| 59 | def write(self, s): |
| 60 | if not isinstance(s, str): |
| 61 | raise TypeError( |
| 62 | f"write() argument must be str, not {type(s).__name__}") |
| 63 | |
| 64 | # In case `s` is a str subclass that writes itself to stdout or stderr |
| 65 | # when we call its methods, convert it to an actual str. |
| 66 | s = str.__str__(s) |
| 67 | |
| 68 | # We want to emit one log message per line wherever possible, so split |
| 69 | # the string into lines first. Note that "".splitlines() == [], so |
| 70 | # nothing will be logged for an empty string. |
| 71 | with self._lock: |
| 72 | for line in s.splitlines(keepends=True): |
| 73 | while line: |
| 74 | chunk = line[:MAX_CHARS_PER_WRITE] |
| 75 | line = line[MAX_CHARS_PER_WRITE:] |
| 76 | self._write_chunk(chunk) |
| 77 | |
| 78 | return len(s) |
| 79 | |
| 80 | # The size and behavior of TextIOWrapper's buffer is not part of its public |
| 81 | # API, so we handle buffering ourselves to avoid truncation. |
| 82 | def _write_chunk(self, s): |
| 83 | b = s.encode(self.encoding, self.errors) |
| 84 | if self._pending_bytes_count + len(b) > MAX_BYTES_PER_WRITE: |
| 85 | self.flush() |
| 86 | |
| 87 | self._pending_bytes.append(b) |
| 88 | self._pending_bytes_count += len(b) |
| 89 | if ( |
| 90 | self.write_through |
| 91 | or b.endswith(b"\n") |
| 92 | or self._pending_bytes_count > MAX_BYTES_PER_WRITE |
| 93 | ): |
no outgoing calls
searching dependent graphs…