Skip to content

Commit

Permalink
Improve import time (#6591)
Browse files Browse the repository at this point in the history
See
aio-libs/aiohttp-debugtoolbar#394 (comment)

Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
Co-authored-by: Sviatoslav Sydorenko <sviat@redhat.com>
  • Loading branch information
3 people authored Nov 20, 2022
1 parent 7d1ada8 commit a8dbb68
Show file tree
Hide file tree
Showing 9 changed files with 90 additions and 36 deletions.
4 changes: 4 additions & 0 deletions .coveragerc
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
[run]
branch = True
source = aiohttp, tests
omit = site-packages
1 change: 1 addition & 0 deletions CHANGES/6591.misc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Changed importing Gunicorn to happen on-demand, decreasing import time by ~53% -- :user:`Dreamsorcerer`.
36 changes: 30 additions & 6 deletions aiohttp/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
__version__ = "4.0.0a2.dev0"

from typing import Tuple
from typing import TYPE_CHECKING, Tuple

from . import hdrs as hdrs
from .client import (
Expand Down Expand Up @@ -103,6 +103,13 @@
TraceResponseChunkReceivedParams as TraceResponseChunkReceivedParams,
)

if TYPE_CHECKING:
# At runtime these are lazy-loaded at the bottom of the file.
from .worker import (
GunicornUVLoopWebWorker as GunicornUVLoopWebWorker,
GunicornWebWorker as GunicornWebWorker,
)

__all__: Tuple[str, ...] = (
"hdrs",
# client
Expand Down Expand Up @@ -203,11 +210,28 @@
"TraceRequestRedirectParams",
"TraceRequestStartParams",
"TraceResponseChunkReceivedParams",
# workers (imported lazily with __getattr__)
"GunicornUVLoopWebWorker",
"GunicornWebWorker",
)

try:
from .worker import GunicornUVLoopWebWorker, GunicornWebWorker

__all__ += ("GunicornWebWorker", "GunicornUVLoopWebWorker")
except ImportError: # pragma: no cover
pass
def __dir__() -> Tuple[str, ...]:
return __all__ + ("__author__", "__doc__")


def __getattr__(name: str) -> object:
global GunicornUVLoopWebWorker, GunicornWebWorker

# Importing gunicorn takes a long time (>100ms), so only import if actually needed.
if name in ("GunicornUVLoopWebWorker", "GunicornWebWorker"):
try:
from .worker import GunicornUVLoopWebWorker as guv, GunicornWebWorker as gw
except ImportError:
return None

GunicornUVLoopWebWorker = guv # type: ignore[misc]
GunicornWebWorker = gw # type: ignore[misc]
return guv if name == "GunicornUVLoopWebWorker" else gw

raise AttributeError(f"module {__name__} has no attribute {name}")
2 changes: 1 addition & 1 deletion requirements/constraints.txt
Original file line number Diff line number Diff line change
Expand Up @@ -151,7 +151,7 @@ pyjwt==2.3.0
# via gidgethub
pyparsing==2.4.7
# via packaging
pytest==7.1.3
pytest==6.2.5
# via
# -r requirements/lint.txt
# -r requirements/test.txt
Expand Down
2 changes: 1 addition & 1 deletion requirements/lint.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,5 @@
aioredis==2.0.1
mypy==0.982; implementation_name=="cpython"
pre-commit==2.17.0
pytest==7.1.3
pytest==6.2.5
slotscheck==0.8.0
2 changes: 1 addition & 1 deletion requirements/test.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ freezegun==1.1.0
mypy==0.982; implementation_name=="cpython"
mypy-extensions==0.4.3; implementation_name=="cpython"
proxy.py ~= 2.4.4rc3
pytest==7.1.3
pytest==6.2.5
pytest-cov==3.0.0
pytest-mock==3.6.1
python-on-whales==0.36.1
Expand Down
5 changes: 0 additions & 5 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -104,11 +104,6 @@ exclude_lines =
@abc.abstractmethod
@abstractmethod

[coverage:run]
branch = True
source = aiohttp, tests
omit = site-packages

[tool:pytest]
addopts =
# show 10 slowest invocations:
Expand Down
22 changes: 0 additions & 22 deletions tests/test___all__.py

This file was deleted.

52 changes: 52 additions & 0 deletions tests/test_imports.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import os
import platform
import sys
from pathlib import Path

import pytest


def test___all__(pytester: pytest.Pytester) -> None:
"""See https://github.com/aio-libs/aiohttp/issues/6197"""
pytester.makepyfile(
test_a="""
from aiohttp import *
assert 'GunicornWebWorker' in globals()
"""
)
result = pytester.runpytest("-vv")
result.assert_outcomes(passed=0, errors=0)


def test_web___all__(pytester: pytest.Pytester) -> None:
pytester.makepyfile(
test_b="""
from aiohttp.web import *
"""
)
result = pytester.runpytest("-vv")
result.assert_outcomes(passed=0, errors=0)


@pytest.mark.skipif(
not sys.platform.startswith("linux") or platform.python_implementation() == "PyPy",
reason="Timing is more reliable on Linux",
)
def test_import_time(pytester: pytest.Pytester) -> None:
"""Check that importing aiohttp doesn't take too long.
Obviously, the time may vary on different machines and may need to be adjusted
from time to time, but this should provide an early warning if something is
added that significantly increases import time.
"""
root = Path(__file__).parent.parent
old_path = os.environ.get("PYTHONPATH")
os.environ["PYTHONPATH"] = os.pathsep.join([str(root)] + sys.path)
r = pytester.run(sys.executable, "-We", "-c", "import aiohttp", timeout=0.41)
if old_path is None:
os.environ.pop("PYTHONPATH")
else:
os.environ["PYTHONPATH"] = old_path

assert not r.stdout.str()
assert not r.stderr.str()

0 comments on commit a8dbb68

Please sign in to comment.