Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reduce import time #1

Open
wants to merge 1 commit into
base: import-time
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions src/packaging/_logging.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
# This file is dual licensed under the terms of the Apache License, Version
# 2.0, and the BSD License. See the LICENSE file in the root of this repository
# for complete details.
from __future__ import annotations

from functools import lru_cache
from typing import TYPE_CHECKING

if TYPE_CHECKING:
from logging import Logger


@lru_cache(maxsize=None)
def get_logger(name: str) -> Logger:
import logging

return logging.getLogger(name)
3 changes: 2 additions & 1 deletion src/packaging/_manylinux.py
Original file line number Diff line number Diff line change
@@ -6,7 +6,6 @@
import os
import re
import sys
import warnings
from typing import Generator, Iterator, NamedTuple, Sequence

from ._elffile import EIClass, EIData, ELFFile, EMachine
@@ -152,6 +151,8 @@ def _parse_glibc_version(version_str: str) -> tuple[int, int]:
"""
m = re.match(r"(?P<major>[0-9]+)\.(?P<minor>[0-9]+)", version_str)
if not m:
import warnings

warnings.warn(
f"Expected glibc version with 2 components major.minor,"
f" got: {version_str}",
4 changes: 3 additions & 1 deletion src/packaging/_musllinux.py
Original file line number Diff line number Diff line change
@@ -7,7 +7,6 @@

import functools
import re
import subprocess
import sys
from typing import Iterator, NamedTuple, Sequence

@@ -48,6 +47,9 @@ def _get_musl_version(executable: str) -> _MuslVersion | None:
return None
if ld is None or "musl" not in ld:
return None

import subprocess

proc = subprocess.run([ld], stderr=subprocess.PIPE, text=True)
return _parse_musl_version(proc.stderr)

11 changes: 6 additions & 5 deletions src/packaging/_tokenizer.py
Original file line number Diff line number Diff line change
@@ -2,17 +2,18 @@

import contextlib
import re
from dataclasses import dataclass
from typing import Iterator, NoReturn

from .specifiers import Specifier


@dataclass
class Token:
name: str
text: str
position: int
__slots__ = ("name", "text", "position")

def __init__(self, name: str, text: str, position: int) -> None:
self.name = name
self.text = text
self.position = position


class ParserSyntaxError(Exception):
3 changes: 2 additions & 1 deletion src/packaging/markers.py
Original file line number Diff line number Diff line change
@@ -5,7 +5,6 @@

import operator
import os
import platform
import sys
from typing import TYPE_CHECKING, Any, Callable

@@ -170,6 +169,8 @@ def format_full_version(info: sys._version_info) -> str:


def default_environment() -> dict[str, str]:
import platform

iver = format_full_version(sys.implementation.version)
implementation_name = sys.implementation.name
return {
24 changes: 14 additions & 10 deletions src/packaging/tags.py
Original file line number Diff line number Diff line change
@@ -3,23 +3,17 @@
# for complete details.
from __future__ import annotations

import logging
import platform
import re
import struct
import subprocess
import sys
import sysconfig
from importlib.machinery import EXTENSION_SUFFIXES
from typing import TYPE_CHECKING, Iterable, Iterator, cast

from . import _manylinux, _musllinux
from . import _logging, _manylinux, _musllinux

if TYPE_CHECKING:
from ._types import MacVersion, PythonVersion

logger = logging.getLogger(__name__)

INTERPRETER_SHORT_NAMES: dict[str, str] = {
"python": "py", # Generic.
"cpython": "cp",
@@ -103,9 +97,11 @@ def parse_tag(tag: str) -> frozenset[Tag]:


def _get_config_var(name: str, warn: bool = False) -> int | str | None:
import sysconfig

value: int | str | None = sysconfig.get_config_var(name)
if value is None and warn:
logger.debug(
_logging.get_logger(__name__).debug(
"Config variable '%s' is unset, Python ABI tag may be incorrect", name
)
return value
@@ -399,10 +395,14 @@ def mac_platforms(
generate platform tags for. Both parameters default to the appropriate value
for the current system.
"""
import platform

version_str, _, cpu_arch = platform.mac_ver()
if version is None:
version = cast("MacVersion", tuple(map(int, version_str.split(".")[:2])))
if version == (10, 16):
import subprocess

# When built against an older macOS SDK, Python will report macOS 10.16
# instead of the real version.
version_str = subprocess.run(
@@ -477,6 +477,8 @@ def mac_platforms(


def _linux_platforms(is_32bit: bool = _32_BIT_INTERPRETER) -> Iterator[str]:
import sysconfig

linux = _normalize_string(sysconfig.get_platform())
if not linux.startswith("linux_"):
# we should never be here, just yield the sysconfig one and return
@@ -496,16 +498,18 @@ def _linux_platforms(is_32bit: bool = _32_BIT_INTERPRETER) -> Iterator[str]:


def _generic_platforms() -> Iterator[str]:
import sysconfig

yield _normalize_string(sysconfig.get_platform())


def platform_tags() -> Iterator[str]:
"""
Provides the platform tags for this installation.
"""
if platform.system() == "Darwin":
if sys.platform == "darwin":
return mac_platforms()
elif platform.system() == "Linux":
elif sys.platform.lower().startswith("linux"):
return _linux_platforms()
else:
return _generic_platforms()
3 changes: 1 addition & 2 deletions tests/test_musllinux.py
Original file line number Diff line number Diff line change
@@ -5,7 +5,6 @@
import pretend
import pytest

from packaging import _musllinux
from packaging._musllinux import _get_musl_version, _MuslVersion, _parse_musl_version

MUSL_AMD64 = "musl libc (x86_64)\nVersion 1.2.2\n"
@@ -63,7 +62,7 @@ def mock_run(*args, **kwargs):
return collections.namedtuple("Proc", "stderr")(output)

run_recorder = pretend.call_recorder(mock_run)
monkeypatch.setattr(_musllinux.subprocess, "run", run_recorder)
monkeypatch.setattr(subprocess, "run", run_recorder)

assert _get_musl_version(str(executable)) == version

27 changes: 17 additions & 10 deletions tests/test_tags.py
Original file line number Diff line number Diff line change
@@ -11,6 +11,7 @@
except ImportError:
ctypes = None
import importlib
import logging
import os
import pathlib
import platform
@@ -22,7 +23,7 @@
import pretend
import pytest

from packaging import tags
from packaging import _logging, tags
from packaging._manylinux import _GLibCVersion
from packaging._musllinux import _MuslVersion

@@ -344,13 +345,19 @@ def teardown_method(self):

def test_get_config_var_does_not_log(self, monkeypatch):
debug = pretend.call_recorder(lambda *a: None)
monkeypatch.setattr(tags.logger, "debug", debug)
logger = logging.getLogger(__name__)
loggers = {"packaging.tags": logger}
monkeypatch.setattr(logger, "debug", debug)
monkeypatch.setattr(_logging, "get_logger", lambda name: loggers[name])
tags._get_config_var("missing")
assert debug.calls == []

def test_get_config_var_does_log(self, monkeypatch):
debug = pretend.call_recorder(lambda *a: None)
monkeypatch.setattr(tags.logger, "debug", debug)
logger = logging.getLogger(__name__)
loggers = {"packaging.tags": logger}
monkeypatch.setattr(logger, "debug", debug)
monkeypatch.setattr(_logging, "get_logger", lambda name: loggers[name])
tags._get_config_var("missing", warn=True)
assert debug.calls == [
pretend.call(
@@ -620,14 +627,14 @@ def test_linux_not_linux(self, monkeypatch):
@pytest.mark.parametrize(
"platform_name,dispatch_func",
[
("Darwin", "mac_platforms"),
("Linux", "_linux_platforms"),
("Generic", "_generic_platforms"),
("darwin", "mac_platforms"),
("linux", "_linux_platforms"),
("generic", "_generic_platforms"),
],
)
def test_platform_tags(platform_name, dispatch_func, monkeypatch):
expected = ["sillywalk"]
monkeypatch.setattr(platform, "system", lambda: platform_name)
monkeypatch.setattr(sys, "platform", platform_name)
monkeypatch.setattr(tags, dispatch_func, lambda: expected)
assert tags.platform_tags() == expected

@@ -1180,7 +1187,7 @@ def test_mac_cpython(self, mock_interpreter_name, monkeypatch):
if mock_interpreter_name("CPython"):
monkeypatch.setattr(tags, "_cpython_abis", lambda *a: ["cp33m"])
if platform.system() != "Darwin":
monkeypatch.setattr(platform, "system", lambda: "Darwin")
monkeypatch.setattr(sys, "platform", "darwin")
monkeypatch.setattr(tags, "mac_platforms", lambda: ["macosx_10_5_x86_64"])
abis = tags._cpython_abis(sys.version_info[:2])
platforms = list(tags.mac_platforms())
@@ -1197,7 +1204,7 @@ def test_windows_cpython(self, mock_interpreter_name, monkeypatch):
if mock_interpreter_name("CPython"):
monkeypatch.setattr(tags, "_cpython_abis", lambda *a: ["cp33m"])
if platform.system() != "Windows":
monkeypatch.setattr(platform, "system", lambda: "Windows")
monkeypatch.setattr(sys, "platform", "win32")
monkeypatch.setattr(tags, "_generic_platforms", lambda: ["win_amd64"])
abis = list(tags._cpython_abis(sys.version_info[:2]))
platforms = list(tags._generic_platforms())
@@ -1215,7 +1222,7 @@ def test_linux_cpython(self, mock_interpreter_name, monkeypatch):
if mock_interpreter_name("CPython"):
monkeypatch.setattr(tags, "_cpython_abis", lambda *a: ["cp33m"])
if platform.system() != "Linux":
monkeypatch.setattr(platform, "system", lambda: "Linux")
monkeypatch.setattr(sys, "platform", "linux")
monkeypatch.setattr(tags, "_linux_platforms", lambda: ["linux_x86_64"])
abis = list(tags._cpython_abis(sys.version_info[:2]))
platforms = list(tags._linux_platforms())