Sanic on pypy (#2682)

Co-authored-by: L. Kärkkäinen <98187+Tronic@users.noreply.github.com>
Co-authored-by: Adam Hopkins <admhpkns@gmail.com>
Co-authored-by: Adam Hopkins <adam@amhopkins.com>
This commit is contained in:
Mohammad Almoghrabi 2023-07-05 12:14:47 +03:00 committed by GitHub
parent 9a7dafd531
commit 273825dab6
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 54 additions and 10 deletions

1
.gitignore vendored
View File

@ -21,4 +21,5 @@ dist/*
pip-wheel-metadata/ pip-wheel-metadata/
.pytest_cache/* .pytest_cache/*
.venv/* .venv/*
venv/*
.vscode/* .vscode/*

View File

@ -3,7 +3,7 @@ import sys
from os import environ from os import environ
from sanic.compat import is_atty from sanic.helpers import is_atty
BASE_LOGO = """ BASE_LOGO = """

View File

@ -4,7 +4,7 @@ from textwrap import indent, wrap
from typing import Dict, Optional from typing import Dict, Optional
from sanic import __version__ from sanic import __version__
from sanic.compat import is_atty from sanic.helpers import is_atty
from sanic.log import logger from sanic.log import logger

View File

@ -1,5 +1,6 @@
import asyncio import asyncio
import os import os
import platform
import signal import signal
import sys import sys
@ -10,6 +11,7 @@ from typing import Awaitable, Union
from multidict import CIMultiDict # type: ignore from multidict import CIMultiDict # type: ignore
from sanic.helpers import Default from sanic.helpers import Default
from sanic.log import error_logger
if sys.version_info < (3, 8): # no cov if sys.version_info < (3, 8): # no cov
@ -22,6 +24,7 @@ else: # no cov
] ]
OS_IS_WINDOWS = os.name == "nt" OS_IS_WINDOWS = os.name == "nt"
PYPY_IMPLEMENTATION = platform.python_implementation() == "PyPy"
UVLOOP_INSTALLED = False UVLOOP_INSTALLED = False
try: try:
@ -73,6 +76,38 @@ def enable_windows_color_support():
kernel.SetConsoleMode(kernel.GetStdHandle(-11), 7) kernel.SetConsoleMode(kernel.GetStdHandle(-11), 7)
def pypy_os_module_patch() -> None:
"""
The PyPy os module is missing the 'readlink' function, which causes issues
withaiofiles. This workaround replaces the missing 'readlink' function
with 'os.path.realpath', which serves the same purpose.
"""
if hasattr(os, "readlink"):
error_logger.warning(
"PyPy: Skipping patching of the os module as it appears the "
"'readlink' function has been added."
)
return
module = sys.modules["os"]
module.readlink = os.path.realpath # type: ignore
def pypy_windows_set_console_cp_patch() -> None:
"""
A patch function for PyPy on Windows that sets the console code page to
UTF-8 encodingto allow for proper handling of non-ASCII characters. This
function uses ctypes to call the Windows API functions SetConsoleCP and
SetConsoleOutputCP to set the code page.
"""
from ctypes import windll # type: ignore
code: int = windll.kernel32.GetConsoleOutputCP()
if code != 65001:
windll.kernel32.SetConsoleCP(65001)
windll.kernel32.SetConsoleOutputCP(65001)
class Header(CIMultiDict): class Header(CIMultiDict):
""" """
Container used for both request and response headers. It is a subclass of Container used for both request and response headers. It is a subclass of
@ -86,7 +121,7 @@ class Header(CIMultiDict):
<https://multidict.readthedocs.io/en/stable/multidict.html#multidict>`_ <https://multidict.readthedocs.io/en/stable/multidict.html#multidict>`_
for more details about how to use the object. In general, it should work for more details about how to use the object. In general, it should work
very similar to a regular dictionary. very similar to a regular dictionary.
""" """ # noqa: E501
def __getattr__(self, key: str) -> str: def __getattr__(self, key: str) -> str:
if key.startswith("_"): if key.startswith("_"):
@ -112,6 +147,12 @@ if use_trio: # pragma: no cover
open_async = trio.open_file open_async = trio.open_file
CancelledErrors = tuple([asyncio.CancelledError, trio.Cancelled]) CancelledErrors = tuple([asyncio.CancelledError, trio.Cancelled])
else: else:
if PYPY_IMPLEMENTATION:
pypy_os_module_patch()
if OS_IS_WINDOWS:
pypy_windows_set_console_cp_patch()
from aiofiles import open as aio_open # type: ignore from aiofiles import open as aio_open # type: ignore
from aiofiles.os import stat as stat_async # type: ignore # noqa: F401 from aiofiles.os import stat as stat_async # type: ignore # noqa: F401
@ -143,7 +184,3 @@ def ctrlc_workaround_for_windows(app):
die = False die = False
signal.signal(signal.SIGINT, ctrlc_handler) signal.signal(signal.SIGINT, ctrlc_handler)
app.add_task(stay_active) app.add_task(stay_active)
def is_atty() -> bool:
return bool(sys.stdout and sys.stdout.isatty())

View File

@ -1,5 +1,7 @@
"""Defines basics of HTTP standard.""" """Defines basics of HTTP standard."""
import sys
from importlib import import_module from importlib import import_module
from inspect import ismodule from inspect import ismodule
from typing import Dict from typing import Dict
@ -157,6 +159,10 @@ def import_string(module_name, package=None):
return obj() return obj()
def is_atty() -> bool:
return bool(sys.stdout and sys.stdout.isatty())
class Default: class Default:
""" """
It is used to replace `None` or `object()` as a sentinel It is used to replace `None` or `object()` as a sentinel

View File

@ -5,7 +5,7 @@ from enum import Enum
from typing import TYPE_CHECKING, Any, Dict from typing import TYPE_CHECKING, Any, Dict
from warnings import warn from warnings import warn
from sanic.compat import is_atty from sanic.helpers import is_atty
# Python 3.11 changed the way Enum formatting works for mixed-in types. # Python 3.11 changed the way Enum formatting works for mixed-in types.

View File

@ -41,9 +41,9 @@ from sanic.application.logo import get_logo
from sanic.application.motd import MOTD from sanic.application.motd import MOTD
from sanic.application.state import ApplicationServerInfo, Mode, ServerStage from sanic.application.state import ApplicationServerInfo, Mode, ServerStage
from sanic.base.meta import SanicMeta from sanic.base.meta import SanicMeta
from sanic.compat import OS_IS_WINDOWS, StartMethod, is_atty from sanic.compat import OS_IS_WINDOWS, StartMethod
from sanic.exceptions import ServerKilled from sanic.exceptions import ServerKilled
from sanic.helpers import Default, _default from sanic.helpers import Default, _default, is_atty
from sanic.http.constants import HTTP from sanic.http.constants import HTTP
from sanic.http.tls import get_ssl_context, process_to_context from sanic.http.tls import get_ssl_context, process_to_context
from sanic.http.tls.context import SanicSSLContext from sanic.http.tls.context import SanicSSLContext