+
Skip to content
Merged
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
110 changes: 56 additions & 54 deletions birdseye/import_hook.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
import logging
from __future__ import annotations

import sys
from importlib.machinery import ModuleSpec
from importlib.util import spec_from_loader
import ast
from types import ModuleType
from typing import Sequence, Iterator, cast


# This is based on the MacroPy import hook
# https://github.com/lihaoyi/macropy/blob/46ee500b877d5a32b17391bb8122c09b15a1826a/macropy/core/import_hooks.py

class BirdsEyeLoader:

def __init__(self, spec, source, deep):
self._spec = spec
self.source = source
Expand All @@ -34,61 +34,63 @@ def is_package(self, fullname):
return self._spec.loader.is_package(fullname)


class BirdsEyeFinder(object):
class BirdsEyeFinder:
"""Loads a module and looks for tracing inside, only providing a loader
if it finds some.
"""

def _find_plain_spec(self, fullname, path, target):
"""Try to find the original module using all the
remaining meta_path finders."""
spec = None
def _find_plain_specs(
self, fullname: str, path: Sequence[str] | None, target: ModuleType | None
) -> Iterator[ModuleSpec]:
"""Yield module specs returned by other finders on `sys.meta_path`."""
for finder in sys.meta_path:
# when testing with pytest, it installs a finder that for
# some yet unknown reasons makes birdseye
# fail. For now it will just avoid using it and pass to
# the next one
if finder is self or 'pytest' in finder.__module__:
# Skip this finder or any like it to avoid infinite recursion.
if isinstance(finder, BirdsEyeFinder):
continue

try:
plain_spec = finder.find_spec(fullname, path, target)
except Exception: # pragma: no cover
continue

if plain_spec:
yield plain_spec

def find_spec(
self, fullname: str, path: Sequence[str] | None, target: ModuleType | None = None
) -> ModuleSpec | None:
"""This is the method that is called by the import system.

It uses the other existing meta path finders to do most of the standard work,
particularly finding the module's source code.
If it finds a module spec, it returns a new spec that uses the BirdsEyeLoader.
"""
for plain_spec in self._find_plain_specs(fullname, path, target):
# Not all loaders have get_source, but it's an abstract method of the standard ABC InspectLoader.
# In particular it's implemented by `importlib.machinery.SourceFileLoader`
# which is provided by default.
get_source = getattr(plain_spec.loader, 'get_source', None)
if not callable(get_source): # pragma: no cover
continue
if hasattr(finder, 'find_spec'):
spec = finder.find_spec(fullname, path, target=target)
elif hasattr(finder, 'load_module'):
spec = spec_from_loader(fullname, finder)

if spec is not None and spec.origin != 'builtin':
return spec

def find_spec(self, fullname, path, target=None):
spec = self._find_plain_spec(fullname, path, target)
if spec is None or not (hasattr(spec.loader, 'get_source') and
callable(spec.loader.get_source)): # noqa: E128
if fullname != 'org':
# stdlib pickle.py at line 94 contains a ``from
# org.python.core for Jython which is always failing,
# of course
logging.debug('Failed finding spec for %s', fullname)
return

try:
source = spec.loader.get_source(fullname)
except ImportError:
logging.debug('Loader for %s was unable to find the sources',
fullname)
return
except Exception:
logging.exception('Loader for %s raised an error', fullname)
return

if not source or 'birdseye' not in source:
return

deep, trace_stmt = should_trace(source)

if not trace_stmt:
return

loader = BirdsEyeLoader(spec, source, deep)
return spec_from_loader(fullname, loader)

try:
source = cast(str, get_source(fullname))
except Exception: # pragma: no cover
continue

if not source:
continue

if "birdseye" not in source:
return None

deep, trace_stmt = should_trace(source)

if not trace_stmt:
return None

loader = BirdsEyeLoader(plain_spec, source, deep)
return spec_from_loader(fullname, loader)


def should_trace(source):
Expand Down
点击 这是indexloc提供的php浏览器服务,不要输入任何密码和下载