From 8097a1fcf796fcfa459448adaf731ef693b0937d Mon Sep 17 00:00:00 2001 From: "Terence S.-C. Tsang" Date: Fri, 21 Mar 2025 11:44:46 +0100 Subject: [PATCH 1/8] More convenience updates in `kernprof` CHANGELOG.rst Edited entry kernprof.py __doc__ Updated main() - Updated function used to find name of the Python executable to be more lenient in abbreviating the name, requiring file identity (via `os.path.samefile()`) instead of string-path equality - Added new option `-c`, which causes the positional argument to be interpreted as an inline script (as with `python -c`) instead of the path to a script file - Added special case for `script = -` to read the script to profile from stdin --- CHANGELOG.rst | 1 + kernprof.py | 108 +++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 86 insertions(+), 23 deletions(-) diff --git a/CHANGELOG.rst b/CHANGELOG.rst index b9d96b1c..7d2f1809 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -11,6 +11,7 @@ Changes * FIX: Fixed explicit profiling of class methods; added handling for profiling static, bound, and partial methods, ``functools.partial`` objects, (cached) properties, and async generator functions * FIX: Fixed namespace bug when running ``kernprof -m`` on certain modules (e.g. ``calendar`` on Python 3.12+). * FIX: Fixed ``@contextlib.contextmanager`` bug where the cleanup code (e.g. restoration of ``sys`` attributes) is not run if exceptions occurred inside the context +* ENH: Added CLI arguments ``-c`` to ``kernprof`` for (auto-)profiling module/package/inline-script execution instead of that of script files; passing ``'-'`` as the script-file name now also reads from and profiles ``stdin`` 4.2.0 ~~~~~ diff --git a/kernprof.py b/kernprof.py index 52867331..67e7ebfd 100755 --- a/kernprof.py +++ b/kernprof.py @@ -51,12 +51,14 @@ def main(): .. code:: - usage: kernprof [-h] [-V] [-l] [-b] [-o OUTFILE] [-s SETUP] [-v] [-r] [-u UNIT] [-z] [-i [OUTPUT_INTERVAL]] [-p PROF_MOD] [-m] [--prof-imports] {script | -m module} ... + usage: kernprof [-h] [-V] [-l] [-b] [-o OUTFILE] [-s SETUP] [-v] [-r] [-u UNIT] [-z] [-i [OUTPUT_INTERVAL]] [-p PROF_MOD] [--prof-imports] + {path/to/script | -m path.to.module | -c "literal code"} ... Run and profile a python script. positional arguments: - {script | -m module} The python script file or module to run + {path/to/script | -m path.to.module | -c "literal code"} + The python script file, module, or literal code to run args Optional script arguments options: @@ -64,16 +66,16 @@ def main(): -V, --version show program's version number and exit -l, --line-by-line Use the line-by-line profiler instead of cProfile. Implies --builtin. -b, --builtin Put 'profile' in the builtins. Use 'profile.enable()'/'.disable()', '@profile' to decorate functions, or 'with profile:' to profile a section of code. - -o OUTFILE, --outfile OUTFILE + -o, --outfile OUTFILE Save stats to (default: 'scriptname.lprof' with --line-by-line, 'scriptname.prof' without) - -s SETUP, --setup SETUP - Code to execute before the code to profile + -s, --setup SETUP Code to execute before the code to profile -v, --view View the results of the profile in addition to saving it -r, --rich Use rich formatting if viewing output - -u UNIT, --unit UNIT Output unit (in seconds) in which the timing info is displayed (default: 1e-6) + -u, --unit UNIT Output unit (in seconds) in which the timing info is displayed (default: 1e-6) -z, --skip-zero Hide functions which have not been called - -i [OUTPUT_INTERVAL], --output-interval [OUTPUT_INTERVAL] - Enables outputting of cumulative profiling results to file every n seconds. Uses the threading module. Minimum value is 1 (second). Defaults to disabled. + -i, --output-interval [OUTPUT_INTERVAL] + Enables outputting of cumulative profiling results to file every n seconds. Uses the threading module. Minimum value is 1 (second). Defaults to + disabled. -p, --prof-mod PROF_MOD List of modules, functions and/or classes to profile specified by their name or path. List is comma separated, adding the current script path profiles the full script. Multiple copies of this flag can be supplied and the.list is extended. Only works with line_profiler -l, --line-by-line @@ -215,12 +217,10 @@ def _python_command(): Return a command that corresponds to :py:obj:`sys.executable`. """ import shutil - if shutil.which('python') == sys.executable: - return 'python' - elif shutil.which('python3') == sys.executable: - return 'python3' - else: - return sys.executable + for abbr in 'python', 'python3': + if os.path.samefile(shutil.which(abbr), sys.executable): + return abbr + return sys.executable class _restore_list: @@ -345,11 +345,22 @@ def positive_float(value): if args is None: args = sys.argv[1:] - # Special case: `kernprof [...] -m ` should terminate the - # parsing of all subsequent options - args, module, post_args = pre_parse_single_arg_directive(args, '-m') + # Special cases: `kernprof [...] -m ` or + # `kernprof [...] -c