Ticket #12920: trac_12920-is_test_methods-nt.patch

File trac_12920-is_test_methods-nt.patch, 6.7 KB (added by nthiery, 9 years ago)
  • sage/misc/sage_unittest.py

    # HG changeset patch
    # User Nicolas M. Thiery <nthiery@users.sf.net>
    # Date 1336619350 14400
    # Node ID 2fb400a6eebbf5f477ec33cd92c99d116bf03c42
    # Parent  76f159bc11d2f8303c57dda51a2a3388eeca5f8a
    #12920: Decorators for sharing code between is_bar and _test_bar methods
    
    diff --git a/sage/misc/sage_unittest.py b/sage/misc/sage_unittest.py
    a b class InstanceTester(unittest.TestCase): 
    360360        Testing utilities for Rational Field
    361361    """
    362362
    363     def __init__(self, instance, elements = None, verbose = False, prefix = "", **options):
     363    def __init__(self, instance, elements = None, verbose = False, prefix = "", failure_exception = None, **options):
    364364        """
    365365        A gadget attached to an instance providing it with testing utilities.
    366366
    class InstanceTester(unittest.TestCase): 
    380380        self._verbose = verbose
    381381        self._elements = elements
    382382        self._prefix = prefix
     383        if failure_exception:
     384            self.failureException = failure_exception
    383385
    384386    def runTest(self):
    385387        """
    class InstanceTester(unittest.TestCase): 
    463465
    464466class PythonObjectWithTests(object):
    465467    """
    466     Utility class for running basis tests on a plain Python object
     468    Utility class for running basic tests on a plain Python object
    467469    (that is not in SageObject). More test methods can be added here.
    468470
    469471    EXAMPLES::
    class PythonObjectWithTests(object): 
    497499        tester = instance_tester(self, **options)
    498500        from sage.misc.all import loads, dumps
    499501        tester.assertEqual(loads(dumps(self._instance)), self._instance)
     502
     503class ReturnOnError(Exception):
     504    pass
     505
     506from sage.misc.method_decorator import MethodDecorator
     507class IsMethod(MethodDecorator):
     508    """
     509    ``is_method`` / ``_test_from_is_method``: method wrappers to factor out code between ``is_bar`` and ``_test_bar`` methods
     510
     511    The typical usecase for those wrappers is as follow: Consider a
     512    category ``Foo`` with a subcategory ``Bar``.  ``Foo`` wants to
     513    provide a method ``Foo.ParentMethods.is_bar`` such that, for ``F``
     514    in ``Foo()``, ``F.is_bar()`` returns whether ``F`` satisfies all
     515    the properties of ``Bar()``. The method ``is_bar`` is allowed to
     516    assume that ``F`` indeed satisfies all the properties specified by
     517    ``Foo()``. It may decide to upgrade the category of ``F`` to
     518    ``Bar()``.
     519
     520    ``Bar`` itself wants to provide a method
     521    ``Bar.ParentMethod._test_bar`` which is supposed to do some
     522    reasonable sanity checks on ``F`` to determine whether it
     523    satisfies all the properties of ``Bar``. If yes, ``F._test_bla()``
     524    should return None; otherwise it should raise some (hopefully
     525    meaningful) assertion.  Note that ``Bar()`` will typically
     526    override ``is_bar`` by a trivial method that always returns
     527    ``True``.
     528
     529    The purpose of two decorators ``is_method`` and
     530    ``_test_method_from_is`` is to factor out the logic between the two
     531    related methods ``F.is_bla()`` and ``F._test_bla()``. They take as
     532    input a Python function ``is_bla(self, proof=False, **options)``.
     533    This function should proceed as usual for a ``_test method`` (see
     534    :class:`TestSuite`). If ``proof`` is ``True``, then the answer
     535    should be provably correct. At the end, ``is_bla`` should return
     536    ``None``, or a category that will be used to refine the category
     537    of ``F``.
     538
     539    EXAMPLES:
     540
     541    For the sake of brievity, we don't use categories in this example,
     542    but just two parents ``Union`` and ``FiniteUnion``::
     543
     544        sage: from sage.misc.sage_unittest import is_method, _test_method_from_is
     545        sage: class Union(Parent):
     546        ...       def __init__(self, sets):
     547        ...           self._sets = sets
     548        ...       @is_method
     549        ...       def is_finite(self, proof = False, **options):
     550        ...           tester = self._tester(**options)
     551        ...           if proof: # we only run a full test if proof=True
     552        ...               sets = self._sets
     553        ...           else:     # otherwise we only check the first element
     554        ...               sets = self._sets[:1]
     555        ...           for set in sets:
     556        ...               tester.assert_( set.is_finite(), "The set %s is not finite"%set )
     557        ...           return FiniteEnumeratedSets()       # todo: use Sets().Finite() after #10963
     558        ...
     559        sage: class FiniteUnion(Union):
     560        ...       _test_finite = _test_method_from_is(Union.is_finite)
     561
     562        sage: U = Union([GF(2), GF(3)])
     563        sage: U.category()
     564        Category of sets
     565        sage: U.is_finite()
     566        True
     567        sage: U.category()
     568        Category of finite enumerated sets
     569
     570        sage: U = Union([GF(2), ZZ])
     571        sage: U.is_finite()
     572        False
     573
     574    With ``raise_on_failure=True`` an exception is raised,
     575    giving information on the reason for the failure::
     576
     577        sage: U.is_finite(raise_on_failure=True)
     578        Traceback (most recent call last):
     579        ...
     580        AssertionError: The set Integer Ring is not finite
     581
     582    We now create ``FiniteUnion`` instances, and check whether they
     583    are correct::
     584
     585        sage: V = FiniteUnion([GF(2), GF(3)])
     586        sage: V._test_finite()
     587
     588        sage: V = FiniteUnion([ZZ, GF(2)])
     589        sage: V._test_finite()
     590        Traceback (most recent call last):
     591        ...
     592        AssertionError: The set Integer Ring is not finite
     593
     594    The checks are not guaranteed to be thorought::
     595
     596        sage: V = FiniteUnion([GF(2), ZZ])
     597        sage: V._test_finite()
     598
     599    TESTS::
     600
     601        sage: from sage.misc.sageinspect import sage_getsource
     602        sage: sage_getsource(U.is_finite)
     603        '      @is_method...def is_finite(self, proof = False, **options):...'
     604        sage: from sage.misc.sageinspect import sage_getsource
     605        sage: sage_getsource(V._test_finite)
     606        '      @is_method...def is_finite(self, proof = False, **options):...'
     607    """
     608    def __call__(self, raise_on_failure = False, proof = True, **kwds):
     609        """
     610        """
     611        try:
     612            category = self.f(self._instance, failure_exception = None if raise_on_failure else ReturnOnError, proof = proof, **kwds)
     613            if category:
     614                self._instance._refine_category_(category)
     615                return True
     616        except ReturnOnError:
     617            return False
     618
     619is_method = IsMethod
     620
     621class TestMethodFromIs(MethodDecorator):
     622    __doc__ = IsMethod.__doc__
     623
     624    def __init__(self, f):
     625        r"""
     626        """
     627        assert isinstance(f, IsMethod)
     628        return super(TestMethodFromIs, self).__init__(f.f)
     629
     630    def __call__(self, **kwds):
     631        r"""
     632        """
     633        self.f(self._instance, **kwds)
     634
     635_test_method_from_is = TestMethodFromIs