From 16ab0442c9a549b81ac18669571494cd8533e1dd Mon Sep 17 00:00:00 2001 From: Eric Kerfoot <17726042+ericspod@users.noreply.github.com> Date: Thu, 10 Sep 2026 23:01:46 +0100 Subject: [PATCH 1/6] Adding action test for changed Python source Signed-off-by: Eric Kerfoot <17726042+ericspod@users.noreply.github.com> --- .github/workflows/cicd_tests.yml | 82 ++++++++++++++++------ monai/utils/__init__.py | 1 + monai/utils/compare_sources.py | 102 ++++++++++++++++++++++++++++ tests/utils/test_compare_sources.py | 102 ++++++++++++++++++++++++++++ 4 files changed, 268 insertions(+), 19 deletions(-) create mode 100644 monai/utils/compare_sources.py create mode 100644 tests/utils/test_compare_sources.py diff --git a/.github/workflows/cicd_tests.yml b/.github/workflows/cicd_tests.yml index d5cb03a33f..eee043d4aa 100644 --- a/.github/workflows/cicd_tests.yml +++ b/.github/workflows/cicd_tests.yml @@ -7,19 +7,11 @@ on: - dev - main - releasing/* - paths-ignore: # skip if only docs are modified - - '**.md' - - '**.rst' - - 'docs/**' pull_request: branches: - dev - main - releasing/* - paths-ignore: # skip if only docs are modified - - '**.md' - - '**.rst' - - 'docs/**' permissions: contents: read @@ -55,6 +47,44 @@ env: # When support is dropped for a version it is important to update these as appropriate. jobs: + # This job will set its output value `skip` to 2 if only documentation was changed, either documentation files or + # the docstrings of source files. If any source lines are changed, or non-documentation files are changed, the + # result is 0 which then triggers tests to run. This job works by using the compare_sources.py script to compare + # changed files to see if they differ in ways other than comments or docstrings. + test_skip: + runs-on: ubuntu-latest + outputs: + skip: ${{ steps.check-changes.outputs.skip }} + steps: + - uses: actions/checkout@v7 + - name: Set up Python ${{ env.PYTHON_VER1 }} + uses: actions/setup-python@v6 + with: + python-version: ${{ env.PYTHON_VER1 }} + cache: 'pip' + - name: Check File Changes + id: check-changes + shell: bash + run: | + echo "skip=1" >> $GITHUB_OUTPUT + mkdir -p /tmp/check_changes + git diff --name-only HEAD^..HEAD ':!docs' | while read i + do + echo "Checking for changes: $i" + if ! git show HEAD^:$i > /tmp/check_changes/$(basename $i) + then + echo "New file found, or 'git show' failed, running tests." + elif ! python monai/utils/compare_sources.py $i /tmp/check_changes/$(basename $i) + then + echo "Change found, running tests." + else + continue + fi + + echo "skip=0" >> $GITHUB_OUTPUT + break + done + static-checks: # Perform static type and other checks using runtests.sh runs-on: ubuntu-latest strategy: @@ -87,6 +117,7 @@ jobs: min-dep: # Test with minumum dependencies installed for different OS, Python, and PyTorch combinations runs-on: ${{ matrix.os }} + needs: test_skip strategy: fail-fast: false matrix: @@ -123,8 +154,8 @@ jobs: timeout-minutes: 40 steps: - - if: runner.os == 'Linux' - name: Clean unused tools + - name: Clean unused tools + if: runner.os == 'Linux' && needs.test_skip.outputs.skip == 0 run: | find /opt/hostedtoolcache/* -maxdepth 0 ! -name 'Python' -exec rm -rf {} \; sudo rm -rf /usr/share/dotnet @@ -132,16 +163,20 @@ jobs: sudo rm -rf /opt/ghc /usr/local/.ghcup sudo docker system prune -f - uses: actions/checkout@v7 + if: needs.test_skip.outputs.skip == 0 - name: Set up Python ${{ matrix.python-version }} + if: needs.test_skip.outputs.skip == 0 uses: actions/setup-python@v6 with: python-version: ${{ matrix.python-version }} cache: 'pip' - name: Prepare pip wheel + if: needs.test_skip.outputs.skip == 0 run: | which python python -m pip install --upgrade pip wheel tomli - name: Install the minimum dependencies + if: needs.test_skip.outputs.skip == 0 run: | # min. requirements python -m pip install torch==${{ matrix.pytorch-version }} @@ -150,6 +185,7 @@ jobs: python -m pip list shell: bash - name: Run quick tests + if: needs.test_skip.outputs.skip == 0 run: | nvidia-smi || true python monai/config/check_env.py --env --monai @@ -168,51 +204,56 @@ jobs: matrix: os: [windows-latest, ubuntu-latest, macOS-latest] # macOS-latest is very slow (#8864), testing install only timeout-minutes: 120 + needs: test_skip env: QUICKTEST: True steps: - - if: runner.os == 'Linux' - name: Clean unused tools + - name: Clean unused tools + if: runner.os == 'windows' && needs.test_skip.outputs.skip == 0 run: | find /opt/hostedtoolcache/* -maxdepth 0 ! -name 'Python' -exec rm -rf {} \; sudo rm -rf /usr/share/dotnet sudo rm -rf /usr/local/lib/android sudo rm -rf /opt/ghc /usr/local/.ghcup sudo docker system prune -f - - if: runner.os == 'windows' - name: Config pagefile (Windows only) + - name: Config pagefile (Windows only) + if: runner.os == 'windows' && needs.test_skip.outputs.skip == 0 uses: al-cheb/configure-pagefile-action@v1.5 with: minimum-size: 8GB maximum-size: 16GB disk-root: "D:" - uses: actions/checkout@v7 + if: needs.test_skip.outputs.skip == 0 - name: Set up Python ${{ env.PYTHON_VER1 }} + if: needs.test_skip.outputs.skip == 0 uses: actions/setup-python@v6 with: python-version: ${{ env.PYTHON_VER1 }} cache: 'pip' - name: Prepare pip wheel + if: needs.test_skip.outputs.skip == 0 run: | which python python -m pip install --upgrade pip wheel - - if: runner.os == 'windows' + - if: runner.os == 'windows' && needs.test_skip.outputs.skip == 0 name: Install torch cpu from pytorch.org (Windows only) run: | python -m pip install torch==${WIN_FULLDEP_VER} torchvision==${WIN_FULLDEP_VISION_VER}+cpu --index-url https://download.pytorch.org/whl/cpu shell: bash - - if: runner.os != 'windows' + - if: runner.os != 'windows' && needs.test_skip.outputs.skip == 0 name: Install torch gpu run: | # install the lowest version of PyTorch supported python -m pip install torch==${PYTORCH_VER1} torchvision==${TORCHVISION_VER1} shell: bash - - if: runner.os == 'Linux' + - if: runner.os == 'Linux' && needs.test_skip.outputs.skip == 0 name: Install itk pre-release (Linux only) run: | python -m pip install --pre -U itk shell: bash - name: Install the complete dependencies + if: needs.test_skip.outputs.skip == 0 run: | python -m pip install --user --upgrade pip wheel tomli python monai/config/print_dependencies.py build-system | xargs pip install --no-build-isolation @@ -220,6 +261,7 @@ jobs: python -m pip list shell: bash - name: Run compiled (${{ runner.os }}) + if: needs.test_skip.outputs.skip == 0 run: | python -m pip uninstall -y monai BUILD_MONAI=1 python -m pip install --no-build-isolation -e . # compile the cpp extensions @@ -227,12 +269,12 @@ jobs: nvidia-smi || true python monai/config/check_env.py --env --monai shell: bash - - if: runner.os != 'macOS' + - if: runner.os != 'macOS' && needs.test_skip.outputs.skip == 0 name: Run full tests run: | python -m unittest -v shell: bash - - if: runner.os == 'macOS' + - if: runner.os == 'macOS' && needs.test_skip.outputs.skip == 0 name: Run min tests run: | # TODO: enable large range of macOS tests which don't take a very long time @@ -252,6 +294,8 @@ jobs: # ``.github/workflows/pythonapp-hyena-gpu.yml``. runs-on: ubuntu-latest timeout-minutes: 30 + needs: test_skip + if: needs.test_skip.outputs.skip == 0 steps: - name: Clean unused tools run: | diff --git a/monai/utils/__init__.py b/monai/utils/__init__.py index 944f34aab7..2080c78377 100644 --- a/monai/utils/__init__.py +++ b/monai/utils/__init__.py @@ -11,6 +11,7 @@ from __future__ import annotations +from .compare_sources import files_considered_equal, sources_equal, SKIP_EXTS from .component_store import ComponentStore from .decorators import MethodReplacer, RestartGenerator from .deprecate_utils import DeprecatedError, deprecated, deprecated_arg, deprecated_arg_default diff --git a/monai/utils/compare_sources.py b/monai/utils/compare_sources.py new file mode 100644 index 0000000000..1df3122cdf --- /dev/null +++ b/monai/utils/compare_sources.py @@ -0,0 +1,102 @@ +# Copyright (c) MONAI Consortium +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +""" +This module is used in Github Actions to determine if a Python source file has been modified in a PR. +""" + +import ast +import sys +from os.path import splitext + +from monai.config.type_definitions import PathLike + +SKIP_EXTS = (".md", ".rst") + + +class RemoveDocstrings(ast.NodeTransformer): + """ + Strips docstrings from source files so that they aren't used for comparing ASTs. + """ + + def visit(self, node: ast.AST) -> ast.AST: + try: + # remove docstrings from the files + if ast.get_docstring(node) is not None: + del node.body[0] + except: + pass + return super().visit(node) + + +def sources_equal(src1: str, src2: str) -> bool: + """ + Compare Python source texts at the AST level without docstrings or comments. If two texts are equal except for + changes to docstrings or comments, they will be considered equal by this function. Any other changes will appear + as differences in the AST representations, which are compared inefficiently by comparing tree string dumps. + + Args: + src1: first Python source text. + src2: second Python source text. + + Returns: + True if the texts `src1` and `src2` are equal with docstrings and comments removed, False otherwise. + """ + remdoc = RemoveDocstrings() + + m1: ast.Module = remdoc.generic_visit(ast.parse(src1)) + m2: ast.Module = remdoc.generic_visit(ast.parse(src2)) + + list1 = list(ast.walk(m1)) + list2 = list(ast.walk(m2)) + + if len(list1) != len(list2) or any(type(n1) is not type(n2) for n1, n2 in zip(list1, list2)): + return False + + return ast.dump(m1) == ast.dump(m2) + + +def files_considered_equal(file1: PathLike, file2: PathLike) -> bool: + """ + Returns True if the files are considered equal, that is they are doc files or differ only in docstrings or comments. + + Args: + file1: first file path to compare. + file2: second file path to compare. + + Returns: + True if the files have the same extension, and either this extension is in `SKIP_EXTS`, or is ".py" with the + files having the same content according to `sources_equal`. If the extensions differ, or are both ".py" but the + file contents differ, returns False. + """ + _, ext1 = splitext(str(file1)) + _, ext2 = splitext(str(file2)) + + # if extensions aren't equal then definitely different (which shouldn't happen anyway) + if ext1 != ext2: + return False + + # if extensions are an ignored type, ie. docs, don't compare at all + if ext1 in SKIP_EXTS: + return True + + # if not Python source files, they aren't doc files at this point so assume different + if ext1 != ".py": + return False + + # compare the actual parsed contents of the source files + with open(file1) as o1, open(file2) as o2: + return sources_equal(o1.read(), o2.read()) + + +if __name__ == "__main__": + _, file1, file2 = sys.argv + sys.exit(0 if files_considered_equal(file1, file2) else 1) \ No newline at end of file diff --git a/tests/utils/test_compare_sources.py b/tests/utils/test_compare_sources.py new file mode 100644 index 0000000000..9cd8178ef2 --- /dev/null +++ b/tests/utils/test_compare_sources.py @@ -0,0 +1,102 @@ +# Copyright (c) MONAI Consortium +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# http://www.apache.org/licenses/LICENSE-2.0 +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from __future__ import annotations + +from pathlib import Path +from tempfile import TemporaryDirectory +import unittest +from parameterized import parameterized +from monai.utils import files_considered_equal, sources_equal, SKIP_EXTS + +text1 = """ +def foo(arg1, arg2): + ''' + Compute the sum of arg1 and arg2 and return it. + ''' + return arg1 + arg2 +""" + +text2 = """ + +def foo(arg1, arg2): + ''' + Add arg1 and arg2 together and return it. + ''' + return arg1 + arg2 + +""" + +text3 = "def foo(arg1, arg2):return arg1 + arg2" + +text4 = "def bar(arg1, arg2):return arg1 + arg2" + +text5 = """ +class Foo: + def method(self,arg1): + '''Returns arg1.''' + return arg1 +""" + +TEXT_EQUAL_PAIRS = [(text1, text2), (text1, text3), (text2, text3), ("", "")] + +TEXT_UNEQUAL_PAIRS = [(text1, text4), (text2, text4), (text3, text4), (text1, text5), (text1, "")] + + +class TestSourcesEqual(unittest.TestCase): + @parameterized.expand(TEXT_EQUAL_PAIRS) + def test_equal_texts(self, t1, t2): + self.assertTrue(sources_equal(t1, t2)) + self.assertTrue(sources_equal(t2, t1)) + + @parameterized.expand(TEXT_UNEQUAL_PAIRS) + def test_unequal_texts(self, t1, t2): + self.assertFalse(sources_equal(t1, t2)) + self.assertFalse(sources_equal(t2, t1)) + + +class TestFilesEqual(unittest.TestCase): + def test_equal_exts(self): + """Test that two non-existent files with .py extensions result in an exception rather than False return.""" + with self.assertRaises(FileNotFoundError): + files_considered_equal("/path/to/file1.py", "/path/to/somewhere/else/file2.py") + + def test_unequal_exts(self): + """Test that two non-existent files with different extensions result in a False return.""" + self.assertFalse(files_considered_equal("/path/to/file1.text", "/path/to/file1.py")) + + @parameterized.expand(SKIP_EXTS) + def test_skip_docs(self, ext): + self.assertTrue(files_considered_equal("file1" + ext, "file2" + ext)) + + @parameterized.expand(TEXT_EQUAL_PAIRS) + def test_equal_text(self, t1, t2): + with TemporaryDirectory() as tmpdir: + p1 = Path(tmpdir, "t1.py") + p2 = Path(tmpdir, "t2.py") + p1.write_text(t1) + p2.write_text(t2) + + self.assertTrue(files_considered_equal(p1, p2)) + + @parameterized.expand(TEXT_UNEQUAL_PAIRS) + def test_unequal_text(self, t1, t2): + with TemporaryDirectory() as tmpdir: + p1 = Path(tmpdir, "t1.py") + p2 = Path(tmpdir, "t2.py") + p1.write_text(t1) + p2.write_text(t2) + + self.assertFalse(files_considered_equal(p1, p2)) + + +if __name__ == "__main__": + unittest.main() From b2f125b0a4c06ce4301e05af36e6e835a6437deb Mon Sep 17 00:00:00 2001 From: Eric Kerfoot <17726042+ericspod@users.noreply.github.com> Date: Thu, 10 Sep 2026 23:09:43 +0100 Subject: [PATCH 2/6] Fixes Signed-off-by: Eric Kerfoot <17726042+ericspod@users.noreply.github.com> --- monai/utils/__init__.py | 2 +- monai/utils/compare_sources.py | 10 ++++++---- tests/utils/test_compare_sources.py | 6 ++++-- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/monai/utils/__init__.py b/monai/utils/__init__.py index 2080c78377..f844789cd7 100644 --- a/monai/utils/__init__.py +++ b/monai/utils/__init__.py @@ -11,7 +11,7 @@ from __future__ import annotations -from .compare_sources import files_considered_equal, sources_equal, SKIP_EXTS +from .compare_sources import SKIP_EXTS, files_considered_equal, sources_equal from .component_store import ComponentStore from .decorators import MethodReplacer, RestartGenerator from .deprecate_utils import DeprecatedError, deprecated, deprecated_arg, deprecated_arg_default diff --git a/monai/utils/compare_sources.py b/monai/utils/compare_sources.py index 1df3122cdf..ce2a5328e6 100644 --- a/monai/utils/compare_sources.py +++ b/monai/utils/compare_sources.py @@ -9,10 +9,12 @@ # See the License for the specific language governing permissions and # limitations under the License. -""" +""" This module is used in Github Actions to determine if a Python source file has been modified in a PR. """ +from __future__ import annotations + import ast import sys from os.path import splitext @@ -32,7 +34,7 @@ def visit(self, node: ast.AST) -> ast.AST: # remove docstrings from the files if ast.get_docstring(node) is not None: del node.body[0] - except: + except TypeError: pass return super().visit(node) @@ -73,7 +75,7 @@ def files_considered_equal(file1: PathLike, file2: PathLike) -> bool: file2: second file path to compare. Returns: - True if the files have the same extension, and either this extension is in `SKIP_EXTS`, or is ".py" with the + True if the files have the same extension, and either this extension is in `SKIP_EXTS`, or is ".py" with the files having the same content according to `sources_equal`. If the extensions differ, or are both ".py" but the file contents differ, returns False. """ @@ -99,4 +101,4 @@ def files_considered_equal(file1: PathLike, file2: PathLike) -> bool: if __name__ == "__main__": _, file1, file2 = sys.argv - sys.exit(0 if files_considered_equal(file1, file2) else 1) \ No newline at end of file + sys.exit(0 if files_considered_equal(file1, file2) else 1) diff --git a/tests/utils/test_compare_sources.py b/tests/utils/test_compare_sources.py index 9cd8178ef2..74c3067fc5 100644 --- a/tests/utils/test_compare_sources.py +++ b/tests/utils/test_compare_sources.py @@ -11,11 +11,13 @@ from __future__ import annotations +import unittest from pathlib import Path from tempfile import TemporaryDirectory -import unittest + from parameterized import parameterized -from monai.utils import files_considered_equal, sources_equal, SKIP_EXTS + +from monai.utils import SKIP_EXTS, files_considered_equal, sources_equal text1 = """ def foo(arg1, arg2): From ff7db355608e37eace6d945738d34a7b4ed63a1e Mon Sep 17 00:00:00 2001 From: Eric Kerfoot <17726042+ericspod@users.noreply.github.com> Date: Thu, 10 Sep 2026 23:13:06 +0100 Subject: [PATCH 3/6] docs Signed-off-by: Eric Kerfoot <17726042+ericspod@users.noreply.github.com> --- docs/source/utils.rst | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/source/utils.rst b/docs/source/utils.rst index 958d27337e..557fe2100f 100644 --- a/docs/source/utils.rst +++ b/docs/source/utils.rst @@ -85,3 +85,8 @@ Safe Evaluation --------------- .. automodule:: monai.utils.safeeval :members: + +Compare Sources +--------------= +.. automodule:: monai.utils.compare_sources + :members: From 1481cd3d9a51b3499fec411b9994a94fc3c4d06e Mon Sep 17 00:00:00 2001 From: Eric Kerfoot <17726042+ericspod@users.noreply.github.com> Date: Thu, 10 Sep 2026 23:15:23 +0100 Subject: [PATCH 4/6] Fetch depth Signed-off-by: Eric Kerfoot <17726042+ericspod@users.noreply.github.com> --- .github/workflows/cicd_tests.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/cicd_tests.yml b/.github/workflows/cicd_tests.yml index eee043d4aa..4b5409f987 100644 --- a/.github/workflows/cicd_tests.yml +++ b/.github/workflows/cicd_tests.yml @@ -57,6 +57,8 @@ jobs: skip: ${{ steps.check-changes.outputs.skip }} steps: - uses: actions/checkout@v7 + with: + fetch-depth: 2 - name: Set up Python ${{ env.PYTHON_VER1 }} uses: actions/setup-python@v6 with: From 2770cb21b7520256ec689a43779ffb9946d85a08 Mon Sep 17 00:00:00 2001 From: Eric Kerfoot <17726042+ericspod@users.noreply.github.com> Date: Thu, 10 Sep 2026 23:18:58 +0100 Subject: [PATCH 5/6] Fix Signed-off-by: Eric Kerfoot <17726042+ericspod@users.noreply.github.com> --- .github/workflows/cicd_tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/cicd_tests.yml b/.github/workflows/cicd_tests.yml index 4b5409f987..5cf9a31ef4 100644 --- a/.github/workflows/cicd_tests.yml +++ b/.github/workflows/cicd_tests.yml @@ -211,7 +211,7 @@ jobs: QUICKTEST: True steps: - name: Clean unused tools - if: runner.os == 'windows' && needs.test_skip.outputs.skip == 0 + if: runner.os == 'Linux' && needs.test_skip.outputs.skip == 0 run: | find /opt/hostedtoolcache/* -maxdepth 0 ! -name 'Python' -exec rm -rf {} \; sudo rm -rf /usr/share/dotnet From f8302b90ee20572a7efa07ff1bd77f1822e08941 Mon Sep 17 00:00:00 2001 From: Eric Kerfoot <17726042+ericspod@users.noreply.github.com> Date: Thu, 10 Sep 2026 23:33:28 +0100 Subject: [PATCH 6/6] Comment fixes Signed-off-by: Eric Kerfoot <17726042+ericspod@users.noreply.github.com> --- .github/workflows/cicd_tests.yml | 11 +++++++++-- docs/source/utils.rst | 2 +- monai/utils/compare_sources.py | 9 ++++++--- tests/utils/test_compare_sources.py | 14 +++++++++++++- 4 files changed, 29 insertions(+), 7 deletions(-) diff --git a/.github/workflows/cicd_tests.yml b/.github/workflows/cicd_tests.yml index 5cf9a31ef4..28a420d8c8 100644 --- a/.github/workflows/cicd_tests.yml +++ b/.github/workflows/cicd_tests.yml @@ -69,8 +69,15 @@ jobs: shell: bash run: | echo "skip=1" >> $GITHUB_OUTPUT + mkdir -p /tmp/check_changes - git diff --name-only HEAD^..HEAD ':!docs' | while read i + if ! git diff --name-only HEAD^..HEAD ':!docs' > /tmp/check_changes/changed_files + then + echo "skip=0" >> "$GITHUB_OUTPUT" + exit 0 + fi + + while read i do echo "Checking for changes: $i" if ! git show HEAD^:$i > /tmp/check_changes/$(basename $i) @@ -85,7 +92,7 @@ jobs: echo "skip=0" >> $GITHUB_OUTPUT break - done + done < /tmp/check_changes/changed_files static-checks: # Perform static type and other checks using runtests.sh runs-on: ubuntu-latest diff --git a/docs/source/utils.rst b/docs/source/utils.rst index 557fe2100f..ef4bcc3670 100644 --- a/docs/source/utils.rst +++ b/docs/source/utils.rst @@ -87,6 +87,6 @@ Safe Evaluation :members: Compare Sources ---------------= +--------------- .. automodule:: monai.utils.compare_sources :members: diff --git a/monai/utils/compare_sources.py b/monai/utils/compare_sources.py index ce2a5328e6..f0e8bd9e41 100644 --- a/monai/utils/compare_sources.py +++ b/monai/utils/compare_sources.py @@ -16,10 +16,11 @@ from __future__ import annotations import ast +import os import sys from os.path import splitext -from monai.config.type_definitions import PathLike +PathLike = str | os.PathLike # needs to be duplicated here to avoid importing anything from MONAI inside actions SKIP_EXTS = (".md", ".rst") @@ -54,8 +55,10 @@ def sources_equal(src1: str, src2: str) -> bool: """ remdoc = RemoveDocstrings() - m1: ast.Module = remdoc.generic_visit(ast.parse(src1)) - m2: ast.Module = remdoc.generic_visit(ast.parse(src2)) + m1 = ast.parse(src1) + m2 = ast.parse(src2) + remdoc.visit(m1) + remdoc.visit(m2) list1 = list(ast.walk(m1)) list2 = list(ast.walk(m2)) diff --git a/tests/utils/test_compare_sources.py b/tests/utils/test_compare_sources.py index 74c3067fc5..2bbb8db0fc 100644 --- a/tests/utils/test_compare_sources.py +++ b/tests/utils/test_compare_sources.py @@ -48,7 +48,19 @@ def method(self,arg1): return arg1 """ -TEXT_EQUAL_PAIRS = [(text1, text2), (text1, text3), (text2, text3), ("", "")] +text6 = """ +''' +This is a module containing only a docstring. +''' +""" + +text7 = """ +''' +So is this but it's different. +''' +""" + +TEXT_EQUAL_PAIRS = [(text1, text2), (text1, text3), (text2, text3), (text6, text7), (text6, ""), ("", "")] TEXT_UNEQUAL_PAIRS = [(text1, text4), (text2, text4), (text3, text4), (text1, text5), (text1, "")]