Ticket #9107: trac9107_nesting_nested_classes.patch

File trac9107_nesting_nested_classes.patch, 5.2 KB (added by SimonKing, 7 years ago)
  • sage/misc/nested_class.pyx

    # HG changeset patch
    # User Simon King <simon.king@uni-jena.de>
    # Date 1335975846 -7200
    # Node ID 77eac395c91a06bfe863079602f1add1b6ca05c3
    # Parent  12b2b39a39cdc741520e270f7546815db71684ba
    #9107: Enable nesting of a nested class into a nested class
    
    diff --git a/sage/misc/nested_class.pyx b/sage/misc/nested_class.pyx
    a b  
    7979           #, 'SubClass', 'CopiedClass', 'A1'
    8080           ]
    8181
    82 cpdef modify_for_nested_pickle(cls, str name_prefix, module):
     82cpdef modify_for_nested_pickle(cls, str name_prefix, module, first_run=True):
    8383    r"""
    8484    Modify the subclasses of the given class to be picklable, by
    8585    giving them a mangled name and putting the mangled name in the
     
    8888    INPUTS:
    8989
    9090    - ``cls`` - The class to modify.
     91    - ``name_prefix`` - The prefix to prepend to the class name.
     92    - ``module`` - The module object to modify with the mangled name.
     93    - ``first_run`` - optional bool (default True): Whether or not
     94      this function is run for the first time on ``cls``.
    9195
    92     - ``name_prefix`` - The prefix to prepend to the class name.
     96    NOTE:
    9397
    94     - ``module`` - The module object to modify with the mangled name.
     98    This function would usually not be directly called. It is internally used
     99    in :class:`NestedClassMetaclass`.
    95100
    96101    EXAMPLES::
    97102
     
    111116        sage: getattr(module, 'A.B', 'Not found')
    112117        <class '__main__.A.B'>
    113118
     119    Here we demonstrate the effect of the ``first_run`` argument::
     120
     121        sage: modify_for_nested_pickle(A, 'X', sys.modules['__main__'])
     122        sage: A.B.__name__ # nothing changed
     123        'A.B'
     124        sage: modify_for_nested_pickle(A, 'X', sys.modules['__main__'], first_run=False)
     125        sage: A.B.__name__
     126        'X.A.B'
     127
     128    Note that the class is now found in the module under both its old and
     129    its new name::
     130
     131        sage: getattr(module, 'A.B', 'Not found')
     132        <class '__main__.X.A.B'>
     133        sage: getattr(module, 'X.A.B', 'Not found')
     134        <class '__main__.X.A.B'>
     135
     136
     137    TESTS:
     138
     139    The following is a real life example, that was enabled by the internal
     140    use of the``first_run`` in :trac:`9107`::
     141
     142        sage: cython_code = [
     143        ....:  "from sage.structure.unique_representation import UniqueRepresentation",
     144        ....:  "class A1(UniqueRepresentation):",
     145        ....:  "    class B1(UniqueRepresentation):",
     146        ....:  "        class C1: pass",
     147        ....:  "    class B2:",
     148        ....:  "        class C2: pass"]
     149        sage: import os
     150        sage: cython(os.linesep.join(cython_code))
     151
     152    Before :trac:`9107`, the name of ``A1.B1.C1`` would have been wrong::
     153
     154        sage: A1.B1.C1.__name__
     155        'A1.B1.C1'
     156        sage: A1.B2.C2.__name__
     157        'A1.B2.C2'
     158        sage: A_module = sys.modules[A1.__module__]
     159        sage: getattr(A_module, 'A1.B1.C1', 'Not found').__name__
     160        'A1.B1.C1'
     161        sage: getattr(A_module, 'A1.B2.C2', 'Not found').__name__
     162        'A1.B2.C2'
     163
    114164    """
    115165    cdef str name, dotted_name
    116166    cdef str mod_name = module.__name__
    117     for (name, v) in cls.__dict__.iteritems():
    118         if isinstance(v, (type, ClassType)):
    119             if v.__name__ == name and v.__module__ == mod_name and getattr(module, name, None) is not v:
    120                 # OK, probably this is a nested class.
    121                 dotted_name = name_prefix + '.' + name
    122                 v.__name__ = dotted_name
    123                 setattr(module, dotted_name, v)
    124                 modify_for_nested_pickle(v, dotted_name, module)
     167    cdef str cls_name = cls.__name__+'.'
     168    cdef str v_name
     169    if first_run:
     170        for (name, v) in cls.__dict__.iteritems():
     171            if isinstance(v, NestedClassMetaclass):
     172                v_name = v.__name__
     173                if v_name==name and v.__module__ == mod_name and getattr(module, v_name, None) is not v:
     174                    # OK, probably this is a nested class.
     175                    dotted_name = name_prefix + '.' + v_name
     176                    setattr(module, dotted_name, v)
     177                    modify_for_nested_pickle(v, name_prefix, module, False)
     178                    v.__name__ = dotted_name
     179            elif isinstance(v, (type, ClassType)):
     180                v_name = v.__name__
     181                if v_name==name and v.__module__ == mod_name and getattr(module, v_name, None) is not v:
     182                    # OK, probably this is a nested class.
     183                    dotted_name = name_prefix + '.' + v_name
     184                    setattr(module, dotted_name, v)
     185                    modify_for_nested_pickle(v, dotted_name, module)
     186                    v.__name__ = dotted_name
     187    else:
     188        for (name, v) in cls.__dict__.iteritems():
     189            if isinstance(v, (type, ClassType, NestedClassMetaclass)):
     190                v_name = v.__name__
     191                if v_name==cls_name+name and v.__module__ == mod_name:
     192                    # OK, probably this is a nested class.
     193                    dotted_name = name_prefix + '.' + v_name
     194                    setattr(module, dotted_name, v)
     195                    modify_for_nested_pickle(v, name_prefix, module, False)
     196                    v.__name__ = dotted_name
     197       
    125198
    126199def nested_pickle(cls):
    127200    r"""