Skip to content

Commit 6ddd7e4

Browse files
Lazy load package data to improve import time (#414)
1 parent f308905 commit 6ddd7e4

File tree

6 files changed

+96
-40
lines changed

6 files changed

+96
-40
lines changed

.mypy.ini

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,3 @@ ignore_missing_imports = True
2626

2727
[mypy-aiohttp_mako.*]
2828
ignore_missing_imports = True
29-
30-
[mypy-pkg_resources.*]
31-
ignore_missing_imports = True

aiohttp_debugtoolbar/panels/versions.py

Lines changed: 40 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,21 @@
11
import platform
22
import sys
33
from operator import itemgetter
4-
5-
import pkg_resources # noqa: I900
4+
from typing import ClassVar, Dict, List, Optional
65

76
from .base import DebugPanel
87

9-
__all__ = ["VersionDebugPanel"]
10-
11-
12-
packages = []
13-
for distribution in pkg_resources.working_set:
14-
name = distribution.project_name
15-
dependencies = [d.project_name for d in distribution.requires()]
16-
17-
# parse home page url
18-
metadata_file = distribution.PKG_INFO
19-
lines = distribution.get_metadata_lines(metadata_file)
20-
url = "#"
21-
for line in lines:
22-
if line.startswith("Home-page:"):
23-
url = line[10:].strip()
24-
break
25-
26-
packages.append(
27-
{
28-
"version": distribution.version,
29-
"lowername": name.lower(),
30-
"name": name,
31-
"dependencies": dependencies,
32-
"url": url,
33-
}
34-
)
8+
if sys.version_info >= (3, 8):
9+
from importlib.metadata import Distribution, version
10+
else:
11+
Distribution = None
12+
13+
def version(_v):
14+
return ""
3515

36-
packages = sorted(packages, key=itemgetter("lowername"))
37-
aiohttp_version = pkg_resources.get_distribution("aiohttp").version
16+
17+
__all__ = ("VersionDebugPanel",)
18+
aiohttp_version = version("aiohttp")
3819

3920

4021
class VersionDebugPanel(DebugPanel):
@@ -48,15 +29,43 @@ class VersionDebugPanel(DebugPanel):
4829
template = "versions.jinja2"
4930
title = "Versions"
5031
nav_title = title
32+
packages: ClassVar[Optional[List[Dict[str, str]]]] = None
5133

5234
def __init__(self, request):
5335
super().__init__(request)
5436
self.data = {
5537
"platform": self.get_platform(),
56-
"packages": packages,
38+
"packages": self.get_packages(),
5739
"aiohttp_version": aiohttp_version,
5840
}
5941

42+
@classmethod
43+
def get_packages(cls) -> List[Dict[str, str]]:
44+
if VersionDebugPanel.packages:
45+
return VersionDebugPanel.packages
46+
47+
if Distribution is None:
48+
return () # type: ignore[unreachable]
49+
50+
packages = []
51+
for distribution in Distribution.discover():
52+
name = distribution.metadata["Name"]
53+
dependencies = [d for d in distribution.requires or ()]
54+
url = distribution.metadata["Home-page"]
55+
56+
packages.append(
57+
{
58+
"version": distribution.version,
59+
"lowername": name.lower(),
60+
"name": name,
61+
"dependencies": dependencies,
62+
"url": url,
63+
}
64+
)
65+
66+
VersionDebugPanel.packages = sorted(packages, key=itemgetter("lowername"))
67+
return VersionDebugPanel.packages
68+
6069
def _get_platform_name(self):
6170
return platform.platform()
6271

tests/conftest.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55

66
from aiohttp_debugtoolbar import setup
77

8+
pytest_plugins = ("pytester",)
9+
810

911
@pytest.fixture
1012
def create_server(unused_tcp_port_factory):

tests/test_imports.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import platform
2+
import sys
3+
4+
import pytest
5+
6+
7+
@pytest.mark.skipif(
8+
not sys.platform.startswith("linux") or platform.python_implementation() == "PyPy",
9+
reason="Unreliable",
10+
)
11+
def test_import_time(pytester: pytest.Pytester) -> None:
12+
"""Check that importing aiohttp-debugtoolbar doesn't take too long.
13+
Obviously, the time may vary on different machines and may need to be adjusted
14+
from time to time, but this should provide an early warning if something is
15+
added that significantly increases import time.
16+
"""
17+
r = pytester.run(
18+
sys.executable, "-We", "-c", "import aiohttp_debugtoolbar", timeout=0.6
19+
)
20+
21+
assert not r.stdout.str()
22+
assert not r.stderr.str()

tests/test_middleware.py

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import asyncio
2+
13
import aiohttp_jinja2
24
import pytest
35
from aiohttp import web
@@ -209,14 +211,16 @@ async def handler(request: web.Request) -> web.Response:
209211
client = await aiohttp_client(app)
210212

211213
# Should be logged
212-
r = await client.get("/")
213-
assert r.status == 200
214+
async with client.get("/") as r:
215+
assert r.status == 200
214216
# Should not be logged
215-
r = await client.get("/favicon.ico")
216-
assert r.status == 200
217-
r = await client.get("/_debugtoolbar/static/img/aiohttp.svg")
218-
assert r.status == 200
217+
async with client.get("/favicon.ico") as r:
218+
assert r.status == 200
219+
async with client.get("/_debugtoolbar/static/img/aiohttp.svg") as r:
220+
assert r.status == 200
219221

220222
request_history = tuple(app[aiohttp_debugtoolbar.APP_KEY]["request_history"])
221223
assert len(request_history) == 1
222224
assert request_history[0][1].request.path == "/"
225+
226+
await asyncio.sleep(0) # Workaround, maybe fixed in aiohttp 4.

tests/test_panels_versions.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import sys
2+
from unittest.mock import create_autospec
3+
4+
import pytest
5+
from aiohttp import web
6+
7+
from aiohttp_debugtoolbar.panels import VersionDebugPanel
8+
9+
10+
@pytest.mark.skipif(sys.version_info < (3, 8), reason="Missing importlib.metadata")
11+
async def test_packages():
12+
request_mock = create_autospec(web.Request)
13+
panel = VersionDebugPanel(request_mock)
14+
15+
jinja2_metadata = {
16+
"version": "3.0.3",
17+
"lowername": "jinja2",
18+
"name": "Jinja2",
19+
"dependencies": ["MarkupSafe (>=2.0)", "Babel (>=2.7) ; extra == 'i18n'"],
20+
"url": "https://palletsprojects.com/p/jinja/",
21+
}
22+
assert jinja2_metadata in panel.data["packages"]

0 commit comments

Comments
 (0)