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

faster baseline import #445

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
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
16 changes: 14 additions & 2 deletions src/pluggy/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,13 +25,25 @@
from ._hooks import HookCaller
from ._hooks import HookImpl
from ._hooks import HookimplMarker
from ._hooks import HookimplOpts
from ._hooks import HookRelay
from ._hooks import HookspecMarker
from ._hooks import HookspecOpts
from ._manager import PluginManager
from ._manager import PluginValidationError
from ._result import HookCallError
from ._result import Result
from ._warnings import PluggyTeardownRaisedWarning
from ._warnings import PluggyWarning


Check warning on line 37 in src/pluggy/__init__.py

View check run for this annotation

Codecov / codecov/patch

src/pluggy/__init__.py#L36-L37

Added lines #L36 - L37 were not covered by tests
TYPE_CHECKING = False
if TYPE_CHECKING:
from ._types import HookimplOpts
from ._types import HookspecOpts
else:

def __getattr__(name: str) -> object:
if name.endswith("Opts"):
from . import _types

return getattr(_types, name)
raise AttributeError(name)
37 changes: 22 additions & 15 deletions src/pluggy/_callers.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,6 @@

from __future__ import annotations

from typing import cast
from typing import Generator
from typing import Mapping
from typing import NoReturn
from typing import Sequence
from typing import Tuple
from typing import Union
import warnings

from ._hooks import HookImpl
Expand All @@ -19,12 +12,26 @@
from ._warnings import PluggyTeardownRaisedWarning


# Need to distinguish between old- and new-style hook wrappers.
# Wrapping with a tuple is the fastest type-safe way I found to do it.
Teardown = Union[
Tuple[Generator[None, Result[object], None], HookImpl],
Generator[None, object, object],
]
TYPE_CHECKING = False
if TYPE_CHECKING:
from typing import cast
from typing import Generator
from typing import Mapping

Check warning on line 19 in src/pluggy/_callers.py

View check run for this annotation

Codecov / codecov/patch

src/pluggy/_callers.py#L17-L19

Added lines #L17 - L19 were not covered by tests
from typing import NoReturn
from typing import Sequence
from typing import Tuple
from typing import Union

Check warning on line 24 in src/pluggy/_callers.py

View check run for this annotation

Codecov / codecov/patch

src/pluggy/_callers.py#L24

Added line #L24 was not covered by tests
# Need to distinguish between old- and new-style hook wrappers.
# Wrapping with a tuple is the fastest type-safe way I found to do it.
Teardown = Union[
Tuple[Generator[None, Result[object], None], HookImpl],
Generator[None, object, object],
]
else:

def cast(t, v):
return v


def _raise_wrapfail(
Expand Down Expand Up @@ -84,7 +91,7 @@
# If this cast is not valid, a type error is raised below,
# which is the desired response.
res = hook_impl.function(*args)
wrapper_gen = cast(Generator[None, Result[object], None], res)
wrapper_gen = cast("Generator[None, Result[object], None]", res)
next(wrapper_gen) # first yield
teardowns.append((wrapper_gen, hook_impl))
except StopIteration:
Expand All @@ -94,7 +101,7 @@
# If this cast is not valid, a type error is raised below,
# which is the desired response.
res = hook_impl.function(*args)
function_gen = cast(Generator[None, object, object], res)
function_gen = cast("Generator[None, object, object]", res)
next(function_gen) # first yield
teardowns.append(function_gen)
except StopIteration:
Expand Down
110 changes: 42 additions & 68 deletions src/pluggy/_hooks.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,76 +4,51 @@

from __future__ import annotations

import inspect
import sys
from types import ModuleType
from typing import AbstractSet
from typing import Any
from typing import Callable
from typing import Final
from typing import final
from typing import Generator
from typing import List
from typing import Mapping
from typing import Optional
from typing import overload
from typing import Sequence
from typing import Tuple
from typing import TYPE_CHECKING
from typing import TypedDict
from typing import TypeVar
from typing import Union
import warnings

from ._result import Result


_T = TypeVar("_T")
_F = TypeVar("_F", bound=Callable[..., object])
_Namespace = Union[ModuleType, type]
_Plugin = object
_HookExec = Callable[
[str, Sequence["HookImpl"], Mapping[str, object], bool],
Union[object, List[object]],
]
_HookImplFunction = Callable[..., Union[_T, Generator[None, Result[_T], None]]]


class HookspecOpts(TypedDict):
"""Options for a hook specification."""

#: Whether the hook is :ref:`first result only <firstresult>`.
firstresult: bool
#: Whether the hook is :ref:`historic <historic>`.
historic: bool
#: Whether the hook :ref:`warns when implemented <warn_on_impl>`.
warn_on_impl: Warning | None
#: Whether the hook warns when :ref:`certain arguments are requested
#: <warn_on_impl>`.
#:
#: .. versionadded:: 1.5
warn_on_impl_args: Mapping[str, Warning] | None


class HookimplOpts(TypedDict):
"""Options for a hook implementation."""

#: Whether the hook implementation is a :ref:`wrapper <hookwrapper>`.
wrapper: bool
#: Whether the hook implementation is an :ref:`old-style wrapper
#: <old_style_hookwrappers>`.
hookwrapper: bool
#: Whether validation against a hook specification is :ref:`optional
#: <optionalhook>`.
optionalhook: bool
#: Whether to try to order this hook implementation :ref:`first
#: <callorder>`.
tryfirst: bool
#: Whether to try to order this hook implementation :ref:`last
#: <callorder>`.
trylast: bool
#: The name of the hook specification to match, see :ref:`specname`.
specname: str | None

Check warning on line 12 in src/pluggy/_hooks.py

View check run for this annotation

Codecov / codecov/patch

src/pluggy/_hooks.py#L12

Added line #L12 was not covered by tests
TYPE_CHECKING = False
if TYPE_CHECKING:
from types import ModuleType
from typing import AbstractSet
from typing import Any
from typing import Callable
from typing import Final
from typing import final
from typing import Generator
from typing import List
from typing import Mapping
from typing import Optional
from typing import overload
from typing import Sequence

Check warning on line 26 in src/pluggy/_hooks.py

View check run for this annotation

Codecov / codecov/patch

src/pluggy/_hooks.py#L15-L26

Added lines #L15 - L26 were not covered by tests
from typing import Tuple
from typing import TYPE_CHECKING

Check warning on line 28 in src/pluggy/_hooks.py

View check run for this annotation

Codecov / codecov/patch

src/pluggy/_hooks.py#L28

Added line #L28 was not covered by tests
from typing import TypeVar
from typing import Union

from ._result import Result

_T = TypeVar("_T")
_F = TypeVar("_F", bound=Callable[..., object])

Check warning on line 35 in src/pluggy/_hooks.py

View check run for this annotation

Codecov / codecov/patch

src/pluggy/_hooks.py#L31-L35

Added lines #L31 - L35 were not covered by tests
_Namespace = Union[ModuleType, type]
_HookExec = Callable[
[str, Sequence["HookImpl"], Mapping[str, object], bool],
Union[object, List[object]],

Check warning on line 39 in src/pluggy/_hooks.py

View check run for this annotation

Codecov / codecov/patch

src/pluggy/_hooks.py#L39

Added line #L39 was not covered by tests
]
_HookImplFunction = Callable[..., Union[_T, Generator[None, Result[_T], None]]]
_CallHistory = List[Tuple[Mapping[str, object], Optional[Callable[[Any], None]]]]

Check warning on line 42 in src/pluggy/_hooks.py

View check run for this annotation

Codecov / codecov/patch

src/pluggy/_hooks.py#L42

Added line #L42 was not covered by tests

from ._types import HookimplOpts
from ._types import HookspecOpts
else:

Check warning on line 46 in src/pluggy/_hooks.py

View check run for this annotation

Codecov / codecov/patch

src/pluggy/_hooks.py#L46

Added line #L46 was not covered by tests

def final(func: _F) -> _F:
return func

Check warning on line 50 in src/pluggy/_hooks.py

View check run for this annotation

Codecov / codecov/patch

src/pluggy/_hooks.py#L50

Added line #L50 was not covered by tests
overload = final


@final
Expand Down Expand Up @@ -299,6 +274,8 @@
In case of a class, its ``__init__`` method is considered.
For methods the ``self`` parameter is not included.
"""
import inspect

Check warning on line 278 in src/pluggy/_hooks.py

View check run for this annotation

Codecov / codecov/patch

src/pluggy/_hooks.py#L278

Added line #L278 was not covered by tests
if inspect.isclass(func):
try:
func = func.__init__
Expand Down Expand Up @@ -376,9 +353,6 @@
_HookRelay = HookRelay


_CallHistory = List[Tuple[Mapping[str, object], Optional[Callable[[Any], None]]]]


class HookCaller:
"""A caller of all registered implementations of a hook specification."""

Expand Down
69 changes: 69 additions & 0 deletions src/pluggy/_importlib_metadata.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
"""this module contains importlib_metadata usage and importing

it's deferred to avoid import-time dependency on importlib_metadata

.. code-block:: console

python -X importtime -c 'import pluggy' 2> import0.log
tuna import0.log


"""

from __future__ import annotations

import importlib.metadata
from typing import Any

from . import _manager


class DistFacade:
"""Emulate a pkg_resources Distribution"""

def __init__(self, dist: importlib.metadata.Distribution) -> None:
self._dist = dist

@property
def project_name(self) -> str:
name: str = self.metadata["name"]
return name

def __getattr__(self, attr: str, default: object | None = None) -> Any:
return getattr(self._dist, attr, default)

def __dir__(self) -> list[str]:
return sorted(dir(self._dist) + ["_dist", "project_name"])

Check warning on line 36 in src/pluggy/_importlib_metadata.py

View check run for this annotation

Codecov / codecov/patch

src/pluggy/_importlib_metadata.py#L36

Added line #L36 was not covered by tests


def load_importlib_entrypoints(
manager: _manager.PluginManager,
group: str,
name: str | None = None,
) -> int:
"""Load modules from querying the specified setuptools ``group``.

:param group:
Entry point group to load plugins.
:param name:
If given, loads only plugins with the given ``name``.

:return:
The number of plugins loaded by this call.
"""
count = 0
for dist in list(importlib.metadata.distributions()):
for ep in dist.entry_points:
if (
ep.group != group
or (name is not None and ep.name != name)
# already registered
or manager.get_plugin(ep.name)
or manager.is_blocked(ep.name)
):
continue
plugin = ep.load()
manager.register(plugin, name=ep.name)
manager._plugin_dist_metadata.append((plugin, dist))
count += 1
return count
Loading