Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 0 additions & 9 deletions src/sage/combinat/backtrack.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,15 +117,6 @@ class PositiveIntegerSemigroup(UniqueRepresentation, RecursivelyEnumeratedSet_fo
sage: some_elements = list(PP.some_elements()); some_elements
[1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100]

TESTS::

sage: from sage.combinat.backtrack import PositiveIntegerSemigroup
sage: PP = PositiveIntegerSemigroup()

We factor out the long test from the ``TestSuite``::

sage: TestSuite(PP).run(skip='_test_enumerated_set_contains')
sage: PP._test_enumerated_set_contains() # long time
"""

def __init__(self):
Expand Down
1 change: 1 addition & 0 deletions src/sage/combinat/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,7 @@ py.install_sources(
'permutation_cython.pxd',
'permutation_cython.pyx',
'plane_partition.py',
'positive_integer_semigroup_test.py',
'q_analogues.py',
'q_bernoulli.pyx',
'quickref.py',
Expand Down
16 changes: 16 additions & 0 deletions src/sage/combinat/positive_integer_semigroup_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import pytest


def test_positive_integer_semigroup():
r"""
Run the ``TestSuite()`` for ``PositiveIntegerSemigroup``
(this can take quite a long time).
"""
from sage.misc.sage_unittest import TestSuite
from sage.combinat.backtrack import PositiveIntegerSemigroup
PP = PositiveIntegerSemigroup()

# fewer max_runs since these are kind of slow
TestSuite(PP).run(verbose=True,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the verbose = True option needed for the functionality? If not, I think, I would prefer the quiet version, or maybe couple it to pytests verbosity level via pytestconfig.get_verbosity() > 0 (or 1?)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pytest already swallows stdout unless you pass -s, so i was thinking the added verbosity would only matter if the tests fail. I guess we could go back and make them all depend on the pytest verbosity though. I don't feel strongly about it either way.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I noticed it mostly in CI, where it bloats the log without adding too much value (at least when everything passes). For the moment, that's okay but might get more annoying once more tests are migrated.

So maybe I do this later if I get annoyed enough - or I'll actually extract all of these testsuite _test methods (in the actual classes) to pytest.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could probably get away without -s in the CI. I think pytest will still dump the output that it captured in the event of a failure, while leaving everything nice and quiet if it passes.

raise_on_failure=True,
max_runs=256)
6 changes: 5 additions & 1 deletion src/sage/misc/dev_tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -275,8 +275,12 @@ def find_objects_from_name(name, module_name=None, include_lazy_imports=False):
"""
from sage.misc.lazy_import import LazyImport

# Create a copy to avoid errors if the sys.modules dict changes
# while we are iterating over it.
mods = sys.modules.copy()

obj = []
for smodule_name, smodule in sys.modules.items():
for smodule_name, smodule in mods.items():
if module_name and not smodule_name.startswith(module_name):
continue
if hasattr(smodule, '__dict__') and name in smodule.__dict__:
Expand Down
1 change: 1 addition & 0 deletions src/sage/rings/padics/meson.build
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ py.install_sources(
'padic_generic_element.pxd',
'padic_generic_element.pyx',
'padic_lattice_element.py',
'padic_lattice_element_test.py',
'padic_printing.pxd',
'padic_printing.pyx',
'padic_relaxed_element.pxd',
Expand Down
20 changes: 3 additions & 17 deletions src/sage/rings/padics/padic_lattice_element.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,23 +5,6 @@

- Xavier Caruso (2018-02): initial version

TESTS:

We create some rings and run the test suite for them. We skip the Smith form
tests because they take a few minutes as of mid 2018, see :issue:`25431`::

sage: R1 = ZpLC(2)
doctest:...: FutureWarning: This class/method/function is marked as experimental. It, its functionality or its interface might change without a formal deprecation.
See https://github.com/sagemath/sage/issues/23505 for details.
sage: R2 = ZpLF(2)
sage: R3 = QpLC(2)
sage: R4 = QpLF(2)

sage: # long time, needs sage.rings.padics
sage: TestSuite(R1).run(skip=['_test_teichmuller', '_test_matrix_smith'])
sage: TestSuite(R2).run(skip=['_test_teichmuller', '_test_matrix_smith'])
sage: TestSuite(R3).run(skip=['_test_teichmuller', '_test_matrix_smith'])
sage: TestSuite(R4).run(skip=['_test_teichmuller', '_test_matrix_smith'])
"""

# ****************************************************************************
Expand Down Expand Up @@ -65,6 +48,7 @@ def unpickle_le(parent, value, prec):

sage: from sage.rings.padics.padic_lattice_element import unpickle_le
sage: R = ZpLC(5,8)
doctest:...: FutureWarning:...
sage: a = unpickle_le(R, 42, 6); a
2 + 3*5 + 5^2 + O(5^6)
sage: a.parent() is R
Expand Down Expand Up @@ -209,6 +193,7 @@ def _is_base_elt(self, p):
EXAMPLES::

sage: K = QpLC(7)
doctest:...: FutureWarning:...
sage: K.random_element()._is_base_elt(7) # not tested, known bug (see :issue:`32126`)
True
"""
Expand Down Expand Up @@ -1308,6 +1293,7 @@ def _declare_new_element(self, dx, prec, dx_mode):
TESTS::

sage: R = ZpLF(17)
doctest:...: FutureWarning:...
sage: prec = R.precision()

sage: prec.del_elements()
Expand Down
54 changes: 54 additions & 0 deletions src/sage/rings/padics/padic_lattice_element_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
import pytest
from sage.rings.padics.factory import ZpLC, ZpLF, QpLC, QpLF

# ZpLC, ZpLF, QpLC, and QpLF all raise FutureWarnings
from warnings import catch_warnings, filterwarnings
filterwarnings("ignore", category=FutureWarning)


@pytest.fixture
def R1():
return ZpLC(2)


@pytest.fixture
def R2():
return ZpLF(2)


@pytest.fixture
def R3():
return QpLC(2)


@pytest.fixture
def R4():
return QpLF(2)


# Use strings for the fixture names here, and then later convert them
# to the actual fixture objects using request.getfixturevalue(). This
# is a workaround for being unable to pass fixtures directly as
# parameters:
#
# https://github.com/pytest-dev/pytest/issues/349
#
elements = ( "R1", "R2", "R3", "R4" )


@pytest.mark.parametrize("e", elements)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the amount of boilerplate is scary.

Why can't you parametrize e over [ZpLC(2), ZpLF(2), etc.] directly?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There are a couple of related problems. (I'm not happy about the boilerplate either, but it should decrease in relative volume as more tests are moved to pytest.)

The big one is that we want to cache the fixtures and create them on-demand, so they aren't recreated multiple times, or created when they won't be used (if only a subset of tests is being run). For that magic to work, we need to mark the fixtures with @pytest.fixture... but then you can't call them directly:

_____ ERROR collecting src/sage/rings/padics/padic_lattice_element_test.py _____
Fixture "R1" called directly. Fixtures are not meant to be called directly,
but are created automatically when test functions request them as parameters.

If we forego the caching / on-demand creation, we can create the objects ourselves, but then we lose the nice fixture naming. if I pass in ZpLC(2), for example, we get...

src/sage/rings/padics/padic_lattice_element_test.py::test_padic_lattice_element[e0]...

instead of

src/sage/rings/padics/padic_lattice_element_test.py::test_padic_lattice_element[R1]...

which uses the fixture name. Now obviously R1 is a lame name for a fixture and doesn't improve anything, but I'm trying to structure these in a decent way for future copy/pasting where the fixtures may actually be meaningful and take a while to construct.

Copy link
Contributor

@user202729 user202729 Oct 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"so that they aren't recreated multiple times"? Aren't they only used in this one test anyway?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The idea of fixtures is that they can be easily reused in other tests (either in the same file or other test files), and pytest then makes sure that only one instance is created for all those tests. This obviously only scales once more and more tests are migrated to pytest. (eg here if all the _test_xyz methods from the actual class are moved into the pytest file.

See eg symplectic forms tests for a more complicated example that would be hard to realize without fixtures.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Right, there's only one test using it because there's only one test. But there are other doctests that construct (say) ZpLC(2), and we could re-use the existing object in that case. Unlike with doctests, we don't have to reset the namespace before every test, so there's lots of room to reduce duplication.

If it was trivial to parameterize a test with fixtures, I probably wouldn't have bothered, since n >= 2 is usually where you'd stop and refactor. But it took me a few hours to figure out the trick, so I figure the polite thing to do is to set everything up nicely for the next person who wants to add a test using one of these lattice elements.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

doctest doesn't need to reset the namespace before every test either. But usually reusing variables from another doctest would be confusing.

If you do say

sage: R1 = ZpLC(2)
sage: R1.test1()

another test::

sage: R1.test2()

then you're no worse off than the situation with pytest (and it's shorter…)

pytest then makes sure that only one instance is created for all those tests

aren't the different files tested in parallel?

def test_padic_lattice_element(e, request):
r"""
Run the ``TestSuite()`` for some examples that previously
lived in the TESTS:: block of the padic_lattice_element module.
"""
from sage.misc.sage_unittest import TestSuite

# Convert the string to a real fixture
e = request.getfixturevalue(e)

# Only do a few runs, _test_matrix_smith() in particular is slow.
TestSuite(e).run(verbose=True,
raise_on_failure=True,
skip="_test_teichmuller",
max_runs=8)
4 changes: 0 additions & 4 deletions src/sage/rings/valuation/mapped_valuation.py
Original file line number Diff line number Diff line change
Expand Up @@ -595,10 +595,6 @@ class FiniteExtensionFromLimitValuation(FiniteExtensionFromInfiniteValuation):
[[ (x - 1)-adic valuation, v(y + 1) = 1 ]-adic valuation,
[ (x - 1)-adic valuation, v(y - 1) = 1 ]-adic valuation]

TESTS::

sage: TestSuite(w[0]).run() # long time # needs sage.rings.function_field
sage: TestSuite(w[1]).run() # long time # needs sage.rings.function_field
"""
def __init__(self, parent, approximant, G, approximants):
r"""
Expand Down
32 changes: 32 additions & 0 deletions src/sage/rings/valuation/mapped_valuation_test.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import pytest


@pytest.fixture
def w():
from sage.rings.function_field.constructor import FunctionField
from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
from sage.rings.integer import Integer
from sage.rings.rational_field import QQ

K = FunctionField(QQ, 'x')
x = K.gen()
R = PolynomialRing(K, 'y')
y = R.gen()
L = K.extension(y**2 - x)
v = K.valuation(Integer(1))

return v.extensions(L)


@pytest.mark.parametrize("idx", [0,1])
def test_finite_extension_from_limit_valuation_w(w, idx):
r"""
Run the ``TestSuite()`` for two examples given in the
``FiniteExtensionFromLimitValuation`` documentation.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Speaking of more downsides of pytest migration, a cost of having the test far from the code is that now if the example given in the FiniteExtensionFromLimitValuation documentation is changed, this docstring will silently become false.

You can change the docstring to "run the test suite for two examples that were given in the FiniteExtensionFromLimitValuation documentation", but then what's the value of the docstring?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is just laziness on my part and not inherent to pytest. I'd love to describe the objects being tested more intelligently, or explain why they're being tested... but the original doctests didn't come with an explanation, and I don't know anything about mapped valuations, so this was all I could think of.

"""
from sage.misc.sage_unittest import TestSuite

# fewer max_runs, these are kind of slow
TestSuite(w[idx]).run(verbose=True,
raise_on_failure=True,
max_runs=512)
Loading