File: base.py

package info (click to toggle)
python-falcon 4.0.2-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 5,172 kB
  • sloc: python: 33,608; javascript: 92; sh: 50; makefile: 50
file content (247 lines) | stat: -rw-r--r-- 9,320 bytes parent folder | download
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
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
from __future__ import annotations

import abc
import io
from typing import Optional, Union

from falcon._typing import DeserializeSync
from falcon._typing import SerializeSync
from falcon.constants import MEDIA_JSON
from falcon.typing import AsyncReadableIO
from falcon.typing import ReadableIO


class BaseHandler(metaclass=abc.ABCMeta):
    """Abstract Base Class for an internet media type handler."""

    # NOTE(kgriffs): The following special methods are used to enable an
    #   optimized media (de)serialization protocol for ASGI. This is not
    #   currently part of the public interface for Falcon, and is not
    #   included in the docs. Once we are happy with the protocol, we
    #   might make it part of the public interface for use by custom
    #   media type handlers.

    _serialize_sync: Optional[SerializeSync] = None
    """Override to provide a synchronous serialization method that takes an object."""

    _deserialize_sync: Optional[DeserializeSync] = None
    """Override to provide a synchronous deserialization method that
    takes a byte string."""

    def serialize(self, media: object, content_type: str) -> bytes:
        """Serialize the media object on a :any:`falcon.Response`.

        By default, this method raises an instance of
        :class:`NotImplementedError`. Therefore, it must be
        overridden in order to work with WSGI apps. Child classes
        can ignore this method if they are only to be used
        with ASGI apps, as long as they override
        :meth:`~.BaseHandler.serialize_async`.

        Note:

            The JSON media handler is an exception in requiring the implementation of
            the sync version also for ASGI apps. See the
            :ref:`this section<note_json_handler>` for more details.

        Args:
            media (object): A serializable object.
            content_type (str): Type of response content.

        Returns:
            bytes: The resulting serialized bytes from the input object.
        """
        if MEDIA_JSON in content_type:
            raise NotImplementedError(
                'The JSON media handler requires the sync interface to be '
                "implemented even in ASGI applications, because it's used "
                'internally by the Falcon framework.'
            )
        else:
            raise NotImplementedError()

    async def serialize_async(self, media: object, content_type: str) -> bytes:
        """Serialize the media object on a :any:`falcon.Response`.

        This method is similar to :meth:`~.BaseHandler.serialize`
        except that it is asynchronous. The default implementation simply calls
        :meth:`~.BaseHandler.serialize`. If the media object may be
        awaitable, or is otherwise something that should be read
        asynchronously, subclasses must override the default implementation
        in order to handle that case.

        Note:
            By default, the :meth:`~.BaseHandler.serialize`
            method raises an instance of :class:`NotImplementedError`.
            Therefore, child classes must either override
            :meth:`~.BaseHandler.serialize` or
            :meth:`~.BaseHandler.serialize_async` in order to be
            compatible with ASGI apps.

        Args:
            media (object): A serializable object.
            content_type (str): Type of response content.

        Returns:
            bytes: The resulting serialized bytes from the input object.
        """
        return self.serialize(media, content_type)

    def deserialize(
        self,
        stream: ReadableIO,
        content_type: Optional[str],
        content_length: Optional[int],
    ) -> object:
        """Deserialize the :any:`falcon.Request` body.

        By default, this method raises an instance of
        :class:`NotImplementedError`. Therefore, it must be
        overridden in order to work with WSGI apps. Child classes
        can ignore this method if they are only to be used
        with ASGI apps, as long as they override
        :meth:`~.BaseHandler.deserialize_async`.

        Note:

            The JSON media handler is an exception in requiring the implementation of
            the sync version also for ASGI apps. See the
            :ref:`this section<note_json_handler>` for more details.

        Args:
            stream (object): Readable file-like object to deserialize.
            content_type (str): Type of request content.
            content_length (int): Length of request content.

        Returns:
            object: A deserialized object.
        """
        if content_type and MEDIA_JSON in content_type:
            raise NotImplementedError(
                'The JSON media handler requires the sync interface to be '
                "implemented even in ASGI applications, because it's used "
                'internally by the Falcon framework.'
            )
        else:
            raise NotImplementedError()

    async def deserialize_async(
        self,
        stream: AsyncReadableIO,
        content_type: Optional[str],
        content_length: Optional[int],
    ) -> object:
        """Deserialize the :any:`falcon.Request` body.

        This method is similar to :meth:`~.BaseHandler.deserialize` except
        that it is asynchronous. The default implementation adapts the
        synchronous :meth:`~.BaseHandler.deserialize` method
        via :class:`io.BytesIO`. For improved performance, media handlers should
        override this method.

        Note:
            By default, the :meth:`~.BaseHandler.deserialize`
            method raises an instance of :class:`NotImplementedError`.
            Therefore, child classes must either override
            :meth:`~.BaseHandler.deserialize` or
            :meth:`~.BaseHandler.deserialize_async` in order to be
            compatible with ASGI apps.

        Args:
            stream (object): Asynchronous file-like object to deserialize.
            content_type (str): Type of request content.
            content_length (int): Length of request content, or ``None`` if the
                Content-Length header is missing.

        Returns:
            object: A deserialized object.
        """
        data = await stream.read()

        # NOTE(kgriffs): Override content length to make sure it is correct,
        #   since we know what it is in this case.
        content_length = len(data)

        return self.deserialize(io.BytesIO(data), content_type, content_length)

    exhaust_stream = False
    """Whether to exhaust the input stream upon finishing deserialization.

    Exhausting the stream may be useful for handlers that do not necessarily
    consume the whole stream, but the deserialized media object is complete and
    does not involve further streaming.
    """


class TextBaseHandlerWS(metaclass=abc.ABCMeta):
    """Abstract Base Class for a WebSocket TEXT media handler."""

    def serialize(self, media: object) -> str:
        """Serialize the media object to a Unicode string.

        By default, this method raises an instance of
        :class:`NotImplementedError`. Therefore, it must be
        overridden if the child class wishes to support
        serialization to TEXT (0x01) message payloads.

        Args:
            media (object): A serializable object.

        Returns:
            str: The resulting serialized string from the input object.
        """
        raise NotImplementedError()

    def deserialize(self, payload: str) -> object:
        """Deserialize TEXT payloads from a Unicode string.

        By default, this method raises an instance of
        :class:`NotImplementedError`. Therefore, it must be
        overridden if the child class wishes to support
        deserialization from TEXT (0x01) message payloads.

        Args:
            payload (str): Message payload to deserialize.

        Returns:
            object: A deserialized object.
        """
        raise NotImplementedError()


class BinaryBaseHandlerWS(metaclass=abc.ABCMeta):
    """Abstract Base Class for a WebSocket BINARY media handler."""

    def serialize(self, media: object) -> Union[bytes, bytearray, memoryview]:
        """Serialize the media object to a byte string.

        By default, this method raises an instance of
        :class:`NotImplementedError`. Therefore, it must be
        overridden if the child class wishes to support
        serialization to BINARY (0x02) message payloads.

        Args:
            media (object): A serializable object.

        Returns:
            bytes: The resulting serialized byte string from the input
            object. May be an instance of :class:`bytes`,
            :class:`bytearray`, or :class:`memoryview`.
        """
        raise NotImplementedError()

    def deserialize(self, payload: bytes) -> object:
        """Deserialize BINARY payloads from a byte string.

        By default, this method raises an instance of
        :class:`NotImplementedError`. Therefore, it must be
        overridden if the child class wishes to support
        deserialization from BINARY (0x02) message payloads.

        Args:
            payload (bytes): Message payload to deserialize.

        Returns:
            object: A deserialized object.
        """
        raise NotImplementedError()