line_profiler.autoprofile.eager_preimports module

Tools for eagerly pre-importing everything as specified in line_profiler.autoprof.run(prof_mod=...).

line_profiler.autoprofile.eager_preimports.is_dotted_path(obj: Any) bool[source]

Example

>>> assert not is_dotted_path(object())
>>> assert is_dotted_path('foo')
>>> assert is_dotted_path('foo.bar')
>>> assert not is_dotted_path('not an identifier')
>>> assert not is_dotted_path('keyword.return.not.allowed')
line_profiler.autoprofile.eager_preimports.split_dotted_path(dotted_path: str, static: bool = True) tuple[str, str | None][source]
Parameters:
  • dotted_path (str) – Dotted path indicating an import target (module, package, or a from ... import ...-able name under that), or an object accessible via (chained) attribute access thereon

  • static (bool) – Whether to use static analysis (true) or the import system (false) to resolve targets

Returns:

  • module: dotted path indicating the module that should be imported

  • target: dotted path indicating the chained-attribute access target on the imported module corresponding to dotted_path; if the import is just a module, this is set to None

Return type:

module, target (tuple[str, Union[str, None]])

Raises:
  • TypeError – If dotted_path is not a dotted path (Python identifiers joined by periods)

  • ModuleNotFoundError – If a matching module cannot be found

Note

static=False can cause the ancestor module objects of dotted_path to be imported (and hence alter the state of sys.modules.

Example

>>> split_dotted_path('importlib.util.find_spec')
('importlib.util', 'find_spec')
>>> split_dotted_path('importlib.util')
('importlib.util', None)
>>> split_dotted_path('importlib.abc.Loader.exec_module')
('importlib.abc', 'Loader.exec_module')
>>> split_dotted_path(
...     'not a dotted path')
Traceback (most recent call last):
  ...
TypeError: dotted_path = 'not a dotted path':
expected a dotted path (string of period-joined identifiers)
>>> split_dotted_path(
...     'foo.bar.baz')
Traceback (most recent call last):
  ...
ModuleNotFoundError: dotted_path = 'foo.bar.baz':
none of the below looks like an importable module:
['foo.bar.baz', 'foo.bar', 'foo']
line_profiler.autoprofile.eager_preimports.resolve_profiling_targets(dotted_paths: Collection[str], static: bool = True, recurse: Collection[str] | bool = False) ResolvedResult[source]
Parameters:
  • dotted_paths (Collection[str]) – Dotted paths (strings of period-joined identifiers) indicating what should be profiled

  • static (bool) – Whether to use static analysis (true) or the import system (false) to resolve targets

  • recurse (Union[Collection[str], bool]) – Dotted paths (strings of period-joined identifiers) indicating the profiling targets that should be recursed into if they are packages; can also be a boolean value, indicating:

    True

    Recurse into any entry in dotted_paths that is a package

    False

    Don’t recurse into any entry

Returns:

3-named-tuple with the following fields/items:

.targets (dict[str, set[str | None]]):

Mapping from module names to the names of the attributes therein that should be passed to the profiler; if the attribute name is None, the whole module is to be passed to the profiler

.indirect (set[str]):

Set of subpackage/-module names included only via recurse-ing into packages (i.e. not directly in either dotted_paths or recurse)

.unresolved (list[str]):

List of unresolved profiling targets, i.e. those which cannot be resolved into a module part and an attribute part by split_dotted_path()

Return type:

result (ResolvedResult)

Note

static=False can cause the ancestor module objects of dotted_paths and recurse to be imported (and hence alter the state of sys.modules.

line_profiler.autoprofile.eager_preimports.write_eager_import_module(dotted_paths: Collection[str], stream: TextIO | None = None, *, static: bool = True, recurse: Collection[str] | bool = False, adder: str = 'profile.add_imported_function_or_module', indent: str = '    ') None[source]

Write a module which autoprofiles all its imports.

Parameters:
  • dotted_paths (Collection[str]) – Dotted paths (strings of period-joined identifiers) indicating what should be profiled

  • stream (Union[TextIO, None]) – Optional text-mode writable file object to which to write the module

  • static (bool) – Whether to use static analysis (true) or the import system (false) to resolve targets

  • recurse (Union[Collection[str], bool]) – Dotted paths (strings of period-joined identifiers) indicating the profiling targets that should be recursed into if they are packages; can also be a boolean value, indicating:

    True

    Recurse into any entry in dotted_paths that is a package

    False

    Don’t recurse into any entry

  • adder (str) – Single-line string ast.parse(mode='eval')-able to a single expression, indicating the callable (which is assumed to exist in the builtin namespace by the time the module is executed) to be called to add the profiling target

  • indent (str) – Single-line, non-empty whitespace string to indent the output with

Side effects:
  • stream (or sys.stdout if None) written to

  • Warning issued if the module can’t be located for one or more dotted paths

Raises:
  • TypeError – If adder and indent are not strings, OR if dotted_paths is not a collection of dotted paths

  • ValueError – If adder is a non-single-line string or is not parsable to a single expression, OR if indent isn’t single-line, non-empty, and whitespace

Example

>>> import io
>>> import textwrap
>>> import warnings
>>>
>>>
>>> def strip(s):
...     return textwrap.dedent(s).strip('\n')
...
>>>
>>> with warnings.catch_warnings(record=True) as record:
...     with io.StringIO() as sio:
...         write_eager_import_module(
...             ['importlib.util',
...              'foo.bar',
...              'importlib.abc.Loader.exec_module',
...              'importlib.abc.Loader.find_module'],
...             sio)
...         written = strip(sio.getvalue())
...
>>> assert written == strip('''
... add = profile.add_imported_function_or_module  # noqa: F821
... failures = []
...
...
... try:
...     import importlib.abc as module
... except ImportError:
...     pass
... else:
...     try:
...         add(module.Loader.exec_module)
...     except AttributeError:
...         failures.append('importlib.abc.Loader.exec_module')
...     try:
...         add(module.Loader.find_module)
...     except AttributeError:
...         failures.append('importlib.abc.Loader.find_module')
...
... try:
...     import importlib.util as module
... except ImportError:
...     failures.append('importlib.util')
... else:
...     add(module)
...
...
... if failures:
...     import warnings
...
...     msg = '{} target{} cannot be imported: {!r}'.format(
...         len(failures),
...         '' if len(failures) == 1 else 's',
...         failures)
...     warnings.warn(msg, stacklevel=2)
... '''), written
>>> assert len(record) == 1
>>> assert (record[0].message.args[0]
...         == ("1 import target cannot be resolved: "
...             "['foo.bar']"))

Note

static=False can cause the ancestor module objects of dotted_paths and recurse to be imported (and hence alter the state of sys.modules.