From f00915d6b42ef6360696244eb38ceed7057cc355 Mon Sep 17 00:00:00 2001 From: Polina Date: Thu, 2 Jul 2026 21:02:49 +0000 Subject: [PATCH] =?UTF-8?q?=D0=97=D0=B0=D0=B3=D1=80=D1=83=D0=B7=D0=B8?= =?UTF-8?q?=D1=82=D1=8C=20=D1=84=D0=B0=D0=B9=D0=BB=D1=8B=20=D0=B2=20=C2=AB?= =?UTF-8?q?venv/Lib/site-packages/websockets=C2=BB?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- venv/Lib/site-packages/websockets/py.typed | Bin 0 -> 1024 bytes venv/Lib/site-packages/websockets/server.py | 589 ++++++++++++++++++ venv/Lib/site-packages/websockets/speedups.c | 229 +++++++ .../websockets/speedups.cp314-win_amd64.pyd | Bin 0 -> 11776 bytes .../Lib/site-packages/websockets/speedups.pyi | 3 + 5 files changed, 821 insertions(+) create mode 100644 venv/Lib/site-packages/websockets/py.typed create mode 100644 venv/Lib/site-packages/websockets/server.py create mode 100644 venv/Lib/site-packages/websockets/speedups.c create mode 100644 venv/Lib/site-packages/websockets/speedups.cp314-win_amd64.pyd create mode 100644 venv/Lib/site-packages/websockets/speedups.pyi diff --git a/venv/Lib/site-packages/websockets/py.typed b/venv/Lib/site-packages/websockets/py.typed new file mode 100644 index 0000000000000000000000000000000000000000..06d7405020018ddf3cacee90fd4af10487da3d20 GIT binary patch literal 1024 ScmZQz7zLvtFd70QH3R?z00031 literal 0 HcmV?d00001 diff --git a/venv/Lib/site-packages/websockets/server.py b/venv/Lib/site-packages/websockets/server.py new file mode 100644 index 0000000..c4ff6cf --- /dev/null +++ b/venv/Lib/site-packages/websockets/server.py @@ -0,0 +1,589 @@ +from __future__ import annotations + +import base64 +import binascii +import email.utils +import http +import re +import warnings +from collections.abc import Generator, Sequence +from typing import Any, Callable, cast + +from .datastructures import Headers, MultipleValuesError +from .exceptions import ( + InvalidHandshake, + InvalidHeader, + InvalidHeaderValue, + InvalidMessage, + InvalidOrigin, + InvalidUpgrade, + NegotiationError, +) +from .extensions import Extension, ServerExtensionFactory +from .headers import ( + build_extension, + parse_connection, + parse_extension, + parse_subprotocol, + parse_upgrade, +) +from .http11 import Request, Response +from .imports import lazy_import +from .protocol import CONNECTING, OPEN, SERVER, Protocol, State +from .typing import ( + ConnectionOption, + ExtensionHeader, + LoggerLike, + Origin, + StatusLike, + Subprotocol, + UpgradeProtocol, +) +from .utils import accept_key + + +__all__ = ["ServerProtocol"] + + +class ServerProtocol(Protocol): + """ + Sans-I/O implementation of a WebSocket server connection. + + Args: + origins: Acceptable values of the ``Origin`` header. Values can be + :class:`str` to test for an exact match or regular expressions + compiled by :func:`re.compile` to test against a pattern. Include + :obj:`None` in the list if the lack of an origin is acceptable. + This is useful for defending against Cross-Site WebSocket + Hijacking attacks. + extensions: List of supported extensions, in order in which they + should be tried. + subprotocols: List of supported subprotocols, in order of decreasing + preference. + select_subprotocol: Callback for selecting a subprotocol among + those supported by the client and the server. It has the same + signature as the :meth:`select_subprotocol` method, including a + :class:`ServerProtocol` instance as first argument. + state: Initial state of the WebSocket connection. + max_size: Maximum size of incoming messages in bytes. + :obj:`None` disables the limit. You may pass a ``(max_message_size, + max_fragment_size)`` tuple to set different limits for messages and + fragments when you expect long messages sent in short fragments. + logger: Logger for this connection; + defaults to ``logging.getLogger("websockets.server")``; + see the :doc:`logging guide <../../topics/logging>` for details. + + """ + + def __init__( + self, + *, + origins: Sequence[Origin | re.Pattern[str] | None] | None = None, + extensions: Sequence[ServerExtensionFactory] | None = None, + subprotocols: Sequence[Subprotocol] | None = None, + select_subprotocol: ( + Callable[ + [ServerProtocol, Sequence[Subprotocol]], + Subprotocol | None, + ] + | None + ) = None, + state: State = CONNECTING, + max_size: int | None | tuple[int | None, int | None] = 2**20, + logger: LoggerLike | None = None, + ) -> None: + super().__init__( + side=SERVER, + state=state, + max_size=max_size, + logger=logger, + ) + self.origins = origins + self.available_extensions = extensions + self.available_subprotocols = subprotocols + if select_subprotocol is not None: + # Bind select_subprotocol then shadow self.select_subprotocol. + # Use setattr to work around https://github.com/python/mypy/issues/2427. + setattr( + self, + "select_subprotocol", + select_subprotocol.__get__(self, self.__class__), + ) + + def accept(self, request: Request) -> Response: + """ + Create a handshake response to accept the connection. + + If the handshake request is valid and the handshake successful, + :meth:`accept` returns an HTTP response with status code 101. + + Else, it returns an HTTP response with another status code. This rejects + the connection, like :meth:`reject` would. + + You must send the handshake response with :meth:`send_response`. + + You may modify the response before sending it, typically by adding HTTP + headers. + + Args: + request: WebSocket handshake request received from the client. + + Returns: + WebSocket handshake response or HTTP response to send to the client. + + """ + try: + ( + accept_header, + extensions_header, + protocol_header, + ) = self.process_request(request) + except InvalidOrigin as exc: + request._exception = exc + self.handshake_exc = exc + if self.debug: + self.logger.debug("! invalid origin", exc_info=True) + return self.reject( + http.HTTPStatus.FORBIDDEN, + f"Failed to open a WebSocket connection: {exc}.\n", + ) + except InvalidUpgrade as exc: + request._exception = exc + self.handshake_exc = exc + if self.debug: + self.logger.debug("! invalid upgrade", exc_info=True) + response = self.reject( + http.HTTPStatus.UPGRADE_REQUIRED, + ( + f"Failed to open a WebSocket connection: {exc}.\n" + f"\n" + f"You cannot access a WebSocket server directly " + f"with a browser. You need a WebSocket client.\n" + ), + ) + response.headers["Upgrade"] = "websocket" + return response + except InvalidHandshake as exc: + request._exception = exc + self.handshake_exc = exc + if self.debug: + self.logger.debug("! invalid handshake", exc_info=True) + exc_chain = cast(BaseException, exc) + exc_str = f"{exc_chain}" + while exc_chain.__cause__ is not None: + exc_chain = exc_chain.__cause__ + exc_str += f"; {exc_chain}" + return self.reject( + http.HTTPStatus.BAD_REQUEST, + f"Failed to open a WebSocket connection: {exc_str}.\n", + ) + except Exception as exc: + # Handle exceptions raised by user-provided select_subprotocol and + # unexpected errors. + request._exception = exc + self.handshake_exc = exc + self.logger.error("opening handshake failed", exc_info=True) + return self.reject( + http.HTTPStatus.INTERNAL_SERVER_ERROR, + ( + "Failed to open a WebSocket connection.\n" + "See server log for more information.\n" + ), + ) + + headers = Headers() + headers["Date"] = email.utils.formatdate(usegmt=True) + headers["Upgrade"] = "websocket" + headers["Connection"] = "Upgrade" + headers["Sec-WebSocket-Accept"] = accept_header + if extensions_header is not None: + headers["Sec-WebSocket-Extensions"] = extensions_header + if protocol_header is not None: + headers["Sec-WebSocket-Protocol"] = protocol_header + return Response(101, "Switching Protocols", headers) + + def process_request( + self, + request: Request, + ) -> tuple[str, str | None, str | None]: + """ + Check a handshake request and negotiate extensions and subprotocol. + + This function doesn't verify that the request is an HTTP/1.1 or higher + GET request and doesn't check the ``Host`` header. These controls are + usually performed earlier in the HTTP request handling code. They're + the responsibility of the caller. + + Args: + request: WebSocket handshake request received from the client. + + Returns: + ``Sec-WebSocket-Accept``, ``Sec-WebSocket-Extensions``, and + ``Sec-WebSocket-Protocol`` headers for the handshake response. + + Raises: + InvalidHandshake: If the handshake request is invalid; + then the server must return 400 Bad Request error. + + """ + headers = request.headers + + connection: list[ConnectionOption] = sum( + [parse_connection(value) for value in headers.get_all("Connection")], [] + ) + if not any(value.lower() == "upgrade" for value in connection): + raise InvalidUpgrade( + "Connection", ", ".join(connection) if connection else None + ) + + upgrade: list[UpgradeProtocol] = sum( + [parse_upgrade(value) for value in headers.get_all("Upgrade")], [] + ) + # For compatibility with non-strict implementations, ignore case when + # checking the Upgrade header. The RFC always uses "websocket", except + # in section 11.2. (IANA registration) where it uses "WebSocket". + if not (len(upgrade) == 1 and upgrade[0].lower() == "websocket"): + raise InvalidUpgrade("Upgrade", ", ".join(upgrade) if upgrade else None) + + try: + key = headers["Sec-WebSocket-Key"] + except KeyError: + raise InvalidHeader("Sec-WebSocket-Key") from None + except MultipleValuesError: + raise InvalidHeader("Sec-WebSocket-Key", "multiple values") from None + try: + raw_key = base64.b64decode(key.encode(), validate=True) + except binascii.Error as exc: + raise InvalidHeaderValue("Sec-WebSocket-Key", key) from exc + if len(raw_key) != 16: + raise InvalidHeaderValue("Sec-WebSocket-Key", key) + accept_header = accept_key(key) + + try: + version = headers["Sec-WebSocket-Version"] + except KeyError: + raise InvalidHeader("Sec-WebSocket-Version") from None + except MultipleValuesError: + raise InvalidHeader("Sec-WebSocket-Version", "multiple values") from None + if version != "13": + raise InvalidHeaderValue("Sec-WebSocket-Version", version) + + self.origin = self.process_origin(headers) + extensions_header, self.extensions = self.process_extensions(headers) + protocol_header = self.subprotocol = self.process_subprotocol(headers) + + return (accept_header, extensions_header, protocol_header) + + def process_origin(self, headers: Headers) -> Origin | None: + """ + Handle the Origin HTTP request header. + + Args: + headers: WebSocket handshake request headers. + + Returns: + origin, if it is acceptable. + + Raises: + InvalidHandshake: If the Origin header is invalid. + InvalidOrigin: If the origin isn't acceptable. + + """ + # "The user agent MUST NOT include more than one Origin header field" + # per https://datatracker.ietf.org/doc/html/rfc6454#section-7.3. + try: + origin = headers.get("Origin") + except MultipleValuesError: + raise InvalidHeader("Origin", "multiple values") from None + if origin is not None: + origin = cast(Origin, origin) + if self.origins is not None: + for origin_or_regex in self.origins: + if origin_or_regex == origin or ( + isinstance(origin_or_regex, re.Pattern) + and origin is not None + and origin_or_regex.fullmatch(origin) is not None + ): + break + else: + raise InvalidOrigin(origin) + return origin + + def process_extensions( + self, + headers: Headers, + ) -> tuple[str | None, list[Extension]]: + """ + Handle the Sec-WebSocket-Extensions HTTP request header. + + Accept or reject each extension proposed in the client request. + Negotiate parameters for accepted extensions. + + Per :rfc:`6455`, negotiation rules are defined by the specification of + each extension. + + To provide this level of flexibility, for each extension proposed by + the client, we check for a match with each extension available in the + server configuration. If no match is found, the extension is ignored. + + If several variants of the same extension are proposed by the client, + it may be accepted several times, which won't make sense in general. + Extensions must implement their own requirements. For this purpose, + the list of previously accepted extensions is provided. + + This process doesn't allow the server to reorder extensions. It can + only select a subset of the extensions proposed by the client. + + Other requirements, for example related to mandatory extensions or the + order of extensions, may be implemented by overriding this method. + + Args: + headers: WebSocket handshake request headers. + + Returns: + ``Sec-WebSocket-Extensions`` HTTP response header and list of + accepted extensions. + + Raises: + InvalidHandshake: If the Sec-WebSocket-Extensions header is invalid. + + """ + response_header_value: str | None = None + + extension_headers: list[ExtensionHeader] = [] + accepted_extensions: list[Extension] = [] + + header_values = headers.get_all("Sec-WebSocket-Extensions") + + if header_values and self.available_extensions: + parsed_header_values: list[ExtensionHeader] = sum( + [parse_extension(header_value) for header_value in header_values], [] + ) + + for name, request_params in parsed_header_values: + for ext_factory in self.available_extensions: + # Skip non-matching extensions based on their name. + if ext_factory.name != name: + continue + + # Skip non-matching extensions based on their params. + try: + response_params, extension = ext_factory.process_request_params( + request_params, accepted_extensions + ) + except NegotiationError: + continue + + # Add matching extension to the final list. + extension_headers.append((name, response_params)) + accepted_extensions.append(extension) + + # Break out of the loop once we have a match. + break + + # If we didn't break from the loop, no extension in our list + # matched what the client sent. The extension is declined. + + # Serialize extension header. + if extension_headers: + response_header_value = build_extension(extension_headers) + + return response_header_value, accepted_extensions + + def process_subprotocol(self, headers: Headers) -> Subprotocol | None: + """ + Handle the Sec-WebSocket-Protocol HTTP request header. + + Args: + headers: WebSocket handshake request headers. + + Returns: + Subprotocol, if one was selected; this is also the value of the + ``Sec-WebSocket-Protocol`` response header. + + Raises: + InvalidHandshake: If the Sec-WebSocket-Subprotocol header is invalid. + + """ + subprotocols: Sequence[Subprotocol] = sum( + [ + parse_subprotocol(header_value) + for header_value in headers.get_all("Sec-WebSocket-Protocol") + ], + [], + ) + return self.select_subprotocol(subprotocols) + + def select_subprotocol( + self, + subprotocols: Sequence[Subprotocol], + ) -> Subprotocol | None: + """ + Pick a subprotocol among those offered by the client. + + If several subprotocols are supported by both the client and the server, + pick the first one in the list declared the server. + + If the server doesn't support any subprotocols, continue without a + subprotocol, regardless of what the client offers. + + If the server supports at least one subprotocol and the client doesn't + offer any, abort the handshake with an HTTP 400 error. + + You provide a ``select_subprotocol`` argument to :class:`ServerProtocol` + to override this logic. For example, you could accept the connection + even if client doesn't offer a subprotocol, rather than reject it. + + Here's how to negotiate the ``chat`` subprotocol if the client supports + it and continue without a subprotocol otherwise:: + + def select_subprotocol(protocol, subprotocols): + if "chat" in subprotocols: + return "chat" + + Args: + subprotocols: List of subprotocols offered by the client. + + Returns: + Selected subprotocol, if a common subprotocol was found. + + :obj:`None` to continue without a subprotocol. + + Raises: + NegotiationError: Custom implementations may raise this exception + to abort the handshake with an HTTP 400 error. + + """ + # Server doesn't offer any subprotocols. + if not self.available_subprotocols: # None or empty list + return None + + # Server offers at least one subprotocol but client doesn't offer any. + if not subprotocols: + raise NegotiationError("missing subprotocol") + + # Server and client both offer subprotocols. Look for a shared one. + proposed_subprotocols = set(subprotocols) + for subprotocol in self.available_subprotocols: + if subprotocol in proposed_subprotocols: + return subprotocol + + # No common subprotocol was found. + raise NegotiationError( + "invalid subprotocol; expected one of " + + ", ".join(self.available_subprotocols) + ) + + def reject(self, status: StatusLike, text: str) -> Response: + """ + Create a handshake response to reject the connection. + + A short plain text response is the best fallback when failing to + establish a WebSocket connection. + + You must send the handshake response with :meth:`send_response`. + + You may modify the response before sending it, for example by changing + HTTP headers. + + Args: + status: HTTP status code. + text: HTTP response body; it will be encoded to UTF-8. + + Returns: + HTTP response to send to the client. + + """ + # If status is an int instead of an HTTPStatus, fix it automatically. + status = http.HTTPStatus(status) + body = text.encode() + headers = Headers( + [ + ("Date", email.utils.formatdate(usegmt=True)), + ("Connection", "close"), + ("Content-Length", str(len(body))), + ("Content-Type", "text/plain; charset=utf-8"), + ] + ) + return Response(status.value, status.phrase, headers, body) + + def send_response(self, response: Response) -> None: + """ + Send a handshake response to the client. + + Args: + response: WebSocket handshake response event to send. + + """ + if self.debug: + code, phrase = response.status_code, response.reason_phrase + self.logger.debug("> HTTP/1.1 %d %s", code, phrase) + for key, value in response.headers.raw_items(): + self.logger.debug("> %s: %s", key, value) + if response.body: + self.logger.debug("> [body] (%d bytes)", len(response.body)) + + self.writes.append(response.serialize()) + + if response.status_code == 101: + assert self.state is CONNECTING + self.state = OPEN + self.logger.info("connection open") + + else: + self.logger.info( + "connection rejected (%d %s)", + response.status_code, + response.reason_phrase, + ) + + self.send_eof() + self.parser = self.discard() + next(self.parser) # start coroutine + + def parse(self) -> Generator[None]: + if self.state is CONNECTING: + try: + request = yield from Request.parse( + self.reader.read_line, + ) + except Exception as exc: + self.handshake_exc = InvalidMessage( + "did not receive a valid HTTP request" + ) + self.handshake_exc.__cause__ = exc + self.send_eof() + self.parser = self.discard() + next(self.parser) # start coroutine + yield + + if self.debug: + self.logger.debug("< GET %s HTTP/1.1", request.path) + for key, value in request.headers.raw_items(): + self.logger.debug("< %s: %s", key, value) + + self.events.append(request) + + yield from super().parse() + + +class ServerConnection(ServerProtocol): + def __init__(self, *args: Any, **kwargs: Any) -> None: + warnings.warn( # deprecated in 11.0 - 2023-04-02 + "ServerConnection was renamed to ServerProtocol", + DeprecationWarning, + ) + super().__init__(*args, **kwargs) + + +lazy_import( + globals(), + deprecated_aliases={ + # deprecated in 14.0 - 2024-11-09 + "WebSocketServer": ".legacy.server", + "WebSocketServerProtocol": ".legacy.server", + "broadcast": ".legacy.server", + "serve": ".legacy.server", + "unix_serve": ".legacy.server", + }, +) diff --git a/venv/Lib/site-packages/websockets/speedups.c b/venv/Lib/site-packages/websockets/speedups.c new file mode 100644 index 0000000..5c0235a --- /dev/null +++ b/venv/Lib/site-packages/websockets/speedups.c @@ -0,0 +1,229 @@ +/* C implementation of performance sensitive functions. */ + +#define PY_SSIZE_T_CLEAN +#include +#include /* uint8_t, uint32_t, uint64_t */ + +#if __ARM_NEON +#include +#elif __SSE2__ +#include +#endif + +static const Py_ssize_t MASK_LEN = 4; + +/* Similar to PyBytes_AsStringAndSize, but accepts more types */ + +static int +_PyBytesLike_AsStringAndSize(PyObject *obj, PyObject **tmp, char **buffer, Py_ssize_t *length) +{ + // This supports bytes, bytearrays, and memoryview objects, + // which are common data structures for handling byte streams. + // If *tmp isn't NULL, the caller gets a new reference. + if (PyBytes_Check(obj)) + { + *tmp = NULL; + *buffer = PyBytes_AS_STRING(obj); + *length = PyBytes_GET_SIZE(obj); + } + else if (PyByteArray_Check(obj)) + { + *tmp = NULL; + *buffer = PyByteArray_AS_STRING(obj); + *length = PyByteArray_GET_SIZE(obj); + } + else if (PyMemoryView_Check(obj)) + { + *tmp = PyMemoryView_GetContiguous(obj, PyBUF_READ, 'C'); + if (*tmp == NULL) + { + return -1; + } + Py_buffer *mv_buf; + mv_buf = PyMemoryView_GET_BUFFER(*tmp); + *buffer = mv_buf->buf; + *length = mv_buf->len; + } + else + { + PyErr_Format( + PyExc_TypeError, + "expected a bytes-like object, %.200s found", + Py_TYPE(obj)->tp_name); + return -1; + } + + return 0; +} + +/* C implementation of websockets.utils.apply_mask */ + +static PyObject * +apply_mask(PyObject *self, PyObject *args, PyObject *kwds) +{ + + // In order to support various bytes-like types, accept any Python object. + + static char *kwlist[] = {"data", "mask", NULL}; + PyObject *input_obj; + PyObject *mask_obj; + + // A pointer to a char * + length will be extracted from the data and mask + // arguments, possibly via a Py_buffer. + + PyObject *input_tmp = NULL; + char *input; + Py_ssize_t input_len; + PyObject *mask_tmp = NULL; + char *mask; + Py_ssize_t mask_len; + + // Initialize a PyBytesObject then get a pointer to the underlying char * + // in order to avoid an extra memory copy in PyBytes_FromStringAndSize. + + PyObject *result = NULL; + char *output; + + // Other variables. + + Py_ssize_t i = 0; + + // Parse inputs. + + if (!PyArg_ParseTupleAndKeywords( + args, kwds, "OO", kwlist, &input_obj, &mask_obj)) + { + goto exit; + } + + if (_PyBytesLike_AsStringAndSize(input_obj, &input_tmp, &input, &input_len) == -1) + { + goto exit; + } + + if (_PyBytesLike_AsStringAndSize(mask_obj, &mask_tmp, &mask, &mask_len) == -1) + { + goto exit; + } + + if (mask_len != MASK_LEN) + { + PyErr_SetString(PyExc_ValueError, "mask must contain 4 bytes"); + goto exit; + } + + // Create output. + + result = PyBytes_FromStringAndSize(NULL, input_len); + if (result == NULL) + { + goto exit; + } + + // Since we just created result, we don't need error checks. + output = PyBytes_AS_STRING(result); + + // Perform the masking operation. + + // Apparently GCC cannot figure out the following optimizations by itself. + + // We need a new scope for MSVC 2010 (non C99 friendly) + { +#if __ARM_NEON + + // With NEON support, XOR by blocks of 16 bytes = 128 bits. + + Py_ssize_t input_len_128 = input_len & ~15; + uint8x16_t mask_128 = vreinterpretq_u8_u32(vdupq_n_u32(*(uint32_t *)mask)); + + for (; i < input_len_128; i += 16) + { + uint8x16_t in_128 = vld1q_u8((uint8_t *)(input + i)); + uint8x16_t out_128 = veorq_u8(in_128, mask_128); + vst1q_u8((uint8_t *)(output + i), out_128); + } + +#elif __SSE2__ + + // With SSE2 support, XOR by blocks of 16 bytes = 128 bits. + + // Since we cannot control the 16-bytes alignment of input and output + // buffers, we rely on loadu/storeu rather than load/store. + + Py_ssize_t input_len_128 = input_len & ~15; + __m128i mask_128 = _mm_set1_epi32(*(uint32_t *)mask); + + for (; i < input_len_128; i += 16) + { + __m128i in_128 = _mm_loadu_si128((__m128i *)(input + i)); + __m128i out_128 = _mm_xor_si128(in_128, mask_128); + _mm_storeu_si128((__m128i *)(output + i), out_128); + } + +#else + + // Without SSE2 support, XOR by blocks of 8 bytes = 64 bits. + + // We assume the memory allocator aligns everything on 8 bytes boundaries. + + Py_ssize_t input_len_64 = input_len & ~7; + uint32_t mask_32 = *(uint32_t *)mask; + uint64_t mask_64 = ((uint64_t)mask_32 << 32) | (uint64_t)mask_32; + + for (; i < input_len_64; i += 8) + { + *(uint64_t *)(output + i) = *(uint64_t *)(input + i) ^ mask_64; + } + +#endif + } + + // XOR the remainder of the input byte by byte. + + for (; i < input_len; i++) + { + output[i] = input[i] ^ mask[i & (MASK_LEN - 1)]; + } + +exit: + Py_XDECREF(input_tmp); + Py_XDECREF(mask_tmp); + return result; + +} + +static PyMethodDef speedups_methods[] = { + { + "apply_mask", + (PyCFunction)apply_mask, + METH_VARARGS | METH_KEYWORDS, + "Apply masking to the data of a WebSocket message.", + }, + {NULL, NULL, 0, NULL}, /* Sentinel */ +}; + +static struct PyModuleDef speedups_module = { + PyModuleDef_HEAD_INIT, + "websocket.speedups", /* m_name */ + "C implementation of performance sensitive functions.", + /* m_doc */ + -1, /* m_size */ + speedups_methods, /* m_methods */ + NULL, + NULL, + NULL, + NULL +}; + +PyMODINIT_FUNC +PyInit_speedups(void) +{ + PyObject *m = PyModule_Create(&speedups_module); + if (m == NULL) { + return NULL; + } +#ifdef Py_GIL_DISABLED + PyUnstable_Module_SetGIL(m, Py_MOD_GIL_NOT_USED); +#endif + return m; +} diff --git a/venv/Lib/site-packages/websockets/speedups.cp314-win_amd64.pyd b/venv/Lib/site-packages/websockets/speedups.cp314-win_amd64.pyd new file mode 100644 index 0000000000000000000000000000000000000000..ddfc3349adc54a7082ddb5f2e6e3c6a3f4527273 GIT binary patch literal 11776 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 bytes: ...