diff --git a/docs/source/auto/kernprof.rst b/docs/source/auto/kernprof.rst index 0927b3e7..40df42e4 100644 --- a/docs/source/auto/kernprof.rst +++ b/docs/source/auto/kernprof.rst @@ -7,4 +7,3 @@ kernprof module :members: :undoc-members: :show-inheritance: - :private-members: diff --git a/docs/source/auto/line_profiler.__main__.rst b/docs/source/auto/line_profiler.__main__.rst index 65636606..21c8a20f 100644 --- a/docs/source/auto/line_profiler.__main__.rst +++ b/docs/source/auto/line_profiler.__main__.rst @@ -5,4 +5,3 @@ line\_profiler.\_\_main\_\_ module :members: :undoc-members: :show-inheritance: - :private-members: diff --git a/docs/source/auto/line_profiler._line_profiler.rst b/docs/source/auto/line_profiler._line_profiler.rst index 106dc5e1..d7f02731 100644 --- a/docs/source/auto/line_profiler._line_profiler.rst +++ b/docs/source/auto/line_profiler._line_profiler.rst @@ -5,4 +5,3 @@ line\_profiler.\_line\_profiler module :members: :undoc-members: :show-inheritance: - :private-members: diff --git a/docs/source/auto/line_profiler.autoprofile.ast_profile_transformer.rst b/docs/source/auto/line_profiler.autoprofile.ast_profile_transformer.rst index cd1bd0c5..06e7fdb5 100644 --- a/docs/source/auto/line_profiler.autoprofile.ast_profile_transformer.rst +++ b/docs/source/auto/line_profiler.autoprofile.ast_profile_transformer.rst @@ -5,4 +5,3 @@ line\_profiler.autoprofile.ast\_profle\_transformer module :members: :undoc-members: :show-inheritance: - :private-members: diff --git a/docs/source/auto/line_profiler.autoprofile.ast_tree_profiler.rst b/docs/source/auto/line_profiler.autoprofile.ast_tree_profiler.rst index 5863c896..6a842bef 100644 --- a/docs/source/auto/line_profiler.autoprofile.ast_tree_profiler.rst +++ b/docs/source/auto/line_profiler.autoprofile.ast_tree_profiler.rst @@ -5,4 +5,3 @@ line\_profiler.autoprofile.ast\_tree\_profiler module :members: :undoc-members: :show-inheritance: - :private-members: diff --git a/docs/source/auto/line_profiler.autoprofile.autoprofile.rst b/docs/source/auto/line_profiler.autoprofile.autoprofile.rst index a514da68..b271043b 100644 --- a/docs/source/auto/line_profiler.autoprofile.autoprofile.rst +++ b/docs/source/auto/line_profiler.autoprofile.autoprofile.rst @@ -5,4 +5,3 @@ line\_profiler.autoprofile.autoprofile module :members: :undoc-members: :show-inheritance: - :private-members: diff --git a/docs/source/auto/line_profiler.autoprofile.eager_preimports.rst b/docs/source/auto/line_profiler.autoprofile.eager_preimports.rst index 6803f2db..a0844988 100644 --- a/docs/source/auto/line_profiler.autoprofile.eager_preimports.rst +++ b/docs/source/auto/line_profiler.autoprofile.eager_preimports.rst @@ -5,4 +5,3 @@ line\_profiler.autoprofile.eager\_preimports module :members: :undoc-members: :show-inheritance: - :private-members: diff --git a/docs/source/auto/line_profiler.autoprofile.line_profiler_utils.rst b/docs/source/auto/line_profiler.autoprofile.line_profiler_utils.rst index cbd78ed3..c9dffd35 100644 --- a/docs/source/auto/line_profiler.autoprofile.line_profiler_utils.rst +++ b/docs/source/auto/line_profiler.autoprofile.line_profiler_utils.rst @@ -5,4 +5,3 @@ line\_profiler.autoprofile.line\_profiler\_utils module :members: :undoc-members: :show-inheritance: - :private-members: diff --git a/docs/source/auto/line_profiler.autoprofile.profmod_extractor.rst b/docs/source/auto/line_profiler.autoprofile.profmod_extractor.rst index f6c33f31..a9fe88c3 100644 --- a/docs/source/auto/line_profiler.autoprofile.profmod_extractor.rst +++ b/docs/source/auto/line_profiler.autoprofile.profmod_extractor.rst @@ -5,4 +5,3 @@ line\_profiler.autoprofile.profmod\_extractor module :members: :undoc-members: :show-inheritance: - :private-members: diff --git a/docs/source/auto/line_profiler.autoprofile.rst b/docs/source/auto/line_profiler.autoprofile.rst index 4f6b40d7..a4780143 100644 --- a/docs/source/auto/line_profiler.autoprofile.rst +++ b/docs/source/auto/line_profiler.autoprofile.rst @@ -23,4 +23,3 @@ Module contents :members: :undoc-members: :show-inheritance: - :private-members: diff --git a/docs/source/auto/line_profiler.autoprofile.run_module.rst b/docs/source/auto/line_profiler.autoprofile.run_module.rst index 238d24e2..532eaf55 100644 --- a/docs/source/auto/line_profiler.autoprofile.run_module.rst +++ b/docs/source/auto/line_profiler.autoprofile.run_module.rst @@ -5,4 +5,3 @@ line\_profiler.autoprofile.run\_module module :members: :undoc-members: :show-inheritance: - :private-members: diff --git a/docs/source/auto/line_profiler.autoprofile.util_static.rst b/docs/source/auto/line_profiler.autoprofile.util_static.rst index 9d7317d1..56d16a8a 100644 --- a/docs/source/auto/line_profiler.autoprofile.util_static.rst +++ b/docs/source/auto/line_profiler.autoprofile.util_static.rst @@ -5,4 +5,3 @@ line\_profiler.autoprofile.util\_static module :members: :undoc-members: :show-inheritance: - :private-members: diff --git a/docs/source/auto/line_profiler.explicit_profiler.rst b/docs/source/auto/line_profiler.explicit_profiler.rst index 093cf34a..4ce85238 100644 --- a/docs/source/auto/line_profiler.explicit_profiler.rst +++ b/docs/source/auto/line_profiler.explicit_profiler.rst @@ -5,4 +5,3 @@ line\_profiler.explicit\_profiler module :members: :undoc-members: :show-inheritance: - :private-members: diff --git a/docs/source/auto/line_profiler.ipython_extension.rst b/docs/source/auto/line_profiler.ipython_extension.rst index 3a6e184e..57415a43 100644 --- a/docs/source/auto/line_profiler.ipython_extension.rst +++ b/docs/source/auto/line_profiler.ipython_extension.rst @@ -5,4 +5,3 @@ line\_profiler.ipython\_extension module :members: :undoc-members: :show-inheritance: - :private-members: diff --git a/docs/source/auto/line_profiler.line_profiler.rst b/docs/source/auto/line_profiler.line_profiler.rst index 8d37d151..72c45204 100644 --- a/docs/source/auto/line_profiler.line_profiler.rst +++ b/docs/source/auto/line_profiler.line_profiler.rst @@ -5,4 +5,3 @@ line\_profiler.line\_profiler module :members: :undoc-members: :show-inheritance: - :private-members: diff --git a/docs/source/auto/line_profiler.profiler_mixin.rst b/docs/source/auto/line_profiler.profiler_mixin.rst index 82e600f1..b766763e 100644 --- a/docs/source/auto/line_profiler.profiler_mixin.rst +++ b/docs/source/auto/line_profiler.profiler_mixin.rst @@ -5,4 +5,3 @@ line\_profiler.profiler\_mixin module :members: :undoc-members: :show-inheritance: - :private-members: diff --git a/docs/source/auto/line_profiler.rst b/docs/source/auto/line_profiler.rst index a2b108b3..28a17f71 100644 --- a/docs/source/auto/line_profiler.rst +++ b/docs/source/auto/line_profiler.rst @@ -30,4 +30,3 @@ Module contents :members: :undoc-members: :show-inheritance: - :private-members: diff --git a/docs/source/auto/line_profiler.scoping_policy.rst b/docs/source/auto/line_profiler.scoping_policy.rst index 20ce4822..931cbaa3 100644 --- a/docs/source/auto/line_profiler.scoping_policy.rst +++ b/docs/source/auto/line_profiler.scoping_policy.rst @@ -5,4 +5,3 @@ line\_profiler.scoping\_policy module :members: :undoc-members: :show-inheritance: - :private-members: diff --git a/docs/source/auto/line_profiler.timers.rst b/docs/source/auto/line_profiler.timers.rst index 40bee47d..72203db2 100644 --- a/docs/source/auto/line_profiler.timers.rst +++ b/docs/source/auto/line_profiler.timers.rst @@ -5,4 +5,3 @@ line\_profiler.timers module :members: :undoc-members: :show-inheritance: - :private-members: diff --git a/docs/source/auto/line_profiler.unset_trace.rst b/docs/source/auto/line_profiler.unset_trace.rst index 5b595182..d2e4bcc8 100644 --- a/docs/source/auto/line_profiler.unset_trace.rst +++ b/docs/source/auto/line_profiler.unset_trace.rst @@ -5,4 +5,3 @@ line\_profiler.unset\_trace module :members: :undoc-members: :show-inheritance: - :private-members: diff --git a/kernprof.py b/kernprof.py index a11cb2ad..a94f8dc2 100755 --- a/kernprof.py +++ b/kernprof.py @@ -706,7 +706,7 @@ def main(args=None): diagnostics.log.debug( f'Profiling script read from: {tempfile_source_and_content[0]}') else: - diagnostics.log.debug(f'Profiling script: {options.script}') + diagnostics.log.debug(f'Profiling script: {options.script!r}') with contextlib.ExitStack() as stack: enter = stack.enter_context @@ -869,8 +869,7 @@ def _write_preimports(prof, options, exclude): with temp_file as fobj: print(code, file=fobj) diagnostics.log.debug( - 'Wrote temporary module for pre-imports ' - f'to {temp_mod_path!r}:') + f'Wrote temporary module for pre-imports to {temp_mod_path!r}') else: with temp_file as fobj: write_eager_import_module(stream=fobj, **write_module_kwargs) @@ -904,7 +903,12 @@ def _dump_filtered_stats(tmpdir, prof, filename): for fname in fnames ] - if not tempfile_paths: + if not tempfile_paths or isinstance(prof, ContextualProfile): + # - No tempfiles written -> no function lives in tempfiles + # -> no need to filter anything + # - Not using `line_profiler` + # -> doesn't matter if the source lines can't be retrieved + # -> no need to filter anything prof.dump_stats(filename) return @@ -1060,36 +1064,33 @@ def _main_profile(options, module=False): """ script_file, prof = _pre_profile(options, module) try: - try: - rmod = functools.partial(run_module, - run_name='__main__', alter_sys=True) - ns = {'__file__': script_file, '__name__': '__main__', - 'execfile': execfile, 'rmod': rmod, - 'prof': prof} - if options.prof_mod and options.line_by_line: - from line_profiler.autoprofile import autoprofile - _call_with_diagnostics( - options, - autoprofile.run, script_file, ns, - prof_mod=options.prof_mod, - profile_imports=options.prof_imports, - as_module=module is not None) - elif module and options.builtin: - _call_with_diagnostics(options, rmod, options.script, ns) - elif options.builtin: - _call_with_diagnostics(options, execfile, script_file, ns, ns) - elif module: - _call_with_diagnostics( - options, - prof.runctx, f'rmod({options.script!r}, globals())', - ns, ns) - else: - _call_with_diagnostics( - options, - prof.runctx, f'execfile({script_file!r}, globals())', - ns, ns) - except (KeyboardInterrupt, SystemExit): - pass + rmod = functools.partial(run_module, + run_name='__main__', alter_sys=True) + ns = {'__file__': script_file, '__name__': '__main__', + 'execfile': execfile, 'rmod': rmod, + 'prof': prof} + if options.prof_mod and options.line_by_line: + from line_profiler.autoprofile import autoprofile + _call_with_diagnostics( + options, + autoprofile.run, script_file, ns, + prof_mod=options.prof_mod, + profile_imports=options.prof_imports, + as_module=module is not None) + elif module and options.builtin: + _call_with_diagnostics(options, rmod, options.script, ns) + elif options.builtin: + _call_with_diagnostics(options, execfile, script_file, ns, ns) + elif module: + _call_with_diagnostics( + options, + prof.runctx, f'rmod({options.script!r}, globals())', + ns, ns) + else: + _call_with_diagnostics( + options, + prof.runctx, f'execfile({script_file!r}, globals())', + ns, ns) finally: _post_profile(options, prof) diff --git a/line_profiler/profiler_mixin.py b/line_profiler/profiler_mixin.py index ab4cc59a..4b2e3867 100644 --- a/line_profiler/profiler_mixin.py +++ b/line_profiler/profiler_mixin.py @@ -126,43 +126,22 @@ def get_underlying_functions(cls, func): def _get_underlying_functions(cls, func, seen=None, stop_at_classes=False): if seen is None: seen = set() - get_underlying = functools.partial( - cls._get_underlying_functions, - seen=seen, stop_at_classes=stop_at_classes) + kwargs = {'seen': seen, 'stop_at_classes': stop_at_classes} + # Extract inner functions if any(check(func) for check in (is_boundmethod, is_classmethod, is_staticmethod)): - return get_underlying(func.__func__) + return cls._get_underlying_functions(func.__func__, **kwargs) if any(check(func) for check in (is_partial, is_partialmethod, is_cached_property)): - return get_underlying(func.func) + return cls._get_underlying_functions(func.func, **kwargs) + # Dispatch to specific handlers if is_property(func): - result = [] - for impl in func.fget, func.fset, func.fdel: - if impl is not None: - result.extend(get_underlying(impl)) - return result + return cls._get_underlying_functions_from_property(func, **kwargs) if isinstance(func, type): if stop_at_classes: return [func] - result = [] - get_filter = cls._class_scoping_policy.get_filter - func_check = get_filter(func, 'func') - cls_check = get_filter(func, 'class') - for member in vars(func).values(): - try: - member_funcs = get_underlying(member, stop_at_classes=True) - except TypeError: - continue - for impl in member_funcs: - is_type = isinstance(impl, type) - check = cls_check if is_type else func_check - if not check(impl): - continue - if is_type: - result.extend(get_underlying(impl)) - else: - result.append(impl) - return result + return cls._get_underlying_functions_from_type(func, **kwargs) + # Otherwise, the object should either be a function... if not callable(func): raise TypeError(f'func = {func!r}: ' f'cannot get functions from {type(func)} objects') @@ -173,11 +152,50 @@ def _get_underlying_functions(cls, func, seen=None, stop_at_classes=False): return [func] if is_c_level_callable(func): return [] + # ... or a generic callable func = type(func).__call__ if is_c_level_callable(func): # Can happen with builtin types return [] return [func] + @classmethod + def _get_underlying_functions_from_property( + cls, prop, seen, stop_at_classes): + result = [] + for impl in prop.fget, prop.fset, prop.fdel: + if impl is not None: + result.extend( + cls._get_underlying_functions(impl, seen, stop_at_classes)) + return result + + @classmethod + def _get_underlying_functions_from_type(cls, kls, seen, stop_at_classes): + result = [] + get_filter = cls._class_scoping_policy.get_filter + func_check = get_filter(kls, 'func') + cls_check = get_filter(kls, 'class') + for member in vars(kls).values(): + try: # Stop at class boundaries to enforce scoping behavior + member_funcs = cls._get_underlying_functions( + member, seen, stop_at_classes=True) + except TypeError: + continue + for impl in member_funcs: + if isinstance(impl, type): + # Only descend into nested classes if the policy + # says so + if cls_check(impl): + result.extend(cls._get_underlying_functions( + impl, seen, stop_at_classes)) + else: + # For non-class callables, they are already filtered + # (and added to `seen`) by the above call to + # `.get_underlying_functions()`, so just add them + # here + if func_check(impl): + result.append(impl) + return result + def _wrap_callable_wrapper(self, wrapper, impl_attrs, *, args=None, kwargs=None, name_attr=None): """ @@ -410,12 +428,11 @@ def wrap_class(self, func): get_filter = self._class_scoping_policy.get_filter func_check = get_filter(func, 'func') cls_check = get_filter(func, 'class') - get_underlying = functools.partial( - self._get_underlying_functions, stop_at_classes=True) members_to_wrap = {} for name, member in vars(func).items(): try: - impls = get_underlying(member) + impls = self._get_underlying_functions( + member, stop_at_classes=True) except TypeError: # Not a callable (wrapper) continue if any((cls_check(impl) diff --git a/tests/test_kernprof.py b/tests/test_kernprof.py index de9bb871..a835b344 100644 --- a/tests/test_kernprof.py +++ b/tests/test_kernprof.py @@ -183,10 +183,8 @@ def main(): [('', # Neutral verbosity level {'^Output to stdout': True, r"^Wrote .* '.*script\.py\.lprof'": True, - r'Parser output:''(?:\n)+'r'.*namespace\((?:.+,\n)*.*\)': False, r'^Inspect results with:''\n' r'python -m line_profiler .*script\.py\.lprof': True, - '^ *[0-9]+ *import zipfile': False, r'line_profiler\.autoprofile\.autoprofile' r'\.run\(\n(?:.+,\n)*.*\)': False, r'^\[kernprof .*\]': False,