From 55bd0322bd79f8476d75c4a686b421256ef794bc Mon Sep 17 00:00:00 2001 From: Keith Valin Date: Mon, 9 Feb 2026 15:19:26 -0500 Subject: [PATCH 1/9] refactor: Use well-known absolute path for test_tools --- pyperf/pyperf_run | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/pyperf/pyperf_run b/pyperf/pyperf_run index 9e3463c..ad824e7 100755 --- a/pyperf/pyperf_run +++ b/pyperf/pyperf_run @@ -26,6 +26,9 @@ copy_dirs="" benchmarks="all" test_name="pyperf" +TOOLS_BIN="$HOME/test_tools" +export TOOLS_BIN + base_dir=$(dirname $(realpath $0)) # # To make sure. @@ -104,8 +107,8 @@ install_tools() # Check to see if the test tools directory exists. If it does, we do not need to # clone the repo. # - if [ ! -d "test_tools" ]; then - git clone $tools_git test_tools + if [ ! -d "$TOOLS_BIN" ]; then + git clone $tools_git "$TOOLS_BIN" if [ $? -ne 0 ]; then exit_out "pulling git $tools_git failed." 1 fi @@ -243,7 +246,7 @@ else fi # Gather hardware information -${curdir}/test_tools/gather_data ${curdir} +$TOOLS_BIN/gather_data ${curdir} if [ ! -f "/tmp/pyperf.out" ]; then @@ -277,7 +280,7 @@ if [ -d "workloads" ]; then done fi -source test_tools/general_setup "$@" +source $TOOLS_BIN/general_setup "$@" ARGUMENT_LIST=( "pyperf_version" @@ -420,5 +423,5 @@ fi # # Process the data. # -${curdir}/test_tools/save_results --curdir $curdir --home_root $to_home_root --results /tmp/pyperf.out --test_name pyperf --tuned_setting=$to_tuned_setting --version NONE --user $to_user --other_files "python_results/*,test_results_report" $copy_dirs +${TOOLS_BIN}/save_results --curdir $curdir --home_root $to_home_root --results /tmp/pyperf.out --test_name pyperf --tuned_setting=$to_tuned_setting --version NONE --user $to_user --other_files "python_results/*,test_results_report" $copy_dirs exit 0 From 7c03ab5c5c09518c411cded7dda1ab1c345e034e Mon Sep 17 00:00:00 2001 From: Keith Valin Date: Wed, 11 Feb 2026 11:21:05 -0500 Subject: [PATCH 2/9] feat(verify): Add barebones results verification --- pyperf/pyperf_run | 3 +++ pyperf_schema.py | 9 +++++++++ 2 files changed, 12 insertions(+) create mode 100644 pyperf_schema.py diff --git a/pyperf/pyperf_run b/pyperf/pyperf_run index ad824e7..0d0afe0 100755 --- a/pyperf/pyperf_run +++ b/pyperf/pyperf_run @@ -409,6 +409,9 @@ fi generate_csv_file ${pyresults} ${start_time} ${end_time} +$TOOLS_BIN/csv_to_json $to_json_flags --csv_file ${pyresults}.csv --output_file pyperf.json +$TOOLS_BIN/verify_results $to_verify_flags --file pyperf.json --schema_file $to_script_dir/../pyperf_schema.py --class_name PyperfResults + if [[ $to_use_pcp -eq 1 ]]; then stop_pcp_subset stop_pcp diff --git a/pyperf_schema.py b/pyperf_schema.py new file mode 100644 index 0000000..560accf --- /dev/null +++ b/pyperf_schema.py @@ -0,0 +1,9 @@ +import pydantic +import datetime + +class PyperfResults(pydantic.BaseModel): + Test: str #todo make an enum + Avg: float = pydantic.Field(gt=0, allow_inf_nan=False) + Unit: str #todo make an enum + Start_Date: datetime.datetime + End_Date: datetime.datetime From f2b02f084bd783382385378fce25a00dd7489a04 Mon Sep 17 00:00:00 2001 From: Keith Valin Date: Thu, 12 Feb 2026 08:47:06 -0500 Subject: [PATCH 3/9] refactor(verify): Add Enums to verify the relevant fields --- pyperf_schema.py | 119 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 117 insertions(+), 2 deletions(-) diff --git a/pyperf_schema.py b/pyperf_schema.py index 560accf..438f5a3 100644 --- a/pyperf_schema.py +++ b/pyperf_schema.py @@ -1,9 +1,124 @@ import pydantic import datetime +from enum import Enum + +class TestNames(Enum): + two_to_three = "2to3" + async_generators = "async_generators" + async_tree_none = "async_tree_none" + async_tree_cpu_io_mixed = "async_tree_cpu_io_mixed" + async_tree_cpu_io_mixed_tg = "async_tree_cpu_io_mixed_tg" + async_tree_eager = "async_tree_eager" + async_tree_eager_cpu_io_mixed = "async_tree_eager_cpu_io_mixed" + async_tree_eager_cpu_io_mixed_tg = "async_tree_eager_cpu_io_mixed_tg" + async_tree_eager_io = "async_tree_eager_io" + async_tree_eager_io_tg = "async_tree_eager_io_tg" + async_tree_eager_memoization = "async_tree_eager_memoization" + async_tree_eager_memoization_tg = "async_tree_eager_memoization_tg" + async_tree_eager_tg = "async_tree_eager_tg" + async_tree_io = "async_tree_io" + async_tree_io_tg = "async_tree_io_tg" + async_tree_memoization = "async_tree_memoization" + async_tree_memoization_tg = "async_tree_memoization_tg" + async_tree_none_tg = "async_tree_none_tg" + asyncio_tcp = "asyncio_tcp" + asyncio_tcp_ssl = "asyncio_tcp_ssl" + asyncio_websockets = "asyncio_websockets" + chameleon = "chameleon" + chaos = "chaos" + comprehensions = "comprehensions" + bench_mp_pool = "bench_mp_pool" + bench_thread_pool = "bench_thread_pool" + coroutines = "coroutines" + coverage = "coverage" + crypto_pyaes = "crypto_pyaes" + dask = "dask" + deepcopy = "deepcopy" + deepcopy_reduce = "deepcopy_reduce" + deepcopy_memo = "deepcopy_memo" + deltablue = "deltablue" + django_template = "django_template" + docutils = "docutils" + dulwich_log = "dulwich_log" + fannkuch = "fannkuch" + float_test = "float" + create_gc_cycles = "create_gc_cycles" + gc_traversal = "gc_traversal" + generators = "generators" + genshi_text = "genshi_text" + genshi_xml = "genshi_xml" + go = "go" + hexiom = "hexiom" + html5lib = "html5lib" + json_dumps = "json_dumps" + json_loads = "json_loads" + logging_format = "logging_format" + logging_silent = "logging_silent" + logging_simple = "logging_simple" + mako = "mako" + mdp = "mdp" + meteor_contest = "meteor_contest" + nbody = "nbody" + nqueens = "nqueens" + pathlib = "pathlib" + pickle = "pickle" + pickle_dict = "pickle_dict" + pickle_list = "pickle_list" + pickle_pure_python = "pickle_pure_python" + pidigits = "pidigits" + pprint_safe_repr = "pprint_safe_repr" + pprint_pformat = "pprint_pformat" + pyflate = "pyflate" + python_startup = "python_startup" + python_startup_no_site = "python_startup_no_site" + raytrace = "raytrace" + regex_compile = "regex_compile" + regex_dna = "regex_dna" + regex_effbot = "regex_effbot" + regex_v8 = "regex_v8" + richards = "richards" + richards_super = "richards_super" + scimark_fft = "scimark_fft" + scimark_lu = "scimark_lu" + scimark_monte_carlo = "scimark_monte_carlo" + scimark_sor = "scimark_sor" + scimark_sparse_mat_mult = "scimark_sparse_mat_mult" + spectral_norm = "spectral_norm" + sqlalchemy_declarative = "sqlalchemy_declarative" + sqlalchemy_imperative = "sqlalchemy_imperative" + sqlglot_normalize = "sqlglot_normalize" + sqlglot_optimize = "sqlglot_optimize" + sqlglot_parse = "sqlglot_parse" + sqlglot_transpile = "sqlglot_transpile" + sqlite_synth = "sqlite_synth" + sympy_expand = "sympy_expand" + sympy_integrate = "sympy_integrate" + sympy_sum = "sympy_sum" + sympy_str = "sympy_str" + telco = "telco" + tomli_loads = "tomli_loads" + tornado_http = "tornado_http" + typing_runtime_protocols = "typing_runtime_protocols" + unpack_sequence = "unpack_sequence" + unpickle = "unpickle" + unpickle_list = "unpickle_list" + unpickle_pure_python = "unpickle_pure_python" + xml_etree_parse = "xml_etree_parse" + xml_etree_iterparse = "xml_etree_iterparse" + xml_etree_generate = "xml_etree_generate" + xml_etree_process = "xml_etree_process" + + +class DurationUnits(Enum): + second = "sec" + millisecond = "ms" + microsecond = "us" + nanosecond = "ns" + class PyperfResults(pydantic.BaseModel): - Test: str #todo make an enum + Test: TestNames Avg: float = pydantic.Field(gt=0, allow_inf_nan=False) - Unit: str #todo make an enum + Unit: DurationUnits Start_Date: datetime.datetime End_Date: datetime.datetime From 61d387df69a5641af27a38ee102223d3b02ab433 Mon Sep 17 00:00:00 2001 From: Keith Valin Date: Thu, 12 Feb 2026 12:47:11 -0500 Subject: [PATCH 4/9] fix: Cap setuptools version for chameleon benchmark Caps the setuptools version on pyperformance versions <=1.11.0. This is due to setuptools removing `pkg_resources` which breaks the chameleon benchmark in that pyperformance range. --- pyperf/pyperf_run | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/pyperf/pyperf_run b/pyperf/pyperf_run index 0d0afe0..4044fed 100755 --- a/pyperf/pyperf_run +++ b/pyperf/pyperf_run @@ -76,6 +76,24 @@ verify_benchmark_names() fi } +setup_pyperformance() +{ + major=$(echo $PYPERF_VERSION | cut -d. -f1) + minor=$(echo $PYPERF_VERSION | cut -d. -f2) + + # Revert to setuptools v81.0.0 due to `pkg_resources` removal at v82.0.0 + # https://setuptools.pypa.io/en/stable/history.html#deprecations-and-removals + # Skipping using pkg_tool to ensure that all relevant venv PATHs are honored + if [[ "$major" -le 1 ]] && [[ "$minor" -le 11 ]]; then + echo "Detected <=1.11.0, applying setuptools fix" + $python_exec -m pyperformance venv create + venv_path=$($python_exec -m pyperformance venv show | cut -d: -f2 | awk '{ print $1 }') + source $venv_path/bin/activate + python3 -m pip install setuptools==81.0.0 #Operating within venv, so $python_exec is not needed + deactivate + fi +} + install_tools() { show_usage=0 @@ -392,6 +410,9 @@ if [[ "$benchmarks" != "all" ]]; then verify_benchmark_names pyperf_flags="-b $benchmarks" fi + +setup_pyperformance + start_time=$(retrieve_time_stamp) $python_exec -m pyperformance run --output ${pyresults}.json $pyperf_flags if [ $? -ne 0 ]; then From a456bd3ebd6146b7987b3380b232726f6ee209db Mon Sep 17 00:00:00 2001 From: Keith Valin Date: Thu, 12 Feb 2026 15:40:11 -0500 Subject: [PATCH 5/9] refactor: Exit based off verification return code Exit based off verification return code, if it's a no-op it would return 0. This also moves the writing of /tmp/pyperf.out to an `exec` instead of trying to figure it out via tee/shell redirection. This removes a recursive call to the wrapper. --- pyperf/pyperf_run | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/pyperf/pyperf_run b/pyperf/pyperf_run index 4044fed..a7657f1 100755 --- a/pyperf/pyperf_run +++ b/pyperf/pyperf_run @@ -25,6 +25,7 @@ PYPERF_VERSION="1.11.0" copy_dirs="" benchmarks="all" test_name="pyperf" +rtc=0 TOOLS_BIN="$HOME/test_tools" export TOOLS_BIN @@ -266,22 +267,7 @@ fi # Gather hardware information $TOOLS_BIN/gather_data ${curdir} - -if [ ! -f "/tmp/pyperf.out" ]; then - command="${0} $@" - echo $command - $command 2>&1 | tee /tmp/pyperf.out - rtc=$? - if [ -f /tmp/pyperf.out ]; then - echo ================================= - echo Output from the test. - echo ================================= - cat /tmp/pyperf.out - rm /tmp/pyperf.out - fi - exit $rtc -fi - +exec &> >(tee /tmp/pyperf.out) if [ -d "workloads" ]; then # @@ -432,6 +418,7 @@ generate_csv_file ${pyresults} ${start_time} ${end_time} $TOOLS_BIN/csv_to_json $to_json_flags --csv_file ${pyresults}.csv --output_file pyperf.json $TOOLS_BIN/verify_results $to_verify_flags --file pyperf.json --schema_file $to_script_dir/../pyperf_schema.py --class_name PyperfResults +rtc=$? if [[ $to_use_pcp -eq 1 ]]; then stop_pcp_subset @@ -448,4 +435,4 @@ fi # Process the data. # ${TOOLS_BIN}/save_results --curdir $curdir --home_root $to_home_root --results /tmp/pyperf.out --test_name pyperf --tuned_setting=$to_tuned_setting --version NONE --user $to_user --other_files "python_results/*,test_results_report" $copy_dirs -exit 0 +exit $rtc From 7c193c8fb1e2a7bef04ee3aaa41da97953dd3525 Mon Sep 17 00:00:00 2001 From: Keith Valin Date: Thu, 12 Feb 2026 16:26:04 -0500 Subject: [PATCH 6/9] refactor: Code review Remove venv sourcing and use the pip binary directly. Use sed for cleaning up the path --- pyperf/pyperf_run | 9 ++++----- pyperf_schema.py | 2 +- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/pyperf/pyperf_run b/pyperf/pyperf_run index a7657f1..90006b7 100755 --- a/pyperf/pyperf_run +++ b/pyperf/pyperf_run @@ -82,16 +82,15 @@ setup_pyperformance() major=$(echo $PYPERF_VERSION | cut -d. -f1) minor=$(echo $PYPERF_VERSION | cut -d. -f2) + $python_exec -m pyperformance venv create + venv_path=$($python_exec -m pyperformance venv show | sed -e "s/Virtual environment path: //g" -e "s/ (already created)//" ) + # Revert to setuptools v81.0.0 due to `pkg_resources` removal at v82.0.0 # https://setuptools.pypa.io/en/stable/history.html#deprecations-and-removals # Skipping using pkg_tool to ensure that all relevant venv PATHs are honored if [[ "$major" -le 1 ]] && [[ "$minor" -le 11 ]]; then echo "Detected <=1.11.0, applying setuptools fix" - $python_exec -m pyperformance venv create - venv_path=$($python_exec -m pyperformance venv show | cut -d: -f2 | awk '{ print $1 }') - source $venv_path/bin/activate - python3 -m pip install setuptools==81.0.0 #Operating within venv, so $python_exec is not needed - deactivate + $venv_path/bin/pip3 install setuptools==81.0.0 # Execute venv pip instead fi } diff --git a/pyperf_schema.py b/pyperf_schema.py index 438f5a3..0f4c98d 100644 --- a/pyperf_schema.py +++ b/pyperf_schema.py @@ -32,7 +32,7 @@ class TestNames(Enum): coroutines = "coroutines" coverage = "coverage" crypto_pyaes = "crypto_pyaes" - dask = "dask" + dask = "dasks" deepcopy = "deepcopy" deepcopy_reduce = "deepcopy_reduce" deepcopy_memo = "deepcopy_memo" From ef093e4b677280414e4a31616ede05b95c900942 Mon Sep 17 00:00:00 2001 From: Keith Valin Date: Thu, 12 Feb 2026 16:34:59 -0500 Subject: [PATCH 7/9] review: Remove typo, add validation, use python venv interpreter --- pyperf/pyperf_run | 6 +++++- pyperf_schema.py | 2 +- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/pyperf/pyperf_run b/pyperf/pyperf_run index 90006b7..bb2b96a 100755 --- a/pyperf/pyperf_run +++ b/pyperf/pyperf_run @@ -85,12 +85,16 @@ setup_pyperformance() $python_exec -m pyperformance venv create venv_path=$($python_exec -m pyperformance venv show | sed -e "s/Virtual environment path: //g" -e "s/ (already created)//" ) + if [[ ! -x "$venv_path/bin/python3" ]]; then + exit_out "Error: Could not find interpreter at $venv_path/bin/python3, exiting" 1 + fi + # Revert to setuptools v81.0.0 due to `pkg_resources` removal at v82.0.0 # https://setuptools.pypa.io/en/stable/history.html#deprecations-and-removals # Skipping using pkg_tool to ensure that all relevant venv PATHs are honored if [[ "$major" -le 1 ]] && [[ "$minor" -le 11 ]]; then echo "Detected <=1.11.0, applying setuptools fix" - $venv_path/bin/pip3 install setuptools==81.0.0 # Execute venv pip instead + "$venv_path/bin/python3" install setuptools==81.0.0 # Execute venv pip instead fi } diff --git a/pyperf_schema.py b/pyperf_schema.py index 0f4c98d..438f5a3 100644 --- a/pyperf_schema.py +++ b/pyperf_schema.py @@ -32,7 +32,7 @@ class TestNames(Enum): coroutines = "coroutines" coverage = "coverage" crypto_pyaes = "crypto_pyaes" - dask = "dasks" + dask = "dask" deepcopy = "deepcopy" deepcopy_reduce = "deepcopy_reduce" deepcopy_memo = "deepcopy_memo" From 2268475f0d8be951c927d0258330924c7131dd29 Mon Sep 17 00:00:00 2001 From: Keith Valin Date: Thu, 12 Feb 2026 16:46:00 -0500 Subject: [PATCH 8/9] Update pyperf/pyperf_run Co-authored-by: qodo-code-review[bot] <151058649+qodo-code-review[bot]@users.noreply.github.com> --- pyperf/pyperf_run | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyperf/pyperf_run b/pyperf/pyperf_run index bb2b96a..5830fa6 100755 --- a/pyperf/pyperf_run +++ b/pyperf/pyperf_run @@ -94,7 +94,7 @@ setup_pyperformance() # Skipping using pkg_tool to ensure that all relevant venv PATHs are honored if [[ "$major" -le 1 ]] && [[ "$minor" -le 11 ]]; then echo "Detected <=1.11.0, applying setuptools fix" - "$venv_path/bin/python3" install setuptools==81.0.0 # Execute venv pip instead + "$venv_path/bin/python3" -m pip install --upgrade setuptools==81.0.0 # Execute venv pip instead fi } From f676b266adfe39aedd6fe71036090064cd03cdcb Mon Sep 17 00:00:00 2001 From: Keith Valin Date: Thu, 12 Feb 2026 16:48:30 -0500 Subject: [PATCH 9/9] Update pyperf/pyperf_run Co-authored-by: qodo-code-review[bot] <151058649+qodo-code-review[bot]@users.noreply.github.com> --- pyperf/pyperf_run | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pyperf/pyperf_run b/pyperf/pyperf_run index 5830fa6..e178597 100755 --- a/pyperf/pyperf_run +++ b/pyperf/pyperf_run @@ -85,6 +85,9 @@ setup_pyperformance() $python_exec -m pyperformance venv create venv_path=$($python_exec -m pyperformance venv show | sed -e "s/Virtual environment path: //g" -e "s/ (already created)//" ) + if [[ -z "$venv_path" ]]; then + exit_out "Error: Could not determine pyperformance venv path, exiting" 1 + fi if [[ ! -x "$venv_path/bin/python3" ]]; then exit_out "Error: Could not find interpreter at $venv_path/bin/python3, exiting" 1 fi