From 52773da0faddc808056ffec34ed0112e2b42b5f7 Mon Sep 17 00:00:00 2001 From: Daniel Moody Date: Fri, 15 May 2020 23:31:41 -0400 Subject: use signature to check function signature instead of relying on TypeErrors --- SCons/Subst.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/SCons/Subst.py b/SCons/Subst.py index a1ef053..29698d5 100644 --- a/SCons/Subst.py +++ b/SCons/Subst.py @@ -30,7 +30,7 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" import collections import re - +from inspect import signature import SCons.Errors from SCons.Util import is_String, is_Sequence @@ -420,12 +420,13 @@ class StringSubber(object): return conv(substitute(l, lvars)) return list(map(func, s)) elif callable(s): - try: + if (s and + set(signature(s).parameters.keys()) == set(['target', 'source', 'env', 'for_signature'])): s = s(target=lvars['TARGETS'], source=lvars['SOURCES'], env=self.env, for_signature=(self.mode != SUBST_CMD)) - except TypeError: + else: # This probably indicates that it's a callable # object that doesn't match our calling arguments # (like an Action). -- cgit v0.12 From cffd40b44080dcce1d238d037e026051eaa7a60b Mon Sep 17 00:00:00 2001 From: Daniel Moody Date: Sun, 17 May 2020 15:41:12 -0400 Subject: cover other type of subber and add test --- SCons/Subst.py | 5 +++-- test/Subst/TypeError.py | 28 ++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 2 deletions(-) diff --git a/SCons/Subst.py b/SCons/Subst.py index 29698d5..fbb89b3 100644 --- a/SCons/Subst.py +++ b/SCons/Subst.py @@ -591,12 +591,13 @@ class ListSubber(collections.UserList): self.substitute(a, lvars, 1) self.next_word() elif callable(s): - try: + if (s and + set(signature(s).parameters.keys()) == set(['target', 'source', 'env', 'for_signature'])): s = s(target=lvars['TARGETS'], source=lvars['SOURCES'], env=self.env, for_signature=(self.mode != SUBST_CMD)) - except TypeError: + else: # This probably indicates that it's a callable # object that doesn't match our calling arguments # (like an Action). diff --git a/test/Subst/TypeError.py b/test/Subst/TypeError.py index 628db2f..b288961 100644 --- a/test/Subst/TypeError.py +++ b/test/Subst/TypeError.py @@ -85,8 +85,36 @@ expect = expect_build % (r' \[foo\.bar\]', r'\$\{func\(1\)\}') test.run(status=2, stderr=expect) +# callable exceptions: +test.write('foo.c', """\ +#include +#include +int +main(int argc, char *argv[]) +{ + argv[argc++] = "--"; + printf("foo.c"); + exit (0); +} +""") + +test.write('SConstruct', """\ +class TestCallable(object): + def __init__(self, thing, makePathsRelative = True, debug = False): + pass + def __call__(self, target, source, env, for_signature): + raise TypeError("User callable exception") + +env = Environment() +env["TESTCLASS"] = TestCallable +env["CCCOM"] = "$CC $_CCCOMCOM $CCFLAGS -o ${TESTCLASS('$TARGET')} -c ${TESTCLASS('$SOURCES')}" + +env.Program(target='foo', source='foo.c') +""") +test.run(status=2, stderr=r'.*TypeError\s:\sUser\scallable\sexception.*') +print(test.stdout()) test.pass_test() # Local Variables: -- cgit v0.12 From 2a9eb7778a553d44bb2103953b4d563c9dab59d7 Mon Sep 17 00:00:00 2001 From: Daniel Moody Date: Sun, 17 May 2020 23:17:43 -0400 Subject: update CHANGES.txt --- CHANGES.txt | 16 ++++++++++------ 1 file changed, 10 insertions(+), 6 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 431ecc1..e22dafd 100755 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -10,12 +10,6 @@ NOTE: Please include a reference to any Issues resolved by your changes in the b RELEASE VERSION/DATE TO BE FILLED IN LATER - From Daniel Moody: - - Add no_progress (-Q) option as a set-able option. However, setting it in the - SConstruct/SConscript will still cause "scons: Reading SConscript files ..." to be - printed, since the option is not set when the build scripts first get read. - - Added check for SONAME in environment to setup symlinks correctly (Github Issue #3246) - From James Benton: - Improve Visual Studio solution/project generation code to add support for a per-variant cppflags. Intellisense can be affected by cppflags, @@ -86,6 +80,16 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER macro which is not located in a state TABLE. * Cleanup CPP expressions before evaluating (strip comments, carriage returns) + From Daniel Moody: + - Add no_progress (-Q) option as a set-able option. However, setting it in the + SConstruct/SConscript will still cause "scons: Reading SConscript files ..." to be + printed, since the option is not set when the build scripts first get read. + - Added check for SONAME in environment to setup symlinks correctly (Github Issue #3246) + - User callable's called during substition expansion could possibly throw a TypeError + exception, however SCons was using TypeError to detect if the callable had a different + signature than expected, and would silently fail to report user's exceptions. Fixed to + use signature module to detect function signature instead of TypeError. (Github Issue #3654) + From Andrew Morrow: - Fix Issue #3469 - Fixed improper reuse of temporary and compiled files by Configure when changing the order and/or number of tests. This is done by using the hash of the generated temporary files -- cgit v0.12 From 41bb0ffdb731767f7d251a8fa314820d02aa9c44 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Mon, 18 May 2020 10:19:01 -0700 Subject: Add py3.9 dev --- .travis.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index d2f06be..122e8d1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -58,6 +58,10 @@ jobs: python: 3.8 dist: bionic # required for Python >= 3.8 + - <<: *test_job + python: 3.9-dev + dist: bionic # required for Python >= 3.8 + - &coverage_jobs dist: bionic python: 3.7 -- cgit v0.12 From e17c6fee560cc6cdc14893f5b3453a8a4664bb05 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Mon, 18 May 2020 10:19:19 -0700 Subject: Update LDC version, remove py27 bits, add rpm --- .travis/install.sh | 19 ++++++++----------- 1 file changed, 8 insertions(+), 11 deletions(-) diff --git a/.travis/install.sh b/.travis/install.sh index 64a300b..29b9caa 100755 --- a/.travis/install.sh +++ b/.travis/install.sh @@ -19,6 +19,9 @@ else sudo ln -s /usr/local/clang-5.0.0/bin/clang++ /usr/bin/clang++ fi + # dependencies for rpm packaging tests + sudo apt-get -y install rpm + # dependencies for gdc tests sudo apt-get -y install gdc @@ -42,17 +45,11 @@ else sudo apt-get update && sudo apt-get -y --allow-unauthenticated install dmd-bin # dependencies for ldc tests - wget https://github.com/ldc-developers/ldc/releases/download/v1.15.0/ldc2-1.15.0-linux-x86_64.tar.xz - tar xf ldc2-1.15.0-linux-x86_64.tar.xz - sudo cp -rf ldc2-1.15.0-linux-x86_64/* / + export SCONS_LDC_VERSION=1.21.0 + wget https://github.com/ldc-developers/ldc/releases/download/v${SCONS_LDC_VERSION}/ldc2-${SCONS_LDC_VERSION}-linux-x86_64.tar.xz +# wget https://github.com/ldc-developers/ldc/releases/download/v1.15.0/ldc2-1.15.0-linux-x86_64.tar.xz + tar xf ldc2-${SCONS_LDC_VERSION}-linux-x86_64.tar.xz + sudo cp -rf ldc2-${SCONS_LDC_VERSION}-linux-x86_64/* / ls -l /usr/lib/*python*{so,a}* - - # For now skip swig if py27 - if [[ "$PYVER" == 27 ]]; then - # dependencies for swig tests - wget https://github.com/swig/swig/archive/rel-3.0.12.tar.gz - tar xzf rel-3.0.12.tar.gz - cd swig-rel-3.0.12 && ./autogen.sh && ./configure --prefix=/usr && make && sudo make install && cd .. - fi fi -- cgit v0.12 From 047975c68b4b74bebacb4721dbfcb472ec2b8715 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Mon, 18 May 2020 10:27:46 -0700 Subject: update path we look for python libs --- .travis/install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis/install.sh b/.travis/install.sh index 29b9caa..843f3b6 100755 --- a/.travis/install.sh +++ b/.travis/install.sh @@ -51,5 +51,5 @@ else tar xf ldc2-${SCONS_LDC_VERSION}-linux-x86_64.tar.xz sudo cp -rf ldc2-${SCONS_LDC_VERSION}-linux-x86_64/* / - ls -l /usr/lib/*python*{so,a}* + ls -l /usr/lib*/*python*{so,a}* fi -- cgit v0.12 From fec871dbdd03de409f93f5c2b64005bb9f4c17c3 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Mon, 18 May 2020 10:40:28 -0700 Subject: Update base ubuntu from trusty(14.04)->xenial(16.04), update install logic for dmd --- .travis.yml | 2 +- .travis/install.sh | 11 ++++++++--- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index 122e8d1..f0d1b45 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,4 +1,4 @@ -dist: trusty +dist: xenial language: python # Used: travis encrypt "chat.freenode.net#scons" --add notifications.irc diff --git a/.travis/install.sh b/.travis/install.sh index 843f3b6..2c07fc8 100755 --- a/.travis/install.sh +++ b/.travis/install.sh @@ -40,9 +40,14 @@ else sudo apt-get -y install python-pip python-dev build-essential libpcre3-dev autoconf automake libtool bison subversion git # dependencies for D tests - sudo wget http://master.dl.sourceforge.net/project/d-apt/files/d-apt.list -O /etc/apt/sources.list.d/d-apt.list - wget -qO - https://dlang.org/d-keyring.gpg | sudo apt-key add - - sudo apt-get update && sudo apt-get -y --allow-unauthenticated install dmd-bin + sudo wget https://netcologne.dl.sourceforge.net/project/d-apt/files/d-apt.list -O /etc/apt/sources.list.d/d-apt.list + sudo apt-get update --allow-insecure-repositories + sudo apt-get -y --allow-unauthenticated install --reinstall d-apt-keyring + sudo apt-get update && sudo apt-get install dmd-compiler dub + +# sudo wget http://master.dl.sourceforge.net/project/d-apt/files/d-apt.list -O /etc/apt/sources.list.d/d-apt.list +# wget -qO - https://dlang.org/d-keyring.gpg | sudo apt-key add - +# sudo apt-get update && sudo apt-get -y --allow-unauthenticated install dmd-bin # dependencies for ldc tests export SCONS_LDC_VERSION=1.21.0 -- cgit v0.12 From 49ebf1eda47c4a44b0717ff52b863d52f3eb5ab2 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Mon, 18 May 2020 10:49:26 -0700 Subject: not sure why 'ls -l /usr/lib*/*python*{so,a}*' is failing on these systems.. but commenting out --- .travis/install.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis/install.sh b/.travis/install.sh index 2c07fc8..25660c0 100755 --- a/.travis/install.sh +++ b/.travis/install.sh @@ -56,5 +56,6 @@ else tar xf ldc2-${SCONS_LDC_VERSION}-linux-x86_64.tar.xz sudo cp -rf ldc2-${SCONS_LDC_VERSION}-linux-x86_64/* / - ls -l /usr/lib*/*python*{so,a}* + # Failing.. ? +# ls -l /usr/lib*/*python*{so,a}* fi -- cgit v0.12 From a17a722ff8f79ea3027944b44914f7367873b4cf Mon Sep 17 00:00:00 2001 From: William Deegan Date: Mon, 18 May 2020 10:56:14 -0700 Subject: update to lxml 4.5.0 --- .travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index f0d1b45..c31b1fc 100644 --- a/.travis.yml +++ b/.travis.yml @@ -15,7 +15,7 @@ os: install: # needed for Docbook tests, must be in virtualenv context - - pip install lxml==4.3.3 + - pip install lxml==4.5.0 # do the rest of the image setup - ./.travis/install.sh -- cgit v0.12 From 4a0cd4864a82803c414023e69e5ba9419c295309 Mon Sep 17 00:00:00 2001 From: William Deegan Date: Mon, 18 May 2020 11:22:30 -0700 Subject: change test to change expected line # depending on python version --- test/packaging/rpm/explicit-target.py | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/test/packaging/rpm/explicit-target.py b/test/packaging/rpm/explicit-target.py index 0cf486e..66f5e4a 100644 --- a/test/packaging/rpm/explicit-target.py +++ b/test/packaging/rpm/explicit-target.py @@ -30,6 +30,7 @@ Test the ability to create a rpm package from a explicit target name. import os import TestSCons +import sys _python_ = TestSCons._python_ @@ -75,9 +76,15 @@ env.Package( NAME = 'foo', ) """ % locals()) + +if sys.version_info.minor >= 8: + line_number = 12 +else: + line_number = 23 + expect = """ scons: *** Setting target is not supported for rpm. -""" + test.python_file_line(test.workpath('SConstruct'), 12) +""" + test.python_file_line(test.workpath('SConstruct'), line_number) test.run(arguments='', status=2, stderr=expect) -- cgit v0.12 From 23a99693e3603a47f4bcb7b08351a774923b0f2e Mon Sep 17 00:00:00 2001 From: Daniel Moody Date: Mon, 18 May 2020 17:24:40 -0400 Subject: add check for SCons null class --- SCons/Subst.py | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/SCons/Subst.py b/SCons/Subst.py index fbb89b3..cc2eeab 100644 --- a/SCons/Subst.py +++ b/SCons/Subst.py @@ -420,7 +420,10 @@ class StringSubber(object): return conv(substitute(l, lvars)) return list(map(func, s)) elif callable(s): - if (s and + # SCons has the unusual Null class where any __getattr__ call returns it's self, + # which does not work the signature module, and the Null class returns an empty + # string if called on, so we make an exception in this condition for Null class + if (isinstance(s, SCons.Util.Null) or set(signature(s).parameters.keys()) == set(['target', 'source', 'env', 'for_signature'])): s = s(target=lvars['TARGETS'], source=lvars['SOURCES'], @@ -591,7 +594,10 @@ class ListSubber(collections.UserList): self.substitute(a, lvars, 1) self.next_word() elif callable(s): - if (s and + # SCons has the unusual Null class where any __getattr__ call returns it's self, + # which does not work the signature module, and the Null class returns an empty + # string if called on, so we make an exception in this condition for Null class + if (isinstance(s, SCons.Util.Null) or set(signature(s).parameters.keys()) == set(['target', 'source', 'env', 'for_signature'])): s = s(target=lvars['TARGETS'], source=lvars['SOURCES'], -- cgit v0.12 From b05ef58b9cc94e819a6f5477d4200237d384ef2b Mon Sep 17 00:00:00 2001 From: Daniel Moody Date: Tue, 19 May 2020 16:53:01 -0400 Subject: test coverage --- .appveyor.yml | 137 ++++++++-------------------------------------------------- .travis.yml | 12 ++--- 2 files changed, 24 insertions(+), 125 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index 40f13de..f6b56d7 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -14,10 +14,9 @@ cache: - C:\ProgramData\chocolatey\lib -> appveyor.yml install: - ### WINDOWS ### # add python and python user-base to path for pip installs - cmd: "C:\\%WINPYTHON%\\python.exe --version" - - cmd: for /F "tokens=*" %%g in ('C:\\%WINPYTHON%\\python.exe -m site --user-site') do (set PYSITEDIR=%%g) + - cmd: for /F "tokens=*" %%g in ('C:\\%WINPYTHON%\\python.exe -c "import sys; print(sys.path[-1])"') do (set PYSITEDIR=%%g) # use mingw 32 bit until #3291 is resolved - cmd: "set PATH=C:\\%WINPYTHON%;C:\\%WINPYTHON%\\Scripts;C:\\ProgramData\\chocolatey\\bin;C:\\MinGW\\bin;C:\\MinGW\\msys\\1.0\\bin;C:\\cygwin\\bin;C:\\msys64\\usr\\bin;C:\\msys64\\mingw64\\bin;%PATH%" - cmd: "C:\\%WINPYTHON%\\python.exe -m pip install -U --progress-bar off pip setuptools wheel " @@ -28,77 +27,24 @@ install: - cmd: set SCONS_CACHE_MSVC_CONFIG=true - cmd: set - ### LINUX ### - - sh: sudo add-apt-repository -y ppa:deadsnakes/ppa - # allow the CI to continue even if some pkg in the pkglist may not be up to date - - sh: sudo apt-get update || true - - sh: sudo apt-get -y install python$PYTHON - - sh: wget https://bootstrap.pypa.io/get-pip.py - - sh: sudo -H python$PYTHON get-pip.py - - sh: which python$PYTHON - - sh: python$PYTHON --version - - sh: export PYSITEDIR=$(python$PYTHON -m site --user-site) - - sh: python$PYTHON -m pip install --user -U --progress-bar off pip setuptools wheel - - sh: python$PYTHON -m pip install --user -U --progress-bar off coverage codecov - - sh: STATIC_DEPS=true python$PYTHON -m pip install --user -U --progress-bar off lxml - - sh: ./.travis/install.sh - - sh: printenv - # build matrix will be number of images multiplied by each '-' below, # less any exclusions. # split builds into sets of four jobs due to appveyor per-job time limit environment: matrix: - WINPYTHON: "Python35" - PYTHON: "3.5" - PYVER: 35 - BUILD_JOB_NUM: 1 COVERAGE: 0 - WINPYTHON: "Python36" - PYTHON: "3.6" - PYVER: 36 - BUILD_JOB_NUM: 1 COVERAGE: 1 - WINPYTHON: "Python37" - PYTHON: "3.7" - PYVER: 37 - BUILD_JOB_NUM: 1 COVERAGE: 0 - WINPYTHON: "Python38" - PYTHON: "3.8" - PYVER: 38 - BUILD_JOB_NUM: 1 COVERAGE: 0 - # - WINPYTHON: "Python35" - # PYTHON: "3.5" - # PYVER: 35 - # BUILD_JOB_NUM: 2 - # COVERAGE: 0 - - # - WINPYTHON: "Python36" - # PYTHON: "3.6" - # PYVER: 36 - # BUILD_JOB_NUM: 2 - # COVERAGE: 1 - - # - WINPYTHON: "Python37" - # PYTHON: "3.7" - # PYVER: 37 - # BUILD_JOB_NUM: 2 - # COVERAGE: 0 - - # - WINPYTHON: "Python38" - # PYTHON: "3.8" - # PYVER: 38 - # BUILD_JOB_NUM: 2 - # COVERAGE: 0 - - -# remove sets of build jobs based on critia below +# remove sets of build jobs based on criteria below # to fine tune the number and platforms tested matrix: exclude: @@ -124,13 +70,7 @@ matrix: - image: Visual Studio 2019 WINPYTHON: "Python36" - # skip on Ubuntu - - image: Ubuntu - WINPYTHON: "Python35" - - image: Ubuntu - WINPYTHON: "Python36" - -# remove some binaries we dont to be found +# remove some binaries we don't want to be found before_build: - ps: | if ($isWindows) { @@ -144,79 +84,36 @@ before_build: build: off build_script: - # get all tests into a list - - cmd: "C:\\%WINPYTHON%\\python.exe runtest.py -l -a > all_tests.txt" - - sh: python$PYTHON runtest.py -l -a > all_tests.txt # setup coverage by creating the coverage config file, and adding coverage - # to the usercustomize so that all python processes start with coverage + # to the sitecustomize so that all python processes start with coverage - ps: | if ($env:COVERAGE -eq 1) { $env:COVERAGE_PROCESS_START = "$($env:APPVEYOR_BUILD_FOLDER)/.coveragerc"; - $env:PYTHONNOUSERSITE = ""; - New-Item -ItemType Directory -Force -Path "$($env:PYSITEDIR)"; $env:COVERAGE_FILE = "$($env:APPVEYOR_BUILD_FOLDER)/.coverage"; - $usercustomizeText = "import os`r`nos.environ['COVERAGE_PROCESS_START'] = '$($env:COVERAGE_PROCESS_START)'`r`nimport coverage`r`ncoverage.process_startup()"; - $usercustomizeText|Set-Content "$($env:PYSITEDIR)/usercustomize.py"; - if ($isWindows) { - $coveragercFile = "[run]`r`nsource = $($env:APPVEYOR_BUILD_FOLDER)/SCons`r`nparallel = True`r`ndisable_warnings = trace-changed`r`nomit =`r`n`t*Tests.py`r`n`tsrc\test_*`r`n`tsrc\setup.py`r`n`r`n[path]`r`nsource = $($env:APPVEYOR_BUILD_FOLDER)`r`n[report]`r`nomit =`r`n`t*Tests.py`r`n`tsrc\test_*`r`n`tsrc\setup.py`r`n`r`n" - } - else - { - $coveragercFile = "[run]`nsource = $($env:APPVEYOR_BUILD_FOLDER)/SCons`nparallel = True`ndisable_warnings = trace-changed`nomit =`n`t*Tests.py`n`tsrc/test_*`n`tsrc/setup.py`n`n[path]`nsource = $($env:APPVEYOR_BUILD_FOLDER)`n[report]`nomit =`n`t*Tests.py`n`tsrc/test_*`n`tsrc/setup.py`n`n" - } + New-Item -ItemType Directory -Force -Path "$($env:PYSITEDIR)"; + $sitecustomizeText = "import os`r`nos.environ['COVERAGE_PROCESS_START'] = '$($env:COVERAGE_PROCESS_START)'`r`nos.environ['COVERAGE_FILE'] = '$($env:COVERAGE_FILE)'`r`nimport coverage`r`ncoverage.process_startup()"; + $sitecustomizeText|Set-Content "$($env:PYSITEDIR)/sitecustomize.py"; + Get-Content -Path "$($env:PYSITEDIR)/sitecustomize.py"; + $coveragercFile = "[run]`r`nsource = $($env:APPVEYOR_BUILD_FOLDER)/SCons`r`nparallel = True`r`ndisable_warnings = trace-changed`r`nomit =`r`n`t*Tests.py`r`n`t*\src\*`r`n`t*\test\*`r`n`t*\testing\*`r`n`t*\template\*`r`n`t*\scripts\*`r`n`t*\scons-time.py`r`n`t*\bootstrap.py`r`n`t*\runtest.py`r`n`t*\setup.py`r`n`r`n[path]`r`nsource = $($env:APPVEYOR_BUILD_FOLDER)`r`n[report]`r`nomit =`r`n`t*Tests.py`r`n`t*\src\*`r`n`t*\test\*`r`n`t*\testing\*`r`n`t*\template\*`r`n`t*\scripts\*`r`n`t*\scons-time.py`r`n`t*\bootstrap.py`r`n`t*\runtest.py`r`n`t*\setup.py`r`n`r`n" $coveragercFile|Set-Content "$($env:COVERAGE_PROCESS_START)"; + Get-Content -Path "$($env:COVERAGE_PROCESS_START)"; } - # exclude VS 10.0 because it hangs the testing until this is resolved: - # https://help.appveyor.com/discussions/problems/19283-visual-studio-2010-trial-license-has-expired - - ps: | - New-Item -Name exclude_list.txt -ItemType File - $workaround_image = "Visual Studio 2015" - if ($env:APPVEYOR_BUILD_WORKER_IMAGE -eq $workaround_image) { - Add-Content -Path 'exclude_list.txt' -Value 'test\MSVS\vs-10.0-exec.py' - } - # setup portion of tests for this build job (1-4) - - ps: | - $TOTAL_BUILD_JOBS = 1; - $Lines = (Get-Content all_tests.txt | Measure-Object -line).Lines; - $start = ($Lines / $TOTAL_BUILD_JOBS) * ($Env:BUILD_JOB_NUM - 1); - $end = ($Lines / $TOTAL_BUILD_JOBS) * $Env:BUILD_JOB_NUM; - if ( $Env:BUILD_JOB_NUM -eq $TOTAL_BUILD_JOBS){ $end = $Lines }; - if ( $start -eq 0 ){ $start = 1 }; - get-content all_tests.txt | select -first ($end - $start) -skip ($start - 1) | Out-File -Encoding ASCII build_tests.txt; - - # Windows run the tests - # NOTE: running powershell from cmd on purpose because it formats the output - # correctly - - cmd: powershell -Command "& { if($env:COVERAGE -eq 1) { coverage run -p --rcfile=$($env:COVERAGE_PROCESS_START) runtest.py -j 2 -t --exclude-list exclude_list.txt -f build_tests.txt } else { C:\\%WINPYTHON%\\python.exe runtest.py -j 2 -t --exclude-list exclude_list.txt -f build_tests.txt }; if($LastExitCode -eq 2 -Or $LastExitCode -eq 0) { $host.SetShouldExit(0 )} else {$host.SetShouldExit(1)}}" - - # linux run the tests - # unset JAVA_TOOL_OPTIONS because newer java prints this to stderr - - sh: | - unset JAVA_TOOL_OPTIONS - if [ "$COVERAGE" -eq "1" ]; then - coverage run -p --rcfile="$COVERAGE_PROCESS_START" runtest.py --exclude-list exclude_list.txt -f build_tests.txt || if [[ $? == 2 ]]; then true; else false; fi; - else - python$PYTHON runtest.py -j 2 --exclude-list exclude_list.txt -f build_tests.txt || if [[ $? == 2 ]]; then true; else false; fi; - fi - -# run converage even if there was a test failure + # NOTE: running powershell from cmd is intended because + # it formats the output correctly + - cmd: powershell -Command "& { if($env:COVERAGE -eq 1) { coverage run -p --rcfile=$($env:COVERAGE_PROCESS_START) runtest.py -j 2 -t -a } else { C:\\%WINPYTHON%\\python.exe runtest.py -j 2 -t -a }; if($LastExitCode -eq 2 -Or $LastExitCode -eq 0) { $host.SetShouldExit(0 )} else {$host.SetShouldExit(1)}}" + +# run coverage even if there was a test failure on_finish: - ps: | if ($env:COVERAGE -eq 1) { & coverage combine & coverage report - & coverage xml -o coverage_xml.xml + & coverage -i xml -o coverage_xml.xml } # running codecov in powershell causes an error so running in platform # shells - cmd: if %COVERAGE% equ 1 codecov -X gcov --file coverage_xml.xml - - sh: if [ $COVERAGE -eq 1 ]; then codecov -X gcov --file coverage_xml.xml; fi - # not using coveralls, so leaving it commented out in case we switch back - #- cmd: "C:\\%WINPYTHON%\\python.exe -m pip install --user -U coveralls" - #- sh: python$PYTHON -m pip install --user -U coveralls - #- ps: coveralls --rcfile="$($env:COVERAGE_PROCESS_START)" - # - ps: $blockRdp = $true; iex ((new-object net.webclient).DownloadString('https://raw.githubusercontent.com/appveyor/ci/master/scripts/enable-rdp.ps1')) - + diff --git a/.travis.yml b/.travis.yml index c31b1fc..25da402 100644 --- a/.travis.yml +++ b/.travis.yml @@ -72,23 +72,25 @@ jobs: # setup sitecustomize so we can make all subprocess start coverage - export PYSITEDIR=$(python -c "import sys; print(sys.path[-1])") - export COVERAGE_PROCESS_START="$PWD/.coveragerc" + - export COVERAGE_FILE="$PWD/.coverage" - mkdir -p "$PYSITEDIR" - echo "$PYSITEDIR" - touch "${PYSITEDIR}/sitecustomize.py" - echo "import os" | tee --append "${PYSITEDIR}/sitecustomize.py" - echo "os.environ['COVERAGE_PROCESS_START'] = '$PWD/.coveragerc'" | tee --append "${PYSITEDIR}/sitecustomize.py" + - echo "os.environ['COVERAGE_FILE'] = '$PWD/.coverage'" | tee --append "${PYSITEDIR}/sitecustomize.py" - echo "import coverage" | tee --append "${PYSITEDIR}/sitecustomize.py" - echo "coverage.process_startup()" | tee --append "${PYSITEDIR}/sitecustomize.py" - cat "${PYSITEDIR}/sitecustomize.py" # write the coverage config file - echo "[run]" >> "$PWD/.coveragerc" - - echo "source = $PWD/SCons" >> "$PWD/.coveragerc" + - echo "source = $PWD" >> "$PWD/.coveragerc" - echo "parallel = True" >> "$PWD/.coveragerc" - - echo "omit = *Tests.py" >> "$PWD/.coveragerc" + - printf "omit =\n\t*Tests.py\n\t*/src/*\n\t*/test/*\n\t*/testing/*\n\t*/template/*\n\t*/scripts/*\n\t*/scons-time.py\n\t*/bootstrap.py\n\t*/runtest.py\n\t*/setup.py\n\n" >> "$PWD/.coveragerc" - echo "[path]" >> "$PWD/.coveragerc" - echo "source = $PWD" >> "$PWD/.coveragerc" - echo "[report]" >> "$PWD/.coveragerc" - - echo "omit = *Tests.py" >> "$PWD/.coveragerc" + - printf "omit =\n\t*Tests.py\n\t*/src/*\n\t*/test/*\n\t*/testing/*\n\t*/template/*\n\t*/scripts/*\n\t*/scons-time.py\n\t*/bootstrap.py\n\t*/runtest.py\n\t*/setup.py\n\n" >> "$PWD/.coveragerc" - cat "$PWD/.coveragerc" script: @@ -97,5 +99,5 @@ jobs: after_script: - coverage combine - coverage report - - coverage xml -o coverage_xml.xml - - codecov -X gcov --file coverage_xml.xml + - coverage xml -i -o coverage_xml.xml + - codecov -X gcov --file coverage_xml.xml \ No newline at end of file -- cgit v0.12 From deacd0898818a0358fc29e0edff387acda5b7bc5 Mon Sep 17 00:00:00 2001 From: Daniel Moody Date: Tue, 19 May 2020 17:14:41 -0400 Subject: used fixtures in test TypeError test --- test/Subst/TypeError.py | 32 +++--------------------- test/Subst/fixture/SConstruct.callable_exception | 11 ++++++++ 2 files changed, 15 insertions(+), 28 deletions(-) create mode 100644 test/Subst/fixture/SConstruct.callable_exception diff --git a/test/Subst/TypeError.py b/test/Subst/TypeError.py index b288961..c52434f 100644 --- a/test/Subst/TypeError.py +++ b/test/Subst/TypeError.py @@ -85,36 +85,12 @@ expect = expect_build % (r' \[foo\.bar\]', r'\$\{func\(1\)\}') test.run(status=2, stderr=expect) -# callable exceptions: -test.write('foo.c', """\ -#include -#include -int -main(int argc, char *argv[]) -{ - argv[argc++] = "--"; - printf("foo.c"); - exit (0); -} -""") - -test.write('SConstruct', """\ - -class TestCallable(object): - def __init__(self, thing, makePathsRelative = True, debug = False): - pass - def __call__(self, target, source, env, for_signature): - raise TypeError("User callable exception") - -env = Environment() -env["TESTCLASS"] = TestCallable -env["CCCOM"] = "$CC $_CCCOMCOM $CCFLAGS -o ${TESTCLASS('$TARGET')} -c ${TESTCLASS('$SOURCES')}" - -env.Program(target='foo', source='foo.c') -""") +# user callable exceptions (Github issue #3654): +test.file_fixture('test_main.c') +test.file_fixture('./fixture/SConstruct.callable_exception', 'SConstruct') test.run(status=2, stderr=r'.*TypeError\s:\sUser\scallable\sexception.*') -print(test.stdout()) + test.pass_test() # Local Variables: diff --git a/test/Subst/fixture/SConstruct.callable_exception b/test/Subst/fixture/SConstruct.callable_exception new file mode 100644 index 0000000..3f1b337 --- /dev/null +++ b/test/Subst/fixture/SConstruct.callable_exception @@ -0,0 +1,11 @@ +class TestCallable(object): + def __init__(self, thing, makePathsRelative = True, debug = False): + pass + def __call__(self, target, source, env, for_signature): + raise TypeError("User callable exception") + +env = Environment() +env["TESTCLASS"] = TestCallable +env["CCCOM"] = "$CC $_CCCOMCOM $CCFLAGS -o ${TESTCLASS('$TARGET')} -c ${TESTCLASS('$SOURCES')}" + +env.Program(target='test_main', source='test_main.c') \ No newline at end of file -- cgit v0.12 From aa5378e37ff2ab4e70be6d6b2af0cd79597da7dc Mon Sep 17 00:00:00 2001 From: Daniel Moody Date: Tue, 19 May 2020 20:08:22 -0400 Subject: msvc 10 still hangs --- .appveyor.yml | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/.appveyor.yml b/.appveyor.yml index f6b56d7..4d7706e 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -85,6 +85,15 @@ build: off build_script: + # exclude VS 10.0 because it hangs the testing until this is resolved: + # https://help.appveyor.com/discussions/problems/19283-visual-studio-2010-trial-license-has-expired + - ps: | + New-Item -Name exclude_list.txt -ItemType File + $workaround_image = "Visual Studio 2015" + if ($env:APPVEYOR_BUILD_WORKER_IMAGE -eq $workaround_image) { + Add-Content -Path 'exclude_list.txt' -Value 'test\MSVS\vs-10.0-exec.py' + } + # setup coverage by creating the coverage config file, and adding coverage # to the sitecustomize so that all python processes start with coverage - ps: | @@ -103,7 +112,7 @@ build_script: # NOTE: running powershell from cmd is intended because # it formats the output correctly - - cmd: powershell -Command "& { if($env:COVERAGE -eq 1) { coverage run -p --rcfile=$($env:COVERAGE_PROCESS_START) runtest.py -j 2 -t -a } else { C:\\%WINPYTHON%\\python.exe runtest.py -j 2 -t -a }; if($LastExitCode -eq 2 -Or $LastExitCode -eq 0) { $host.SetShouldExit(0 )} else {$host.SetShouldExit(1)}}" + - cmd: powershell -Command "& { if($env:COVERAGE -eq 1) { coverage run -p --rcfile=$($env:COVERAGE_PROCESS_START) runtest.py -j 2 -t --exclude-list exclude_list.txt -a } else { C:\\%WINPYTHON%\\python.exe runtest.py -j 2 -t --exclude-list exclude_list.txt -a }; if($LastExitCode -eq 2 -Or $LastExitCode -eq 0) { $host.SetShouldExit(0 )} else {$host.SetShouldExit(1)}}" # run coverage even if there was a test failure on_finish: @@ -111,7 +120,7 @@ on_finish: if ($env:COVERAGE -eq 1) { & coverage combine & coverage report - & coverage -i xml -o coverage_xml.xml + & coverage xml -i -o coverage_xml.xml } # running codecov in powershell causes an error so running in platform # shells -- cgit v0.12 From 1f122243645de24911a5b6196d1cf735e56c4452 Mon Sep 17 00:00:00 2001 From: Daniel Moody Date: Wed, 20 May 2020 12:10:40 -0400 Subject: clean up CI files --- .appveyor.yml | 52 ++++----------------------- .appveyor/coverage_report.ps1 | 5 +++ .appveyor/coverage_setup.ps1 | 13 +++++++ .appveyor/disable_msvc_10.ps1 | 5 +++ .appveyor/ignore_git_bins.ps1 | 5 +++ .appveyor/install.bat | 11 ++++++ .coverage_templates/.coveragerc.template | 30 ++++++++++++++++ .coverage_templates/sitecustomize.py.template | 5 +++ .travis.yml | 26 +------------- .travis/.coveragerc | 8 ----- .travis/coverage_setup.sh | 21 +++++++++++ 11 files changed, 102 insertions(+), 79 deletions(-) create mode 100644 .appveyor/coverage_report.ps1 create mode 100644 .appveyor/coverage_setup.ps1 create mode 100644 .appveyor/disable_msvc_10.ps1 create mode 100644 .appveyor/ignore_git_bins.ps1 create mode 100644 .appveyor/install.bat create mode 100644 .coverage_templates/.coveragerc.template create mode 100644 .coverage_templates/sitecustomize.py.template delete mode 100644 .travis/.coveragerc create mode 100755 .travis/coverage_setup.sh diff --git a/.appveyor.yml b/.appveyor.yml index 4d7706e..6689594 100644 --- a/.appveyor.yml +++ b/.appveyor.yml @@ -2,7 +2,6 @@ image: # linux builds done in Travis CI for now - # - Ubuntu - Visual Studio 2015 - Visual Studio 2017 - Visual Studio 2019 @@ -15,17 +14,7 @@ cache: install: # add python and python user-base to path for pip installs - - cmd: "C:\\%WINPYTHON%\\python.exe --version" - - cmd: for /F "tokens=*" %%g in ('C:\\%WINPYTHON%\\python.exe -c "import sys; print(sys.path[-1])"') do (set PYSITEDIR=%%g) - # use mingw 32 bit until #3291 is resolved - - cmd: "set PATH=C:\\%WINPYTHON%;C:\\%WINPYTHON%\\Scripts;C:\\ProgramData\\chocolatey\\bin;C:\\MinGW\\bin;C:\\MinGW\\msys\\1.0\\bin;C:\\cygwin\\bin;C:\\msys64\\usr\\bin;C:\\msys64\\mingw64\\bin;%PATH%" - - cmd: "C:\\%WINPYTHON%\\python.exe -m pip install -U --progress-bar off pip setuptools wheel " - - cmd: "C:\\%WINPYTHON%\\python.exe -m pip install -U --progress-bar off pypiwin32 coverage codecov" - - cmd: set STATIC_DEPS=true & C:\\%WINPYTHON%\\python.exe -m pip install -U --progress-bar off lxml - # install 3rd party tools to test with - - cmd: choco install --allow-empty-checksums dmd ldc swig vswhere xsltproc winflexbison - - cmd: set SCONS_CACHE_MSVC_CONFIG=true - - cmd: set + - cmd: .\.appveyor\install.bat # build matrix will be number of images multiplied by each '-' below, # less any exclusions. @@ -72,14 +61,7 @@ matrix: # remove some binaries we don't want to be found before_build: - - ps: | - if ($isWindows) { - dir "C:\Program Files\Git\usr\bin\x*.exe" - if (Test-Path "C:\Program Files\Git\usr\bin\xsltproc.EXE" ) { - Remove-Item "C:\Program Files\Git\usr\bin\xsltproc.EXE" -ErrorAction Ignore - } - dir "C:\Program Files\Git\usr\bin\x*.exe" - } + - ps: .\.appveyor\ignore_git_bins.ps1 build: off @@ -87,41 +69,19 @@ build_script: # exclude VS 10.0 because it hangs the testing until this is resolved: # https://help.appveyor.com/discussions/problems/19283-visual-studio-2010-trial-license-has-expired - - ps: | - New-Item -Name exclude_list.txt -ItemType File - $workaround_image = "Visual Studio 2015" - if ($env:APPVEYOR_BUILD_WORKER_IMAGE -eq $workaround_image) { - Add-Content -Path 'exclude_list.txt' -Value 'test\MSVS\vs-10.0-exec.py' - } + - ps: .\.appveyor\disable_msvc_10.ps1 # setup coverage by creating the coverage config file, and adding coverage # to the sitecustomize so that all python processes start with coverage - - ps: | - if ($env:COVERAGE -eq 1) { - $env:COVERAGE_PROCESS_START = "$($env:APPVEYOR_BUILD_FOLDER)/.coveragerc"; - $env:COVERAGE_FILE = "$($env:APPVEYOR_BUILD_FOLDER)/.coverage"; - New-Item -ItemType Directory -Force -Path "$($env:PYSITEDIR)"; - $sitecustomizeText = "import os`r`nos.environ['COVERAGE_PROCESS_START'] = '$($env:COVERAGE_PROCESS_START)'`r`nos.environ['COVERAGE_FILE'] = '$($env:COVERAGE_FILE)'`r`nimport coverage`r`ncoverage.process_startup()"; - $sitecustomizeText|Set-Content "$($env:PYSITEDIR)/sitecustomize.py"; - Get-Content -Path "$($env:PYSITEDIR)/sitecustomize.py"; - $coveragercFile = "[run]`r`nsource = $($env:APPVEYOR_BUILD_FOLDER)/SCons`r`nparallel = True`r`ndisable_warnings = trace-changed`r`nomit =`r`n`t*Tests.py`r`n`t*\src\*`r`n`t*\test\*`r`n`t*\testing\*`r`n`t*\template\*`r`n`t*\scripts\*`r`n`t*\scons-time.py`r`n`t*\bootstrap.py`r`n`t*\runtest.py`r`n`t*\setup.py`r`n`r`n[path]`r`nsource = $($env:APPVEYOR_BUILD_FOLDER)`r`n[report]`r`nomit =`r`n`t*Tests.py`r`n`t*\src\*`r`n`t*\test\*`r`n`t*\testing\*`r`n`t*\template\*`r`n`t*\scripts\*`r`n`t*\scons-time.py`r`n`t*\bootstrap.py`r`n`t*\runtest.py`r`n`t*\setup.py`r`n`r`n" - $coveragercFile|Set-Content "$($env:COVERAGE_PROCESS_START)"; - Get-Content -Path "$($env:COVERAGE_PROCESS_START)"; - } - - + - ps: .\.appveyor\coverage_setup.ps1 + # NOTE: running powershell from cmd is intended because # it formats the output correctly - cmd: powershell -Command "& { if($env:COVERAGE -eq 1) { coverage run -p --rcfile=$($env:COVERAGE_PROCESS_START) runtest.py -j 2 -t --exclude-list exclude_list.txt -a } else { C:\\%WINPYTHON%\\python.exe runtest.py -j 2 -t --exclude-list exclude_list.txt -a }; if($LastExitCode -eq 2 -Or $LastExitCode -eq 0) { $host.SetShouldExit(0 )} else {$host.SetShouldExit(1)}}" # run coverage even if there was a test failure on_finish: - - ps: | - if ($env:COVERAGE -eq 1) { - & coverage combine - & coverage report - & coverage xml -i -o coverage_xml.xml - } + - ps: .\.appveyor\coverage_report.ps1 # running codecov in powershell causes an error so running in platform # shells - cmd: if %COVERAGE% equ 1 codecov -X gcov --file coverage_xml.xml diff --git a/.appveyor/coverage_report.ps1 b/.appveyor/coverage_report.ps1 new file mode 100644 index 0000000..43e2328 --- /dev/null +++ b/.appveyor/coverage_report.ps1 @@ -0,0 +1,5 @@ +if ($env:COVERAGE -eq 1) { + & coverage combine; + & coverage report; + & coverage xml -i -o coverage_xml.xml; +} diff --git a/.appveyor/coverage_setup.ps1 b/.appveyor/coverage_setup.ps1 new file mode 100644 index 0000000..0906cb9 --- /dev/null +++ b/.appveyor/coverage_setup.ps1 @@ -0,0 +1,13 @@ +if ($env:COVERAGE -eq 1) { + $env:COVERAGE_PROCESS_START = "$($env:APPVEYOR_BUILD_FOLDER)\.coveragerc"; + $env:COVERAGE_FILE = "$($env:APPVEYOR_BUILD_FOLDER)\.coverage"; + New-Item -ItemType Directory -Force -Path "$($env:PYSITEDIR)"; + + (Get-Content -path .coverage_templates\.coveragerc.template -Raw) -replace '\$PWD',"$((Get-Location) -replace '\\', '/')" | Set-Content -Path "$($env:COVERAGE_PROCESS_START)"; + (Get-Content -path .coverage_templates\sitecustomize.py.template -Raw) -replace '\$PWD',"$((Get-Location) -replace '\\', '/')" | Set-Content -Path "$($env:PYSITEDIR)\sitecustomize.py"; + + Write-Host "$($env:PYSITEDIR)\sitecustomize.py"; + Get-Content -Path "$($env:PYSITEDIR)\sitecustomize.py"; + Write-Host "$($env:COVERAGE_PROCESS_START)"; + Get-Content -Path "$($env:COVERAGE_PROCESS_START)"; +} diff --git a/.appveyor/disable_msvc_10.ps1 b/.appveyor/disable_msvc_10.ps1 new file mode 100644 index 0000000..086f1e4 --- /dev/null +++ b/.appveyor/disable_msvc_10.ps1 @@ -0,0 +1,5 @@ +New-Item -Name exclude_list.txt -ItemType File; +$workaround_image = "Visual Studio 2015"; +if ($env:APPVEYOR_BUILD_WORKER_IMAGE -eq $workaround_image) { + Add-Content -Path 'exclude_list.txt' -Value 'test\MSVS\vs-10.0-exec.py'; +} diff --git a/.appveyor/ignore_git_bins.ps1 b/.appveyor/ignore_git_bins.ps1 new file mode 100644 index 0000000..4e4202e --- /dev/null +++ b/.appveyor/ignore_git_bins.ps1 @@ -0,0 +1,5 @@ +dir "C:\Program Files\Git\usr\bin\x*.exe"; +if (Test-Path "C:\Program Files\Git\usr\bin\xsltproc.EXE" ) { + Remove-Item "C:\Program Files\Git\usr\bin\xsltproc.EXE" -ErrorAction Ignore; +} +dir "C:\Program Files\Git\usr\bin\x*.exe"; diff --git a/.appveyor/install.bat b/.appveyor/install.bat new file mode 100644 index 0000000..67a064d --- /dev/null +++ b/.appveyor/install.bat @@ -0,0 +1,11 @@ +C:\\%WINPYTHON%\\python.exe --version +for /F "tokens=*" %%g in ('C:\\%WINPYTHON%\\python.exe -c "import sys; print(sys.path[-1])"') do (set PYSITEDIR=%%g) +REM use mingw 32 bit until #3291 is resolved +set PATH=C:\\%WINPYTHON%;C:\\%WINPYTHON%\\Scripts;C:\\ProgramData\\chocolatey\\bin;C:\\MinGW\\bin;C:\\MinGW\\msys\\1.0\\bin;C:\\cygwin\\bin;C:\\msys64\\usr\\bin;C:\\msys64\\mingw64\\bin;%PATH% +C:\\%WINPYTHON%\\python.exe -m pip install -U --progress-bar off pip setuptools wheel +C:\\%WINPYTHON%\\python.exe -m pip install -U --progress-bar off pypiwin32 coverage codecov +set STATIC_DEPS=true & C:\\%WINPYTHON%\\python.exe -m pip install -U --progress-bar off lxml +REM install 3rd party tools to test with +choco install --allow-empty-checksums dmd ldc swig vswhere xsltproc winflexbison +set SCONS_CACHE_MSVC_CONFIG=true +set diff --git a/.coverage_templates/.coveragerc.template b/.coverage_templates/.coveragerc.template new file mode 100644 index 0000000..6413924 --- /dev/null +++ b/.coverage_templates/.coveragerc.template @@ -0,0 +1,30 @@ +[run] +source = $PWD +parallel = True +omit = + *Tests.py + */src/* + */test/* + */testing/* + */template/* + */scripts/* + */scons-time.py + */bootstrap.py + */runtest.py + */setup.py + +[path] +source = $PWD + +[report] +omit = + *Tests.py + */src/* + */test/* + */testing/* + */template/* + */scripts/* + */scons-time.py + */bootstrap.py + */runtest.py + */setup.py diff --git a/.coverage_templates/sitecustomize.py.template b/.coverage_templates/sitecustomize.py.template new file mode 100644 index 0000000..06bed8a --- /dev/null +++ b/.coverage_templates/sitecustomize.py.template @@ -0,0 +1,5 @@ +import os +os.environ['COVERAGE_PROCESS_START'] = r'$PWD/.coveragerc' +os.environ['COVERAGE_FILE'] = r'$PWD/.coverage' +import coverage +coverage.process_startup() diff --git a/.travis.yml b/.travis.yml index 25da402..9fa9376 100644 --- a/.travis.yml +++ b/.travis.yml @@ -67,31 +67,7 @@ jobs: python: 3.7 name: coverage before_script: - - python -m pip install -U coverage codecov - - python -m site - # setup sitecustomize so we can make all subprocess start coverage - - export PYSITEDIR=$(python -c "import sys; print(sys.path[-1])") - - export COVERAGE_PROCESS_START="$PWD/.coveragerc" - - export COVERAGE_FILE="$PWD/.coverage" - - mkdir -p "$PYSITEDIR" - - echo "$PYSITEDIR" - - touch "${PYSITEDIR}/sitecustomize.py" - - echo "import os" | tee --append "${PYSITEDIR}/sitecustomize.py" - - echo "os.environ['COVERAGE_PROCESS_START'] = '$PWD/.coveragerc'" | tee --append "${PYSITEDIR}/sitecustomize.py" - - echo "os.environ['COVERAGE_FILE'] = '$PWD/.coverage'" | tee --append "${PYSITEDIR}/sitecustomize.py" - - echo "import coverage" | tee --append "${PYSITEDIR}/sitecustomize.py" - - echo "coverage.process_startup()" | tee --append "${PYSITEDIR}/sitecustomize.py" - - cat "${PYSITEDIR}/sitecustomize.py" - # write the coverage config file - - echo "[run]" >> "$PWD/.coveragerc" - - echo "source = $PWD" >> "$PWD/.coveragerc" - - echo "parallel = True" >> "$PWD/.coveragerc" - - printf "omit =\n\t*Tests.py\n\t*/src/*\n\t*/test/*\n\t*/testing/*\n\t*/template/*\n\t*/scripts/*\n\t*/scons-time.py\n\t*/bootstrap.py\n\t*/runtest.py\n\t*/setup.py\n\n" >> "$PWD/.coveragerc" - - echo "[path]" >> "$PWD/.coveragerc" - - echo "source = $PWD" >> "$PWD/.coveragerc" - - echo "[report]" >> "$PWD/.coveragerc" - - printf "omit =\n\t*Tests.py\n\t*/src/*\n\t*/test/*\n\t*/testing/*\n\t*/template/*\n\t*/scripts/*\n\t*/scons-time.py\n\t*/bootstrap.py\n\t*/runtest.py\n\t*/setup.py\n\n" >> "$PWD/.coveragerc" - - cat "$PWD/.coveragerc" + - ./.travis/coverage_setup.sh script: - coverage run -p --rcfile="$PWD/.coveragerc" runtest.py -a -j 2 || if [[ $? == 2 ]]; then true; else false; fi diff --git a/.travis/.coveragerc b/.travis/.coveragerc deleted file mode 100644 index 3aaf35b..0000000 --- a/.travis/.coveragerc +++ /dev/null @@ -1,8 +0,0 @@ -echo "[run]" >> .coveragerc -echo "source = $PWD/SCons" >> .coveragerc -echo "parallel = True" >> .coveragerc -printf "omit =\n\t*Tests.py\n\tsrc/test_*\n\tsetup.py\n\n" >> .coveragerc -echo "[path]" >> .coveragerc -echo "source = $PWD" >> .coveragerc -echo "[report]" >> .coveragerc -printf "omit =\n\t*Tests.py\n\tsrc/test_*\n\tsetup.py\n\n" >> .coveragerc diff --git a/.travis/coverage_setup.sh b/.travis/coverage_setup.sh new file mode 100755 index 0000000..e71fde8 --- /dev/null +++ b/.travis/coverage_setup.sh @@ -0,0 +1,21 @@ +#!/usr/bin/env bash + +python -m pip install -U coverage codecov + +# setup sitecustomize so we can make all subprocess start coverage +export PYSITEDIR=$(python -c "import sys; print(sys.path[-1])") +mkdir -p "$PYSITEDIR" + +# coverage.py environment variable for multiprocess +export COVERAGE_PROCESS_START="$PWD/.coveragerc" +export COVERAGE_FILE="$PWD/.coverage" + +# replace PWD in the template files so we have absolute paths from out /tmp test folders +sed -e "s#\$PWD#$PWD#" .coverage_templates/.coveragerc.template > "$PWD/.coveragerc" +sed -e "s#\$PWD#$PWD#" .coverage_templates/sitecustomize.py.template > "${PYSITEDIR}/sitecustomize.py" + +# print the results +echo "${PYSITEDIR}/sitecustomize.py" +cat "${PYSITEDIR}/sitecustomize.py" +echo "$PWD/.coveragerc" +cat "$PWD/.coveragerc" -- cgit v0.12 From 2003ff3fec129cb9b3ebd34d4f5e96ef0f0adddc Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Fri, 22 May 2020 10:21:31 -0600 Subject: If ParseFlags called with dict argument, leave unchanged Previously, if the dict values were mutable elements, i.e. lists, and the key was not for a *PATH variable, the value would be reversed before being processed into the env. Instead use a slice to iterate over. Fixes issue #3665 Signed-off-by: Mats Wichmann --- CHANGES.txt | 2 ++ SCons/Environment.py | 3 +-- SCons/EnvironmentTests.py | 14 ++++++++++++++ 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index 555e4d1..b0f3b5d 100755 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -147,6 +147,8 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER definitions using a metaclass be written the same for Py2/Py3. - Bump python_version_unsupported (and deprecated) to indicate 3.5 is lowest supported Python. + - ParseFlags should not modify the user's passed in dict in case it's + a compound data structure (e.g. values are lists) (issue #3665) diff --git a/SCons/Environment.py b/SCons/Environment.py index 00379e1..db8af11 100644 --- a/SCons/Environment.py +++ b/SCons/Environment.py @@ -855,8 +855,7 @@ class SubstitutionEnvironment(object): t.append(v) else: ### keep right-most occurence - orig.reverse() - for v in orig: + for v in orig[::-1]: if v not in t: t.insert(0, v) self[key] = t diff --git a/SCons/EnvironmentTests.py b/SCons/EnvironmentTests.py index a9ec674..f28f05c 100644 --- a/SCons/EnvironmentTests.py +++ b/SCons/EnvironmentTests.py @@ -856,6 +856,20 @@ sys.exit(0) assert env['A'] == ['aaa'], env['A'] assert env['B'] == ['bbb'], env['B'] + # issue #3665: if merging dict which is a compound object + # (i.e. value can be lists, etc.), the value object should not + # be modified. per the issue, this happened if key not in env. + env = SubstitutionEnvironment() + try: + del env['CFLAGS'] # just to be sure + except KeyError: + pass + flags = {'CFLAGS': ['-pipe', '-pthread', '-g']} + import copy + saveflags = copy.deepcopy(flags) + env.MergeFlags(flags) + self.assertEqual(flags, saveflags) + class BaseTestCase(unittest.TestCase,TestEnvironmentFixture): -- cgit v0.12 From 5b288f1b67a85bcccc533f101311b1c2c7f2b6eb Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Sat, 23 May 2020 07:46:18 -0600 Subject: classes no longer explicitly inherit from object In Python3 this is the default. Signed-off-by: Mats Wichmann --- CHANGES.txt | 1 + SCons/Action.py | 37 ++++++++++++-------- SCons/ActionTests.py | 42 +++++++++++------------ SCons/Builder.py | 6 ++-- SCons/BuilderTests.py | 18 +++++----- SCons/CacheDir.py | 2 +- SCons/CacheDirTests.py | 6 ++-- SCons/Defaults.py | 4 +-- SCons/Environment.py | 6 ++-- SCons/EnvironmentTests.py | 42 +++++++++++------------ SCons/EnvironmentValues.py | 7 ++-- SCons/Executor.py | 4 +-- SCons/ExecutorTests.py | 12 +++---- SCons/Job.py | 10 +++--- SCons/JobTests.py | 10 +++--- SCons/Memoize.py | 2 +- SCons/MemoizeTests.py | 4 +-- SCons/Node/AliasTests.py | 2 +- SCons/Node/FS.py | 8 ++--- SCons/Node/FSTests.py | 22 ++++++------ SCons/Node/NodeTests.py | 28 +++++++-------- SCons/Node/PythonTests.py | 2 +- SCons/Node/__init__.py | 8 ++--- SCons/PathList.py | 4 +-- SCons/PathListTests.py | 8 ++--- SCons/Platform/PlatformTests.py | 4 +-- SCons/Platform/__init__.py | 4 +-- SCons/Platform/virtualenvTests.py | 2 +- SCons/Platform/win32.py | 2 +- SCons/SConf.py | 8 ++--- SCons/SConfTests.py | 6 ++-- SCons/SConsign.py | 4 +-- SCons/SConsignTests.py | 12 +++---- SCons/Scanner/C.py | 4 +-- SCons/Scanner/CTests.py | 2 +- SCons/Scanner/DirTests.py | 2 +- SCons/Scanner/FortranTests.py | 4 +-- SCons/Scanner/IDLTests.py | 4 +-- SCons/Scanner/LaTeX.py | 8 ++--- SCons/Scanner/ProgTests.py | 4 +-- SCons/Scanner/ScannerTests.py | 12 +++---- SCons/Scanner/__init__.py | 6 ++-- SCons/Script/Main.py | 10 +++--- SCons/Script/Main.xml | 4 ++- SCons/Script/SConscript.py | 4 +-- SCons/Subst.py | 10 +++--- SCons/SubstTests.py | 20 +++++------ SCons/Taskmaster.py | 6 ++-- SCons/TaskmasterTests.py | 8 ++--- SCons/Tool/FortranCommonTests.py | 2 +- SCons/Tool/GettextCommon.py | 4 +-- SCons/Tool/JavaCommon.py | 14 ++++---- SCons/Tool/MSCommon/arch.py | 2 +- SCons/Tool/MSCommon/sdk.py | 2 +- SCons/Tool/MSCommon/vs.py | 2 +- SCons/Tool/ToolTests.py | 2 +- SCons/Tool/__init__.py | 14 ++++---- SCons/Tool/install.py | 2 +- SCons/Tool/javac.py | 2 +- SCons/Tool/javacTests.py | 2 +- SCons/Tool/linkloc.py | 2 +- SCons/Tool/msvs.py | 8 ++--- SCons/Tool/msvsTests.py | 8 ++--- SCons/Tool/mwcc.py | 2 +- SCons/Tool/packaging/rpm.py | 2 +- SCons/Tool/qt.py | 2 +- SCons/Tool/xgettext.py | 2 +- SCons/Util.py | 14 ++++---- SCons/UtilTests.py | 14 ++++---- SCons/Utilities/sconsign.py | 4 +-- SCons/Variables/PathVariable.py | 2 +- SCons/Variables/VariablesTests.py | 4 +-- SCons/Variables/__init__.py | 4 +-- SCons/WarningsTests.py | 2 +- SCons/cpp.py | 4 +-- SCons/dblite.py | 2 +- bench/dependency-func.py | 2 +- bench/env.__setitem__.py | 4 +-- bench/is_types.py | 2 +- bin/Command.py | 2 +- bin/SConsDoc.py | 12 +++---- bin/SConsExamples.py | 4 +-- bin/caller-tree.py | 2 +- bin/import-test.py | 2 +- bin/linecount.py | 2 +- bin/scons-proc.py | 2 +- bin/scons-time.py | 6 ++-- bin/time-scons.py | 2 +- bin/update-release-info.py | 4 +-- doc/generated/builders.gen | 2 +- doc/generated/functions.gen | 19 ++++++++-- doc/generated/tools.gen | 2 +- doc/generated/variables.gen | 2 +- doc/man/scons.xml | 10 +++--- site_scons/BuildCommandLine.py | 2 +- src/test_strings.py | 2 +- test/Actions/actions.py | 2 +- test/Batch/SConstruct_changed_sources_alwaysBuild | 4 +-- test/CPPPATH/expand-object.py | 2 +- test/CPPPATH/list-expansion.py | 2 +- test/Progress/object.py | 2 +- test/Scanner/generated.py | 2 +- test/ToolSurrogate.py | 4 +-- test/Value.py | 2 +- testing/framework/TestCmd.py | 4 +-- 105 files changed, 351 insertions(+), 327 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index b0f3b5d..ecf3255 100755 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -149,6 +149,7 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER is lowest supported Python. - ParseFlags should not modify the user's passed in dict in case it's a compound data structure (e.g. values are lists) (issue #3665) + - In Py3 classes no longer need to be listed as deriving from object. diff --git a/SCons/Action.py b/SCons/Action.py index 5bc85d5..1f499ee 100644 --- a/SCons/Action.py +++ b/SCons/Action.py @@ -119,7 +119,7 @@ import SCons.Subst # we use these a lot, so try to optimize them from SCons.Util import is_String, is_List -class _null(object): +class _null: pass print_actions = 1 @@ -515,7 +515,7 @@ def Action(act, *args, **kw): return _do_create_action(act, kw) -class ActionBase(object): +class ActionBase: """Base class for all types of action objects that can be held by other objects (Builders, Executors, etc.) This provides the common methods for manipulating and combining those actions.""" @@ -649,10 +649,14 @@ class _ActionAction(ActionBase): presub = self.presub if presub is _null: presub = print_actions_presub - if exitstatfunc is _null: exitstatfunc = self.exitstatfunc - if show is _null: show = print_actions - if execute is _null: execute = execute_actions - if chdir is _null: chdir = self.chdir + if exitstatfunc is _null: + exitstatfunc = self.exitstatfunc + if show is _null: + show = print_actions + if execute is _null: + execute = execute_actions + if chdir is _null: + chdir = self.chdir save_cwd = None if chdir: save_cwd = os.getcwd() @@ -795,12 +799,12 @@ def _subproc(scons_env, cmd, error='ignore', **kw): except EnvironmentError as e: if error == 'raise': raise # return a dummy Popen instance that only returns error - class dummyPopen(object): + class dummyPopen: def __init__(self, e): self.exception = e def communicate(self, input=None): return ('', '') def wait(self): return -self.exception.errno stdin = None - class f(object): + class f: def read(self): return '' def readline(self): return '' def __iter__(self): return iter(()) @@ -1110,11 +1114,14 @@ class CommandGeneratorAction(ActionBase): show=_null, execute=_null, chdir=_null, executor=None): act = self._generate(target, source, env, 0, executor) if act is None: - raise SCons.Errors.UserError("While building `%s': " - "Cannot deduce file extension from source files: %s" - % (repr(list(map(str, target))), repr(list(map(str, source))))) - return act(target, source, env, exitstatfunc, presub, - show, execute, chdir, executor) + raise SCons.Errors.UserError( + "While building `%s': " + "Cannot deduce file extension from source files: %s" + % (repr(list(map(str, target))), repr(list(map(str, source)))) + ) + return act( + target, source, env, exitstatfunc, presub, show, execute, chdir, executor + ) def get_presig(self, target, source, env, executor=None): """Return the signature contents of this action's command line. @@ -1372,7 +1379,7 @@ class ListAction(ActionBase): return list(result.keys()) -class ActionCaller(object): +class ActionCaller: """A class for delaying calling an Action function with specific (positional and keyword) arguments until the Action is actually executed. @@ -1443,7 +1450,7 @@ class ActionCaller(object): return self.parent.strfunc(*self.args, **self.kw) -class ActionFactory(object): +class ActionFactory: """A factory class that will wrap up an arbitrary function as an SCons-executable Action object. diff --git a/SCons/ActionTests.py b/SCons/ActionTests.py index bb4aa40..81dacfa 100644 --- a/SCons/ActionTests.py +++ b/SCons/ActionTests.py @@ -32,7 +32,7 @@ def GlobalFunc(): pass -class GlobalActFunc(object): +class GlobalActFunc: def __call__(self): pass @@ -105,7 +105,7 @@ scons_env = SCons.Environment.Environment() sys.stdout = io.StringIO() -class CmdStringHolder(object): +class CmdStringHolder: def __init__(self, cmd, literal=None): self.data = str(cmd) self.literal = literal @@ -131,7 +131,7 @@ class CmdStringHolder(object): return self.data -class Environment(object): +class Environment: def __init__(self, **kw): self.d = {} self.d['SHELL'] = scons_env['SHELL'] @@ -187,7 +187,7 @@ class Environment(object): return d -class DummyNode(object): +class DummyNode: def __init__(self, name): self.name = name @@ -1008,21 +1008,21 @@ class CommandActionTestCase(unittest.TestCase): s = act.strfunction([], [], env) assert s == "sf was called", s - class actclass1(object): + class actclass1: def __init__(self, targets, sources, env): pass def __call__(self): return 1 - class actclass2(object): + class actclass2: def __init__(self, targets, sources, env): self.strfunction = 5 def __call__(self): return 2 - class actclass3(object): + class actclass3: def __init__(self, targets, sources, env): pass @@ -1033,7 +1033,7 @@ class CommandActionTestCase(unittest.TestCase): return 'actclass3 on %s to get %s' % (str(sources[0]), str(targets[0])) - class actclass4(object): + class actclass4: def __init__(self, targets, sources, env): pass @@ -1164,7 +1164,7 @@ class CommandActionTestCase(unittest.TestCase): c = test.read(outfile, 'r') assert c == "act.py: 'out5' 'XYZZY'\nact.py: 'xyzzy5'\n", c - class Obj(object): + class Obj: def __init__(self, str): self._str = str @@ -1251,7 +1251,7 @@ class CommandActionTestCase(unittest.TestCase): """Test setting the command handler... """ - class Test(object): + class Test: def __init__(self): self.executed = 0 @@ -1265,7 +1265,7 @@ class CommandActionTestCase(unittest.TestCase): def escape_func(cmd): return '**' + cmd + '**' - class LiteralStr(object): + class LiteralStr: def __init__(self, x): self.data = x @@ -1464,7 +1464,7 @@ class CommandGeneratorActionTestCase(unittest.TestCase): assert self.dummy == 2, self.dummy del self.dummy - class DummyFile(object): + class DummyFile: def __init__(self, t): self.t = t @@ -1596,7 +1596,7 @@ class FunctionActionTestCase(unittest.TestCase): s = str(a) assert s == "func1(target, source, env)", s - class class1(object): + class class1: def __call__(self): pass @@ -1645,7 +1645,7 @@ class FunctionActionTestCase(unittest.TestCase): c = test.read(outfile2, 'r') assert c == "function1\n", c - class class1a(object): + class class1a: def __init__(self, target, source, env): with open(env['out'], 'w') as f: f.write("class1a\n") @@ -1656,7 +1656,7 @@ class FunctionActionTestCase(unittest.TestCase): c = test.read(outfile, 'r') assert c == "class1a\n", c - class class1b(object): + class class1b: def __call__(self, target, source, env): with open(env['out'], 'w') as f: f.write("class1b\n") @@ -1742,7 +1742,7 @@ class FunctionActionTestCase(unittest.TestCase): c = a.get_contents(target=[], source=[], env=Environment(XYZ='foo')) assert c in matches_foo, repr(c) - class Foo(object): + class Foo: def get_contents(self, target, source, env): return b'xyzzy' @@ -1750,7 +1750,7 @@ class FunctionActionTestCase(unittest.TestCase): c = a.get_contents(target=[], source=[], env=Environment()) assert c == b'xyzzy', repr(c) - class LocalClass(object): + class LocalClass: def LocalMethod(self): pass @@ -1847,13 +1847,13 @@ class ListActionTestCase(unittest.TestCase): f.write("function2\n") return 0 - class class2a(object): + class class2a: def __call__(self, target, source, env): with open(env['out'], 'a') as f: f.write("class2a\n") return 0 - class class2b(object): + class class2b: def __init__(self, target, source, env): with open(env['out'], 'a') as f: f.write("class2b\n") @@ -2024,7 +2024,7 @@ class ActionCallerTestCase(unittest.TestCase): assert c == matches[sys.version_info[:2]], "Got\n" + repr(c) + "\nExpected one of \n" + repr( matches[sys.version_info[:2]]) - class LocalActFunc(object): + class LocalActFunc: def __call__(self): pass @@ -2183,7 +2183,7 @@ class ActionCompareTestCase(unittest.TestCase): assert dog.get_name(env) == 'DOG', dog.get_name(env) -class TestClass(object): +class TestClass: """A test class used by ObjectContentsTestCase.test_object_contents""" def __init__(self): diff --git a/SCons/Builder.py b/SCons/Builder.py index 3f0be63..d54ef24 100644 --- a/SCons/Builder.py +++ b/SCons/Builder.py @@ -111,7 +111,7 @@ import SCons.Memoize import SCons.Util import SCons.Warnings -class _Null(object): +class _Null: pass _null = _Null @@ -328,7 +328,7 @@ def _node_errors(builder, env, tlist, slist): if len(slist) > 1: raise UserError("More than one source given for single-source builder: targets=%s sources=%s" % (list(map(str,tlist)), list(map(str,slist)))) -class EmitterProxy(object): +class EmitterProxy: """This is a callable class that can act as a Builder emitter. It holds on to a string that is a key into an Environment dictionary, and will @@ -361,7 +361,7 @@ class EmitterProxy(object): def __lt__(self, other): return self.var < other.var -class BuilderBase(object): +class BuilderBase: """Base class for Builders, objects that create output nodes (files) from input nodes (files). """ diff --git a/SCons/BuilderTests.py b/SCons/BuilderTests.py index d509219..6dfefd5 100644 --- a/SCons/BuilderTests.py +++ b/SCons/BuilderTests.py @@ -71,7 +71,7 @@ scons_env = SCons.Environment.Environment() env_arg2nodes_called = None -class Environment(object): +class Environment: def __init__(self, **kw): self.d = {} self.d['SHELL'] = scons_env['SHELL'] @@ -147,7 +147,7 @@ class Environment(object): def __eq__(self, other): return self.scanner == other.scanner or self.d == other.d -class MyAction(object): +class MyAction: def __init__(self, action): self.action = action def __call__(self, *args, **kw): @@ -155,7 +155,7 @@ class MyAction(object): def get_executor(self, env, overrides, tlist, slist, executor_kw): return ['executor'] + [self.action] -class MyNode_without_target_from_source(object): +class MyNode_without_target_from_source: def __init__(self, name): self.name = name self.sources = [] @@ -219,7 +219,7 @@ class BuilderTestCase(unittest.TestCase): exc_caught = 1 assert exc_caught, "did not catch expected InternalError exception" - class Node(object): + class Node: pass n = Node() @@ -404,7 +404,7 @@ class BuilderTestCase(unittest.TestCase): def test_target_factory(self): """Test a Builder that creates target nodes of a specified class """ - class Foo(object): + class Foo: pass def FooFactory(target): global Foo @@ -416,7 +416,7 @@ class BuilderTestCase(unittest.TestCase): def test_source_factory(self): """Test a Builder that creates source nodes of a specified class """ - class Foo(object): + class Foo: pass def FooFactory(source): global Foo @@ -829,7 +829,7 @@ class BuilderTestCase(unittest.TestCase): def test_target_scanner(self): """Testing ability to set target and source scanners through a builder.""" global instanced - class TestScanner(object): + class TestScanner: pass tscan = TestScanner() sscan = TestScanner() @@ -871,7 +871,7 @@ class BuilderTestCase(unittest.TestCase): def test_src_scanner(self): """Testing ability to set a source file scanner through a builder.""" - class TestScanner(object): + class TestScanner: def key(self, env): return 'TestScannerkey' def instance(self, env): @@ -900,7 +900,7 @@ class BuilderTestCase(unittest.TestCase): # An Environment that has suffix-specified SCANNERS should # provide a source scanner to the target. - class EnvTestScanner(object): + class EnvTestScanner: def key(self, env): return '.y' def instance(self, env): diff --git a/SCons/CacheDir.py b/SCons/CacheDir.py index 9982555..5ecbc6f 100644 --- a/SCons/CacheDir.py +++ b/SCons/CacheDir.py @@ -135,7 +135,7 @@ def CachePushFunc(target, source, env): CachePush = SCons.Action.Action(CachePushFunc, None) -class CacheDir(object): +class CacheDir: def __init__(self, path): """ diff --git a/SCons/CacheDirTests.py b/SCons/CacheDirTests.py index a3114c1..17294d5 100644 --- a/SCons/CacheDirTests.py +++ b/SCons/CacheDirTests.py @@ -36,7 +36,7 @@ import SCons.CacheDir built_it = None -class Action(object): +class Action: def __call__(self, targets, sources, env, **kw): global built_it if kw.get('execute', 1): @@ -47,7 +47,7 @@ class Action(object): def get_contents(self, target, source, env): return bytearray('','utf-8') -class Builder(object): +class Builder: def __init__(self, environment, action): self.env = environment self.action = action @@ -55,7 +55,7 @@ class Builder(object): self.source_scanner = None self.target_scanner = None -class Environment(object): +class Environment: def __init__(self, cachedir): self.cachedir = cachedir def Override(self, overrides): diff --git a/SCons/Defaults.py b/SCons/Defaults.py index 2bac41e..b3f6d1d 100644 --- a/SCons/Defaults.py +++ b/SCons/Defaults.py @@ -498,7 +498,7 @@ def _defines(prefix, defs, suffix, env, c=_concat_ixes): return c(prefix, env.subst_path(processDefines(defs)), suffix, env) -class NullCmdGenerator(object): +class NullCmdGenerator: """This is a callable class that can be used in place of other command generators if you don't want them to do anything. @@ -517,7 +517,7 @@ class NullCmdGenerator(object): return self.cmd -class Variable_Method_Caller(object): +class Variable_Method_Caller: """A class for finding a construction variable on the stack and calling one of its methods. diff --git a/SCons/Environment.py b/SCons/Environment.py index db8af11..0b4be1b 100644 --- a/SCons/Environment.py +++ b/SCons/Environment.py @@ -60,7 +60,7 @@ import SCons.Tool import SCons.Util import SCons.Warnings -class _Null(object): +class _Null: pass _null = _Null @@ -191,7 +191,7 @@ def _delete_duplicates(l, keep_last): # BuilderWrapper a subclass that overrides __call__() to enforce specific # Builder calling conventions, simplified some of our higher-layer code. -class MethodWrapper(object): +class MethodWrapper: """ A generic Wrapper class that associates a method (which can actually be any callable) with an object. As part of creating this @@ -334,7 +334,7 @@ def is_valid_construction_var(varstr): -class SubstitutionEnvironment(object): +class SubstitutionEnvironment: """Base class for different flavors of construction environments. This class contains a minimal set of methods that handle construction diff --git a/SCons/EnvironmentTests.py b/SCons/EnvironmentTests.py index f28f05c..ef12a6f 100644 --- a/SCons/EnvironmentTests.py +++ b/SCons/EnvironmentTests.py @@ -101,7 +101,7 @@ class Builder(SCons.Builder.BuilderBase): scanned_it = {} -class Scanner(object): +class Scanner: """A dummy Scanner class for testing purposes. "Scanning" a target is simply setting a value in the dictionary. """ @@ -137,7 +137,7 @@ class CLVar(UL): def __radd__(self, other): return UL.__radd__(self, CLVar(other)) -class DummyNode(object): +class DummyNode: def __init__(self, name): self.name = name def __str__(self): @@ -150,7 +150,7 @@ class DummyNode(object): def test_tool( env ): env['_F77INCFLAGS'] = '$( ${_concat(INCPREFIX, F77PATH, INCSUFFIX, __env__, RDirs, TARGET, SOURCE)} $)' -class TestEnvironmentFixture(object): +class TestEnvironmentFixture: def TestEnvironment(self, *args, **kw): if not kw or 'tools' not in kw: kw['tools'] = [test_tool] @@ -283,7 +283,7 @@ class SubstitutionTestCase(unittest.TestCase): assert len(nodes) == 1, nodes assert isinstance(nodes[0], SConsNode), nodes[0] - class OtherNode(object): + class OtherNode: pass nodes = env.arg2nodes(OtherNode()) assert len(nodes) == 1, nodes @@ -525,13 +525,13 @@ class SubstitutionTestCase(unittest.TestCase): def test_subst_path(self): """Test substituting a path list """ - class MyProxy(object): + class MyProxy: def __init__(self, val): self.val = val def get(self): return self.val + '-proxy' - class MyNode(object): + class MyNode: def __init__(self, val): self.val = val def get_subst_proxy(self): @@ -539,7 +539,7 @@ class SubstitutionTestCase(unittest.TestCase): def __str__(self): return self.val - class MyObj(object): + class MyObj: def get(self): return self @@ -571,7 +571,7 @@ class SubstitutionTestCase(unittest.TestCase): r = env.subst_path(['$PROXY', MyProxy('my2'), n]) assert r == ['my1-proxy', 'my2-proxy', n], r - class StringableObj(object): + class StringableObj: def __init__(self, s): self.s = s def __str__(self): @@ -899,7 +899,7 @@ class BaseTestCase(unittest.TestCase,TestEnvironmentFixture): def test_variables(self): """Test that variables only get applied once.""" - class FakeOptions(object): + class FakeOptions: def __init__(self, key, val): self.calls = 0 self.key = key @@ -1300,7 +1300,7 @@ env4.builder1.env, env3) def test_platform(self): """Test specifying a platform callable when instantiating.""" - class platform(object): + class platform: def __str__(self): return "TestPlatform" def __call__(self, env): env['XYZZY'] = 777 @@ -1315,7 +1315,7 @@ env4.builder1.env, env3) def test_Default_PLATFORM(self): """Test overriding the default PLATFORM variable""" - class platform(object): + class platform: def __str__(self): return "DefaultTestPlatform" def __call__(self, env): env['XYZZY'] = 888 @@ -1599,7 +1599,7 @@ def exists(env): assert isinstance(result, CLVar), repr(result) assert result == ['foo', 'bar'], result - class C(object): + class C: def __init__(self, name): self.name = name def __str__(self): @@ -1756,7 +1756,7 @@ def exists(env): # Ensure that lists and dictionaries are # deep copied, but not instances. - class TestA(object): + class TestA: pass env1 = self.TestEnvironment(XXX=TestA(), YYY = [ 1, 2, 3 ], ZZZ = { 1:2, 3:4 }) @@ -1999,7 +1999,7 @@ def generate(env): RPATH=[]) orig_backtick = env.backtick - class my_backtick(object): + class my_backtick: def __init__(self, save_command, output): self.save_command = save_command self.output = output @@ -2701,7 +2701,7 @@ def generate(env): def test_VariantDir(self): """Test the VariantDir() method""" - class MyFS(object): + class MyFS: def Dir(self, name): return name def VariantDir(self, variant_dir, src_dir, duplicate): @@ -2889,7 +2889,7 @@ def generate(env): def test_Dir(self): """Test the Dir() method""" - class MyFS(object): + class MyFS: def Dir(self, name): return 'Dir(%s)' % name @@ -2952,7 +2952,7 @@ def generate(env): def test_Execute(self): """Test the Execute() method""" - class MyAction(object): + class MyAction: def __init__(self, *args, **kw): self.args = args def __call__(self, target, source, env): @@ -2966,7 +2966,7 @@ def generate(env): def test_Entry(self): """Test the Entry() method""" - class MyFS(object): + class MyFS: def Entry(self, name): return 'Entry(%s)' % name @@ -2990,7 +2990,7 @@ def generate(env): def test_File(self): """Test the File() method""" - class MyFS(object): + class MyFS: def File(self, name): return 'File(%s)' % name @@ -3138,7 +3138,7 @@ def generate(env): def test_Repository(self): """Test the Repository() method.""" - class MyFS(object): + class MyFS: def __init__(self): self.list = [] def Repository(self, *dirs): @@ -3176,7 +3176,7 @@ def generate(env): """Test the SConsignFile() method""" import SCons.SConsign - class MyFS(object): + class MyFS: SConstruct_dir = os.sep + 'dir' env = self.TestEnvironment(FOO = 'SConsign', diff --git a/SCons/EnvironmentValues.py b/SCons/EnvironmentValues.py index 6599196..aaa71d0 100644 --- a/SCons/EnvironmentValues.py +++ b/SCons/EnvironmentValues.py @@ -34,7 +34,7 @@ _separate_args = re.compile(r'(%s|\s+|[^\s$]+|\$)' % _dollar_exps_str) # space characters in the string result from the scons_subst() function. _space_sep = re.compile(r'[\t ]+(?![^{]*})') -class ValueTypes(object): +class ValueTypes: """ Enum to store what type of value the variable holds. """ @@ -44,7 +44,7 @@ class ValueTypes(object): VARIABLE = 3 -class EnvironmentValue(object): +class EnvironmentValue: """ Hold a single value. We're going to cache parsed version of the file We're going to keep track of variables which feed into this values evaluation @@ -87,8 +87,7 @@ class EnvironmentValue(object): pass - -class EnvironmentValues(object): +class EnvironmentValues: """ A class to hold all the environment variables """ diff --git a/SCons/Executor.py b/SCons/Executor.py index e012ab6..f658229 100644 --- a/SCons/Executor.py +++ b/SCons/Executor.py @@ -37,7 +37,7 @@ import SCons.Memoize import SCons.Util from SCons.compat import NoSlotsPyPy -class Batch(object): +class Batch: """Remembers exact association between targets and sources of executor.""" @@ -79,7 +79,7 @@ class TSList(collections.UserList): nl = self.func() return repr(nl) -class TSObject(object): +class TSObject: """A class that implements $TARGET or $SOURCE expansions by wrapping an Executor method. """ diff --git a/SCons/ExecutorTests.py b/SCons/ExecutorTests.py index 59ca5c7..41728ef 100644 --- a/SCons/ExecutorTests.py +++ b/SCons/ExecutorTests.py @@ -29,7 +29,7 @@ import unittest import SCons.Executor -class MyEnvironment(object): +class MyEnvironment: def __init__(self, **kw): self._dict = {} self._dict.update(kw) @@ -42,7 +42,7 @@ class MyEnvironment(object): def _update(self, dict): self._dict.update(dict) -class MyAction(object): +class MyAction: def __init__(self, actions=['action1', 'action2']): self.actions = actions def __call__(self, target, source, env, **kw): @@ -59,13 +59,13 @@ class MyAction(object): def get_implicit_deps(self, target, source, env): return [] -class MyBuilder(object): +class MyBuilder: def __init__(self, env, overrides): self.env = env self.overrides = overrides self.action = MyAction() -class MyNode(object): +class MyNode: def __init__(self, name=None, pre=[], post=[]): self.name = name self.implicit = [] @@ -105,7 +105,7 @@ class MyNode(object): def is_up_to_date(self): return self.up_to_date -class MyScanner(object): +class MyScanner: def __init__(self, prefix): self.prefix = prefix def path(self, env, cwd, target, source): @@ -194,7 +194,7 @@ class ExecutorTestCase(unittest.TestCase): [t], ['s1', 's2']) - class LocalScanner(object): + class LocalScanner: def path(self, env, dir, target, source): target = list(map(str, target)) source = list(map(str, source)) diff --git a/SCons/Job.py b/SCons/Job.py index d70125c..4e2f68c 100644 --- a/SCons/Job.py +++ b/SCons/Job.py @@ -52,7 +52,7 @@ default_stack_size = 256 interrupt_msg = 'Build interrupted.' -class InterruptState(object): +class InterruptState: def __init__(self): self.interrupted = False @@ -63,7 +63,7 @@ class InterruptState(object): return self.interrupted -class Jobs(object): +class Jobs: """An instance of this class initializes N jobs, and provides methods for starting, stopping, and waiting on all N jobs. """ @@ -163,7 +163,7 @@ class Jobs(object): except AttributeError: pass -class Serial(object): +class Serial: """This class is used to execute tasks in series, and is more efficient than Parallel, but is only appropriate for non-parallel builds. Only one instance of this class should be in existence at a time. @@ -264,7 +264,7 @@ else: self.resultsQueue.put((task, ok)) - class ThreadPool(object): + class ThreadPool: """This class is responsible for spawning and managing worker threads.""" def __init__(self, num, stack_size, interrupted): @@ -338,7 +338,7 @@ else: worker.join(1.0) self.workers = [] - class Parallel(object): + class Parallel: """This class is used to execute tasks in parallel, and is somewhat less efficient than Serial, but is appropriate for parallel builds. diff --git a/SCons/JobTests.py b/SCons/JobTests.py index 9c9bb41..7fc969b 100644 --- a/SCons/JobTests.py +++ b/SCons/JobTests.py @@ -65,7 +65,7 @@ if num_jobs == 2: # how many tasks to perform for the test num_tasks = num_jobs*5 -class DummyLock(object): +class DummyLock: """fake lock class to use if threads are not supported""" def acquire(self): pass @@ -79,7 +79,7 @@ class NoThreadsException(Exception): def __str__(self): return "the interpreter doesn't support threads" -class Task(object): +class Task: """A dummy task class for testing purposes.""" def __init__(self, i, taskmaster): @@ -152,7 +152,7 @@ class RandomTask(Task): x = math.sin(i) time.sleep(0.01) -class ExceptionTask(object): +class ExceptionTask: """A dummy task class for testing purposes.""" def __init__(self, i, taskmaster): @@ -190,7 +190,7 @@ class ExceptionTask(object): def exception_set(self): self.taskmaster.exception_set() -class Taskmaster(object): +class Taskmaster: """A dummy taskmaster class for testing the job classes.""" def __init__(self, n, test_case, Task): @@ -405,7 +405,7 @@ import SCons.Taskmaster import SCons.Node import time -class DummyNodeInfo(object): +class DummyNodeInfo: def update(self, obj): pass diff --git a/SCons/Memoize.py b/SCons/Memoize.py index 0595fdf..9d6bc51 100644 --- a/SCons/Memoize.py +++ b/SCons/Memoize.py @@ -104,7 +104,7 @@ use_memoizer = None # Global list of counter objects CounterList = {} -class Counter(object): +class Counter: """ Base class for counting memoization hits and misses. diff --git a/SCons/MemoizeTests.py b/SCons/MemoizeTests.py index 726f656..f1e5550 100644 --- a/SCons/MemoizeTests.py +++ b/SCons/MemoizeTests.py @@ -31,7 +31,7 @@ import SCons.Memoize # Enable memoization counting SCons.Memoize.EnableMemoization() -class FakeObject(object): +class FakeObject: def __init__(self): self._memo = {} @@ -76,7 +76,7 @@ class FakeObject(object): def get_memoizer_counter(self, name): return SCons.Memoize.CounterList.get(self.__class__.__name__+'.'+name, None) -class Returner(object): +class Returner: def __init__(self, result): self.result = result self.calls = 0 diff --git a/SCons/Node/AliasTests.py b/SCons/Node/AliasTests.py index 27b75b3..613a5c0 100644 --- a/SCons/Node/AliasTests.py +++ b/SCons/Node/AliasTests.py @@ -51,7 +51,7 @@ class AliasTestCase(unittest.TestCase): def test_get_contents(self): """Test the get_contents() method """ - class DummyNode(object): + class DummyNode: def __init__(self, contents): self.contents = contents def get_csig(self): diff --git a/SCons/Node/FS.py b/SCons/Node/FS.py index 3a497f5..9e5e569 100644 --- a/SCons/Node/FS.py +++ b/SCons/Node/FS.py @@ -367,7 +367,7 @@ def get_MkdirBuilder(): name = "MkdirBuilder") return MkdirBuilder -class _Null(object): +class _Null: pass _null = _Null() @@ -383,7 +383,7 @@ else: -class DiskChecker(object): +class DiskChecker: def __init__(self, type, do, ignore): self.type = type self.do = do @@ -1094,7 +1094,7 @@ class Entry(Base): _classEntry = Entry -class LocalFS(object): +class LocalFS: """ This class implements an abstraction layer for operations involving a local file system. Essentially, this wraps any function in @@ -3685,7 +3685,7 @@ def get_default_fs(): default_fs = FS() return default_fs -class FileFinder(object): +class FileFinder: """ """ diff --git a/SCons/Node/FSTests.py b/SCons/Node/FSTests.py index 29e9d81..bfe2a77 100644 --- a/SCons/Node/FSTests.py +++ b/SCons/Node/FSTests.py @@ -46,7 +46,7 @@ built_it = None scanner_count = 0 -class Scanner(object): +class Scanner: def __init__(self, node=None): global scanner_count scanner_count = scanner_count + 1 @@ -69,7 +69,7 @@ class Scanner(object): return nodes -class Environment(object): +class Environment: def __init__(self): self.scanner = Scanner() @@ -89,7 +89,7 @@ class Environment(object): pass -class Action(object): +class Action: def __call__(self, targets, sources, env, **kw): global built_it if kw.get('execute', 1): @@ -112,7 +112,7 @@ class Action(object): return [] -class Builder(object): +class Builder: def __init__(self, factory, action=Action()): self.factory = factory self.env = Environment() @@ -458,7 +458,7 @@ class VariantDirTestCase(unittest.TestCase): assert r == d1, "%s != %s" % (r, d1) # verify the link creation attempts in file_link() - class LinkSimulator(object): + class LinkSimulator: """A class to intercept os.[sym]link() calls and track them.""" def __init__(self, duplicate, link, symlink, copy): @@ -1299,7 +1299,7 @@ class FSTestCase(_tempdirTestCase): # Make sure we can scan this file even if the target isn't # a file that has a scanner (it might be an Alias, e.g.). - class DummyNode(object): + class DummyNode: pass deps = f12.get_found_includes(env, s, DummyNode()) @@ -2416,11 +2416,11 @@ class EntryTestCase(_tempdirTestCase): assert e4n.__class__ is SCons.Node.FS.File, e4n.__class__ assert not exists, "e4n exists?" - class MyCalc(object): + class MyCalc: def __init__(self, val): self.max_drift = 0 - class M(object): + class M: def __init__(self, val): self.val = val @@ -2556,7 +2556,7 @@ class FileTestCase(_tempdirTestCase): SCons.Environment.Base.__init__(self) self.decide_source = self._changed_timestamp_then_content - class FakeNodeInfo(object): + class FakeNodeInfo: def __init__(self, csig, timestamp): self.csig = csig self.timestamp = timestamp @@ -3438,8 +3438,8 @@ class stored_infoTestCase(unittest.TestCase): bi = f.get_stored_info() assert hasattr(bi, 'ninfo') - class MySConsign(object): - class Null(object): + class MySConsign: + class Null: def __init__(self): self.xyzzy = 7 diff --git a/SCons/Node/NodeTests.py b/SCons/Node/NodeTests.py index 8d9d3a9..2ea22ed 100644 --- a/SCons/Node/NodeTests.py +++ b/SCons/Node/NodeTests.py @@ -55,7 +55,7 @@ def _actionAppend(a1, a2): raise Exception('Cannot Combine Actions') return MyListAction(all) -class MyActionBase(object): +class MyActionBase: def __add__(self, other): return _actionAppend(self, other) @@ -82,7 +82,7 @@ class MyAction(MyActionBase): def get_implicit_deps(self, target, source, env): return [] -class MyExecutor(object): +class MyExecutor: def __init__(self, env=None, targets=[], sources=[]): self.env = env self.targets = targets @@ -113,7 +113,7 @@ class MyListAction(MyActionBase): for A in self.list: A(target, source, env) -class Environment(object): +class Environment: def __init__(self, **kw): self._dict = {} self._dict.update(kw) @@ -139,7 +139,7 @@ class Environment(object): return [] -class Builder(object): +class Builder: def __init__(self, env=None, is_explicit=1): if env is None: env = Environment() self.env = env @@ -173,19 +173,19 @@ class ListBuilder(Builder): target = self.nodes[0] self.status = Builder.execute(self, target, source, env) -class FailBuilder(object): +class FailBuilder: def execute(self, target, source, env): return 1 -class ExceptBuilder(object): +class ExceptBuilder: def execute(self, target, source, env): raise SCons.Errors.BuildError -class ExceptBuilder2(object): +class ExceptBuilder2: def execute(self, target, source, env): raise Exception("foo") -class Scanner(object): +class Scanner: called = None def __call__(self, node): self.called = 1 @@ -211,10 +211,10 @@ class MyNode(SCons.Node.Node): def get_found_includes(self, env, scanner, target): return scanner(self) -class Calculator(object): +class Calculator: def __init__(self, val): self.max_drift = 0 - class M(object): + class M: def __init__(self, val): self.val = val def signature(self, args): @@ -401,7 +401,7 @@ class NodeTestCase(unittest.TestCase): else: self.fail("did not catch expected AttributeError") - class Builder(object): + class Builder: action = 'act' env = 'env1' overrides = {} @@ -660,7 +660,7 @@ class NodeTestCase(unittest.TestCase): class testNode2(SCons.Node.Node): def __str__(self): return 'null_binfo' - class FS(object): + class FS: pass node = testNode2() node.fs = FS() @@ -669,8 +669,8 @@ class NodeTestCase(unittest.TestCase): assert result is None, result def get_null_info(): - class Null_SConsignEntry(object): - class Null_BuildInfo(object): + class Null_SConsignEntry: + class Null_BuildInfo: def prepare_dependencies(self): pass binfo = Null_BuildInfo() diff --git a/SCons/Node/PythonTests.py b/SCons/Node/PythonTests.py index 51a49c0..b6fa859 100644 --- a/SCons/Node/PythonTests.py +++ b/SCons/Node/PythonTests.py @@ -50,7 +50,7 @@ class ValueTestCase(unittest.TestCase): def test_build(self): """Test "building" a Value Node """ - class fake_executor(object): + class fake_executor: def __call__(self, node): node.write('faked') diff --git a/SCons/Node/__init__.py b/SCons/Node/__init__.py index c1bb822..e084610 100644 --- a/SCons/Node/__init__.py +++ b/SCons/Node/__init__.py @@ -353,7 +353,7 @@ store_info_map = {0 : store_info_pass, # Classes for signature info for Nodes. -class NodeInfoBase(object): +class NodeInfoBase: """ The generic base class for signature information for a Node. @@ -448,7 +448,7 @@ class NodeInfoBase(object): setattr(self, key, value) -class BuildInfoBase(object): +class BuildInfoBase: """ The generic base class for build information for a Node. @@ -559,7 +559,7 @@ class Node(object, metaclass=NoSlotsPyPy): '_func_get_contents', '_func_target_from_source'] - class Attrs(object): + class Attrs: __slots__ = ('shared', '__dict__') @@ -1712,7 +1712,7 @@ def get_children(node, parent): return node.children() def ignore_cycle(node, stack): pass def do_nothing(node, parent): pass -class Walker(object): +class Walker: """An iterator for walking a Node tree. This is depth-first, children are visited before the parent. diff --git a/SCons/PathList.py b/SCons/PathList.py index 76cbeab..768198b 100644 --- a/SCons/PathList.py +++ b/SCons/PathList.py @@ -66,7 +66,7 @@ def node_conv(obj): result = get() return result -class _PathList(object): +class _PathList: """ An actual PathList object. """ @@ -143,7 +143,7 @@ class _PathList(object): return tuple(result) -class PathListCache(object): +class PathListCache: """ A class to handle caching of PathList lookups. diff --git a/SCons/PathListTests.py b/SCons/PathListTests.py index 09b1132..0d15c08 100644 --- a/SCons/PathListTests.py +++ b/SCons/PathListTests.py @@ -35,7 +35,7 @@ class subst_pathTestCase(unittest.TestCase): def setUp(self): - class FakeEnvironment(object): + class FakeEnvironment: def __init__(self, **kw): self.kw = kw def subst(self, s, target=None, source=None, conv=lambda x: x): @@ -59,7 +59,7 @@ class subst_pathTestCase(unittest.TestCase): import SCons.Node - class A(object): + class A: pass n = SCons.Node.Node() @@ -74,7 +74,7 @@ class subst_pathTestCase(unittest.TestCase): """Test the subst_path() method on a non-Node object """ - class A(object): + class A: def __str__(self): return '' @@ -90,7 +90,7 @@ class subst_pathTestCase(unittest.TestCase): """Test the subst_path() method on an object with a get() method """ - class B(object): + class B: def get(self): return 'b' diff --git a/SCons/Platform/PlatformTests.py b/SCons/Platform/PlatformTests.py index c89610f..e065c3c 100644 --- a/SCons/Platform/PlatformTests.py +++ b/SCons/Platform/PlatformTests.py @@ -214,8 +214,8 @@ class TempFileMungeTestCase(unittest.TestCase): SCons.Action.print_actions = 0 # Create an instance of object derived class to allow setattrb - class Node(object): - class Attrs(object): + class Node: + class Attrs: pass def __init__(self): diff --git a/SCons/Platform/__init__.py b/SCons/Platform/__init__.py index 21e63b4..12db824 100644 --- a/SCons/Platform/__init__.py +++ b/SCons/Platform/__init__.py @@ -118,7 +118,7 @@ def DefaultToolList(platform, env): return SCons.Tool.tool_list(platform, env) -class PlatformSpec(object): +class PlatformSpec: def __init__(self, name, generate): self.name = name self.generate = generate @@ -130,7 +130,7 @@ class PlatformSpec(object): return self.name -class TempFileMunge(object): +class TempFileMunge: """A callable class. You can set an Environment variable to this, then call it with a string argument, then it will perform temporary file substitution on it. This is used to circumvent the long command diff --git a/SCons/Platform/virtualenvTests.py b/SCons/Platform/virtualenvTests.py index 02b37ab..da0c06b 100644 --- a/SCons/Platform/virtualenvTests.py +++ b/SCons/Platform/virtualenvTests.py @@ -59,7 +59,7 @@ class Environment(collections.UserDict): else: self['ENV'][key] = os.path.pathsep.join([value, current]) -class SysPrefixes(object): +class SysPrefixes: """Used to temporarily mock sys.prefix, sys.real_prefix and sys.base_prefix""" def __init__(self, prefix, real_prefix=None, base_prefix=None): self._prefix = prefix diff --git a/SCons/Platform/win32.py b/SCons/Platform/win32.py index afe2df9..84fd8f8 100644 --- a/SCons/Platform/win32.py +++ b/SCons/Platform/win32.py @@ -298,7 +298,7 @@ def get_program_files_dir(): return val -class ArchDefinition(object): +class ArchDefinition: """ Determine which windows CPU were running on. A class for defining architecture-specific settings and logic. diff --git a/SCons/SConf.py b/SCons/SConf.py index eac4fb0..42bfa8c 100644 --- a/SCons/SConf.py +++ b/SCons/SConf.py @@ -189,7 +189,7 @@ class SConfBuildInfo(SCons.Node.FS.FileBuildInfo): self.string = string -class Streamer(object): +class Streamer: """ 'Sniffer' for a file-like writable object. Similar to the unix tool tee. """ @@ -374,7 +374,7 @@ class SConfBuildTask(SCons.Taskmaster.AlwaysTask): sconsign.set_entry(t.name, sconsign_entry) sconsign.merge() -class SConfBase(object): +class SConfBase: """This is simply a class to represent a configure context. After creating a SConf object, you can call any tests. After finished with your tests, be sure to call the Finish() method, which returns the modified @@ -692,7 +692,7 @@ class SConfBase(object): return( 1, outputStr) return (0, "") - class TestWrapper(object): + class TestWrapper: """A wrapper around Tests (to ensure sanity)""" def __init__(self, test, sconf): self.test = test @@ -802,7 +802,7 @@ class SConfBase(object): _ac_config_hs[self.config_h] = self.config_h_text self.env.fs = self.lastEnvFs -class CheckContext(object): +class CheckContext: """Provides a context for configure tests. Defines how a test writes to the screen and log file. diff --git a/SCons/SConfTests.py b/SCons/SConfTests.py index e2b9133..8ce6b3a 100644 --- a/SCons/SConfTests.py +++ b/SCons/SConfTests.py @@ -158,7 +158,7 @@ class SConfTestCase(unittest.TestCase): import SCons.Builder import SCons.Node - class MyAction(object): + class MyAction: def get_contents(self, target, source, env): return 'MyBuilder-MyAction $SOURCE $TARGET' @@ -170,7 +170,7 @@ class SConfTestCase(unittest.TestCase): self.action = MyAction() def __call__(self, env, target, source): - class MyNode(object): + class MyNode: def __init__(self, name): self.name = name self.state = SCons.Node.no_state @@ -215,7 +215,7 @@ class SConfTestCase(unittest.TestCase): def get_stored_info(self): pass def get_executor(self): - class Executor(object): + class Executor: def __init__(self, targets): self.targets = targets def get_all_targets(self): diff --git a/SCons/SConsign.py b/SCons/SConsign.py index a516e1f..dc2311a 100644 --- a/SCons/SConsign.py +++ b/SCons/SConsign.py @@ -120,7 +120,7 @@ def write(): closemethod() -class SConsignEntry(object): +class SConsignEntry: """ Wrapper class for the generic entry in a .sconsign file. The Node subclass populates it with attributes as it pleases. @@ -163,7 +163,7 @@ class SConsignEntry(object): setattr(self, key, value) -class Base(object): +class Base: """ This is the controlling class for the signatures for the collection of entries associated with a specific directory. The actual directory diff --git a/SCons/SConsignTests.py b/SCons/SConsignTests.py index 782072d..2f832b4 100644 --- a/SCons/SConsignTests.py +++ b/SCons/SConsignTests.py @@ -34,11 +34,11 @@ import SCons.dblite import SCons.SConsign -class BuildInfo(object): +class BuildInfo: def merge(self, object): pass -class DummySConsignEntry(object): +class DummySConsignEntry: def __init__(self, name): self.name = name self.binfo = BuildInfo() @@ -47,12 +47,12 @@ class DummySConsignEntry(object): def convert_from_sconsign(self, dir, name): self.c_from_s = 1 -class FS(object): +class FS: def __init__(self, top): self.Top = top self.Top.repositories = [] -class DummyNode(object): +class DummyNode: def __init__(self, path='not_a_valid_path', binfo=None): self.path = path self.tpath = path @@ -315,7 +315,7 @@ class SConsignFileTestCase(SConsignTestCase): assert SCons.SConsign.DB_Name is file, SCons.SConsign.DB_Name assert SCons.SConsign.DB_Module is None, SCons.SConsign.DB_Module - class Fake_DBM(object): + class Fake_DBM: def open(self, name, mode): self.name = name self.mode = mode @@ -349,7 +349,7 @@ class writeTestCase(SConsignTestCase): test = self.test file = test.workpath('sconsign_file') - class Fake_DBM(object): + class Fake_DBM: def __getitem__(self, key): return None def __setitem__(self, key, value): diff --git a/SCons/Scanner/C.py b/SCons/Scanner/C.py index 29cc396..21da574 100644 --- a/SCons/Scanner/C.py +++ b/SCons/Scanner/C.py @@ -80,7 +80,7 @@ def dictify_CPPDEFINES(env): return {cppdefines : None} return cppdefines -class SConsCPPScannerWrapper(object): +class SConsCPPScannerWrapper: """ The SCons wrapper around a cpp.py scanner. @@ -172,7 +172,7 @@ class SConsCPPConditionalScanner(SCons.cpp.PreProcessor): return "" -class SConsCPPConditionalScannerWrapper(object): +class SConsCPPConditionalScannerWrapper: """ The SCons wrapper around a cpp.py scanner. diff --git a/SCons/Scanner/CTests.py b/SCons/Scanner/CTests.py index af4ce36..8042e3f 100644 --- a/SCons/Scanner/CTests.py +++ b/SCons/Scanner/CTests.py @@ -332,7 +332,7 @@ class CScannerTestCase9(unittest.TestCase): def runTest(self): """Generate a warning when we can't find a #included file""" SCons.Warnings.enableWarningClass(SCons.Warnings.DependencyWarning) - class TestOut(object): + class TestOut: def __call__(self, x): self.out = x diff --git a/SCons/Scanner/DirTests.py b/SCons/Scanner/DirTests.py index c433663..8f866c4 100644 --- a/SCons/Scanner/DirTests.py +++ b/SCons/Scanner/DirTests.py @@ -42,7 +42,7 @@ import SCons.Scanner.Dir # def Entry(self, name): # return self.fs.Entry(name) -class DummyEnvironment(object): +class DummyEnvironment: def __init__(self, root): self.fs = SCons.Node.FS.FS(root) def Dir(self, name): diff --git a/SCons/Scanner/FortranTests.py b/SCons/Scanner/FortranTests.py index c958474..be2acd3 100644 --- a/SCons/Scanner/FortranTests.py +++ b/SCons/Scanner/FortranTests.py @@ -204,7 +204,7 @@ test.write(['modules', 'use.mod'], "\n") # define some helpers: -class DummyEnvironment(object): +class DummyEnvironment: def __init__(self, listCppPath): self.path = listCppPath self.fs = SCons.Node.FS.FS(test.workpath('')) @@ -403,7 +403,7 @@ class FortranScannerTestCase11(unittest.TestCase): def runTest(self): SCons.Warnings.enableWarningClass(SCons.Warnings.DependencyWarning) - class TestOut(object): + class TestOut: def __call__(self, x): self.out = x diff --git a/SCons/Scanner/IDLTests.py b/SCons/Scanner/IDLTests.py index 2cd67d5..9cbb73d 100644 --- a/SCons/Scanner/IDLTests.py +++ b/SCons/Scanner/IDLTests.py @@ -189,7 +189,7 @@ test.write([ 'repository', 'src', 'ddd.idl'], "\n") # define some helpers: -class DummyEnvironment(object): +class DummyEnvironment: def __init__(self, listCppPath): self.path = listCppPath self.fs = SCons.Node.FS.FS(test.workpath('')) @@ -341,7 +341,7 @@ class IDLScannerTestCase7(unittest.TestCase): class IDLScannerTestCase8(unittest.TestCase): def runTest(self): SCons.Warnings.enableWarningClass(SCons.Warnings.DependencyWarning) - class TestOut(object): + class TestOut: def __call__(self, x): self.out = x diff --git a/SCons/Scanner/LaTeX.py b/SCons/Scanner/LaTeX.py index d1cf04d..a85592e 100644 --- a/SCons/Scanner/LaTeX.py +++ b/SCons/Scanner/LaTeX.py @@ -42,7 +42,7 @@ LatexGraphics = [ '.png', '.jpg', '.gif', '.tif'] # Used as a return value of modify_env_var if the variable is not set. -class _Null(object): +class _Null: pass _null = _Null @@ -77,7 +77,7 @@ def modify_env_var(env, var, abspath): return save -class FindENVPathDirs(object): +class FindENVPathDirs: """ A class to bind a specific E{*}PATH variable name to a function that will return all of the E{*}path directories. @@ -211,7 +211,7 @@ class LaTeX(SCons.Scanner.Base): return [] return self.scan_recurse(node, path) - class FindMultiPathDirs(object): + class FindMultiPathDirs: """The stock FindPathDirs function has the wrong granularity: it is called once per target, while we need the path that depends on what kind of included files is being searched. This wrapper @@ -239,7 +239,7 @@ class LaTeX(SCons.Scanner.Base): # To prevent "dict is not hashable error" return tuple(di.items()) - class LaTeXScanCheck(object): + class LaTeXScanCheck: """Skip all but LaTeX source files, i.e., do not scan *.eps, *.pdf, *.jpg, etc. """ diff --git a/SCons/Scanner/ProgTests.py b/SCons/Scanner/ProgTests.py index 8981fd4..b91a2ad 100644 --- a/SCons/Scanner/ProgTests.py +++ b/SCons/Scanner/ProgTests.py @@ -46,7 +46,7 @@ for h in libs: # define some helpers: -class DummyEnvironment(object): +class DummyEnvironment: def __init__(self, **kw): self._dict = {'LIBSUFFIXES' : '.lib'} self._dict.update(kw) @@ -89,7 +89,7 @@ class DummyEnvironment(object): def File(self, filename): return self.fs.File(test.workpath(filename)) -class DummyNode(object): +class DummyNode: def __init__(self, name): self.name = name def rexists(self): diff --git a/SCons/Scanner/ScannerTests.py b/SCons/Scanner/ScannerTests.py index 6206af9..bf9879e 100644 --- a/SCons/Scanner/ScannerTests.py +++ b/SCons/Scanner/ScannerTests.py @@ -32,7 +32,7 @@ import TestUnit import SCons.Scanner -class DummyFS(object): +class DummyFS: def File(self, name): return DummyNode(name) @@ -56,7 +56,7 @@ class DummyEnvironment(collections.UserDict): def get_factory(self, factory): return factory or self.fs.File -class DummyNode(object): +class DummyNode: def __init__(self, name, search_result=()): self.name = name self.search_result = tuple(search_result) @@ -108,7 +108,7 @@ class ScannerTestCase(unittest.TestCase): class BaseTestCase(unittest.TestCase): - class skey_node(object): + class skey_node: def __init__(self, key): self.key = key def scanner_key(self): @@ -340,7 +340,7 @@ class BaseTestCase(unittest.TestCase): assert s == 'xyzzy', s class SelectorTestCase(unittest.TestCase): - class skey_node(object): + class skey_node: def __init__(self, key): self.key = key def scanner_key(self): @@ -399,7 +399,7 @@ class SelectorTestCase(unittest.TestCase): class CurrentTestCase(unittest.TestCase): def test_class(self): """Test the Scanner.Current class""" - class MyNode(object): + class MyNode: def __init__(self): self.called_has_builder = None self.called_is_up_to_date = None @@ -484,7 +484,7 @@ class ClassicTestCase(unittest.TestCase): def test_scan(self): """Test the Scanner.Classic scan() method""" - class MyNode(object): + class MyNode: def __init__(self, name): self.name = name self._rfile = self diff --git a/SCons/Scanner/__init__.py b/SCons/Scanner/__init__.py index a644101..e99c526 100644 --- a/SCons/Scanner/__init__.py +++ b/SCons/Scanner/__init__.py @@ -35,7 +35,7 @@ import SCons.Node.FS import SCons.Util -class _Null(object): +class _Null: pass # This is used instead of None as a default argument value so None can be @@ -61,7 +61,7 @@ def Scanner(function, *args, **kw): -class FindPathDirs(object): +class FindPathDirs: """ A class to bind a specific E{*}PATH variable name to a function that will return all of the E{*}path directories. @@ -81,7 +81,7 @@ class FindPathDirs(object): -class Base(object): +class Base: """ The base class for dependency scanners. This implements straightforward, single-pass scanning of a single file. diff --git a/SCons/Script/Main.py b/SCons/Script/Main.py index 4673d6d..ac29712 100644 --- a/SCons/Script/Main.py +++ b/SCons/Script/Main.py @@ -109,7 +109,7 @@ display = SCons.Util.display progress_display = SCons.Util.DisplayEngine() -class Progressor(object): +class Progressor: prev = '' count = 0 target_string = '$TARGET' @@ -429,7 +429,7 @@ class QuestionTask(SCons.Taskmaster.AlwaysTask): pass -class TreePrinter(object): +class TreePrinter: def __init__(self, derived=False, prune=False, status=False, sLineDraw=False): self.derived = derived self.prune = prune @@ -459,7 +459,7 @@ def python_version_deprecated(version=sys.version_info): return version < deprecated_python_version -class FakeOptionParser(object): +class FakeOptionParser: """ A do-nothing option parser, used for the initial OptionsParser variable. @@ -471,7 +471,7 @@ class FakeOptionParser(object): without blowing up. """ - class FakeOptionValues(object): + class FakeOptionValues: def __getattr__(self, attr): return None values = FakeOptionValues() @@ -495,7 +495,7 @@ def SetOption(name, value): def PrintHelp(file=None): OptionsParser.print_help(file=file) -class Stats(object): +class Stats: def __init__(self): self.stats = [] self.labels = [] diff --git a/SCons/Script/Main.xml b/SCons/Script/Main.xml index 910021c..f0d537a 100644 --- a/SCons/Script/Main.xml +++ b/SCons/Script/Main.xml @@ -626,11 +626,13 @@ will overwrite itself on a display: import sys -class ProgressCounter(object): + +class ProgressCounter: count = 0 def __call__(self, node, *args, **kw): self.count += 100 sys.stderr.write('Evaluated %s nodes\r' % self.count) + Progress(ProgressCounter(), interval=100) diff --git a/SCons/Script/SConscript.py b/SCons/Script/SConscript.py index 0298a69..b380dca 100644 --- a/SCons/Script/SConscript.py +++ b/SCons/Script/SConscript.py @@ -110,7 +110,7 @@ def compute_exports(exports): return retval -class Frame(object): +class Frame: """A frame on the SConstruct/SConscript call stack""" def __init__(self, fs, exports, sconscript): self.globals = BuildDefaultGlobals() @@ -639,7 +639,7 @@ def get_DefaultEnvironmentProxy(): _DefaultEnvironmentProxy = SCons.Environment.NoSubstitutionProxy(default_env) return _DefaultEnvironmentProxy -class DefaultEnvironmentCall(object): +class DefaultEnvironmentCall: """A class that implements "global function" calls of Environment methods by fetching the specified method from the DefaultEnvironment's class. Note that this uses an intermediate diff --git a/SCons/Subst.py b/SCons/Subst.py index a1ef053..4ce292e 100644 --- a/SCons/Subst.py +++ b/SCons/Subst.py @@ -58,7 +58,7 @@ def raise_exception(exception, target, s): -class Literal(object): +class Literal: """A wrapper for a string. If you use this object wrapped around a string, then it will be interpreted as literal. When passed to the command interpreter, all special @@ -89,7 +89,7 @@ class Literal(object): def __hash__(self): return hash(self.lstr) -class SpecialAttrWrapper(object): +class SpecialAttrWrapper: """This is a wrapper for what we call a 'Node special attribute.' This is any of the attributes of a Node that we can reference from Environment variable substitution, such as $TARGET.abspath or @@ -171,7 +171,7 @@ def escape_list(mylist, escape_func): return e(escape_func) return list(map(escape, mylist)) -class NLWrapper(object): +class NLWrapper: """A wrapper class that delays turning a list of sources or targets into a NodeList until it's needed. The specified function supplied when the object is initialized is responsible for turning raw nodes @@ -232,7 +232,7 @@ class Targets_or_Sources(collections.UserList): nl = self.nl._create_nodelist() return repr(nl) -class Target_or_Source(object): +class Target_or_Source: """A class that implements $TARGET or $SOURCE expansions by in turn wrapping a NLWrapper. This class handles the different methods used to access an individual proxy Node, calling the NLWrapper to create @@ -332,7 +332,7 @@ def subst_dict(target, source): return dict -class StringSubber(object): +class StringSubber: """A class to construct the results of a scons_subst() call. This binds a specific construction environment, mode, target and diff --git a/SCons/SubstTests.py b/SCons/SubstTests.py index f2def65..07eb61d 100644 --- a/SCons/SubstTests.py +++ b/SCons/SubstTests.py @@ -34,7 +34,7 @@ import SCons.Errors from SCons.Subst import * -class DummyNode(object): +class DummyNode: """Simple node work-alike.""" def __init__(self, name): self.name = os.path.normpath(name) @@ -47,7 +47,7 @@ class DummyNode(object): def get_subst_proxy(self): return self -class DummyEnv(object): +class DummyEnv: def __init__(self, dict={}): self.dict = dict @@ -82,7 +82,7 @@ def CmdGen1(target, source, env, for_signature): assert str(source) == 's', source return "${CMDGEN2('foo', %d)}" % for_signature -class CmdGen2(object): +class CmdGen2: def __init__(self, mystr, forsig): self.mystr = mystr self.expect_for_signature = forsig @@ -105,7 +105,7 @@ class SubstTestCase(unittest.TestCase): """Simple node work-alike with some extra stuff for testing.""" def __init__(self, name): DummyNode.__init__(self, name) - class Attribute(object): + class Attribute: pass self.attribute = Attribute() self.attribute.attr1 = 'attr$1-' + os.path.basename(name) @@ -114,7 +114,7 @@ class SubstTestCase(unittest.TestCase): return self.name + extra foo = 1 - class TestLiteral(object): + class TestLiteral: def __init__(self, literal): self.literal = literal def __str__(self): @@ -122,7 +122,7 @@ class SubstTestCase(unittest.TestCase): def is_literal(self): return 1 - class TestCallable(object): + class TestCallable: def __init__(self, value): self.value = value def __call__(self): @@ -523,7 +523,7 @@ class scons_subst_TestCase(SubstTestCase): """Test scons_subst(): handling attribute errors""" env = DummyEnv(self.loc) try: - class Foo(object): + class Foo: pass scons_subst('${foo.bar}', env, gvars={'foo':Foo()}) except SCons.Errors.UserError as e: @@ -980,7 +980,7 @@ class scons_subst_list_TestCase(SubstTestCase): """Test scons_subst_list(): handling attribute errors""" env = DummyEnv() try: - class Foo(object): + class Foo: pass scons_subst_list('${foo.bar}', env, gvars={'foo':Foo()}) except SCons.Errors.UserError as e: @@ -1125,7 +1125,7 @@ class quote_spaces_TestCase(unittest.TestCase): q = quote_spaces('x\tx') assert q == '"x\tx"', q - class Node(object): + class Node: def __init__(self, name, children=[]): self.children = children self.name = name @@ -1207,7 +1207,7 @@ class subst_dict_TestCase(unittest.TestCase): assert SOURCES == ['s1', 's2'], d['SOURCES'] assert str(d['SOURCE']) == 's1', d['SOURCE'] - class V(object): + class V: # Fake Value node with no rfile() method. def __init__(self, name): self.name = name diff --git a/SCons/Taskmaster.py b/SCons/Taskmaster.py index 6ca0e03..147cbcd 100644 --- a/SCons/Taskmaster.py +++ b/SCons/Taskmaster.py @@ -79,7 +79,7 @@ print_prepare = 0 # set by option --debug=prepare CollectStats = None -class Stats(object): +class Stats: """ A simple class for holding statistics about the disposition of a Node by the Taskmaster. If we're collecting statistics, each Node @@ -116,7 +116,7 @@ def dump_stats(): -class Task(object): +class Task: """ Default SCons build engine task. @@ -605,7 +605,7 @@ def find_cycle(stack, visited): return None -class Taskmaster(object): +class Taskmaster: """ The Taskmaster for walking the dependency DAG. """ diff --git a/SCons/TaskmasterTests.py b/SCons/TaskmasterTests.py index 58a31aa..961910a 100644 --- a/SCons/TaskmasterTests.py +++ b/SCons/TaskmasterTests.py @@ -39,7 +39,7 @@ visited_nodes = [] executed = None scan_called = 0 -class Node(object): +class Node: def __init__(self, name, kids = [], scans = []): self.name = name self.kids = kids @@ -49,7 +49,7 @@ class Node(object): self.scanner = None self.targets = [self] self.prerequisites = None - class Builder(object): + class Builder: def targets(self, node): return node.targets self.builder = Builder() @@ -217,7 +217,7 @@ class Node(object): def get_executor(self): if not hasattr(self, 'executor'): - class Executor(object): + class Executor: def prepare(self): pass def get_action_targets(self): @@ -883,7 +883,7 @@ class TaskmasterTestCase(unittest.TestCase): assert n10.prepared # Make sure we call an Executor's prepare() method. - class ExceptionExecutor(object): + class ExceptionExecutor: def prepare(self): raise Exception("Executor.prepare() exception") def get_all_targets(self): diff --git a/SCons/Tool/FortranCommonTests.py b/SCons/Tool/FortranCommonTests.py index 6ccd206..b050e26 100644 --- a/SCons/Tool/FortranCommonTests.py +++ b/SCons/Tool/FortranCommonTests.py @@ -41,7 +41,7 @@ test = TestCmd.TestCmd(workdir='') os.chdir(test.workpath('')) -class DummyEnvironment(object): +class DummyEnvironment: dictionary = None # type: Dict[Any, Any] def __init__(self, list_cpp_path): diff --git a/SCons/Tool/GettextCommon.py b/SCons/Tool/GettextCommon.py index 8c5d0b0..fd6e428 100644 --- a/SCons/Tool/GettextCommon.py +++ b/SCons/Tool/GettextCommon.py @@ -69,7 +69,7 @@ SCons.Warnings.enableWarningClass(MsgfmtNotFound) ############################################################################# ############################################################################# -class _POTargetFactory(object): +class _POTargetFactory: """ A factory of `PO` target files. Factory defaults differ from these of `SCons.Node.FS.FS`. We set `precious` @@ -269,7 +269,7 @@ def _translate(env, target=None, source=SCons.Environment._null, *args, **kw): ############################################################################# ############################################################################# -class RPaths(object): +class RPaths: """ Callable object, which returns pathnames relative to SCons current working directory. diff --git a/SCons/Tool/JavaCommon.py b/SCons/Tool/JavaCommon.py index c6c19ae..bb05977 100644 --- a/SCons/Tool/JavaCommon.py +++ b/SCons/Tool/JavaCommon.py @@ -92,7 +92,7 @@ if java_parsing: r'/\*|\*/|\[\])') - class OuterState(object): + class OuterState: """The initial state for parsing a Java file for classes, interfaces, and anonymous inner classes.""" @@ -222,7 +222,7 @@ if java_parsing: self.package = package - class ScopeState(object): + class ScopeState: """ A state that parses code within a scope normally, within the confines of a scope. @@ -290,7 +290,7 @@ if java_parsing: return self - class AnonClassState(object): + class AnonClassState: """A state that looks for anonymous inner classes.""" def __init__(self, old_state): @@ -330,7 +330,7 @@ if java_parsing: return self.old_state.parseToken(token) - class SkipState(object): + class SkipState: """A state that will skip a specified number of tokens before reverting to the previous state.""" @@ -345,7 +345,7 @@ if java_parsing: return self - class ClassState(object): + class ClassState: """A state we go into when we hit a class or interface keyword.""" def __init__(self, outer_state): @@ -376,7 +376,7 @@ if java_parsing: return self.outer_state - class IgnoreState(object): + class IgnoreState: """A state that will ignore all tokens until it gets to a specified token.""" @@ -390,7 +390,7 @@ if java_parsing: return self - class PackageState(object): + class PackageState: """The state we enter when we encounter the package keyword. We assume the next token will be the package name.""" diff --git a/SCons/Tool/MSCommon/arch.py b/SCons/Tool/MSCommon/arch.py index ad17959..ba57a2c 100644 --- a/SCons/Tool/MSCommon/arch.py +++ b/SCons/Tool/MSCommon/arch.py @@ -28,7 +28,7 @@ __doc__ = """Module to define supported Windows chip architectures. import os -class ArchDefinition(object): +class ArchDefinition: """ A class for defining architecture-specific settings and logic. """ diff --git a/SCons/Tool/MSCommon/sdk.py b/SCons/Tool/MSCommon/sdk.py index 9627f17..03306ad 100644 --- a/SCons/Tool/MSCommon/sdk.py +++ b/SCons/Tool/MSCommon/sdk.py @@ -58,7 +58,7 @@ _CURINSTALLED_SDK_HKEY_ROOT = \ r"Software\Microsoft\Microsoft SDKs\Windows\CurrentInstallFolder" -class SDKDefinition(object): +class SDKDefinition: """ An abstract base class for trying to find installed SDK directories. """ diff --git a/SCons/Tool/MSCommon/vs.py b/SCons/Tool/MSCommon/vs.py index e71eb27..5a2523a 100644 --- a/SCons/Tool/MSCommon/vs.py +++ b/SCons/Tool/MSCommon/vs.py @@ -40,7 +40,7 @@ from .common import debug, \ import SCons.Tool.MSCommon.vc -class VisualStudio(object): +class VisualStudio: """ An abstract base class for trying to find installed versions of Visual Studio. diff --git a/SCons/Tool/ToolTests.py b/SCons/Tool/ToolTests.py index 74524f1..75a9454 100644 --- a/SCons/Tool/ToolTests.py +++ b/SCons/Tool/ToolTests.py @@ -33,7 +33,7 @@ import SCons.Errors import SCons.Tool -class DummyEnvironment(object): +class DummyEnvironment: def __init__(self): self.dict = {} def Detect(self, progs): diff --git a/SCons/Tool/__init__.py b/SCons/Tool/__init__.py index 7aab554..acef86a 100644 --- a/SCons/Tool/__init__.py +++ b/SCons/Tool/__init__.py @@ -102,7 +102,7 @@ TOOL_ALIASES = { } -class Tool(object): +class Tool: def __init__(self, name, toolpath=None, **kw): if toolpath is None: toolpath = [] @@ -363,7 +363,7 @@ def _call_env_subst(env, string, *args, **kw): return env.subst(string, *args, **kw2) -class _ShLibInfoSupport(object): +class _ShLibInfoSupport: @property def libtype(self): return 'ShLib' @@ -381,7 +381,7 @@ class _ShLibInfoSupport(object): return _call_env_subst(env, '$SHLIBNOVERSIONSYMLINKS', *args, **kw) -class _LdModInfoSupport(object): +class _LdModInfoSupport: @property def libtype(self): return 'LdMod' @@ -399,7 +399,7 @@ class _LdModInfoSupport(object): return _call_env_subst(env, '$LDMODULENOVERSIONSYMLINKS', *args, **kw) -class _ImpLibInfoSupport(object): +class _ImpLibInfoSupport: @property def libtype(self): return 'ImpLib' @@ -443,7 +443,7 @@ class _ImpLibInfoSupport(object): return disable -class _LibInfoGeneratorBase(object): +class _LibInfoGeneratorBase: """Generator base class for library-related info such as suffixes for versioned libraries, symlink maps, sonames etc. It handles commonities of SharedLibrary and LoadableModule @@ -1011,7 +1011,7 @@ def CreateJavaFileBuilder(env): return java_file -class ToolInitializerMethod(object): +class ToolInitializerMethod: """ This is added to a construction environment in place of a method(s) normally called for a Builder (env.Object, env.StaticObject, @@ -1061,7 +1061,7 @@ class ToolInitializerMethod(object): return builder(*args, **kw) -class ToolInitializer(object): +class ToolInitializer: """ A class for delayed initialization of Tools modules. diff --git a/SCons/Tool/install.py b/SCons/Tool/install.py index 06f7902..4a74b47 100644 --- a/SCons/Tool/install.py +++ b/SCons/Tool/install.py @@ -249,7 +249,7 @@ def add_versioned_targets_to_INSTALLED_FILES(target, source, env): _UNIQUE_INSTALLED_FILES = None return (target, source) -class DESTDIR_factory(object): +class DESTDIR_factory: """ A node factory, where all files will be relative to the dir supplied in the constructor. """ diff --git a/SCons/Tool/javac.py b/SCons/Tool/javac.py index 537913f..fd007eb 100644 --- a/SCons/Tool/javac.py +++ b/SCons/Tool/javac.py @@ -136,7 +136,7 @@ JavaBuilder = SCons.Builder.Builder(action = JavaAction, target_factory = SCons.Node.FS.Entry, source_factory = SCons.Node.FS.Entry) -class pathopt(object): +class pathopt: """ Callable object for generating javac-style path options from a construction variable (e.g. -classpath, -sourcepath). diff --git a/SCons/Tool/javacTests.py b/SCons/Tool/javacTests.py index 96d41b2..ff0b8d4 100644 --- a/SCons/Tool/javacTests.py +++ b/SCons/Tool/javacTests.py @@ -26,7 +26,7 @@ import unittest import SCons.Tool.javac -class DummyNode(object): +class DummyNode: def __init__(self, val): self.val = val diff --git a/SCons/Tool/linkloc.py b/SCons/Tool/linkloc.py index ad189b2..0ca37b3 100644 --- a/SCons/Tool/linkloc.py +++ b/SCons/Tool/linkloc.py @@ -61,7 +61,7 @@ def repl_linker_command(m): # to find it with recursive substitution return m.group(1) + '#' + m.group(2) -class LinklocGenerator(object): +class LinklocGenerator: def __init__(self, cmdline): self.cmdline = cmdline diff --git a/SCons/Tool/msvs.py b/SCons/Tool/msvs.py index 1fda9e1..c10b802 100644 --- a/SCons/Tool/msvs.py +++ b/SCons/Tool/msvs.py @@ -170,7 +170,7 @@ else: python_executable = os.path.join('$$(PYTHON_ROOT)', os.path.split(sys.executable)[1]) -class Config(object): +class Config: pass def splitFully(path): @@ -200,7 +200,7 @@ def makeHierarchy(sources): # print 'Warning: failed to decompose path for '+str(file) return hierarchy -class _UserGenerator(object): +class _UserGenerator: """ Base class for .dsp.user file generator """ @@ -413,7 +413,7 @@ class _GenerateV10User(_UserGenerator): self.usrfile.write(self.usrconf % locals()) self.usrfile.write('') -class _DSPGenerator(object): +class _DSPGenerator: """ Base class for DSP generators """ srcargs = [ @@ -1458,7 +1458,7 @@ class _GenerateV10DSP(_DSPGenerator, _GenerateV10User): _GenerateV10User.Build(self) -class _DSWGenerator(object): +class _DSWGenerator: """ Base class for DSW generators """ def __init__(self, dswfile, source, env): self.dswfile = os.path.normpath(str(dswfile)) diff --git a/SCons/Tool/msvsTests.py b/SCons/Tool/msvsTests.py index e1f098f..30c3b58 100644 --- a/SCons/Tool/msvsTests.py +++ b/SCons/Tool/msvsTests.py @@ -390,7 +390,7 @@ regdata_cv = r'''[HKEY_LOCAL_MACHINE\Software\Microsoft\Windows\CurrentVersion] regdata_none = [] -class DummyEnv(object): +class DummyEnv: def __init__(self, dict=None): self.fs = SCons.Node.FS.FS() if dict: @@ -425,14 +425,14 @@ class DummyEnv(object): return self.fs.Dir(name) -class RegKey(object): +class RegKey: """key class for storing an 'open' registry key""" def __init__(self,key): self.key = key # Warning: this is NOT case-insensitive, unlike the Windows registry. # So e.g. HKLM\Software is NOT the same key as HKLM\SOFTWARE. -class RegNode(object): +class RegNode: """node in the dummy registry""" def __init__(self,name): self.valdict = {} @@ -507,7 +507,7 @@ class RegNode(object): rv = rv + indent + '}\n' return rv -class DummyRegistry(object): +class DummyRegistry: """registry class for storing fake registry attributes""" def __init__(self,data): """parse input data into the fake registry""" diff --git a/SCons/Tool/mwcc.py b/SCons/Tool/mwcc.py index 1455166..8f1201a 100644 --- a/SCons/Tool/mwcc.py +++ b/SCons/Tool/mwcc.py @@ -119,7 +119,7 @@ def find_versions(): return versions -class MWVersion(object): +class MWVersion: def __init__(self, version, path, platform): self.version = version self.path = path diff --git a/SCons/Tool/packaging/rpm.py b/SCons/Tool/packaging/rpm.py index db8ae24..0e44bf9 100644 --- a/SCons/Tool/packaging/rpm.py +++ b/SCons/Tool/packaging/rpm.py @@ -301,7 +301,7 @@ def build_specfile_filesection(spec, files): return str -class SimpleTagCompiler(object): +class SimpleTagCompiler: """ This class is a simple string substition utility: the replacement specfication is stored in the tagset dictionary, something like: diff --git a/SCons/Tool/qt.py b/SCons/Tool/qt.py index 3dc87c0..85d4ca0 100644 --- a/SCons/Tool/qt.py +++ b/SCons/Tool/qt.py @@ -115,7 +115,7 @@ def find_file(filename, paths, node_factory): return node return None -class _Automoc(object): +class _Automoc: """ Callable class, which works as an emitter for Programs, SharedLibraries and StaticLibraries. diff --git a/SCons/Tool/xgettext.py b/SCons/Tool/xgettext.py index 7aba08d..908d44d 100644 --- a/SCons/Tool/xgettext.py +++ b/SCons/Tool/xgettext.py @@ -45,7 +45,7 @@ from SCons.Tool.GettextCommon import _xgettext_exists ############################################################################# -class _CmdRunner(object): +class _CmdRunner: """ Callable object, which runs shell command storing its stdout and stderr to variables. It also provides `strfunction()` method, which shall be used by scons Action objects to print command string. """ diff --git a/SCons/Util.py b/SCons/Util.py index 07cdad9..1ec0cf8 100644 --- a/SCons/Util.py +++ b/SCons/Util.py @@ -187,7 +187,7 @@ def get_environment_var(varstr): return None -class DisplayEngine(object): +class DisplayEngine: print_it = True def __call__(self, text, append_newline=1): @@ -571,7 +571,7 @@ def semi_deepcopy(x): return x -class Proxy(object): +class Proxy: """A simple generic Proxy class, forwarding all calls to subject. So, for the benefit of the python newbie, what does this really mean? Well, it means that you can take an object, let's @@ -621,7 +621,7 @@ class Proxy(object): return self.__dict__ == other.__dict__ -class Delegate(object): +class Delegate: """A Python Descriptor class that delegates attribute fetches to an underlying wrapped subject of a Proxy. Typical use: @@ -1246,7 +1246,7 @@ def logical_lines(physical_lines, joiner=''.join): yield joiner(logical_line) -class LogicalLines(object): +class LogicalLines: """ Wrapper class for the logical_lines method. Allows us to read all "logical" lines at once from a @@ -1352,7 +1352,7 @@ class UniqueList(UserList): self.unique = False -class Unbuffered(object): +class Unbuffered: """ A proxy class that wraps a file object, flushing after every write, and delegating everything else to the wrapped object. @@ -1544,8 +1544,7 @@ def silent_intern(x): # http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/68205 # ASPN: Python Cookbook: Null Object Design Pattern -#TODO??? class Null(object): -class Null(object): +class Null: """ Null objects always and reliably "do nothing." """ def __new__(cls, *args, **kwargs): if '_instance' not in vars(cls): @@ -1570,6 +1569,7 @@ class Null(object): class NullSeq(Null): + """ A Null object that can also be iterated over. """ def __len__(self): return 0 def __iter__(self): diff --git a/SCons/UtilTests.py b/SCons/UtilTests.py index d96b63f..c831108 100644 --- a/SCons/UtilTests.py +++ b/SCons/UtilTests.py @@ -37,7 +37,7 @@ import SCons.Errors from SCons.Util import * -class OutBuffer(object): +class OutBuffer: def __init__(self): self.buffer = "" @@ -64,7 +64,7 @@ class UtilTestCase(unittest.TestCase): assert splitext('foo.bar') == ('foo', '.bar') assert splitext(os.path.join('foo.bar', 'blat')) == (os.path.join('foo.bar', 'blat'), '') - class Node(object): + class Node: def __init__(self, name, children=[]): self.children = children self.name = name @@ -433,7 +433,7 @@ class UtilTestCase(unittest.TestCase): def test_Proxy(self): """Test generic Proxy class.""" - class Subject(object): + class Subject: def foo(self): return 1 @@ -657,7 +657,7 @@ class UtilTestCase(unittest.TestCase): def test_Selector(self): """Test the Selector class""" - class MyNode(object): + class MyNode: def __init__(self, name): self.name = name @@ -790,7 +790,7 @@ class NodeListTestCase(unittest.TestCase): def test_simple_attributes(self): """Test simple attributes of a NodeList class""" - class TestClass(object): + class TestClass: def __init__(self, name, child=None): self.child = child self.bar = name @@ -807,7 +807,7 @@ class NodeListTestCase(unittest.TestCase): def test_callable_attributes(self): """Test callable attributes of a NodeList class""" - class TestClass(object): + class TestClass: def __init__(self, name, child=None): self.child = child self.bar = name @@ -854,7 +854,7 @@ class flattenTestCase(unittest.TestCase): self.assertEqual(sorted(result), [1, 2, 3]) -class OsEnviron(object): +class OsEnviron: """Used to temporarily mock os.environ""" def __init__(self, environ): diff --git a/SCons/Utilities/sconsign.py b/SCons/Utilities/sconsign.py index 89084ba..21aaabc 100644 --- a/SCons/Utilities/sconsign.py +++ b/SCons/Utilities/sconsign.py @@ -71,7 +71,7 @@ def my_import(mname): return imp.load_module(mname, fp, pathname, description) -class Flagger(object): +class Flagger: default_value = 1 def __setitem__(self, item, value): @@ -288,7 +288,7 @@ def printentries(entries, location): printfield(name, entry.binfo) -class Do_SConsignDB(object): +class Do_SConsignDB: def __init__(self, dbm_name, dbm): self.dbm_name = dbm_name self.dbm = dbm diff --git a/SCons/Variables/PathVariable.py b/SCons/Variables/PathVariable.py index eb9b299..c0ebb18 100644 --- a/SCons/Variables/PathVariable.py +++ b/SCons/Variables/PathVariable.py @@ -76,7 +76,7 @@ import os.path import SCons.Errors -class _PathVariableClass(object): +class _PathVariableClass: @staticmethod def PathAccept(key, val, env): diff --git a/SCons/Variables/VariablesTests.py b/SCons/Variables/VariablesTests.py index 43c785f..93866e0 100644 --- a/SCons/Variables/VariablesTests.py +++ b/SCons/Variables/VariablesTests.py @@ -34,7 +34,7 @@ import SCons.Warnings from SCons.Util import cmp -class Environment(object): +class Environment: def __init__(self): self.dict = {} def subst(self, x): @@ -402,7 +402,7 @@ class VariablesTestCase(unittest.TestCase): 'OPT_BOOL_2' : 2}) # Test against some old bugs - class Foo(object): + class Foo: def __init__(self, x): self.x = x def __str__(self): diff --git a/SCons/Variables/__init__.py b/SCons/Variables/__init__.py index 7bd4cd9..59b63eb 100644 --- a/SCons/Variables/__init__.py +++ b/SCons/Variables/__init__.py @@ -44,7 +44,7 @@ from .PackageVariable import PackageVariable # naja from .PathVariable import PathVariable # okay -class Variables(object): +class Variables: """ Holds all the options, updates the environment with the variables, and renders the help text. @@ -79,7 +79,7 @@ class Variables(object): Variables.instance=self def _do_add(self, key, help="", default=None, validator=None, converter=None): - class Variable(object): + class Variable: pass option = Variable() diff --git a/SCons/WarningsTests.py b/SCons/WarningsTests.py index b5c5aef..d80bd57 100644 --- a/SCons/WarningsTests.py +++ b/SCons/WarningsTests.py @@ -28,7 +28,7 @@ import unittest import SCons.Warnings -class TestOutput(object): +class TestOutput: def __call__(self, x): args = x.args[0] if len(args) == 1: diff --git a/SCons/cpp.py b/SCons/cpp.py index c56c965..1113be5 100644 --- a/SCons/cpp.py +++ b/SCons/cpp.py @@ -184,7 +184,7 @@ del override -class FunctionEvaluator(object): +class FunctionEvaluator: """ Handles delayed evaluation of a #define function call. """ @@ -241,7 +241,7 @@ function_arg_separator = re.compile(r',\s*') -class PreProcessor(object): +class PreProcessor: """ The main workhorse class for handling C pre-processing. diff --git a/SCons/dblite.py b/SCons/dblite.py index 765516c..b9269f1 100644 --- a/SCons/dblite.py +++ b/SCons/dblite.py @@ -26,7 +26,7 @@ dblite_suffix = '.dblite' tmp_suffix = '.tmp' -class dblite(object): +class dblite: """ Squirrel away references to the functions in various modules that we'll use when our __del__() method calls our sync() method diff --git a/bench/dependency-func.py b/bench/dependency-func.py index 09c6162..7313cf9 100644 --- a/bench/dependency-func.py +++ b/bench/dependency-func.py @@ -71,7 +71,7 @@ def Func02(t): # {'keyword' : 'arguments'}, # ), -class A(object): +class A: pass Data = [ diff --git a/bench/env.__setitem__.py b/bench/env.__setitem__.py index ed0888e..f9fe0c0 100644 --- a/bench/env.__setitem__.py +++ b/bench/env.__setitem__.py @@ -18,7 +18,7 @@ import timeit # These wrap the basic timeit function to make it a little more # convenient to do side-by-side tests of code. -class Timing(object): +class Timing: def __init__(self, name, num, init, statement): self.__timer = timeit.Timer(statement, init) self.__num = num @@ -95,7 +95,7 @@ global_valid_var = re.compile(r'[_a-zA-Z]\w*$') # After we're done should be the one that shows up at the top of the # list as we run our timings. -class Environment(object): +class Environment: _special_set = { 'BUILDERS' : None, 'SCANNERS' : None, diff --git a/bench/is_types.py b/bench/is_types.py index 69c029f..b47c381 100644 --- a/bench/is_types.py +++ b/bench/is_types.py @@ -244,7 +244,7 @@ def Func12(obj): # {'keyword' : 'arguments'}, # ), -class A(object): +class A: pass Data = [ diff --git a/bin/Command.py b/bin/Command.py index a63e6c5..a7f94f6 100644 --- a/bin/Command.py +++ b/bin/Command.py @@ -13,7 +13,7 @@ class Usage(Exception): def __init__(self, msg): self.msg = msg -class CommandRunner(object): +class CommandRunner: """ Representation of a command to be executed. """ diff --git a/bin/SConsDoc.py b/bin/SConsDoc.py index 960a0c9..958fb4c 100644 --- a/bin/SConsDoc.py +++ b/bin/SConsDoc.py @@ -251,7 +251,7 @@ class DoctypeDeclaration: return content if not has_libxml2: - class TreeFactory(object): + class TreeFactory: def __init__(self): pass @@ -391,7 +391,7 @@ if not has_libxml2: return [root] else: - class TreeFactory(object): + class TreeFactory: def __init__(self): pass @@ -622,7 +622,7 @@ else: tf = TreeFactory() -class SConsDocTree(object): +class SConsDocTree: def __init__(self): self.nsmap = {'dbx': dbxsd} self.doc = None @@ -707,7 +707,7 @@ def validate_all_xml(dpaths, xsdfile=default_xsd): return True -class Item(object): +class Item: def __init__(self, name): self.name = name self.sort_name = name.lower() @@ -745,7 +745,7 @@ class ConstructionVariable(Item): pass -class Arguments(object): +class Arguments: def __init__(self, signature, body=None): if not body: body = [] @@ -763,7 +763,7 @@ class Arguments(object): self.body.append(data) -class SConsDocHandler(object): +class SConsDocHandler: def __init__(self): self.builders = {} self.functions = {} diff --git a/bin/SConsExamples.py b/bin/SConsExamples.py index 6223002..46df103 100644 --- a/bin/SConsExamples.py +++ b/bin/SConsExamples.py @@ -477,7 +477,7 @@ def my_RDirs(self, pathlist, orig_RDirs=orig_RDirs): return [str(x).replace(os.sep, Sep) for x in orig_RDirs(self, pathlist)] SCons.Node.FS.File.RDirs = my_RDirs -class Curry(object): +class Curry: def __init__(self, fun, *args, **kwargs): self.fun = fun self.pending = args[:] @@ -498,7 +498,7 @@ def Str(target, source, env, cmd=""): result.append(' '.join(map(str, cmd))) return '\\n'.join(result) -class ToolSurrogate(object): +class ToolSurrogate: def __init__(self, tool, variable, func, varlist): self.tool = tool if not isinstance(variable, list): diff --git a/bin/caller-tree.py b/bin/caller-tree.py index 184ae30..b2eab28 100644 --- a/bin/caller-tree.py +++ b/bin/caller-tree.py @@ -41,7 +41,7 @@ # fine-grained performance tuning. import sys -class Entry(object): +class Entry: def __init__(self, file_line_func): self.file_line_func = file_line_func self.called_by = [] diff --git a/bin/import-test.py b/bin/import-test.py index 6a71a37..d979017 100644 --- a/bin/import-test.py +++ b/bin/import-test.py @@ -35,7 +35,7 @@ directory = sys.argv[1] Top = None TopPath = None -class Dir(object): +class Dir: def __init__(self, path): self.path = path self.entries = {} diff --git a/bin/linecount.py b/bin/linecount.py index 163401b..ae45c55 100644 --- a/bin/linecount.py +++ b/bin/linecount.py @@ -27,7 +27,7 @@ import os.path fmt = "%-16s %5s %7s %9s %11s %11s" -class Collection(object): +class Collection: def __init__(self, name, files=None, pred=None): self._name = name if files is None: diff --git a/bin/scons-proc.py b/bin/scons-proc.py index 9ec4efd..95a798c 100644 --- a/bin/scons-proc.py +++ b/bin/scons-proc.py @@ -222,7 +222,7 @@ class SCons_XML: v.tag, v.entityfunc(), v.tag)) f.close() -class Proxy(object): +class Proxy: def __init__(self, subject): """Wrap an object as a Proxy object""" self.__subject = subject diff --git a/bin/scons-time.py b/bin/scons-time.py index f4decc9..e7087e7 100644 --- a/bin/scons-time.py +++ b/bin/scons-time.py @@ -57,7 +57,7 @@ def HACK_for_exec(cmd, *args): exec(cmd, args[0], args[1]) -class Plotter(object): +class Plotter: def increment_size(self, largest): """ Return the size of each horizontal increment line for a specified @@ -81,7 +81,7 @@ class Plotter(object): return ((largest + increment - 1) // increment) * increment -class Line(object): +class Line: def __init__(self, points, type, title, label, comment, fmt="%s %s"): self.points = points self.type = type @@ -260,7 +260,7 @@ def tee_to_file(command, log): return '%s 2>&1 | tee %s' % (command, log) -class SConsTimer(object): +class SConsTimer: """ Usage: scons-time SUBCOMMAND [ARGUMENTS] Type "scons-time help SUBCOMMAND" for help on a specific subcommand. diff --git a/bin/time-scons.py b/bin/time-scons.py index c5cd0cc..272283a 100644 --- a/bin/time-scons.py +++ b/bin/time-scons.py @@ -46,7 +46,7 @@ TimeSCons_revision = 4569 TimeSCons_pieces = ['testing/framework', 'timings', 'runtest.py'] -class CommandRunner(object): +class CommandRunner: """ Executor class for commands, including "commands" implemented by Python functions. diff --git a/bin/update-release-info.py b/bin/update-release-info.py index 3fcdf72..fabf7c6 100644 --- a/bin/update-release-info.py +++ b/bin/update-release-info.py @@ -67,7 +67,7 @@ import argparse DEBUG = os.environ.get('DEBUG', 0) -class ReleaseInfo(object): +class ReleaseInfo: def __init__(self, args): self.config = {} self.args = args @@ -181,7 +181,7 @@ class ReleaseInfo(object): + ' %+.4d' % min) -class UpdateFile(object): +class UpdateFile: """ XXX """ rel_info = None diff --git a/doc/generated/builders.gen b/doc/generated/builders.gen index f115e5b..0e85eeb 100644 --- a/doc/generated/builders.gen +++ b/doc/generated/builders.gen @@ -11,7 +11,7 @@ %variables-mod; ]> - + CFile() env.CFile() diff --git a/doc/generated/functions.gen b/doc/generated/functions.gen index 5dc356e..c0b325e 100644 --- a/doc/generated/functions.gen +++ b/doc/generated/functions.gen @@ -11,7 +11,7 @@ %variables-mod; ]> - + Action(action, [cmd/str/fun, [var, ...]] [option=value, ...]) env.Action(action, [cmd/str/fun, [var, ...]] [option=value, ...]) @@ -3043,11 +3043,13 @@ will overwrite itself on a display: import sys -class ProgressCounter(object): + +class ProgressCounter: count = 0 def __call__(self, node, *args, **kw): self.count += 100 sys.stderr.write('Evaluated %s nodes\r' % self.count) + Progress(ProgressCounter(), interval=100) @@ -3772,6 +3774,19 @@ which corresponds to . +no_progress + + +which corresponds to -Q. + + + Note: The initial progress output will still be output as this is done before the SConstruct/SConscript which contains the SetOption is processed + scons: Reading SConscript files ... + +Available since scons 4.0. + + + stack_size diff --git a/doc/generated/tools.gen b/doc/generated/tools.gen index 87140b0..00bfa7c 100644 --- a/doc/generated/tools.gen +++ b/doc/generated/tools.gen @@ -11,7 +11,7 @@ %variables-mod; ]> - + 386asm diff --git a/doc/generated/variables.gen b/doc/generated/variables.gen index 30103ef..8c22b79 100644 --- a/doc/generated/variables.gen +++ b/doc/generated/variables.gen @@ -11,7 +11,7 @@ %variables-mod; ]> - + __LDMODULEVERSIONFLAGS diff --git a/doc/man/scons.xml b/doc/man/scons.xml index 0388a48..b58e14f 100644 --- a/doc/man/scons.xml +++ b/doc/man/scons.xml @@ -2683,8 +2683,8 @@ you can either build a new list by hand: foo = Object('foo.c') bar = Object('bar.c') objects = ['begin.o'] + foo + ['middle.o'] + bar + ['end.o'] -for object in objects: - print(str(object)) +for obj in objects: + print(str(obj)) Or you can use the &Flatten; @@ -2696,8 +2696,8 @@ which may be more convenient: foo = Object('foo.c') bar = Object('bar.c') objects = Flatten(['begin.o', foo, 'middle.o', bar, 'end.o']) -for object in objects: - print(str(object)) +for obj in objects: + print(str(obj)) SCons builder calls return @@ -6307,7 +6307,7 @@ be associated with the instantiation of the class: -class foo(object): +class foo: def __init__(self, arg): self.arg = arg diff --git a/site_scons/BuildCommandLine.py b/site_scons/BuildCommandLine.py index b8c7d63..dc269b6 100644 --- a/site_scons/BuildCommandLine.py +++ b/site_scons/BuildCommandLine.py @@ -4,7 +4,7 @@ import socket from SCons.Script import ARGUMENTS -class BuildCommandLine(object): +class BuildCommandLine: git = None diff --git a/src/test_strings.py b/src/test_strings.py index b43340f..958cb15 100644 --- a/src/test_strings.py +++ b/src/test_strings.py @@ -51,7 +51,7 @@ build_scons = build_path('scons') build_local = build_path('scons-local', 'scons-local-'+scons_version) build_src = build_path('scons-src') -class Checker(object): +class Checker: def __init__(self, directory, search_list = [], remove_list = [], diff --git a/test/Actions/actions.py b/test/Actions/actions.py index cf5890d..3bac188 100644 --- a/test/Actions/actions.py +++ b/test/Actions/actions.py @@ -86,7 +86,7 @@ test.up_to_date(arguments = '.') test.write('SConstruct', """ import subprocess assert 'string' not in globals() -class bld(object): +class bld: def __init__(self): self.cmd = r'%(_python_)s build.py %%s 4 %%s' def __call__(self, env, target, source): diff --git a/test/Batch/SConstruct_changed_sources_alwaysBuild b/test/Batch/SConstruct_changed_sources_alwaysBuild index c260316..d7acb3b 100644 --- a/test/Batch/SConstruct_changed_sources_alwaysBuild +++ b/test/Batch/SConstruct_changed_sources_alwaysBuild @@ -1,7 +1,7 @@ # Testcase for tigris bug 2622 -object = Object('changed_sources_main.cpp') -AlwaysBuild(object) +obj = Object('changed_sources_main.cpp') +AlwaysBuild(obj) program = Program('test', source = [object]) diff --git a/test/CPPPATH/expand-object.py b/test/CPPPATH/expand-object.py index 8c811b7..54e1d39 100644 --- a/test/CPPPATH/expand-object.py +++ b/test/CPPPATH/expand-object.py @@ -34,7 +34,7 @@ import TestSCons test = TestSCons.TestSCons() test.write('SConstruct', """ -class XXX(object): +class XXX: def __init__(self, value): self.value = value def __str__(self): diff --git a/test/CPPPATH/list-expansion.py b/test/CPPPATH/list-expansion.py index cec333d..98817b9 100644 --- a/test/CPPPATH/list-expansion.py +++ b/test/CPPPATH/list-expansion.py @@ -42,7 +42,7 @@ test = TestSCons.TestSCons() test.subdir('sub1', 'sub2', 'sub3', 'sub4') test.write('SConstruct', """\ -class _inc_test(object): +class _inc_test: def __init__(self, name): self.name = name diff --git a/test/Progress/object.py b/test/Progress/object.py index 2886d06..25c53cf 100644 --- a/test/Progress/object.py +++ b/test/Progress/object.py @@ -36,7 +36,7 @@ test.write('SConstruct', """\ import sys env = Environment() env['BUILDERS']['C'] = Builder(action=Copy('$TARGET', '$SOURCE')) -class my_progress(object): +class my_progress: count = 0 def __call__(self, node, *args, **kw): self.count = self.count + 1 diff --git a/test/Scanner/generated.py b/test/Scanner/generated.py index f0fb0cc..f62bf9a 100644 --- a/test/Scanner/generated.py +++ b/test/Scanner/generated.py @@ -316,7 +316,7 @@ def write_out(fname, dict): # because the .__*__() methods in new-style classes are not looked # up on the instance, but resolve to the actual wrapped class methods, # so we have to handle those directly. -class CScannerCounter(object): +class CScannerCounter: def __init__(self, original_CScanner, *args, **kw): self.original_CScanner = original_CScanner def __eq__(self, *args, **kw): diff --git a/test/ToolSurrogate.py b/test/ToolSurrogate.py index 0674db1..a297056 100644 --- a/test/ToolSurrogate.py +++ b/test/ToolSurrogate.py @@ -34,7 +34,7 @@ import TestSCons test = TestSCons.TestSCons() test.write('SConstruct', """\ -class Curry(object): +class Curry: def __init__(self, fun, *args, **kwargs): self.fun = fun self.pending = args[:] @@ -55,7 +55,7 @@ def Str(target, source, env, cmd=""): result.append(" ".join(map(str, cmd))) return '\\n'.join(result) -class ToolSurrogate(object): +class ToolSurrogate: def __init__(self, tool, variable, func): self.tool = tool self.variable = variable diff --git a/test/Value.py b/test/Value.py index eb04eb0..7a702b0 100644 --- a/test/Value.py +++ b/test/Value.py @@ -37,7 +37,7 @@ python = TestSCons.python SConstruct_content = """ Decider(r'%(source_signature)s') -class Custom(object): +class Custom: def __init__(self, value): self.value = value def __str__(self): return "C=" + str(self.value) diff --git a/testing/framework/TestCmd.py b/testing/framework/TestCmd.py index 01d12cb..f980881 100644 --- a/testing/framework/TestCmd.py +++ b/testing/framework/TestCmd.py @@ -311,7 +311,7 @@ IS_WINDOWS = sys.platform == 'win32' IS_64_BIT = sys.maxsize > 2**32 IS_PYPY = hasattr(sys, 'pypy_translation_info') -class null(object): +class null: pass @@ -946,7 +946,7 @@ def _clean(): atexit.register(_clean) -class TestCmd(object): +class TestCmd: """Class TestCmd """ -- cgit v0.12 From 56020df5c9caef184a7a9c1cf4a60cef6a4bfbb3 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Sun, 24 May 2020 13:31:58 -0600 Subject: Fix bug in test for PR #3668 Signed-off-by: Mats Wichmann --- test/Batch/SConstruct_changed_sources_alwaysBuild | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/Batch/SConstruct_changed_sources_alwaysBuild b/test/Batch/SConstruct_changed_sources_alwaysBuild index d7acb3b..dea7908 100644 --- a/test/Batch/SConstruct_changed_sources_alwaysBuild +++ b/test/Batch/SConstruct_changed_sources_alwaysBuild @@ -3,6 +3,6 @@ obj = Object('changed_sources_main.cpp') AlwaysBuild(obj) -program = Program('test', source = [object]) +program = Program('test', source=[obj]) Default(program) -- cgit v0.12 From df7422a008be9236cf7ded05d99704a6b7ce7459 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Sat, 23 May 2020 12:07:39 -0600 Subject: Set Tasks class as abstract needs_execut method set as an abstract method, meaning you can't instantiate Task itself, and derived classes must implement the methid. The former warning framework for this (deprecated) is disabled, and some unit tests that were not implementing needs_execute were fixed - by deriving from the AlwaysTask class. Signed-off-by: Mats Wichmann --- CHANGES.txt | 3 ++ SCons/Taskmaster.py | 23 ++++--------- SCons/TaskmasterTests.py | 9 ++--- test/Deprecated/TaskmasterNeedsExecute.py | 53 ------------------------------ test/Removed/BuildDir/README.md | 2 +- test/Removed/CacheDir/README.md | 2 +- test/Removed/Copy-Method/README.md | 2 +- test/Removed/Old/TaskmasterNeedsExecute.py | 53 ++++++++++++++++++++++++++++++ test/Removed/Old/sconstest.skip | 0 test/Removed/README.md | 13 ++++++++ test/Removed/SourceCode/README.md | 2 +- test/Removed/SourceSignatures/README.md | 2 +- test/Removed/TargetSignatures/README.md | 2 +- 13 files changed, 87 insertions(+), 79 deletions(-) delete mode 100644 test/Deprecated/TaskmasterNeedsExecute.py create mode 100644 test/Removed/Old/TaskmasterNeedsExecute.py create mode 100644 test/Removed/Old/sconstest.skip create mode 100644 test/Removed/README.md diff --git a/CHANGES.txt b/CHANGES.txt index ecf3255..f2f3179 100755 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -150,6 +150,9 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER - ParseFlags should not modify the user's passed in dict in case it's a compound data structure (e.g. values are lists) (issue #3665) - In Py3 classes no longer need to be listed as deriving from object. + - Remove deprecated check for Task subclasses needing a needs_execute + method - this is now enforced via an abstract base class, so the + check and test is no longer needed. diff --git a/SCons/Taskmaster.py b/SCons/Taskmaster.py index 147cbcd..bfb7dc1 100644 --- a/SCons/Taskmaster.py +++ b/SCons/Taskmaster.py @@ -54,10 +54,11 @@ __doc__ = """ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" -from itertools import chain import operator import sys import traceback +from abc import ABC, abstractmethod +from itertools import chain import SCons.Errors import SCons.Node @@ -115,10 +116,8 @@ def dump_stats(): print((fmt % n.attributes.stats.__dict__) + str(n)) - -class Task: - """ - Default SCons build engine task. +class Task(ABC): + """ SCons build engine abstract task class. This controls the interaction of the actual building of node and the rest of the engine. @@ -210,17 +209,9 @@ class Task: """ return self.node + @abstractmethod def needs_execute(self): - # TODO(deprecate): "return True" is the old default behavior; - # change it to NotImplementedError (after running through the - # Deprecation Cycle) so the desired behavior is explicitly - # determined by which concrete subclass is used. - #raise NotImplementedError - msg = ('Taskmaster.Task is an abstract base class; instead of\n' - '\tusing it directly, ' - 'derive from it and override the abstract methods.') - SCons.Warnings.warn(SCons.Warnings.TaskmasterNeedsExecuteWarning, msg) - return True + return def execute(self): """ @@ -577,7 +568,7 @@ class AlwaysTask(Task): dependencies) can use this as follows: class MyTaskSubclass(SCons.Taskmaster.Task): - needs_execute = SCons.Taskmaster.Task.execute_always + needs_execute = SCons.Taskmaster.AlwaysTask.needs_execute """ return True diff --git a/SCons/TaskmasterTests.py b/SCons/TaskmasterTests.py index 961910a..43a5047 100644 --- a/SCons/TaskmasterTests.py +++ b/SCons/TaskmasterTests.py @@ -291,7 +291,7 @@ class TaskmasterTestCase(unittest.TestCase): built_text = "up to date: " top_node = n3 - class MyTask(SCons.Taskmaster.Task): + class MyTask(SCons.Taskmaster.AlwaysTask): def execute(self): global built_text if self.targets[0].get_state() == SCons.Node.up_to_date: @@ -542,10 +542,11 @@ class TaskmasterTestCase(unittest.TestCase): """ ood = [] def TaskGen(tm, targets, top, node, ood=ood): - class MyTask(SCons.Taskmaster.Task): + class MyTask(SCons.Taskmaster.AlwaysTask): def make_ready(self): SCons.Taskmaster.Task.make_ready(self) self.ood.extend(self.out_of_date) + t = MyTask(tm, targets, top, node) t.ood = ood return t @@ -585,7 +586,7 @@ class TaskmasterTestCase(unittest.TestCase): def test_make_ready_exception(self): """Test handling exceptions from Task.make_ready() """ - class MyTask(SCons.Taskmaster.Task): + class MyTask(SCons.Taskmaster.AlwaysTask): def make_ready(self): raise MyException("from make_ready()") @@ -599,7 +600,7 @@ class TaskmasterTestCase(unittest.TestCase): def test_make_ready_all(self): """Test the make_ready_all() method""" - class MyTask(SCons.Taskmaster.Task): + class MyTask(SCons.Taskmaster.AlwaysTask): make_ready = SCons.Taskmaster.Task.make_ready_all n1 = Node("n1") diff --git a/test/Deprecated/TaskmasterNeedsExecute.py b/test/Deprecated/TaskmasterNeedsExecute.py deleted file mode 100644 index 9f7ade1..0000000 --- a/test/Deprecated/TaskmasterNeedsExecute.py +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/env python -# -# __COPYRIGHT__ -# -# Permission is hereby granted, free of charge, to any person obtaining -# a copy of this software and associated documentation files (the -# "Software"), to deal in the Software without restriction, including -# without limitation the rights to use, copy, modify, merge, publish, -# distribute, sublicense, and/or sell copies of the Software, and to -# permit persons to whom the Software is furnished to do so, subject to -# the following conditions: -# -# The above copyright notice and this permission notice shall be included -# in all copies or substantial portions of the Software. -# -# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY -# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -# - -__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" - -""" -Verify the message about the deprecated Taskmaster.needs_task() -method, and the ability to suppress it. -""" - -import TestSCons - -test = TestSCons.TestSCons(match = TestSCons.match_re_dotall) - -test.write('SConscript', """ -import SCons.Taskmaster -tm = SCons.Taskmaster.Taskmaster() -task = SCons.Taskmaster.Task(tm, [], True, None) -task.needs_execute() -""") - -msg ="""Taskmaster.Task is an abstract base class; instead of -\tusing it directly, derive from it and override the abstract methods.""" -test.deprecated_warning('taskmaster-needs-execute', msg) - -test.pass_test() - -# Local Variables: -# tab-width:4 -# indent-tabs-mode:nil -# End: -# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Removed/BuildDir/README.md b/test/Removed/BuildDir/README.md index c4fd879..131e67b 100644 --- a/test/Removed/BuildDir/README.md +++ b/test/Removed/BuildDir/README.md @@ -1,6 +1,6 @@ BuildDir/Old contains old tests which used the now removed BuildDir function, env.BuildDir method, and build_dir argument to SConscript, -preserved here for reference; the presence of an scontest.skip file +preserved here for reference; the presence of an sconstest.skip file means they are never executed. The "new" tests verify failure using these symbols. diff --git a/test/Removed/CacheDir/README.md b/test/Removed/CacheDir/README.md index c5b75bf..46fcbc0 100644 --- a/test/Removed/CacheDir/README.md +++ b/test/Removed/CacheDir/README.md @@ -1,4 +1,4 @@ CacheDir/Old contains old tests of CacheDir which used the now removed SourceSignatures and TargetSignatures methods, preserved here for -reference; the presence of an scontest.skip file means they are never +reference; the presence of an sconstest.skip file means they are never executed. diff --git a/test/Removed/Copy-Method/README.md b/test/Removed/Copy-Method/README.md index 609c6e4..6352522 100644 --- a/test/Removed/Copy-Method/README.md +++ b/test/Removed/Copy-Method/README.md @@ -2,5 +2,5 @@ Copy-Method.py is the "new" test for env.Copy, making sure we get an AttributeError. The Old directory is the former tests from the deprecated state, -preserved here for reference; the presence of an scontest.skip file +preserved here for reference; the presence of an sconstest.skip file means they are never executed. diff --git a/test/Removed/Old/TaskmasterNeedsExecute.py b/test/Removed/Old/TaskmasterNeedsExecute.py new file mode 100644 index 0000000..9f7ade1 --- /dev/null +++ b/test/Removed/Old/TaskmasterNeedsExecute.py @@ -0,0 +1,53 @@ +#!/usr/bin/env python +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +""" +Verify the message about the deprecated Taskmaster.needs_task() +method, and the ability to suppress it. +""" + +import TestSCons + +test = TestSCons.TestSCons(match = TestSCons.match_re_dotall) + +test.write('SConscript', """ +import SCons.Taskmaster +tm = SCons.Taskmaster.Taskmaster() +task = SCons.Taskmaster.Task(tm, [], True, None) +task.needs_execute() +""") + +msg ="""Taskmaster.Task is an abstract base class; instead of +\tusing it directly, derive from it and override the abstract methods.""" +test.deprecated_warning('taskmaster-needs-execute', msg) + +test.pass_test() + +# Local Variables: +# tab-width:4 +# indent-tabs-mode:nil +# End: +# vim: set expandtab tabstop=4 shiftwidth=4: diff --git a/test/Removed/Old/sconstest.skip b/test/Removed/Old/sconstest.skip new file mode 100644 index 0000000..e69de29 diff --git a/test/Removed/README.md b/test/Removed/README.md new file mode 100644 index 0000000..be3f9b9 --- /dev/null +++ b/test/Removed/README.md @@ -0,0 +1,13 @@ +This tree contains tests for formerly deprecated behaviors +that have since been removed. + +If there is a runnable test (i.e. a test that verifies a +particular old behavior actually fails if called), it is +here or in a subdirectory and is left selectable by the +test framework. + +If there is a test that cannot be run, it will be in a +subdirectory named Old, which will contain a sconstest.skip +file, ensuring those test files are never loaded by the +test framework. + diff --git a/test/Removed/SourceCode/README.md b/test/Removed/SourceCode/README.md index c584dc9..61feaad 100644 --- a/test/Removed/SourceCode/README.md +++ b/test/Removed/SourceCode/README.md @@ -2,5 +2,5 @@ SourceCode.py is the "new" test for SourceCode making sure we get a NameError. The Old directory is the former tests from the deprecated state, -preserved here for reference; the presence of an scontest.skip file +preserved here for reference; the presence of an sconstest.skip file means they are never executed. diff --git a/test/Removed/SourceSignatures/README.md b/test/Removed/SourceSignatures/README.md index 05d8d05..4714a68 100644 --- a/test/Removed/SourceSignatures/README.md +++ b/test/Removed/SourceSignatures/README.md @@ -2,5 +2,5 @@ SourceSignatures.py is the "new" test, only makes sure scons actually fails in the presence of the method or setoption call. The Old directory is the former tests from the deprecated state, -preserved here for reference; the presence of an scontest.skip file +preserved here for reference; the presence of an sconstest.skip file means they are never executed. diff --git a/test/Removed/TargetSignatures/README.md b/test/Removed/TargetSignatures/README.md index 00a8b6b..db00b8c 100644 --- a/test/Removed/TargetSignatures/README.md +++ b/test/Removed/TargetSignatures/README.md @@ -2,5 +2,5 @@ TargetSignatures.py is the "new" test, only makes sure scons actually fails in the presence of the method or setoption call. The Old directory is the former tests from the deprecated state, -preserved here for reference; the presence of an scontest.skip file +preserved here for reference; the presence of an sconstest.skip file means they are never executed. -- cgit v0.12 From 9623c19fb4c51d6dd9c15ac270f44fd3298f4be5 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Wed, 27 May 2020 06:45:25 -0600 Subject: [PR #3669] add unit test for abstract Task Make sure a subclass of Task really does fail when instantiated if it does not implement needs_execute(). Signed-off-by: Mats Wichmann --- SCons/TaskmasterTests.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/SCons/TaskmasterTests.py b/SCons/TaskmasterTests.py index 43a5047..ea673a5 100644 --- a/SCons/TaskmasterTests.py +++ b/SCons/TaskmasterTests.py @@ -597,6 +597,19 @@ class TaskmasterTestCase(unittest.TestCase): assert exc_type == MyException, repr(exc_type) assert str(exc_value) == "from make_ready()", exc_value + def test_needs_execute(self): + """Test that we can't instantiate a Task subclass without needs_execute + + We should be getting: + TypeError: Can't instantiate abstract class MyTask with abstract methods needs_execute + """ + class MyTask(SCons.Taskmaster.Task): + pass + + n1 = Node("n1") + tm = SCons.Taskmaster.Taskmaster(targets=[n1], tasker=MyTask) + with self.assertRaises(TypeError): + t = tm.next_task() def test_make_ready_all(self): """Test the make_ready_all() method""" -- cgit v0.12 From 8662a4efdf65dd5be31315c23338dc13d898498b Mon Sep 17 00:00:00 2001 From: "Andrii Doroshenko (Xrayez)" Date: Wed, 27 May 2020 19:43:13 +0300 Subject: Extend `Environment.Dump()` to select serialization format Environment.Dump() produces pretty-printable results only, so the usefulness of this method is limited to debugging purposes. The existing method is extended to allow selecting a serialization format to use via a `format` parameter. Namely, it's now possible to serialize variables as a JSON-formatted string, which makes it possible for automated external tools to inspect the environment more easily. --- CHANGES.txt | 3 +++ SCons/Environment.py | 36 ++++++++++++++++++++++++++---------- SCons/Environment.xml | 27 +++++++++++++++++++++++++-- SCons/EnvironmentTests.py | 12 ++++++++++++ 4 files changed, 66 insertions(+), 12 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index ecf3255..4774881 100755 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -54,6 +54,9 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER - Reorganized source tree. Moved src/engine/SCons to SCons to be more in line with current Python source tree organization practices. + From Andrii Doroshenko: + - Extended `Environment.Dump()` to select a format to serialize construction variables (pretty, json). + From Jeremy Elson: - Updated design doc to use the correct syntax for Depends() diff --git a/SCons/Environment.py b/SCons/Environment.py index 0b4be1b..cd52ee5 100644 --- a/SCons/Environment.py +++ b/SCons/Environment.py @@ -1517,26 +1517,42 @@ class Base(SubstitutionEnvironment): return dlist - def Dump(self, key=None): - """ Return pretty-printed string of construction variables. + def Dump(self, key=None, format='pretty'): + """ Serialize the construction variables to a string. :param key: if None, format the whole dict of variables. Else look up and format just the value for key. + + :param format: specify the format of the variables to be serialized: + - pretty: pretty-printed string. + - json: JSON-formatted string. """ - import pprint - pp = pprint.PrettyPrinter(indent=2) if key: cvars = self.Dictionary(key) else: cvars = self.Dictionary() - # TODO: pprint doesn't do a nice job on path-style values - # if the paths contain spaces (i.e. Windows), because the - # algorithm tries to break lines on spaces, while breaking - # on the path-separator would be more "natural". Is there - # a better way to format those? - return pp.pformat(cvars) + fmt = format.lower() + + if fmt == 'pretty': + import pprint + pp = pprint.PrettyPrinter(indent=2) + + # TODO: pprint doesn't do a nice job on path-style values + # if the paths contain spaces (i.e. Windows), because the + # algorithm tries to break lines on spaces, while breaking + # on the path-separator would be more "natural". Is there + # a better way to format those? + return pp.pformat(cvars) + + elif fmt == 'json': + import json + def non_serializable(obj): + return str(type(obj).__qualname__) + return json.dumps(cvars, indent=4, default=non_serializable) + else: + raise ValueError("Unsupported serialization format: %s." % fmt) def FindIxes(self, paths, prefix, suffix): diff --git a/SCons/Environment.xml b/SCons/Environment.xml index d13d560..898dc5d 100644 --- a/SCons/Environment.xml +++ b/SCons/Environment.xml @@ -1393,11 +1393,34 @@ for more information. -([key]) +([key], [format]) -Returns a pretty printable representation of the environment. +Serializes the construction variables to a string. +The method supports the following formats specified by +format: + + +pretty + + +Returns a pretty printable representation of the environment (if +format +is not specified, this is the default). + + + + +json + + +Returns a JSON-formatted string representation of the environment. + + + + + key, if not None, diff --git a/SCons/EnvironmentTests.py b/SCons/EnvironmentTests.py index ef12a6f..6dcf3c1 100644 --- a/SCons/EnvironmentTests.py +++ b/SCons/EnvironmentTests.py @@ -2941,6 +2941,18 @@ def generate(env): assert env.Dump('FOO') == "'foo'", env.Dump('FOO') assert len(env.Dump()) > 200, env.Dump() # no args version + assert env.Dump('FOO', 'json') == '"foo"' # JSON key version + import json + env_dict = json.loads(env.Dump(format = 'json')) + assert env_dict['FOO'] == 'foo' # full JSON version + + try: + env.Dump(format = 'markdown') + except ValueError as e: + assert str(e) == "Unsupported serialization format: markdown." + else: + self.fail("Did not catch expected ValueError.") + def test_Environment(self): """Test the Environment() method""" env = self.TestEnvironment(FOO = 'xxx', BAR = 'yyy') -- cgit v0.12 From 46810dae9a306c934f89ad6b3f37cc82eb2026a5 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Fri, 29 May 2020 11:14:49 -0600 Subject: Close scons logfiles on completion Files written to in logging operations could remain unclosed: more modern Pythons grumble about this; given the type of Python build, could emit ResourceWarning messages which cause tests to fail. Close by registering calls with atexit. Affects Trace, cache debug, taskmastertrace, configure. Signed-off-by: Mats Wichmann --- CHANGES.txt | 2 ++ SCons/CacheDir.py | 7 ++++++- SCons/Debug.py | 34 ++++++++++++++++++++++++++-------- SCons/SConf.py | 10 ++++++++-- SCons/Script/Main.py | 5 +++++ 5 files changed, 47 insertions(+), 11 deletions(-) diff --git a/CHANGES.txt b/CHANGES.txt index ecf3255..88b8d6f 100755 --- a/CHANGES.txt +++ b/CHANGES.txt @@ -150,6 +150,8 @@ RELEASE VERSION/DATE TO BE FILLED IN LATER - ParseFlags should not modify the user's passed in dict in case it's a compound data structure (e.g. values are lists) (issue #3665) - In Py3 classes no longer need to be listed as deriving from object. + - Close various logfiles (trace, cache, taskmastertrace, configure) + when done using atexit calls. diff --git a/SCons/CacheDir.py b/SCons/CacheDir.py index 5ecbc6f..3bb6671 100644 --- a/SCons/CacheDir.py +++ b/SCons/CacheDir.py @@ -27,13 +27,14 @@ __doc__ = """ CacheDir support """ +import atexit import json import os import stat import sys -import SCons import SCons.Action +import SCons.Errors import SCons.Warnings cache_enabled = True @@ -202,7 +203,11 @@ class CacheDir: if cache_debug == '-': self.debugFP = sys.stdout elif cache_debug: + def debug_cleanup(debugFP): + debugFP.close() + self.debugFP = open(cache_debug, 'w') + atexit.register(debug_cleanup, self.debugFP) else: self.debugFP = None self.current_cache_debug = cache_debug diff --git a/SCons/Debug.py b/SCons/Debug.py index 706b4c4..10cd2f5 100644 --- a/SCons/Debug.py +++ b/SCons/Debug.py @@ -33,6 +33,7 @@ caller_trace() __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" +import atexit import os import sys import time @@ -66,7 +67,7 @@ def string_to_classes(s): def fetchLoggedInstances(classes="*"): classnames = string_to_classes(classes) return [(cn, len(tracked_classes[cn])) for cn in classnames] - + def countLoggedInstances(classes, file=sys.stdout): for classname in string_to_classes(classes): file.write("%s: %d\n" % (classname, len(tracked_classes[classname]))) @@ -201,22 +202,39 @@ if sys.platform == 'win32': TraceDefault = 'con' else: TraceDefault = '/dev/tty' - -TimeStampDefault = None +TimeStampDefault = False StartTime = time.time() PreviousTime = StartTime -def Trace(msg, file=None, mode='w', tstamp=None): - """Write a trace message to a file. Whenever a file is specified, - it becomes the default for the next call to Trace().""" +def Trace(msg, filename=None, mode='w', tstamp=False): + """Write a trace message. + + Write messages when debugging which do not interfere with stdout. + Useful in tests, which monitor stdout and would break with + unexpected output. Trace messages can go to the console (which is + opened as a file), or to a disk file; the file argument persists + across calls unless overridden. + + Args: + filename: file to write trace message to. If omitted, + write to the previous trace file (default: console). + mode: file open mode (default: 'w') + tstamp: write relative timestamps with trace. Outputs time since + scons was started, and time since last trace (default: False) + + """ global TraceDefault global TimeStampDefault global PreviousTime + + def traace_cleanup(traceFP): + traceFP.close() + if file is None: file = TraceDefault else: TraceDefault = file - if tstamp is None: + if not tstamp: tstamp = TimeStampDefault else: TimeStampDefault = tstamp @@ -225,6 +243,7 @@ def Trace(msg, file=None, mode='w', tstamp=None): except KeyError: try: fp = TraceFP[file] = open(file, mode) + atexit.register(trace_cleanup, fp) except TypeError: # Assume we were passed an open file pointer. fp = file @@ -234,7 +253,6 @@ def Trace(msg, file=None, mode='w', tstamp=None): PreviousTime = now fp.write(msg) fp.flush() - fp.close() # Local Variables: # tab-width:4 diff --git a/SCons/SConf.py b/SCons/SConf.py index 42bfa8c..efa9e5b 100644 --- a/SCons/SConf.py +++ b/SCons/SConf.py @@ -37,6 +37,7 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" import SCons.compat +import atexit import io import os import re @@ -750,11 +751,16 @@ class SConfBase: _ac_config_logs[self.logfile] = None log_mode = "w" fp = open(str(self.logfile), log_mode) + + def conflog_cleanup(logf): + logf.close() + + atexit.register(conflog_cleanup, fp) self.logstream = SCons.Util.Unbuffered(fp) # logfile may stay in a build directory, so we tell - # the build system not to override it with a eventually + # the build system not to override it with an eventually # existing file with the same name in the source directory - self.logfile.dir.add_ignore( [self.logfile] ) + self.logfile.dir.add_ignore([self.logfile]) tb = traceback.extract_stack()[-3-self.depth] old_fs_dir = SConfFS.getcwd() diff --git a/SCons/Script/Main.py b/SCons/Script/Main.py index ac29712..d7b07a2 100644 --- a/SCons/Script/Main.py +++ b/SCons/Script/Main.py @@ -40,6 +40,7 @@ __revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" import SCons.compat +import atexit import importlib.util import os import re @@ -1262,10 +1263,14 @@ def _build_targets(fs, options, targets, target_top): """Leave the order of dependencies alone.""" return dependencies + def tmtrace_cleanup(tfile): + tfile.close() + if options.taskmastertrace_file == '-': tmtrace = sys.stdout elif options.taskmastertrace_file: tmtrace = open(options.taskmastertrace_file, 'w') + atexit.register(tmtrace_cleanup, tmtrace) else: tmtrace = None taskmaster = SCons.Taskmaster.Taskmaster(nodes, task_class, order, tmtrace) -- cgit v0.12 From 5708798e8443376cb52819dc1bcae3a59ed94ea6 Mon Sep 17 00:00:00 2001 From: Mats Wichmann Date: Fri, 29 May 2020 12:25:45 -0600 Subject: Fix typo in new trace_cleanup interior func Spotted by sider Signed-off-by: Mats Wichmann --- SCons/Debug.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/SCons/Debug.py b/SCons/Debug.py index 10cd2f5..2212eb8 100644 --- a/SCons/Debug.py +++ b/SCons/Debug.py @@ -227,7 +227,7 @@ def Trace(msg, filename=None, mode='w', tstamp=False): global TimeStampDefault global PreviousTime - def traace_cleanup(traceFP): + def trace_cleanup(traceFP): traceFP.close() if file is None: -- cgit v0.12