Skip to content

Commit

Permalink
sagemathgh-36403: Fix AttributeError doctests when output includes a …
Browse files Browse the repository at this point in the history
…suggestion (part of python 3.12 support)

    
In python 3.12, attribute errors add a suggestion at the end of the
usual error message ("Did you mean ...?").

We add ... at the end of these doctest outputs to fix it.

This PR is split in two commits:

 - The bulk of the changes, in the first commit, was obtained
automatically with:
    ```
    git grep -l "AttributeError:" src | xargs sed -ie \
        's/^ *\(AttributeError: .*\)\?has no attribute.*$/&.../'
    ```
 - The second commit includes 3 changes not catched by the sed pattern
above.

Since this changes a lot of files and is bound to get old, I'd
appreciate a quick review + merge to avoid trouble (the PR is long but
almost trivial and the review should be easy).

I'll soon make a separate PR (on top of this one) with the rest of the
support python 3.12 changes, which are maybe not so trivial but much
shorter than this one.

Related: sagemath#36181

@mkoeppe 🙏
    
URL: sagemath#36403
Reported by: Gonzalo Tornaría
Reviewer(s): Matthias Köppe
Release Manager committed Oct 11, 2023

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature. The key has expired.
2 parents 293e4db + 5e34da9 commit e716501
Showing 64 changed files with 116 additions and 116 deletions.
2 changes: 1 addition & 1 deletion src/doc/en/thematic_tutorials/coercion_and_categories.rst
Original file line number Diff line number Diff line change
@@ -1223,7 +1223,7 @@ However, only "elementary" construction functors have a rank::
sage: (Fract*Poly).rank
Traceback (most recent call last):
...
AttributeError: 'CompositeConstructionFunctor' object has no attribute 'rank'
AttributeError: 'CompositeConstructionFunctor' object has no attribute 'rank'...

.. end of output
Original file line number Diff line number Diff line change
@@ -318,15 +318,15 @@ http://docs.python.org/library/ for a complete list. ::
sage: e.__dict__
Traceback (most recent call last):
...
AttributeError: 'sage.rings.integer.Integer' object has no attribute '__dict__'
AttributeError: 'sage.rings.integer.Integer' object has no attribute '__dict__'...

sage: id4 = SymmetricGroup(4).one()
sage: type(id4)
<class 'sage.groups.perm_gps.permgroup_element.SymmetricGroupElement'>
sage: id4.__dict__
Traceback (most recent call last):
...
AttributeError: 'sage.groups.perm_gps.permgroup_element.SymmetricGroupElement' object has no attribute '__dict__'
AttributeError: 'sage.groups.perm_gps.permgroup_element.SymmetricGroupElement' object has no attribute '__dict__'...

.. note::

2 changes: 1 addition & 1 deletion src/sage/algebras/cluster_algebra.py
Original file line number Diff line number Diff line change
@@ -167,7 +167,7 @@
sage: (t*s).g_vector()
Traceback (most recent call last):
...
AttributeError: 'ClusterAlgebra_with_category.element_class' object has no attribute 'g_vector'
AttributeError: 'ClusterAlgebra_with_category.element_class' object has no attribute 'g_vector'...
sage: A = ClusterAlgebra(['A', 2], principal_coefficients=True)
sage: A.explore_to_depth(infinity)
sage: s = A.cluster_variable((0, -1)); s
Original file line number Diff line number Diff line change
@@ -109,7 +109,7 @@ class WeylLieConformalAlgebra(LieConformalAlgebraWithStructureCoefficients):
sage: alpha0.degree()
Traceback (most recent call last):
...
AttributeError: 'WeylLieConformalAlgebra_with_category.element_class' object has no attribute 'degree'
AttributeError: 'WeylLieConformalAlgebra_with_category.element_class' object has no attribute 'degree'...
TESTS::
2 changes: 1 addition & 1 deletion src/sage/algebras/steenrod/steenrod_algebra.py
Original file line number Diff line number Diff line change
@@ -1080,7 +1080,7 @@ def homogeneous_component(self, n):
sage: a.antipode() # not defined
Traceback (most recent call last):
...
AttributeError: 'CombinatorialFreeModule_with_category.element_class' object has no attribute 'antipode'
AttributeError: 'CombinatorialFreeModule_with_category.element_class' object has no attribute 'antipode'...
sage: A(a).antipode() # convert to elt of A, then compute antipode
Sq(2,1) + Sq(5)
2 changes: 1 addition & 1 deletion src/sage/categories/category.py
Original file line number Diff line number Diff line change
@@ -2129,7 +2129,7 @@ def _with_axioms(self, axioms):
sage: Semigroups().Inverse()
Traceback (most recent call last):
...
AttributeError: 'Semigroups_with_category' object has no attribute 'Inverse'
AttributeError: 'Semigroups_with_category' object has no attribute 'Inverse'...
sage: Semigroups()._with_axioms(["Inverse"])
Category of semigroups
4 changes: 2 additions & 2 deletions src/sage/categories/category_with_axiom.py
Original file line number Diff line number Diff line change
@@ -554,7 +554,7 @@ class from the base category class::
sage: Magmas.Unital.Associative
Traceback (most recent call last):
...
AttributeError: type object 'Magmas.Unital' has no attribute 'Associative'
AttributeError: type object 'Magmas.Unital' has no attribute 'Associative'...
The purpose of this section is to explain the design of the code
layout and the rationale for this mismatch.
@@ -769,7 +769,7 @@ def _(): return LazyImport('sage.categories.rngs', 'Rngs', at_startup=True)
sage: Semirings().NoZeroDivisors()
Traceback (most recent call last):
...
AttributeError: 'Semirings_with_category' object has no attribute 'NoZeroDivisors'
AttributeError: 'Semirings_with_category' object has no attribute 'NoZeroDivisors'...
Concretely, this is to be implemented by defining the new axiom in the
(``SubcategoryMethods`` nested class of the) appropriate category with
6 changes: 3 additions & 3 deletions src/sage/categories/enumerated_sets.py
Original file line number Diff line number Diff line change
@@ -610,7 +610,7 @@ def _list_from_iterator(self):
sage: (QQ^2).list() # indirect test # needs sage.modules
Traceback (most recent call last):
...
AttributeError: 'FreeModule_ambient_field_with_category' object has no attribute 'list'
AttributeError: 'FreeModule_ambient_field_with_category' object has no attribute 'list'...
Here we test that for an object that does not know whether it
is finite or not. Calling ``x.list()`` simply tries to create
@@ -622,11 +622,11 @@ def _list_from_iterator(self):
sage: Q.is_finite()
Traceback (most recent call last):
...
AttributeError: 'QuotientRing_generic_with_category' object has no attribute 'is_finite'
AttributeError: 'QuotientRing_generic_with_category' object has no attribute 'is_finite'...
sage: Q.list() # indirect test
Traceback (most recent call last):
...
AttributeError: 'QuotientRing_generic_with_category' object has no attribute 'list'
AttributeError: 'QuotientRing_generic_with_category' object has no attribute 'list'...
Here is another example. We artificially create a version of
the ring of integers that does not know whether it is finite
2 changes: 1 addition & 1 deletion src/sage/categories/examples/sets_cat.py
Original file line number Diff line number Diff line change
@@ -619,7 +619,7 @@ class PrimeNumbers_Facade(PrimeNumbers_Abstract):
sage: pf.next()
Traceback (most recent call last):
...
AttributeError: 'sage.rings.integer.Integer' object has no attribute 'next'
AttributeError: 'sage.rings.integer.Integer' object has no attribute 'next'...
unlike in the other implementations::
2 changes: 1 addition & 1 deletion src/sage/categories/homset.py
Original file line number Diff line number Diff line change
@@ -632,7 +632,7 @@ def __init__(self, X, Y, category=None, base=None, check=True):
sage: H = MyHomset(X, Y, category=1, base = ZZ, check = False)
Traceback (most recent call last):
...
AttributeError: 'sage.rings.integer.Integer' object has no attribute 'Homsets'
AttributeError: 'sage.rings.integer.Integer' object has no attribute 'Homsets'...
sage: P.<t> = ZZ[]
sage: f = P.hom([1/2*t])
sage: f.parent().domain()
2 changes: 1 addition & 1 deletion src/sage/categories/modules_with_basis.py
Original file line number Diff line number Diff line change
@@ -2456,7 +2456,7 @@ def apply_multilinear_morphism(self, f, codomain=None):
sage: tensor([a, b]).apply_multilinear_morphism(f) # needs sage.modules
Traceback (most recent call last):
...
AttributeError: 'int' object has no attribute 'parent'
AttributeError: 'int' object has no attribute 'parent'...
Here we consider an example where the codomain is a
module with basis with a different base ring::
2 changes: 1 addition & 1 deletion src/sage/categories/monoids.py
Original file line number Diff line number Diff line change
@@ -556,7 +556,7 @@ def algebra_generators(self):
Traceback (most recent call last):
...
AttributeError: 'IntegerModMonoid_with_category' object
has no attribute 'monoid_generators'
has no attribute 'monoid_generators'...
sage: Z12.semigroup_generators()
Family (0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11)
sage: Z12.algebra(QQ).algebra_generators() # needs sage.modules
2 changes: 1 addition & 1 deletion src/sage/combinat/finite_state_machine.py
Original file line number Diff line number Diff line change
@@ -1835,7 +1835,7 @@ def __getstate__(self):
sage: T1.transitions
Traceback (most recent call last):
...
AttributeError: 'FSMState' object has no attribute 'transitions'
AttributeError: 'FSMState' object has no attribute 'transitions'...
sage: A1 = loads(dumps(A))
sage: all(A.state(j) == A1.state(j) for j in [0, 1])
True
2 changes: 1 addition & 1 deletion src/sage/combinat/growth.py
Original file line number Diff line number Diff line change
@@ -371,7 +371,7 @@
sage: GrowthDiagram(RulePascal(), [3,1,2])
Traceback (most recent call last):
...
AttributeError: 'RulePascal' object has no attribute 'forward_rule'
AttributeError: 'RulePascal' object has no attribute 'forward_rule'...
We now re-implement the rule where we provide the dual graded graphs::
2 changes: 1 addition & 1 deletion src/sage/combinat/integer_lists/lists.py
Original file line number Diff line number Diff line change
@@ -261,7 +261,7 @@ def __getattr__(self, name):
sage: L.foo
Traceback (most recent call last):
...
AttributeError: 'NoneType' object has no attribute 'foo'
AttributeError: 'NoneType' object has no attribute 'foo'...
"""
return getattr(self.backend, name)

2 changes: 1 addition & 1 deletion src/sage/combinat/posets/posets.py
Original file line number Diff line number Diff line change
@@ -8825,7 +8825,7 @@ def is_induced_subposet(self, other):
sage: Poset().is_induced_subposet('junk')
Traceback (most recent call last):
...
AttributeError: 'str' object has no attribute 'subposet'
AttributeError: 'str' object has no attribute 'subposet'...
"""
if (not self._is_facade or (isinstance(other, FinitePoset) and
not other._is_facade)):
2 changes: 1 addition & 1 deletion src/sage/cpython/debug.pyx
Original file line number Diff line number Diff line change
@@ -123,7 +123,7 @@ def getattr_debug(obj, name, default=_no_default):
sage: _ = getattr_debug(1, "foo")
Traceback (most recent call last):
...
AttributeError: 'sage.rings.integer.Integer' object has no attribute 'foo'
AttributeError: 'sage.rings.integer.Integer' object has no attribute 'foo'...
sage: _ = getattr_debug(1, "foo", "xyz")
getattr_debug(obj=1, name='foo'):
type(obj) = <class 'sage.rings.integer.Integer'>
22 changes: 11 additions & 11 deletions src/sage/cpython/getattr.pyx
Original file line number Diff line number Diff line change
@@ -53,12 +53,12 @@ cdef class AttributeErrorMessage:
sage: 1.bla #indirect doctest
Traceback (most recent call last):
...
AttributeError: 'sage.rings.integer.Integer' object has no attribute 'bla'
AttributeError: 'sage.rings.integer.Integer' object has no attribute 'bla'...
sage: x = polygen(ZZ, 'x')
sage: QQ[x].gen().bla # needs sage.libs.flint
Traceback (most recent call last):
...
AttributeError: 'sage.rings.polynomial.polynomial_rational_flint.Polynomial_rational_flint' object has no attribute 'bla'
AttributeError: 'sage.rings.polynomial.polynomial_rational_flint.Polynomial_rational_flint' object has no attribute 'bla'...

::

@@ -144,7 +144,7 @@ cpdef raw_getattr(obj, name):
sage: raw_getattr(X, "attr")
Traceback (most recent call last):
...
AttributeError: '...' object has no attribute 'attr'
AttributeError: '...' object has no attribute 'attr'...
sage: x = X()
sage: raw_getattr(x, "prop")
<property object at ...>
@@ -173,7 +173,7 @@ cpdef raw_getattr(obj, name):
sage: raw_getattr(Y, "attr")
Traceback (most recent call last):
...
AttributeError: '...' object has no attribute 'attr'
AttributeError: '...' object has no attribute 'attr'...
sage: y = Y()
sage: raw_getattr(y, "prop")
<property object at ...>
@@ -278,7 +278,7 @@ cpdef getattr_from_other_class(self, cls, name):
sage: getattr_from_other_class(1, A, "lazy_attribute")
Traceback (most recent call last):
...
AttributeError: 'sage.rings.integer.Integer' object has no attribute 'lazy_attribute'
AttributeError: 'sage.rings.integer.Integer' object has no attribute 'lazy_attribute'...

The integer ring is a parent, so, lazy attributes work::

@@ -289,7 +289,7 @@ cpdef getattr_from_other_class(self, cls, name):
sage: getattr_from_other_class(17, A, "lazy_attribute")
Traceback (most recent call last):
...
AttributeError: 'sage.rings.integer.Integer' object has no attribute 'lazy_attribute'
AttributeError: 'sage.rings.integer.Integer' object has no attribute 'lazy_attribute'...

In general, descriptors are not yet well supported, because they
often do not accept to be cheated with the type of their instance::
@@ -305,7 +305,7 @@ cpdef getattr_from_other_class(self, cls, name):
sage: getattr_from_other_class(1, A, "__weakref__")
Traceback (most recent call last):
...
AttributeError: 'sage.rings.integer.Integer' object has no attribute '__weakref__'
AttributeError: 'sage.rings.integer.Integer' object has no attribute '__weakref__'...

This was caught by :trac:`8296` for which we do a couple more tests::

@@ -314,7 +314,7 @@ cpdef getattr_from_other_class(self, cls, name):
sage: 1.__weakref__
Traceback (most recent call last):
...
AttributeError: 'sage.rings.integer.Integer' object has no attribute '__weakref__'
AttributeError: 'sage.rings.integer.Integer' object has no attribute '__weakref__'...

sage: n = 1
sage: ip = get_ipython() # not tested: only works in interactive shell
@@ -329,7 +329,7 @@ cpdef getattr_from_other_class(self, cls, name):
sage: getattr_from_other_class(1, A, "__call__")
Traceback (most recent call last):
...
AttributeError: 'sage.rings.integer.Integer' object has no attribute '__call__'
AttributeError: 'sage.rings.integer.Integer' object has no attribute '__call__'...

TESTS:

@@ -339,14 +339,14 @@ cpdef getattr_from_other_class(self, cls, name):
sage: getattr_from_other_class(1, type, "__name__")
Traceback (most recent call last):
...
AttributeError: 'sage.rings.integer.Integer' object has no attribute '__name__'
AttributeError: 'sage.rings.integer.Integer' object has no attribute '__name__'...

Non-strings as "name" are handled gracefully::

sage: getattr_from_other_class(1, type, None)
Traceback (most recent call last):
...
AttributeError: 'sage.rings.integer.Integer' object has no attribute None
AttributeError: 'sage.rings.integer.Integer' object has no attribute None...
"""
if not isinstance(cls, type):
raise TypeError(f"{cls!r} is not a type")
8 changes: 4 additions & 4 deletions src/sage/crypto/classical.py
Original file line number Diff line number Diff line change
@@ -494,7 +494,7 @@ def rank_by_chi_square(self, C, pdict):
sage: A.rank_by_chi_square("", Plist)
Traceback (most recent call last):
...
AttributeError: 'str' object has no attribute 'parent'
AttributeError: 'str' object has no attribute 'parent'...
sage: A.rank_by_chi_square(A.encoding(""), Plist)
Traceback (most recent call last):
...
@@ -703,7 +703,7 @@ def rank_by_squared_differences(self, C, pdict):
sage: A.rank_by_squared_differences("", Plist)
Traceback (most recent call last):
...
AttributeError: 'str' object has no attribute 'parent'
AttributeError: 'str' object has no attribute 'parent'...
sage: A.rank_by_squared_differences(A.encoding(""), Plist)
Traceback (most recent call last):
...
@@ -2114,7 +2114,7 @@ def rank_by_chi_square(self, C, pdict):
sage: S.rank_by_chi_square("", Pdict)
Traceback (most recent call last):
...
AttributeError: 'str' object has no attribute 'parent'
AttributeError: 'str' object has no attribute 'parent'...
sage: S.rank_by_chi_square(S.encoding(""), Pdict)
Traceback (most recent call last):
...
@@ -2351,7 +2351,7 @@ def rank_by_squared_differences(self, C, pdict):
sage: S.rank_by_squared_differences("", Pdict)
Traceback (most recent call last):
...
AttributeError: 'str' object has no attribute 'parent'
AttributeError: 'str' object has no attribute 'parent'...
sage: S.rank_by_squared_differences(S.encoding(""), Pdict)
Traceback (most recent call last):
...
2 changes: 1 addition & 1 deletion src/sage/crypto/mq/mpolynomialsystemgenerator.py
Original file line number Diff line number Diff line change
@@ -156,7 +156,7 @@ def sbox(self):
sage: msg.sbox()
Traceback (most recent call last):
...
AttributeError: '<class 'sage.crypto.mq.mpolynomialsystemgenerator.MPolynomialSystemGenerator'>' object has no attribute '_sbox'
AttributeError: '<class 'sage.crypto.mq.mpolynomialsystemgenerator.MPolynomialSystemGenerator'>' object has no attribute '_sbox'...
"""
return self._sbox

8 changes: 4 additions & 4 deletions src/sage/data_structures/bitset.pyx
Original file line number Diff line number Diff line change
@@ -967,7 +967,7 @@ cdef class FrozenBitset:
sage: None | FrozenBitset('10101')
Traceback (most recent call last):
...
AttributeError: 'NoneType' object has no attribute '_union'
AttributeError: 'NoneType' object has no attribute '_union'...
"""
return self._union(other)

@@ -1037,7 +1037,7 @@ cdef class FrozenBitset:
sage: None & FrozenBitset("101011")
Traceback (most recent call last):
...
AttributeError: 'NoneType' object has no attribute 'intersection'
AttributeError: 'NoneType' object has no attribute 'intersection'...
"""
return self.intersection(other)

@@ -1106,7 +1106,7 @@ cdef class FrozenBitset:
sage: None - FrozenBitset('10101')
Traceback (most recent call last):
...
AttributeError: 'NoneType' object has no attribute 'difference'
AttributeError: 'NoneType' object has no attribute 'difference'...
"""
return self.difference(other)

@@ -1179,7 +1179,7 @@ cdef class FrozenBitset:
sage: None ^^ FrozenBitset('11111' * 10)
Traceback (most recent call last):
...
AttributeError: 'NoneType' object has no attribute 'symmetric_difference'
AttributeError: 'NoneType' object has no attribute 'symmetric_difference'...
"""
return self.symmetric_difference(other)

2 changes: 1 addition & 1 deletion src/sage/doctest/reporting.py
Original file line number Diff line number Diff line change
@@ -368,7 +368,7 @@ def report(self, source, timeout, return_code, results, output, pid=None):
sage: DTR.report(None, None, None, None, None)
Traceback (most recent call last):
...
AttributeError: 'NoneType' object has no attribute 'basename'
AttributeError: 'NoneType' object has no attribute 'basename'...

The only-errors mode does not output anything on success::

4 changes: 2 additions & 2 deletions src/sage/dynamics/finite_dynamical_system.py
Original file line number Diff line number Diff line change
@@ -240,7 +240,7 @@ class DiscreteDynamicalSystem(SageObject, metaclass=ClasscallMetaclass):
sage: D.inverse_evolution()(4)
Traceback (most recent call last):
...
AttributeError: 'DiscreteDynamicalSystem' object has no attribute 'inverse_evolution'
AttributeError: 'DiscreteDynamicalSystem' object has no attribute 'inverse_evolution'...
sage: D.orbit(3)
[3, 5, 1]

@@ -252,7 +252,7 @@ class DiscreteDynamicalSystem(SageObject, metaclass=ClasscallMetaclass):
sage: D.inverse_evolution()(4)
Traceback (most recent call last):
...
AttributeError: 'DiscreteDynamicalSystem' object has no attribute 'inverse_evolution'
AttributeError: 'DiscreteDynamicalSystem' object has no attribute 'inverse_evolution'...
sage: D.orbit(3)
[3, 5, 1]

2 changes: 1 addition & 1 deletion src/sage/geometry/cone.py
Original file line number Diff line number Diff line change
@@ -340,7 +340,7 @@ def Cone(rays, lattice=None, check=True, normalize=True):
sage: Cone([(1,0), (0,1)], check=False, normalize=False)
Traceback (most recent call last):
...
AttributeError: 'tuple' object has no attribute 'parent'
AttributeError: 'tuple' object has no attribute 'parent'...

You can construct different "not" cones: not full-dimensional, not
strictly convex, not containing any rays::
Loading

0 comments on commit e716501

Please sign in to comment.