Ticket #11298: trac11298_introspection_and_edit.patch

File trac11298_introspection_and_edit.patch, 12.3 KB (added by SimonKing, 11 years ago)

Extend the capabilities of introspection and interactive source code edition

  • doc/en/reference/misc.rst

    # HG changeset patch
    # User Simon King <simon.king@uni-jena.de>
    # Date 1304590123 -7200
    # Node ID 592c0c930d1a191546007ccfe5b0aed8612c6f7b
    # Parent  7ea0e1a8ca4e4675a320915b55c383ce955f3f1f
    #11298: Improve introspection and interactive edit.
    
    diff --git a/doc/en/reference/misc.rst b/doc/en/reference/misc.rst
    a b  
    3232   sage/misc/func_persist
    3333   sage/misc/sage_eval
    3434   sage/misc/random_testing
     35   sage/misc/sageinspect
    3536   sage/misc/sagedoc
     37   sage/misc/edit_module
    3638   sage/rings/arith
    3739   sage/misc/nested_class
    3840   sage/misc/nested_class_test
  • sage/misc/edit_module.py

    diff --git a/sage/misc/edit_module.py b/sage/misc/edit_module.py
    a b  
    11"""
    2 edit_module
     2Edit the source code of Sage interactively
    33
    4 AUTHOR: * Nils Bruin
    5         * William Stein -- touch up for inclusion in Sage. 
     4AUTHORS:
     5
     6- Nils Bruin
     7- William Stein -- touch up for inclusion in Sage.
     8- Simon King: Make it usable on extension classes that do not have
     9  a docstring; include this module into the reference manual and
     10  fix some syntax errors in the doc strings.
    611
    712This module provides a routine to open the source file of a python
    813object in an editor of your choice, if the source file can be figured
     
    6873   the first line of a file is considered to be 1 rather than 0
    6974   because most editors think that this is the case.
    7075   
    71    AUTHOR:
    72       Nils Bruin (2007-10-03)
     76   AUTHORS:
     77
     78   - Nils Bruin (2007-10-03)
     79   - Simon King (2011-05): Use :mod:`~sage.misc.sageinspect` to get the file
     80     and the line.
    7381   
    74    EXAMPLE:
     82   EXAMPLES::
     83
    7584      sage: import sage.misc.edit_module as edit_module
    76       sage: edit_module.file_and_line(sage)     # random output
    77       ('/usr/local/sage/default/devel/sage/sage/__init__.py', 1)
     85      sage: edit_module.file_and_line(sage)
     86      ('...sage/__init__.py', 0)
     87
     88   The following tests against a bug that was fixed in trac ticket #11298::
     89
     90      sage: edit_module.file_and_line(x)
     91      ('...sage/symbolic/expression.pyx', 191)
     92
    7893   """
    79    d = inspect.getdoc(obj)
    80    ret = sage.misc.sageinspect._extract_embedded_position(d);
    81    if ret is not None:
    82      (_, filename, lineno) = ret
    83    else:
    84      filename = inspect.getsourcefile(obj)
    85      _,lineno = inspect.findsource(obj)
     94   #d = inspect.getdoc(obj)
     95   #ret = sage.misc.sageinspect._extract_embedded_position(d);
     96   #if ret is not None:
     97   #  (_, filename, lineno) = ret
     98   #else:
     99   #  filename = inspect.getsourcefile(obj)
     100   #  _,lineno = inspect.findsource(obj)
     101   
    86102     #
    87103     #  for sage files, the registered source file is the result of the preparsing
    88104     #  these files end in ".py" and have "*autogenerated*" on the second line
     
    90106     #  3 from the line number to compensate for the 3 lines that were prefixed
    91107     #  in the preparsing process
    92108     #
    93      if filename.endswith('.py'):
    94        infile=open(filename,'r')
    95        infile.readline()
    96        if infile.readline().find("*autogenerated*") >= 0:
    97          filename=filename[:-3]+'.sage'
    98          lineno = lineno-3
     109   from sage.misc.sageinspect import sage_getfile, sage_getsourcelines
     110   filename = sage_getfile(obj)
     111   lineno = sage_getsourcelines(obj)[1] - 1
     112   if filename.endswith('.py'):
     113     infile=open(filename,'r')
     114     infile.readline()
     115     if infile.readline().find("*autogenerated*") >= 0:
     116       filename=filename[:-3]+'.sage'
     117       lineno = lineno-3
    99118
    100119   sageroot = sage.misc.sageinspect.SAGE_ROOT+'/'
    101120   runpathpattern = '^'+sageroot+'local/lib/python[^/]*/site-packages'
     
    110129   Given a String.Template object, returns the fields.
    111130   
    112131   AUTHOR:
    113       Nils Bruin (2007-10-22)
     132
     133   - Nils Bruin (2007-10-22)
    114134   
    115    EXAMPLE:
     135   EXAMPLES::
     136
    116137      sage: from sage.misc.edit_module import template_fields
    117138      sage: from string import Template
    118139      sage: t=Template("Template ${one} with ${two} and ${three}")
     
    136157   r"""
    137158   Sets default edit template string.
    138159
    139    It should reference ${file} and ${line}. This routine normally
     160   It should reference ``${file}`` and ``${line}``. This routine normally
    140161   needs to be called prior to using 'edit'. However, if the editor
    141162   set in the shell variable EDITOR is known, then the system will
    142163   substitute an appropriate template for you. See
    143164   edit_module.template_defaults for the recognised templates.
    144165
    145166   AUTHOR:
    146       Nils Bruin (2007-10-03)
     167
     168   - Nils Bruin (2007-10-03)
    147169   
    148    EXAMPLE:
     170   EXAMPLES::
     171
    149172      sage: from sage.misc.edit_module import set_edit_template
    150173      sage: set_edit_template("echo EDIT ${file}:${line}")
    151174      sage: edit(sage)      # not tested
     
    173196   of editors. If you want to use another editor, you should set the
    174197   whole edit template via set_edit_template.
    175198
    176    AUTHOR:
    177       Nils Bruin (2007-10-05)
     199   AUTHOR:
    178200
    179    EXAMPLE:
     201   - Nils Bruin (2007-10-05)
     202
     203   EXAMPLES::
     204
    180205      sage: from sage.misc.edit_module import set_editor
    181206      sage: set_editor('vi')
    182207      sage: sage.misc.edit_module.edit_template.template
     
    193218   Open source code of obj in editor of your choice.
    194219
    195220   INPUT:
    196        editor -- str (default: None); If given, use specified editor.
    197                  Choice is stored for next time.
     221
     222   - editor -- str (default: None); If given, use specified editor. Choice is stored for next time.
    198223
    199224   AUTHOR:
    200        Nils Bruin (2007-10-03)
    201225   
    202    EXAMPLE:
     226   - Nils Bruin (2007-10-03)
     227   
     228   EXAMPLES:
    203229
    204230   This is a typical example of how to use this routine.
     231   ::
    205232
    206233      # make some object obj
    207234      sage: edit(obj)    # not tested
    208235
    209    Now for more details and customization:
     236   Now for more details and customization::
    210237     
    211238      sage: import sage.misc.edit_module as m
    212239      sage: m.set_edit_template("vi -c ${line} ${file}")
    213240     
    214241   In fact, since vi is a well-known editor, you could also just use
     242   ::
    215243     
    216244      sage: m.set_editor("vi")
    217245     
    218    To illustrate:
     246   To illustrate::
    219247     
    220248      sage: m.edit_template.template
    221249      'vi -c ${line} ${file}'
     
    223251   And if your environment variable EDITOR is set to a recognised
    224252   editor, you would not have to set anything.
    225253     
    226    To edit the source of an object, just type something like:
     254   To edit the source of an object, just type something like::
    227255
    228256      sage: edit(edit)           # not tested
    229257   """
  • sage/misc/sageinspect.py

    diff --git a/sage/misc/sageinspect.py b/sage/misc/sageinspect.py
    a b  
    188188       sage: _extract_embedded_position(inspect.getdoc(var))[1][-21:]
    189189       'sage/calculus/var.pyx'
    190190       
    191     AUTHOR:
    192         -- William Stein
    193         -- Extensions by Nick Alexander
     191    AUTHORS:
     192
     193    - William Stein
     194    - Extensions by Nick Alexander
    194195    """
    195196    if docstring is None:
    196197        return None
     
    887888        sage: sage_getfile(obj)
    888889        '...sage/combinat/partition_algebra.py'
    889890
    890     AUTHOR:
     891    And here is another bug, fixed in trac ticket #11298::
     892
     893        sage: P.<x,y> = QQ[]
     894        sage: sage_getfile(P)
     895        '...sage/rings/polynomial/multi_polynomial_libsingular.pyx'
     896
     897    AUTHORS:
    891898
    892899    - Nick Alexander
     900    - Simon King
    893901    """
    894     # We try to extract from docstrings, because Python's inspect
    895     # will happily report compiled .so files
    896     d = inspect.getdoc(obj)
     902    # We try to extract from docstrings, but not using Python's inspect
     903    # because _sage_getdoc_unformatted is more robust.
     904    d = _sage_getdoc_unformatted(obj)
    897905    pos = _extract_embedded_position(d)
    898906    if pos is not None:
    899907        (_, filename, _) = pos
     
    903911    if isclassinstance(obj):
    904912        if isinstance(obj, functools.partial):
    905913            return sage_getfile(obj.func)
    906         return inspect.getabsfile(obj.__class__)
     914        return sage_getfile(obj.__class__) #inspect.getabsfile(obj.__class__)
     915
    907916    # No go? fall back to inspect.
    908917    return inspect.getabsfile(obj)
    909918
     
    9921001    an instance of that class does not coincide with the argspec
    9931002    of its call method. That behaviour is intended, since a
    9941003    decorated method appears to have the generic signature
    995     `*args,**kwds`, but in fact it is only supposed to be called
     1004    ``*args,**kwds``, but in fact it is only supposed to be called
    9961005    with the arguments requested by the underlying undecorated
    9971006    method. We saw an easy example above, namely `I.groebner_basis`.
    9981007    Here is a more difficult::
     
    13411350        ...
    13421351        '    raise ValueError, "k must be an integer or an integer + 1/2"\n'], 31)
    13431352
     1353    Here are some cases that where covered in trac ticket #11298::
     1354
     1355        sage: P.<x,y> = QQ[]
     1356        sage: I = P*[x,y]
     1357        sage: sage_getsourcelines(P)
     1358        (['cdef class MPolynomialRing_libsingular(MPolynomialRing_generic):\n',
     1359          "    def __init__(self, base_ring, n, names, order='degrevlex'):\n",
     1360        ...
     1361          '          M.append(new_MP(self, p_Copy(tempvector,_ring)))\n',
     1362          '          return M\n'], 225)
     1363        sage: sage_getsourcelines(I)
     1364        (['class MPolynomialIdeal( MPolynomialIdeal_singular_repr, \\\n',
     1365        ...
     1366        '        return result_ring.ideal(result)\n'], 2612)
     1367        sage: x = var('x')
     1368        sage: sage_getsourcelines(x)
     1369        (['cdef class Expression(CommutativeRingElement):\n',
     1370          '    cpdef object pyobject(self):\n',
     1371        ...
     1372          '        return self / x\n'], 191)
     1373       
     1374
    13441375    AUTHORS:
    13451376   
    13461377    - William Stein
    13471378    - Extensions by Nick Alexander
    13481379    - Extension to interactive Cython code by Simon King
     1380    - Simon King: If a class has no docstring then let the class
     1381      definition be found starting from the ``__init__`` method.
    13491382    """
    13501383
    13511384    try:
     
    13601393        else:
    13611394            obj=obj.__class__
    13621395       
    1363     # If we can handle it, we do.  This is because Python's inspect will
    1364     # happily dump binary for cython extension source code.
     1396    # If we can handle it, we do.  We first try Python's inspect, and
     1397    # if that fails then we try _sage_getdoc_unformatted. We can not use
     1398    # the latter right away, since otherwise there is an import problem
     1399    # at sage startup, believe it or not.
    13651400    d = inspect.getdoc(obj)
    13661401    pos = _extract_embedded_position(d)
    13671402    if pos is None:
    1368         return inspect.getsourcelines(obj)
     1403        d = _sage_getdoc_unformatted(obj)
     1404        pos = _extract_embedded_position(d)
     1405        if pos is None:
     1406            try:
     1407                return inspect.getsourcelines(obj)
     1408            except IOError:
     1409                if obj.__class__ != type:
     1410                    return sage_getsourcelines(obj.__class__)
     1411                raise
    13691412
    13701413    (orig, filename, lineno) = pos
    13711414    try:
     
    13781421            source_lines = open(newname).readlines()
    13791422        except IOError:
    13801423            return None
    1381 
     1424   
     1425    # It is possible that the source lines belong to the __init__ method,
     1426    # rather than to the class. So, we try to look back and find the class
     1427    # definition.
     1428    first_line = source_lines[lineno-1]
     1429    leading_blanks = len(first_line)-len(first_line.lstrip())
     1430    if first_line.lstrip().startswith('def ') and "__init__" in first_line and obj.__name__!='__init__':
     1431        ignore = False
     1432        double_quote = None
     1433        for lnb in xrange(lineno,0,-1):
     1434            new_first_line = source_lines[lnb-1]
     1435            nfl_strip = new_first_line.lstrip()
     1436            if nfl_strip.startswith('"""'):
     1437                if double_quote is None:
     1438                    double_quote=True
     1439                if double_quote:
     1440                    ignore = not ignore
     1441            elif nfl_strip.startswith("'''"):
     1442                if double_quote is None:
     1443                    double_quote=False
     1444                if double_quote is False:
     1445                    ignore = not ignore
     1446            if ignore:
     1447                continue
     1448            if len(new_first_line)-len(nfl_strip)<leading_blanks and nfl_strip:
     1449                # We are not inside a doc string. So, if the indentation
     1450                # is less than the indentation of the __init__ method
     1451                # then we must be at the class definition!
     1452                lineno = lnb
     1453                break
    13821454    return _extract_source(source_lines, lineno), lineno
    13831455
    13841456def sage_getvariablename(obj, omit_underscore_names=True):