From 9d0e9714273eb582c989e49ed1bd2c2fa86eec7c Mon Sep 17 00:00:00 2001 From: Ofek Lev Date: Thu, 28 Dec 2023 15:28:20 -0500 Subject: [PATCH] Reduce import time --- src/packaging/_logging.py | 17 +++++++++++++++++ src/packaging/_manylinux.py | 3 ++- src/packaging/_musllinux.py | 4 +++- src/packaging/_tokenizer.py | 11 ++++++----- src/packaging/markers.py | 3 ++- src/packaging/tags.py | 24 ++++++++++++++---------- tests/test_musllinux.py | 3 +-- tests/test_tags.py | 27 +++++++++++++++++---------- 8 files changed, 62 insertions(+), 30 deletions(-) create mode 100644 src/packaging/_logging.py diff --git a/src/packaging/_logging.py b/src/packaging/_logging.py new file mode 100644 index 00000000..77f9d9ef --- /dev/null +++ b/src/packaging/_logging.py @@ -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) diff --git a/src/packaging/_manylinux.py b/src/packaging/_manylinux.py index 113d397d..2e6748a7 100644 --- a/src/packaging/_manylinux.py +++ b/src/packaging/_manylinux.py @@ -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[0-9]+)\.(?P[0-9]+)", version_str) if not m: + import warnings + warnings.warn( f"Expected glibc version with 2 components major.minor," f" got: {version_str}", diff --git a/src/packaging/_musllinux.py b/src/packaging/_musllinux.py index bc5b8194..95741a7a 100644 --- a/src/packaging/_musllinux.py +++ b/src/packaging/_musllinux.py @@ -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) diff --git a/src/packaging/_tokenizer.py b/src/packaging/_tokenizer.py index 89d04160..b765ba34 100644 --- a/src/packaging/_tokenizer.py +++ b/src/packaging/_tokenizer.py @@ -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): diff --git a/src/packaging/markers.py b/src/packaging/markers.py index 9255b7de..2211c4af 100644 --- a/src/packaging/markers.py +++ b/src/packaging/markers.py @@ -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 { diff --git a/src/packaging/tags.py b/src/packaging/tags.py index fa65123a..90d8a16d 100644 --- a/src/packaging/tags.py +++ b/src/packaging/tags.py @@ -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,6 +498,8 @@ def _linux_platforms(is_32bit: bool = _32_BIT_INTERPRETER) -> Iterator[str]: def _generic_platforms() -> Iterator[str]: + import sysconfig + yield _normalize_string(sysconfig.get_platform()) @@ -503,9 +507,9 @@ 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() diff --git a/tests/test_musllinux.py b/tests/test_musllinux.py index 1a6a9a26..5768f039 100644 --- a/tests/test_musllinux.py +++ b/tests/test_musllinux.py @@ -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 diff --git a/tests/test_tags.py b/tests/test_tags.py index 9821fc25..3587b3dd 100644 --- a/tests/test_tags.py +++ b/tests/test_tags.py @@ -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())