Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Documentation of cached_methods is missing, also affects doctests #33951

Open
saraedum opened this issue Jun 4, 2022 · 2 comments
Open

Documentation of cached_methods is missing, also affects doctests #33951

saraedum opened this issue Jun 4, 2022 · 2 comments

Comments

@saraedum
Copy link
Member

saraedum commented Jun 4, 2022

The __doc__ of a @cached_method is not fully functional. Namely, in some scenarios, its __doc__ is the doc of CachedMethod and not the one of the underlying method. Therefore, @cached_method cannot be used with normal Python doctesting since Python does not detect any doctests in the __doc__:

sage: from sage.misc.cachefunc import cached_method
sage: class A:
....:     @cached_method
....:     def f(self):
....:         r"""
....:         >>> 1 + 1
....:         2
....:         """
....:
sage: A.f.__doc__ # this is not what Python's doctest does
'File: /home/jule/<ipython-input-2-6228e44c998d> (starting at line 2)\n\n        >>> 1 + 1\n        2\n        '
sage: A.__dict__['f'].__doc__
'CachedMethod(f, name=None, key=None, do_pickle=None)\nFile: sage/misc/cachefunc.pyx (starting at line 2516)\n\n    A decorator that creates a cached version of an instance\n    method of a class.\n\n    .. NOTE::\n\n        For proper behavior, the method must be a pure function (no side\n        effects). Arguments to the method must be hashable or transformed into\n        something hashable using ``key`` or they must define\n        :meth:`sage.structure.sage_object.SageObject._cache_key`.\n\n    EXAMPLES::\n\n        sage: class Foo(object):\n        ....:     @cached_method\n        ....:     def f(self, t, x=2):\n        ....:         print(\'computing\')\n        ....:         return t**x\n        sage: a = Foo()\n\n    The example shows that the actual computation\n    takes place only once, and that the result is\n    identical for equivalent input::\n\n        sage: res = a.f(3, 2); res\n        computing\n        9\n        sage: a.f(t = 3, x = 2) is res\n        True\n        sage: a.f(3) is res\n        True\n\n    Note, however, that the :class:`CachedMethod` is replaced by a\n    :class:`CachedMethodCaller` or :class:`CachedMethodCallerNoArgs`\n    as soon as it is bound to an instance or class::\n\n        sage: P.<a,b,c,d> = QQ[]\n        sage: I = P*[a,b]\n        sage: type(I.__class__.gens)\n        <class \'sage.misc.cachefunc.CachedMethodCallerNoArgs\'>\n\n    So, you would hardly ever see an instance of this class alive.\n\n    The parameter ``key`` can be used to pass a function which creates a\n    custom cache key for inputs. In the following example, this parameter is\n    used to ignore the ``algorithm`` keyword for caching::\n\n        sage: class A(object):\n        ....:     def _f_normalize(self, x, algorithm): return x\n        ....:     @cached_method(key=_f_normalize)\n        ....:     def f(self, x, algorithm=\'default\'): return x\n        sage: a = A()\n        sage: a.f(1, algorithm="default") is a.f(1) is a.f(1, algorithm="algorithm")\n        True\n\n    The parameter ``do_pickle`` can be used to enable pickling of the cache.\n    Usually the cache is not stored when pickling::\n\n        sage: class A(object):\n        ....:     @cached_method\n        ....:     def f(self, x): return None\n        sage: import __main__\n        sage: __main__.A = A\n        sage: a = A()\n        sage: a.f(1)\n        sage: len(a.f.cache)\n        1\n        sage: b = loads(dumps(a))\n        sage: len(b.f.cache)\n        0\n\n    When ``do_pickle`` is set, the pickle contains the contents of the cache::\n\n        sage: class A(object):\n        ....:     @cached_method(do_pickle=True)\n        ....:     def f(self, x): return None\n        sage: __main__.A = A\n        sage: a = A()\n        sage: a.f(1)\n        sage: len(a.f.cache)\n        1\n        sage: b = loads(dumps(a))\n        sage: len(b.f.cache)\n        1\n\n    Cached methods cannot be copied like usual methods, see :trac:`12603`.\n    Copying them can lead to very surprising results::\n\n        sage: class A:\n        ....:     @cached_method\n        ....:     def f(self):\n        ....:         return 1\n        sage: class B:\n        ....:     g=A.f\n        ....:     def f(self):\n        ....:         return 2\n\n        sage: b=B()\n        sage: b.f()\n        2\n        sage: b.g()\n        1\n        sage: b.f()\n        1\n\n    '

In Sage this has no consquence. It's only an annoyance when trying to use @cached_method in Python code with Python doctests, see flatsurf/flatsurvey#12

Component: misc

Issue created by migration from https://trac.sagemath.org/ticket/33951

@saraedum saraedum added this to the sage-9.7 milestone Jun 4, 2022
@tobiasdiez
Copy link
Contributor

comment:1

This might be fixed by #30884.

@mkoeppe mkoeppe modified the milestones: sage-9.7, sage-9.8 Sep 19, 2022
@mkoeppe mkoeppe removed this from the sage-9.8 milestone Jan 29, 2023
@mkoeppe mkoeppe changed the title cached_method not detected in Python doctest Documentation of cached_methods is missing, also affects doctests Apr 16, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

3 participants