diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 56f231d9578..d96b79148ee 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -96,39 +96,6 @@ jobs: MAKE: make -j2 --output-sync=recurse SAGE_NUM_THREADS: 2 - - name: Build modularized distributions - if: (success() || failure()) && steps.worktree.outcome == 'success' - run: make V=0 tox && make SAGE_CHECK=no pypi-wheels - working-directory: ./worktree-image - env: - MAKE: make -j2 --output-sync=recurse - SAGE_NUM_THREADS: 2 - - - name: Static code check with pyright - if: (success() || failure()) && steps.worktree.outcome == 'success' - uses: jakebailey/pyright-action@v1 - with: - version: 1.1.332 - # Many warnings issued by pyright are not yet helpful because there is not yet enough type information. - no-comments: true - working-directory: ./worktree-image - env: - # To avoid out of memory errors - NODE_OPTIONS: --max-old-space-size=8192 - - - name: Static code check with pyright (annotated) - if: (success() || failure()) && steps.worktree.outcome == 'success' - uses: jakebailey/pyright-action@v1 - with: - version: 1.1.332 - # Issue errors - no-comments: false - level: error - working-directory: ./worktree-image - env: - # To avoid out of memory errors - NODE_OPTIONS: --max-old-space-size=8192 - - name: Clean (fallback to non-incremental) id: clean if: (success() || failure()) && steps.worktree.outcome == 'success' && steps.incremental.outcome != 'success' @@ -187,9 +154,9 @@ jobs: - name: Pytest if: contains(github.ref, 'pytest') run: | - ../sage -python -m pip install coverage pytest-xdist - ../sage -python -m coverage run -m pytest -c tox.ini --doctest || true - working-directory: ./worktree-image/src + ./sage -python -m pip install coverage pytest-xdist + ./sage -python -m coverage run --rcfile=src/tox.ini -m pytest -c src/tox.ini --doctest-modules || true + working-directory: ./worktree-image env: # Increase the length of the lines in the "short summary" COLUMNS: 120 @@ -197,21 +164,15 @@ jobs: - name: Test all files (sage -t --all --long) if: (success() || failure()) && steps.build.outcome == 'success' run: | - ../sage -python -m pip install coverage - ../sage -python -m coverage run ./bin/sage-runtests --all --long -p2 --random-seed=286735480429121101562228604801325644303 - working-directory: ./worktree-image/src + ./sage -python -m pip install coverage + ./sage -python -m coverage run --rcfile=src/tox.ini src/bin/sage-runtests --all --long -p2 --format github --random-seed=286735480429121101562228604801325644303 + working-directory: ./worktree-image - name: Prepare coverage results if: (success() || failure()) && steps.build.outcome == 'success' run: | - ./venv/bin/python3 -m coverage combine src/.coverage/ - ./venv/bin/python3 -m coverage xml + ./sage -python -m coverage combine --rcfile=src/tox.ini + ./sage -python -m coverage xml --rcfile=src/tox.ini mkdir -p coverage-report mv coverage.xml coverage-report/ working-directory: ./worktree-image - - - name: Upload coverage to codecov - if: (success() || failure()) && steps.build.outcome == 'success' - uses: codecov/codecov-action@v3 - with: - directory: ./worktree-image/coverage-report diff --git a/.github/workflows/ci-conda-known-test-failures.json b/.github/workflows/ci-conda-known-test-failures.json index 2d828ac98fb..47d0adcd8e9 100644 --- a/.github/workflows/ci-conda-known-test-failures.json +++ b/.github/workflows/ci-conda-known-test-failures.json @@ -1,12 +1,18 @@ { + "doc.en.constructions.calculus": { + "failed": "unreported random failures in plotting" + }, "sage_setup.clean": { - "failed": true + "failed": "_find_stale_files finds some stale files under sage/tests when executed under conda" + }, + "sage.algebras.fusion_rings.fusion_ring": { + "failed": "unreported random timeouts" }, "sage.combinat.cluster_algebra_quiver.quiver": { - "failed": true + "failed": "failure seen in https://github.com/sagemath/sage/actions/runs/6836592771/job/18591690058#step:11:10059" }, "sage.geometry.cone": { - "failed": true + "failed": "unreported random timeouts seen in https://github.com/sagemath/sage/actions/runs/6827937663/job/18571052628#step:11:12362" }, "sage.groups.matrix_gps.finitely_generated_gap": { "failed": true @@ -15,28 +21,34 @@ "failed": true }, "sage.libs.gap.element": { - "failed": true + "failed": "failure seen in https://github.com/sagemath/sage/actions/runs/6840579851/job/18600012965#step:11:13016" }, "sage.libs.singular.singular": { "failed": true }, "sage.matrix.matrix2": { - "failed": true + "failed": "failure seen in https://github.com/sagemath/sage/actions/runs/6835143781/job/18588599649#step:11:16939" }, "sage.matrix.matrix_integer_sparse": { - "failed": true + "failed": "failure seen in https://github.com/sagemath/sage/actions/runs/6827937663/job/18571052628#step:11:12362" }, "sage.misc.lazy_import": { "failed": true }, "sage.misc.weak_dict": { - "failed": true + "failed": "failure seen in https://github.com/sagemath/sage/actions/runs/6870325919/job/18684964234#step:11:10059" }, "sage.modular.modform.l_series_gross_zagier": { - "failed": true + "failed": "unreported failure seen in https://github.com/sagemath/sage/actions/runs/6859244281/job/18651257775#step:11:10102" + }, + "sage.parallel.map_reduce": { + "failed": "random failure https://github.com/sagemath/sage/issues/36939" + }, + "sage.plot.plot": { + "failed": "unreported random failure (macOS)" }, "sage.rings.function_field.drinfeld_modules.morphism": { - "failed": true + "failed": "unreported random failure seen in https://github.com/sagemath/sage/actions/runs/6840502530/job/18599835766#step:11:10107" }, "sage.rings.polynomial.multi_polynomial_ideal": { "failed": true @@ -44,9 +56,21 @@ "sage.rings.polynomial.multi_polynomial_libsingular": { "failed": true }, + "sage.rings.polynomial.polynomial_element": { + "failed": "unreported random failure in symbolic 'roots' (macOS)" + }, "sage.rings.polynomial.skew_polynomial_finite_field": { "failed": true }, + "sage.schemes.elliptic_curves.ell_field": { + "failed": "random failure https://github.com/sagemath/sage/issues/36832" + }, + "sage.sets.recursively_enumerated_set": { + "failed": "random failures related to AlarmInterrupt (macOS)" + }, + "sage.structure.coerce_actions": { + "failed": "random failure https://github.com/sagemath/sage/issues/35973" + }, "sage.tests.gap_packages": { "failed": true } diff --git a/.github/workflows/ci-conda.yml b/.github/workflows/ci-conda.yml deleted file mode 100644 index 7fac038718c..00000000000 --- a/.github/workflows/ci-conda.yml +++ /dev/null @@ -1,101 +0,0 @@ -name: Build & Test using Conda - -on: - push: - tags: - - '*' - branches: - - 'public/build/**-runci' - pull_request: - workflow_dispatch: - # Allow to run manually - -concurrency: - # Cancel previous runs of this workflow for the same branch - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -jobs: - test: - name: Conda - runs-on: ${{ matrix.os }}-latest - - strategy: - matrix: - os: [ubuntu, macos] - python: ['3.9', '3.10', '3.11'] - # Optional environment is disabled for now as its not yet working - # environment: [environment, environment-optional] - conda-env: [environment] - - steps: - - uses: actions/checkout@v4 - - - name: Merge CI fixes from sagemath/sage - run: | - .ci/merge-fixes.sh - env: - GH_TOKEN: ${{ github.token }} - SAGE_CI_FIXES_FROM_REPOSITORIES: ${{ vars.SAGE_CI_FIXES_FROM_REPOSITORIES }} - - - name: Cache conda packages - uses: actions/cache@v3 - with: - path: ~/conda_pkgs_dir - key: - ${{ runner.os }}-conda-${{ hashFiles('src/environment-3.11.yml') }} - - - name: Setup Conda environment - uses: conda-incubator/setup-miniconda@v2 - with: - python-version: ${{ matrix.python }} - miniforge-version: latest - use-mamba: true - channels: conda-forge - channel-priority: true - activate-environment: sage - environment-file: src/${{ matrix.conda-env }}-${{ matrix.python }}-${{ startsWith(matrix.os, 'macos') && 'macos' || 'linux' }}.yml - - - name: Print Conda environment - shell: bash -l {0} - run: | - conda info - conda list - - - name: Configure - shell: bash -l {0} - continue-on-error: true - run: | - ./bootstrap - echo "::add-matcher::.github/workflows/configure-systempackage-problem-matcher.json" - ./configure --enable-build-as-root --with-python=$CONDA_PREFIX/bin/python --prefix=$CONDA_PREFIX --enable-system-site-packages $(for pkg in $(./sage -package list :standard: --has-file spkg-configure.m4 --has-file distros/conda.txt --exclude rpy2); do echo --with-system-$pkg=force; done) - echo "::remove-matcher owner=configure-system-package-warning::" - echo "::remove-matcher owner=configure-system-package-error::" - - - name: Build - shell: bash -l {0} - run: | - # Use --no-deps and pip check below to verify that all necessary dependencies are installed via conda. - pip install --no-build-isolation --no-deps -v -v -e ./pkgs/sage-conf ./pkgs/sage-setup - pip install --no-build-isolation --no-deps --config-settings editable_mode=compat -v -v -e ./src - env: - SAGE_NUM_THREADS: 2 - - - name: Verify dependencies - if: success() || failure() - shell: bash -l {0} - run: pip check - - - name: Test - if: success() || failure() - shell: bash -l {0} - run: ./sage -t --all --baseline-stats-path=.github/workflows/ci-conda-known-test-failures.json -p0 - - - name: Print logs - if: always() - run: | - for file in $(find . -type f -name "*.log"); do - echo "::group::$file" - cat "$file" - echo "::endgroup::" - done diff --git a/src/bin/sage-runtests b/src/bin/sage-runtests index 4a8b05cfab6..81efd01a4d8 100755 --- a/src/bin/sage-runtests +++ b/src/bin/sage-runtests @@ -34,6 +34,8 @@ if __name__ == "__main__": what.add_argument("--installed", action="store_true", default=False, help="test all installed modules of the Sage library") parser.add_argument("--logfile", type=argparse.FileType('a'), metavar="FILE", help="log all output to FILE") + parser.add_argument("--format", choices=["sage", "github"], default="sage", + help="set format of error messages and warnings") parser.add_argument("-l", "--long", action="store_true", default=False, help="include lines with the phrase 'long time'") parser.add_argument("-s", "--short", dest="target_walltime", nargs='?', type=int, default=-1, const=300, metavar="SECONDS", diff --git a/src/sage/combinat/words/word.py b/src/sage/combinat/words/word.py index 222501a5dfd..f74656317f2 100644 --- a/src/sage/combinat/words/word.py +++ b/src/sage/combinat/words/word.py @@ -147,7 +147,7 @@ def Word(data=None, alphabet=None, length=None, datatype=None, caching=True, RSK Word over a string with a parent:: - sage: w = Word("abbabaab", alphabet="abc"); w + sage: w = Word("abbabaab", daslkdlsakdlksaldkasldklsakdlsak, alphabet="abc"); w word: abbabaab sage: w.parent() Finite words over {'a', 'b', 'c'} @@ -252,7 +252,7 @@ class FiniteWord_char(WordDatatype_char, FiniteWord_class): True sage: w.is_square_free() - False + Falsish sage: w[:-1].is_square_free() True diff --git a/src/sage/doctest/control.py b/src/sage/doctest/control.py index 1a128983187..396f5e28263 100644 --- a/src/sage/doctest/control.py +++ b/src/sage/doctest/control.py @@ -139,6 +139,7 @@ def __init__(self, **kwds): self.show_skipped = False self.target_walltime = -1 self.baseline_stats_path = None + self.format = "sage" # sage-runtests contains more optional tags. Technically, adding # auto_optional_tags here is redundant, since that is added diff --git a/src/sage/doctest/forker.py b/src/sage/doctest/forker.py index fce553543bb..9590c8a2faa 100644 --- a/src/sage/doctest/forker.py +++ b/src/sage/doctest/forker.py @@ -1235,16 +1235,57 @@ def _failure_header(self, test, example, message='Failed example:'): """ out = [self.DIVIDER] with OriginalSource(example): - if test.filename: - if test.lineno is not None and example.lineno is not None: - lineno = test.lineno + example.lineno + 1 + if self.options.format == 'sage': + if test.filename: + if test.lineno is not None and example.lineno is not None: + lineno = test.lineno + example.lineno + 1 + else: + lineno = '?' + out.append('File "%s", line %s, in %s' % + (test.filename, lineno, test.name)) + else: + out.append('Line %s, in %s' % (example.lineno + 1, test.name)) + out.append(message) + elif self.options.format == 'github': + # https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#using-workflow-commands-to-access-toolkit-functions + if message.startswith('Warning: '): + command = f'::warning title={message}' + message = message[len('Warning: '):] + elif self.baseline.get('failed', False): + command = f'::notice title={message}' + message += ' [failed in baseline]' else: - lineno = '?' - out.append('File "%s", line %s, in %s' % - (test.filename, lineno, test.name)) + command = f'::error title={message}' + if extra := getattr(example, 'extra', None): + message += f': {extra}' + if test.filename: + command += f',file={test.filename}' + if test.lineno is not None and example.lineno is not None: + lineno = test.lineno + example.lineno + 1 + command += f',line={lineno}' + lineno = None + else: + command += f',line={example.lineno + 1}' + # + # Urlencoding trick for multi-line annotations + # https://github.com/actions/starter-workflows/issues/68#issuecomment-581479448 + # + # This only affects the display in the workflow Summary, after clicking "Show more"; + # the message needs to be long enough so that "Show more" becomes available. + # https://github.com/actions/toolkit/issues/193#issuecomment-1867084340 + # + # Unfortunately, this trick does not make the annotations in the diff view multi-line. + # + if '\n' in message: + message = message.replace('\n', '%0A') + # The actual threshold for "Show more" to appear depends on the window size. + show_more_threshold = 500 + if (pad := show_more_threshold - len(message)) > 0: + message += ' ' * pad + command += f'::{message}' + out.append(command) else: - out.append('Line %s, in %s' % (example.lineno + 1, test.name)) - out.append(message) + raise ValueError(f'unknown format option: {self.options.format}') source = example.source out.append(doctest._indent(source)) return '\n'.join(out) @@ -1417,6 +1458,7 @@ def report_failure(self, out, test, example, got, globs): """ if not self.options.initial or self.no_failure_yet: self.no_failure_yet = False + example.extra = f'Got: {got}' returnval = doctest.DocTestRunner.report_failure(self, out, test, example, got) if self.options.debug: self._fakeout.stop_spoofing() @@ -1567,6 +1609,8 @@ def report_unexpected_exception(self, out, test, example, exc_info): """ if not self.options.initial or self.no_failure_yet: self.no_failure_yet = False + + example.extra = "Exception raised:\n" + "".join(traceback.format_exception(*exc_info)) returnval = doctest.DocTestRunner.report_unexpected_exception(self, out, test, example, exc_info) if self.options.debug: self._fakeout.stop_spoofing() diff --git a/src/sage/doctest/reporting.py b/src/sage/doctest/reporting.py index 993ebeaa7a1..9fab0e966bd 100644 --- a/src/sage/doctest/reporting.py +++ b/src/sage/doctest/reporting.py @@ -213,10 +213,13 @@ def report_head(self, source, fail_msg=None): baseline = self.controller.source_baseline(source) if fail_msg: cmd += " # " + fail_msg - if baseline.get('failed', False): + if failed := baseline.get('failed', False): if not fail_msg: cmd += " #" - cmd += " [failed in baseline]" + if failed is True: + cmd += " [failed in baseline]" + else: + cmd += f" [failed in baseline: {failed}]" return cmd def report(self, source, timeout, return_code, results, output, pid=None): diff --git a/src/sage_setup/clean.py b/src/sage_setup/clean.py index e9c81c9ed1a..bac2ed26417 100644 --- a/src/sage_setup/clean.py +++ b/src/sage_setup/clean.py @@ -101,7 +101,7 @@ def _find_stale_files(site_packages, python_packages, python_modules, ext_module sage: stale_iter = _find_stale_files(SAGE_LIB, python_packages, python_modules, [], extra_files) sage: from importlib.machinery import EXTENSION_SUFFIXES sage: skip_extensions = tuple(EXTENSION_SUFFIXES) - sage: for f in stale_iter: + sage: for f in stale_iter:dxfcgvhbjkl;,mkjnbhgvcfgvhbjnkml, ....: if f.endswith(skip_extensions): continue ....: if '/ext_data/' in f: continue ....: print('Found stale file: ' + f) diff --git a/src/tox.ini b/src/tox.ini index 00f7a153f35..f2f1fc158fd 100644 --- a/src/tox.ini +++ b/src/tox.ini @@ -299,3 +299,7 @@ source = sage concurrency = multiprocessing data_file = .coverage/.coverage disable_warnings = no-data-collected + +[coverage:report] +ignore_errors = True +skip_empty = True