aboutsummaryrefslogtreecommitdiffstats
path: root/contrib/python/Twisted/py3/twisted/logger/_io.py
blob: 966342122018d47e80bbaf5398de1bb1a30423a0 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
# -*- test-case-name: twisted.logger.test.test_io -*-
# Copyright (c) Twisted Matrix Laboratories.
# See LICENSE for details.

"""
File-like object that logs.
"""

import sys
from typing import AnyStr, Iterable, Optional

from constantly import NamedConstant
from incremental import Version

from twisted.python.deprecate import deprecatedProperty
from ._levels import LogLevel
from ._logger import Logger


class LoggingFile:
    """
    File-like object that turns C{write()} calls into logging events.

    Note that because event formats are L{str}, C{bytes} received via C{write()}
    are converted to C{str}, which is the opposite of what C{file} does.

    @ivar softspace: Attribute to make this class more file-like under Python 2;
        value is zero or one.  Do not use.
    """

    _softspace = 0

    @deprecatedProperty(Version("Twisted", 21, 2, 0))
    def softspace(self):
        return self._softspace

    @softspace.setter  # type: ignore[no-redef]
    def softspace(self, value):
        self._softspace = value

    def __init__(
        self,
        logger: Logger,
        level: NamedConstant = LogLevel.info,
        encoding: Optional[str] = None,
    ) -> None:
        """
        @param logger: the logger to log through.
        @param level: the log level to emit events with.
        @param encoding: The encoding to expect when receiving bytes via
            C{write()}.  If L{None}, use C{sys.getdefaultencoding()}.
        """
        self.level = level
        self.log = logger

        if encoding is None:
            self._encoding = sys.getdefaultencoding()
        else:
            self._encoding = encoding

        self._buffer = ""
        self._closed = False

    @property
    def closed(self) -> bool:
        """
        Read-only property.  Is the file closed?

        @return: true if closed, otherwise false.
        """
        return self._closed

    @property
    def encoding(self) -> str:
        """
        Read-only property.   File encoding.

        @return: an encoding.
        """
        return self._encoding

    @property
    def mode(self) -> str:
        """
        Read-only property.  File mode.

        @return: "w"
        """
        return "w"

    @property
    def newlines(self) -> None:
        """
        Read-only property.  Types of newlines encountered.

        @return: L{None}
        """
        return None

    @property
    def name(self) -> str:
        """
        The name of this file; a repr-style string giving information about its
        namespace.

        @return: A file name.
        """
        return "<{} {}#{}>".format(
            self.__class__.__name__,
            self.log.namespace,
            self.level.name,
        )

    def close(self) -> None:
        """
        Close this file so it can no longer be written to.
        """
        self._closed = True

    def flush(self) -> None:
        """
        No-op; this file does not buffer.
        """
        pass

    def fileno(self) -> int:
        """
        Returns an invalid file descriptor, since this is not backed by an FD.

        @return: C{-1}
        """
        return -1

    def isatty(self) -> bool:
        """
        A L{LoggingFile} is not a TTY.

        @return: C{False}
        """
        return False

    def write(self, message: AnyStr) -> None:
        """
        Log the given message.

        @param message: The message to write.
        """
        if self._closed:
            raise ValueError("I/O operation on closed file")

        if isinstance(message, bytes):
            text = message.decode(self._encoding)
        else:
            text = message

        lines = (self._buffer + text).split("\n")
        self._buffer = lines[-1]
        lines = lines[0:-1]

        for line in lines:
            self.log.emit(self.level, format="{log_io}", log_io=line)

    def writelines(self, lines: Iterable[AnyStr]) -> None:
        """
        Log each of the given lines as a separate message.

        @param lines: Data to write.
        """
        for line in lines:
            self.write(line)

    def _unsupported(self, *args: object) -> None:
        """
        Template for unsupported operations.

        @param args: Arguments.
        """
        raise OSError("unsupported operation")

    read = _unsupported
    next = _unsupported
    readline = _unsupported
    readlines = _unsupported
    xreadlines = _unsupported
    seek = _unsupported
    tell = _unsupported
    truncate = _unsupported