Ticket #13588: trac_13588_improve_libGAP.patch

File trac_13588_improve_libGAP.patch, 16.6 KB (added by vbraun, 9 years ago)

Updated patch

  • sage/libs/gap/element.pxd

    # HG changeset patch
    # User Volker Braun <vbraun@stp.dias.ie>
    # Date 1353350875 18000
    # Node ID 92c1a501d20e6280ae830aa2adf9fe7ebd37d3fe
    # Parent  17f0eccc62ebd7e4819c4d6b5c36ca72bf7c5159
    Improve error handling in libGAP
    
    diff --git a/sage/libs/gap/element.pxd b/sage/libs/gap/element.pxd
    a b  
    3333    # the pointer to the GAP object (memory managed by GASMAN)
    3434    cdef libGAP_Obj value
    3535
     36    # comparison
     37    cdef bint _compare_by_id
     38    cdef int _cmp_c_impl(self, Element other) except -2
     39    cpdef _set_compare_by_id(self)
     40    cpdef _assert_compare_by_id(self)
     41
    3642    cdef _initialize(self, parent, libGAP_Obj obj)
    37     cdef int _cmp_c_impl(self, Element other)
    3843    cpdef ModuleElement _add_(self, ModuleElement right)
    3944    cpdef ModuleElement _sub_(self, ModuleElement right)
    4045    cpdef RingElement _mul_(self, RingElement right)
  • sage/libs/gap/element.pyx

    diff --git a/sage/libs/gap/element.pyx b/sage/libs/gap/element.pyx
    a b  
    243243            1
    244244        """
    245245        self.value = NULL
     246        self._compare_by_id = False
    246247
    247248
    248249    def __init__(self):
     
    259260            sage: GapElement()
    260261            Traceback (most recent call last):
    261262            ...
    262             TypeError: This class cannot be instantiated from Python
     263            TypeError: this class cannot be instantiated from Python
    263264        """
    264         raise TypeError('This class cannot be instantiated from Python')       
     265        raise TypeError('this class cannot be instantiated from Python')
    265266
    266267
    267268    cdef _initialize(self, parent, libGAP_Obj obj):
     
    356357            sage: lst.Adddddd(1)
    357358            Traceback (most recent call last):
    358359            ...
    359             AttributeError: Name "Adddddd" is not defined in GAP.
     360            AttributeError: name "Adddddd" is not defined in GAP.
    360361
    361362            sage: libgap.eval('some_name := 1')
    362363            1
    363364            sage: lst.some_name
    364365            Traceback (most recent call last):
    365366            ...
    366             AttributeError: Name "some_name" does not define a GAP function.
     367            AttributeError: name "some_name" does not define a GAP function.
    367368        """
     369        # print '__getattr__', name
     370        if name in ('__dict__', '_getAttributeNames', '__custom_name', 'keys'):
     371            raise AttributeError('Python special name, not a GAP function.')
    368372        try:
    369             sig_on()
    370373            proxy = make_GapElement_MethodProxy\
    371374                (self.parent(), gap_eval(name), self)
    372             sig_off()
    373         except RuntimeError:
    374             raise AttributeError, 'Name "'+str(name)+'" is not defined in GAP.'
     375        except ValueError:
     376            raise AttributeError('name "'+str(name)+'" is not defined in GAP.')
    375377        if not proxy.is_function():
    376             raise AttributeError, 'Name "'+str(name)+'" does not define a GAP function.'
     378            raise AttributeError('name "'+str(name)+'" does not define a GAP function.')
    377379        return proxy
    378380       
    379381
    380     def __repr__(self):
     382    def _repr_(self):
    381383        r"""
    382384        Return a string representation of ``self``.
    383385
     
    389391            NULL
    390392            sage: libgap(0)
    391393            0
     394            sage: libgap(0)._repr_()
     395            '0'
    392396        """
    393397        if  self.value == NULL:
    394398            return 'NULL'
     
    403407            libgap_exit()
    404408
    405409
    406     cdef int _cmp_c_impl(self, Element other):
     410    cpdef _set_compare_by_id(self):
     411        """
     412        Set comparison to compare by ``id``
     413
     414        By default, GAP is used to compare libGAP objects. However,
     415        this is not defined for all GAP objects. To have libGAP play
     416        nice with ``UniqueRepresentation``, comparison must always
     417        work. This method allows one to override the comparison to
     418        sort by the (unique) Python ``id``.
     419
     420        Obviously it is a bad idea to change the comparison of objects
     421        after you have inserted them into a set/dict. You also must
     422        not mix libGAP objects with different sort methods in the same
     423        container.
     424
     425        EXAMPLES::
     426
     427            sage: F1 = libgap.FreeGroup(['a'])
     428            sage: F2 = libgap.FreeGroup(['a'])
     429            sage: F1 == F2
     430            Traceback (most recent call last):
     431            ...
     432            ValueError: libGAP: cannot compare: Error, no method found!
     433            Error, no 1st choice method found for `<' on 2 arguments
     434
     435            sage: F1._set_compare_by_id()
     436            sage: F1 == F2
     437            Traceback (most recent call last):
     438            ...
     439            ValueError: comparison style must be the same for both operands
     440
     441            sage: F1._set_compare_by_id()
     442            sage: F2._set_compare_by_id()
     443            sage: F1 == F2
     444            False
     445        """
     446        self._compare_by_id = True
     447
     448
     449    cpdef _assert_compare_by_id(self):
     450        """
     451        Ensure that comparison is by ``id``
     452
     453        See :meth:`_set_compare_by_id`.
     454
     455        OUTPUT:
     456
     457        This method returns nothing. A ``ValueError`` is raised if
     458        :meth:`_set_compare_by_id` has not been called on this libgap
     459        object.
     460
     461        EXAMPLES::
     462
     463            sage: x = libgap.FreeGroup(1)
     464            sage: x._assert_compare_by_id()
     465            Traceback (most recent call last):
     466            ...
     467            ValueError: requires a libGAP objects whose comparison is by "id"
     468
     469            sage: x._set_compare_by_id()
     470            sage: x._assert_compare_by_id()
     471        """
     472        if not self._compare_by_id:
     473            raise ValueError('requires a libGAP objects whose comparison is by "id"')
     474
     475
     476    cdef int _cmp_c_impl(self, Element other) except -2:
    407477        """
    408478        Compare ``self`` with ``other``.
    409479
     480        Uses the GAP comparison by default, or the Python ``id`` if
     481        :meth:`_set_compare_by_id` was called.
     482
    410483        OUTPUT:
    411484
    412         Boolean.
     485        `-1`, `0`, or `+1` depending on the comparison value of
     486        ``self`` and ``other``.  Raises a ``ValueError`` if GAP does
     487        not support comparison of ``self`` and ``other``, unless
     488        :meth:`_set_compare_by_id` was called on both ``self`` and
     489        ``other``.
    413490
    414491        EXAMPLES::
    415492
     
    421498            True
    422499            sage: a._cmp_(libgap(123))
    423500            0
     501
     502        GAP does not have a comparison function for two ``FreeGroup``
     503        objects. LibGAP signals this by raising a ``ValueError`` ::
     504
     505            sage: F1 = libgap.FreeGroup(['a'])
     506            sage: F2 = libgap.FreeGroup(['a'])
     507            sage: F1 == F2
     508            Traceback (most recent call last):
     509            ...
     510            ValueError: libGAP: cannot compare: Error, no method found!
     511            Error, no 1st choice method found for `<' on 2 arguments
     512
     513            sage: F1._set_compare_by_id()
     514            sage: F1 == F2
     515            Traceback (most recent call last):
     516            ...
     517            ValueError: comparison style must be the same for both operands
     518
     519            sage: F1._set_compare_by_id()
     520            sage: F2._set_compare_by_id()
     521            sage: F1 == F2
     522            False
    424523        """
     524        if self._compare_by_id != (<GapElement>other)._compare_by_id:
     525            raise ValueError('comparison style must be the same for both operands')
     526        if self._compare_by_id:
     527            return cmp(id(self), id(other))
    425528        cdef GapElement c_other = <GapElement>other
    426529        libgap_enter()
    427         equal = libGAP_EQ(self.value, c_other.value)
    428         if equal:
     530        try:
     531            sig_on()
     532            equal = libGAP_EQ(self.value, c_other.value)
     533            if equal:
     534                sig_off()
     535                return 0
     536            less = libGAP_LT(self.value, c_other.value)
     537            sig_off()
     538        except RuntimeError, msg:
     539            raise ValueError('libGAP: cannot compare: '+str(msg))
     540        finally:
    429541            libgap_exit()
    430             return 0
    431         less = libGAP_LT(self.value, c_other.value)
    432         libgap_exit()
    433542        return -1 if less else +1
    434543
    435544
     
    450559            Traceback (most recent call last):
    451560            ...
    452561            ValueError: libGAP: Error, no method found!
    453             For debugging hints type ?Recovery from NoMethodFound
    454562            Error, no 1st choice method found for `+' on 2 arguments
    455563        """
    456564        cdef libGAP_Obj result
     
    461569            sig_off()
    462570        except RuntimeError, msg:
    463571            libGAP_ClearError()
    464             raise ValueError, 'libGAP: '+str(msg)
     572            raise ValueError('libGAP: '+str(msg))
    465573        finally:
    466574            libgap_exit()
    467575        return make_any_gap_element(self.parent(), result)
     
    483591            sage: libgap(1) - libgap.CyclicGroup(2)
    484592            Traceback (most recent call last):
    485593            ...
    486             ValueError: libGAP: Error, no method found! For debugging hints type ?Recovery from NoMethodFound
     594            ValueError: libGAP: Error, no method found!
    487595            Error, no 1st choice method found for `-' on 2 arguments
    488596        """
    489597        cdef libGAP_Obj result
     
    516624            sage: libgap(1) * libgap.CyclicGroup(2)
    517625            Traceback (most recent call last):
    518626            ...
    519             ValueError: libGAP: Error, no method found! For debugging hints type ?Recovery from NoMethodFound
     627            ValueError: libGAP: Error, no method found!
    520628            Error, no 1st choice method found for `*' on 2 arguments
    521629        """
    522630        cdef libGAP_Obj result
     
    549657            sage: libgap(1) / libgap.CyclicGroup(2)
    550658            Traceback (most recent call last):
    551659            ...
    552             ValueError: libGAP: Error, no method found! For debugging hints type ?Recovery from NoMethodFound
     660            ValueError: libGAP: Error, no method found!
    553661            Error, no 1st choice method found for `/' on 2 arguments
    554662           
    555663            sage: libgap(1) / libgap(0)
     
    585693            sage: libgap(1) % libgap.CyclicGroup(2)
    586694            Traceback (most recent call last):
    587695            ...
    588             ValueError: libGAP: Error, no method found! For debugging hints type ?Recovery from NoMethodFound
     696            ValueError: libGAP: Error, no method found!
    589697            Error, no 1st choice method found for `mod' on 2 arguments
    590698        """
    591699        cdef libGAP_Obj result
     
    616724            sage: libgap.CyclicGroup(2) ^ 2
    617725            Traceback (most recent call last):
    618726            ...
    619             ValueError: libGAP: Error, no method found! For debugging hints type ?Recovery from NoMethodFound
     727            ValueError: libGAP: Error, no method found!
    620728            Error, no 1st choice method found for `^' on 2 arguments
    621729
    622730            sage: libgap(3) ^ Infinity
     
    9831091        raise ValueError('the GAP boolean value "fail" cannot be represented in Sage')
    9841092
    9851093
     1094    def __nonzero__(self):
     1095        """
     1096        Check that the boolean is "true".
     1097
     1098        This is syntactic sugar for using libgap. See the examples below.
     1099
     1100        OUTPUT:
     1101
     1102        Boolean.
     1103
     1104        EXAMPLES::
     1105
     1106            sage: gap_bool = [libgap.eval('true'), libgap.eval('false'), libgap.eval('fail')]
     1107            sage: for x in gap_bool:
     1108            ...       if x:     # this calls __nonzero__
     1109            ...           print x, type(x)
     1110            true <type 'sage.libs.gap.element.GapElement_Boolean'>
     1111
     1112            sage: for x in gap_bool:
     1113            ...       if not x:     # this calls __nonzero__
     1114            ...           print x, type(x)
     1115            false <type 'sage.libs.gap.element.GapElement_Boolean'>
     1116            fail <type 'sage.libs.gap.element.GapElement_Boolean'>
     1117       """
     1118        return self.value == libGAP_True
     1119
     1120
    9861121############################################################################
    9871122### GapElement_String ####################################################
    9881123############################################################################
     
    11741309            sage: s(libgap(1), libgap(2))
    11751310            Traceback (most recent call last):
    11761311            ...
    1177             ValueError: libGAP: Error, no method found! For debugging hints type ?Recovery from NoMethodFound
     1312            ValueError: libGAP: Error, no method found!
    11781313            Error, no 1st choice method found for `SumOp' on 2 arguments
    11791314
    11801315            sage: for i in range(0,100):
     
    12411376                result = libGAP_CALL_XARGS(self.value, arg_list)
    12421377            sig_off()
    12431378        except RuntimeError, msg:
    1244             raise ValueError, 'libGAP: '+str(msg)
     1379            raise ValueError('libGAP: '+str(msg))
    12451380        finally:
    12461381            libgap_exit()
    12471382
     
    14141549        sage: lst[10]
    14151550        Traceback (most recent call last):
    14161551        ...
    1417         IndexError: Index out of range.
     1552        IndexError: index out of range.
    14181553    """
    14191554   
    14201555    def __len__(self):
     
    14561591            "first"
    14571592        """
    14581593        if i<0 or i>=len(self):
    1459             raise IndexError, 'Index out of range.'
     1594            raise IndexError('index out of range.')
    14601595        return make_any_gap_element(self.parent(),
    14611596                                    libGAP_ELM_PLIST(self.value, i+1))
    14621597           
     
    16761811            result = libGAP_ELM_REC(self.value, i)
    16771812            sig_off()
    16781813        except RuntimeError, msg:
    1679             raise IndexError, 'libGAP: '+str(msg)
     1814            raise IndexError('libGAP: '+str(msg))
    16801815        return make_any_gap_element(self.parent(), result)
    16811816
    16821817
  • sage/libs/gap/util.pxd

    diff --git a/sage/libs/gap/util.pxd b/sage/libs/gap/util.pxd
    a b  
    3939
    4040# To ensure that we call initialize_libgap only once.
    4141cdef bint _gap_is_initialized = False
    42 cdef void initialize()
     42cdef initialize()
    4343
    4444
    4545############################################################################
  • sage/libs/gap/util.pyx

    diff --git a/sage/libs/gap/util.pyx b/sage/libs/gap/util.pyx
    a b  
    160160    gapdir = os.path.join(SAGE_LOCAL, 'gap', 'latest')
    161161    if os.path.exists(gapdir):
    162162        return gapdir
    163     print 'The gap-4.5.5.spkg (or later) seems to be missing!'
     163    print 'The gap-4.5.5.spkg (or later) seems to be not installed!'
    164164    gap_sh = open(os.path.join(SAGE_LOCAL, 'bin', 'gap')).read().splitlines()
    165165    gapdir = filter(lambda dir:dir.strip().startswith('GAP_DIR'), gap_sh)[0]
    166166    gapdir = gapdir.split('"')[1]
     
    168168    return gapdir
    169169   
    170170
    171 cdef void initialize():
     171cdef initialize():
    172172    """
    173173    Initialize the GAP library, if it hasn't already been
    174174    initialized.  It is safe to call this multiple times.
    175    
    176     INPUT:
    177    
    178     - ``max_workspace_size`` -- string. The size hint for GAP's memory
    179       pool. Same syntax as GAP's ``-o`` command line option.
    180    
     175
    181176    TESTS::
    182177   
    183178        sage: libgap(123)   # indirect doctest
     
    194189    argv[1] = "-l"
    195190    s = gap_root()
    196191    argv[2] = s
    197    
     192
     193    from sage.interfaces.gap import get_gap_memory_pool_size
     194    memory_pool = str(get_gap_memory_pool_size())
    198195    argv[3] = "-o"
    199     import platform
    200     if platform.architecture()[0] == '32bit':
    201         argv[4] = "3900m"
    202     else:
    203         argv[4] = "16384G"
     196    argv[4] = memory_pool
    204197               
    205198    argv[5] = "-m"
    206199    argv[6] = "64m"
     
    209202    argv[8] = "-T"    # no debug loop
    210203    argv[9] = NULL
    211204    cdef int argc = 9
     205
     206    # Initialize GAP and capture any error messages
     207    # The initialization just prints error and does not use the error handler
     208    libgap_start_interaction('')
    212209    libgap_initialize(argc, argv)
     210    gap_error_msg = str(libgap_get_output())
     211    libgap_finish_interaction()
     212    if gap_error_msg:
     213        raise RuntimeError('libGAP initialization failed\n' + gap_error_msg)
     214
     215    # The error handler is called if a GAP evaluation fails, e.g. 1/0
    213216    libgap_set_error_handler(&error_handler)
    214     libgap_enter()
    215217   
    216218    # Prepare global GAP variable to hold temporary GAP objects
    217219    global reference_holder
     220    libgap_enter()
    218221    reference_holder = libGAP_GVarName("$SAGE_libgap_reference_holder")
     222    libgap_exit()
    219223   
    220224    # Finished!
    221     libgap_exit()
    222225    _gap_is_initialized = True
    223226
    224227
     
    325328    The libgap error handler
    326329
    327330    We call ``abort()`` which causes us to jump back to the Sage
    328     signal handler.
     331    signal handler. Since we wrap libGAP C calls in ``sig_on`` /
     332    ``sig_off`` blocks, this then jumps back to the ``sig_on`` and
     333    raises a Python ``RuntimeError``.
    329334    """
    330     sig_str(msg)
     335    # print 'error_handler:', msg
     336    msg_py = msg
     337    msg_py = msg_py.replace('For debugging hints type ?Recovery from NoMethodFound\n', '')
     338    sig_str(msg_py)
    331339    abort()
    332340    sig_off()
    333341