From 5851f010757ba0ed077cdc0e14449a93f697865b Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Sat, 4 Dec 2021 18:35:17 +0100 Subject: [PATCH 1/4] Improve doctest interaction with pytest --- src/bin/sage-coverage | 9 ++++++++ src/conftest.py | 8 ------- src/sage/categories/primer.py | 2 +- src/sage/docs/conf.py | 2 +- src/sage/misc/nested_class.pyx | 2 +- src/sage/misc/sageinspect.py | 4 ++-- ...ted_class_test.py => test_nested_class.py} | 22 +++++++++---------- src/sage/repl/rich_output/backend_base.py | 2 +- .../{backend_test.py => test_backend.py} | 18 +++++++-------- src/sage/structure/element_wrapper.pyx | 2 +- ...eprecation_test.py => test_deprecation.py} | 10 ++++----- 11 files changed, 41 insertions(+), 40 deletions(-) rename src/sage/misc/{nested_class_test.py => test_nested_class.py} (88%) rename src/sage/repl/rich_output/{backend_test.py => test_backend.py} (91%) rename src/sage/tests/{deprecation_test.py => test_deprecation.py} (66%) diff --git a/src/bin/sage-coverage b/src/bin/sage-coverage index 80b3b0eca63..fe597edd1bd 100755 --- a/src/bin/sage-coverage +++ b/src/bin/sage-coverage @@ -131,6 +131,11 @@ class CoverageResults: if not docstring: self.no_doc.append(fullname) return + + if "pytest" in docstring: + self.good.append(fullname) + return + if not "sage: " in docstring: self.no_test.append(fullname) return @@ -275,6 +280,10 @@ def go(filename): or filename.endswith('.sage')): return + # Filter pytest files which are not supposed to have doctests + if '_test.py' in filename: + return + with open(filename, 'r') as f: cr = CoverageResults(filename).check_file(f) bad = cr.no_doc or cr.no_test or cr.possibly_wrong diff --git a/src/conftest.py b/src/conftest.py index 33135c9e333..095b094fb8b 100644 --- a/src/conftest.py +++ b/src/conftest.py @@ -12,14 +12,6 @@ from typing import Any import pytest -# Ignore a few test files that are (not yet) using pytest -collect_ignore = [ - "sage/misc/nested_class_test.py", - "sage/repl/rich_output/backend_test.py", - "sage/tests/deprecation_test.py" -] - - @pytest.fixture(autouse=True) def add_imports(doctest_namespace: dict[str, Any]): """ diff --git a/src/sage/categories/primer.py b/src/sage/categories/primer.py index f9a8f35252c..cf64389a864 100644 --- a/src/sage/categories/primer.py +++ b/src/sage/categories/primer.py @@ -1152,7 +1152,7 @@ class ElementMethods: reveal some glitches in their implementation, in particular around class naming and introspection. Sage currently works around the more annoying ones but some remain visible. See - e.g. :mod:`sage.misc.nested_class_test`. + e.g. :mod:`sage.misc.test_nested_class`. Let us now look at the categories of ``C``:: diff --git a/src/sage/docs/conf.py b/src/sage/docs/conf.py index 755c631f203..6c8f2dff19f 100644 --- a/src/sage/docs/conf.py +++ b/src/sage/docs/conf.py @@ -610,7 +610,7 @@ def process_docstring_module_title(app, what, name, obj, options, docstringlines break skip_picklability_check_modules = [ - #'sage.misc.nested_class_test', # for test only + #'sage.misc.test_nested_class', # for test only 'sage.misc.latex', 'sage.misc.explain_pickle', '__builtin__', diff --git a/src/sage/misc/nested_class.pyx b/src/sage/misc/nested_class.pyx index 08750233543..d27ec59923b 100644 --- a/src/sage/misc/nested_class.pyx +++ b/src/sage/misc/nested_class.pyx @@ -23,7 +23,7 @@ This module provides two utilities to workaround this issue: - :class:`NestedClassMetaclass` is a metaclass ensuring that :func:`nested_pickle` is called on a class upon creation. -See also :mod:`sage.misc.nested_class_test`. +See also :mod:`sage.misc.test_nested_class`. .. NOTE:: diff --git a/src/sage/misc/sageinspect.py b/src/sage/misc/sageinspect.py index fb2073f7c44..da491d14b1b 100644 --- a/src/sage/misc/sageinspect.py +++ b/src/sage/misc/sageinspect.py @@ -2314,12 +2314,12 @@ def sage_getsourcelines(obj): use a dummy parent class that has defined an element class by a nested class definition:: - sage: from sage.misc.nested_class_test import TestNestedParent + sage: from sage.misc.test_nested_class import TestNestedParent sage: from sage.misc.sageinspect import sage_getsource sage: P = TestNestedParent() sage: E = P.element_class sage: E.__bases__ - (, + (, ) sage: print(sage_getsource(E)) class Element(object): diff --git a/src/sage/misc/nested_class_test.py b/src/sage/misc/test_nested_class.py similarity index 88% rename from src/sage/misc/nested_class_test.py rename to src/sage/misc/test_nested_class.py index 70187a80777..8e0c1ddf0c1 100644 --- a/src/sage/misc/nested_class_test.py +++ b/src/sage/misc/test_nested_class.py @@ -10,7 +10,7 @@ Currently pickling fails for parents using nested classes (typically for categories), but deriving only from Parent:: - sage: from sage.misc.nested_class_test import TestParent1, TestParent2, TestParent3, TestParent4 + sage: from sage.misc.test_nested_class import TestParent1, TestParent2, TestParent3, TestParent4 sage: P = TestParent1() sage: TestSuite(P).run() Failure ... @@ -59,8 +59,8 @@ def __init__(self): """ EXAMPLES:: - sage: sage.misc.nested_class_test.TestParent1() - + sage: sage.misc.test_nested_class.TestParent1() + """ from sage.categories.sets_cat import Sets Parent.__init__(self, category = Sets()) @@ -74,7 +74,7 @@ def __init__(self): """ EXAMPLES:: - sage: sage.misc.nested_class_test.TestParent2() + sage: sage.misc.test_nested_class.TestParent2() Traceback (most recent call last): ... TypeError: metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases @@ -92,8 +92,8 @@ def __init__(self): """ EXAMPLES:: - sage: sage.misc.nested_class_test.TestParent3() - + sage: sage.misc.test_nested_class.TestParent3() + """ from sage.categories.sets_cat import Sets Parent.__init__(self, category = Sets()) @@ -107,8 +107,8 @@ def __init__(self): """ EXAMPLES:: - sage: sage.misc.nested_class_test.TestParent4() - + sage: sage.misc.test_nested_class.TestParent4() + """ from sage.categories.sets_cat import Sets Parent.__init__(self, category=Sets()) @@ -117,7 +117,7 @@ def __eq__(self, other): """ EXAMPLES:: - sage: from sage.misc.nested_class_test import TestParent4 + sage: from sage.misc.test_nested_class import TestParent4 sage: TestParent4() == TestParent4() True """ @@ -127,7 +127,7 @@ def __ne__(self, other): """ EXAMPLES:: - sage: from sage.misc.nested_class_test import TestParent4 + sage: from sage.misc.test_nested_class import TestParent4 sage: TestParent4() != TestParent4() False """ @@ -139,7 +139,7 @@ def __hash__(self): EXAMPLES:: - sage: from sage.misc.nested_class_test import TestParent4 + sage: from sage.misc.test_nested_class import TestParent4 sage: hash(TestParent4()) == hash(TestParent4()) True """ diff --git a/src/sage/repl/rich_output/backend_base.py b/src/sage/repl/rich_output/backend_base.py index bd89fb9774c..3c222b0cbb6 100644 --- a/src/sage/repl/rich_output/backend_base.py +++ b/src/sage/repl/rich_output/backend_base.py @@ -24,7 +24,7 @@ * Subclass the rich output container to attach your backend-specific functionality. Then :meth:`~BackendBase.display_immediately` will receive instances of your subclass. See - :class:`~sage.repl.rich_output.backend_test.BackendTest` for an + :class:`~sage.repl.rich_output.test_backend.BackendTest` for an example of how this is done. You can also mix both ways of implementing different rich output types. diff --git a/src/sage/repl/rich_output/backend_test.py b/src/sage/repl/rich_output/test_backend.py similarity index 91% rename from src/sage/repl/rich_output/backend_test.py rename to src/sage/repl/rich_output/test_backend.py index d63d6ff1cb8..f0750addb39 100644 --- a/src/sage/repl/rich_output/backend_test.py +++ b/src/sage/repl/rich_output/test_backend.py @@ -10,14 +10,14 @@ sage: from sage.repl.rich_output import get_display_manager sage: dm = get_display_manager() - sage: from sage.repl.rich_output.backend_test import BackendTest, TestObject + sage: from sage.repl.rich_output.test_backend import BackendTest, TestObject sage: doctest_backend = dm.switch_backend(BackendTest()) sage: dm The Sage display manager using the test backend sage: dm._output_promotions {: - } + } sage: dm.displayhook(1/2) 1/2 [TestOutputPlainText] TestOutputPlainText container @@ -60,7 +60,7 @@ def __init__(self, *args, **kwds): EXAMPLES:: - sage: from sage.repl.rich_output.backend_test import TestOutputPlainText + sage: from sage.repl.rich_output.test_backend import TestOutputPlainText sage: TestOutputPlainText() Traceback (most recent call last): ... @@ -81,7 +81,7 @@ def print_to_stdout(self): sage: test_output = dm.displayhook(123) 123 [TestOutputPlainText] sage: type(test_output) - + sage: test_output.print_to_stdout() 123 [TestOutputPlainText] """ @@ -103,7 +103,7 @@ def _repr_(self): EXAMPLES:: - sage: from sage.repl.rich_output.backend_test import TestObject + sage: from sage.repl.rich_output.test_backend import TestObject sage: obj = TestObject() sage: obj._repr_() 'called the _repr_ method' @@ -119,7 +119,7 @@ def _rich_repr_(self, display_manager): EXAMPLES:: sage: display_manager = sage.repl.rich_output.get_display_manager() - sage: from sage.repl.rich_output.backend_test import TestObject + sage: from sage.repl.rich_output.test_backend import TestObject sage: obj = TestObject() sage: rich_output = obj._rich_repr_(display_manager); rich_output OutputPlainText container @@ -157,7 +157,7 @@ def supported_output(self): OUTPUT: Iterable of output container classes. Only the - :class:`~sage.repl.rich_repr.backend_test.TestOutputPlainText` + :class:`~sage.repl.rich_repr.test_backend.TestOutputPlainText` output container is supported by the test backend. EXAMPLES:: @@ -165,7 +165,7 @@ def supported_output(self): sage: display_manager = sage.repl.rich_output.get_display_manager() sage: backend = display_manager._backend sage: list(backend.supported_output()) - [] + [] The output of this method is used by the display manager to set up the actual supported outputs. Compare:: @@ -193,7 +193,7 @@ def display_immediately(self, plain_text, rich_output): sage: from sage.repl.rich_output.output_basic import OutputPlainText sage: plain_text = OutputPlainText.example() - sage: from sage.repl.rich_output.backend_test import BackendTest + sage: from sage.repl.rich_output.test_backend import BackendTest sage: backend = BackendTest() sage: backend.display_immediately(plain_text, plain_text) Example plain text output diff --git a/src/sage/structure/element_wrapper.pyx b/src/sage/structure/element_wrapper.pyx index 845d02c7d8f..da794dcf675 100644 --- a/src/sage/structure/element_wrapper.pyx +++ b/src/sage/structure/element_wrapper.pyx @@ -275,7 +275,7 @@ cdef class ElementWrapper(Element): Check that elements of equal-but-not-identical parents compare properly (see :trac:`19488`):: - sage: from sage.misc.nested_class_test import TestParent4 + sage: from sage.misc.test_nested_class import TestParent4 sage: P = TestParent4() sage: Q = TestParent4() sage: P == Q diff --git a/src/sage/tests/deprecation_test.py b/src/sage/tests/test_deprecation.py similarity index 66% rename from src/sage/tests/deprecation_test.py rename to src/sage/tests/test_deprecation.py index ee776df0816..42632dce5b4 100644 --- a/src/sage/tests/deprecation_test.py +++ b/src/sage/tests/test_deprecation.py @@ -3,10 +3,10 @@ EXAMPLES:: - sage: import sage.tests.deprecation_test - sage: sage.tests.deprecation_test.function_old() + sage: import sage.tests.test_deprecation + sage: sage.tests.test_deprecation.function_old() doctest:...: DeprecationWarning: function_old is deprecated. Please - use sage.tests.deprecation_test.function_new instead. + use sage.tests.test_deprecation.function_new instead. See http://trac.sagemath.org/12345 for details. """ from sage.misc.superseded import deprecated_function_alias @@ -18,10 +18,10 @@ def function_new(): EXAMPLES:: - sage: from sage.tests.deprecation_test import function_old + sage: from sage.tests.test_deprecation import function_old sage: function_old() doctest:...: DeprecationWarning: function_old is deprecated. Please - use sage.tests.deprecation_test.function_new instead. + use sage.tests.test_deprecation.function_new instead. See http://trac.sagemath.org/12345 for details. """ pass From 9c5d5a9af0c0c217b480dea16048758796548186 Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Sun, 20 Mar 2022 13:19:04 +0100 Subject: [PATCH 2/4] Fix docs --- src/doc/en/reference/misc/index.rst | 2 +- src/doc/en/reference/repl/index.rst | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/doc/en/reference/misc/index.rst b/src/doc/en/reference/misc/index.rst index 1d15621f8b4..08d8dd5b49f 100644 --- a/src/doc/en/reference/misc/index.rst +++ b/src/doc/en/reference/misc/index.rst @@ -290,7 +290,7 @@ Testing sage/misc/sage_unittest sage/misc/random_testing - sage/misc/nested_class_test + sage/misc/test_nested_class Benchmarking and Profiling ~~~~~~~~~~~~~~~~~~~~~~~~~~ diff --git a/src/doc/en/reference/repl/index.rst b/src/doc/en/reference/repl/index.rst index 79aad5b96d4..21a2f285f1f 100644 --- a/src/doc/en/reference/repl/index.rst +++ b/src/doc/en/reference/repl/index.rst @@ -77,7 +77,7 @@ Display Backend Infrastructure sage/repl/rich_output/output_catalog sage/repl/rich_output/backend_base - sage/repl/rich_output/backend_test + sage/repl/rich_output/test_backend sage/repl/rich_output/backend_doctest sage/repl/rich_output/backend_ipython From 0c7bf1711fbb4647072d225980f9fe011d419306 Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Sun, 20 Mar 2022 18:09:54 +0100 Subject: [PATCH 3/4] Add documentation for test:pytest --- src/doc/en/developer/coding_basics.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/doc/en/developer/coding_basics.rst b/src/doc/en/developer/coding_basics.rst index c24ef0a2f90..3dc549e71a7 100644 --- a/src/doc/en/developer/coding_basics.rst +++ b/src/doc/en/developer/coding_basics.rst @@ -509,6 +509,12 @@ information. You can use the existing functions of Sage as templates. However, lines only containing double colons `::` do not end "TESTS" blocks. + Sometimes (but rarely) one has private or protected methods that don't need a + proper ``EXAMPLES`` doctest. In these cases, one can either write traditional + doctest using the ``TESTS`` block or use pytest to test the method. + In the latter case, one has to add ``TESTS: pytest`` to the docstring, so that + the method is explicitly marked as tested. + Note about Sphinx directives vs. other blocks ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ From 01f376c26194faa54a219a4f0ed562ba525f19de Mon Sep 17 00:00:00 2001 From: Tobias Diez Date: Sun, 20 Mar 2022 18:12:29 +0100 Subject: [PATCH 4/4] Use endswith to test for pytest files --- src/bin/sage-coverage | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bin/sage-coverage b/src/bin/sage-coverage index 01502464cf0..45c4073a18c 100755 --- a/src/bin/sage-coverage +++ b/src/bin/sage-coverage @@ -282,7 +282,7 @@ def go(filename): return # Filter pytest files which are not supposed to have doctests - if '_test.py' in filename: + if filename.endswith('_test.py'): return with open(filename, 'r') as f: