From 494ab6d159a385602da75942df3e55e650e10cfb Mon Sep 17 00:00:00 2001 From: Jeroen Demeyer Date: Mon, 28 Jan 2019 10:05:37 +0100 Subject: [PATCH] Simplify libGAP error handling --- src/doc/en/reference/libs/index.rst | 4 +- src/sage/combinat/combinat.py | 2 +- src/sage/combinat/sloane_functions.py | 2 +- .../graphs/generators/classical_geometries.py | 4 +- src/sage/groups/class_function.py | 4 +- src/sage/groups/finitely_presented.py | 2 +- src/sage/groups/libgap_mixin.py | 4 +- src/sage/groups/libgap_morphism.py | 10 +- src/sage/groups/libgap_wrapper.pyx | 2 +- src/sage/interfaces/gap.py | 4 +- src/sage/interfaces/gap_workspace.py | 2 +- src/sage/libs/gap/element.pyx | 103 ++++++---------- src/sage/libs/gap/libgap.pyx | 22 ++-- src/sage/libs/gap/test.py | 2 +- src/sage/libs/gap/test/Makefile | 24 ---- src/sage/libs/gap/test/README.txt | 5 - src/sage/libs/gap/test/main.c | 80 ------------- src/sage/libs/gap/test_long.py | 2 +- src/sage/libs/gap/util.pxd | 2 +- src/sage/libs/gap/util.pyx | 111 +++++++++--------- src/sage/rings/number_field/number_field.py | 14 +-- src/sage/rings/universal_cyclotomic_field.py | 6 +- src/sage_setup/docbuild/build_options.py | 2 +- 23 files changed, 138 insertions(+), 275 deletions(-) delete mode 100644 src/sage/libs/gap/test/Makefile delete mode 100644 src/sage/libs/gap/test/README.txt delete mode 100644 src/sage/libs/gap/test/main.c diff --git a/src/doc/en/reference/libs/index.rst b/src/doc/en/reference/libs/index.rst index 382fbcc4aa1..17d4bc0be31 100644 --- a/src/doc/en/reference/libs/index.rst +++ b/src/doc/en/reference/libs/index.rst @@ -80,8 +80,8 @@ libSingular sage/libs/singular/ring sage/libs/singular/groebner_strategy -libGAP ------- +GAP +--- .. toctree:: :maxdepth: 2 diff --git a/src/sage/combinat/combinat.py b/src/sage/combinat/combinat.py index dcef4309eee..4830f6ee3e5 100644 --- a/src/sage/combinat/combinat.py +++ b/src/sage/combinat/combinat.py @@ -177,7 +177,7 @@ def bell_number(n, algorithm='flint', **options): - ``'flint'`` -- Wrap FLINT's ``arith_bell_number`` - - ``'gap'`` -- Wrap libGAP's ``Bell`` + - ``'gap'`` -- Wrap GAP's ``Bell`` - ``'mpmath'`` -- Wrap mpmath's ``bell`` diff --git a/src/sage/combinat/sloane_functions.py b/src/sage/combinat/sloane_functions.py index d1049bcb209..fbc0561cac8 100644 --- a/src/sage/combinat/sloane_functions.py +++ b/src/sage/combinat/sloane_functions.py @@ -381,7 +381,7 @@ def _eval(self, n): sage: sloane.A000001._eval(5000) Traceback (most recent call last): ... - ValueError: libGAP: Error, the library of groups of size 5000 is not available + GAPError: Error, the library of groups of size 5000 is not available """ if n <= 50: return self._small[n-1] diff --git a/src/sage/graphs/generators/classical_geometries.py b/src/sage/graphs/generators/classical_geometries.py index 9328c31f50c..58b41e45af6 100644 --- a/src/sage/graphs/generators/classical_geometries.py +++ b/src/sage/graphs/generators/classical_geometries.py @@ -762,7 +762,7 @@ def UnitaryDualPolarGraph(m, q): sage: graphs.UnitaryDualPolarGraph(6,6) Traceback (most recent call last): ... - ValueError: libGAP: Error, must be a prime or a finite field + GAPError: Error, must be a prime or a finite field """ from sage.libs.gap.libgap import libgap G = _polar_graph(m, q**2, libgap.GeneralUnitaryGroup(m, q), @@ -803,7 +803,7 @@ def SymplecticDualPolarGraph(m, q): sage: graphs.SymplecticDualPolarGraph(6,6) Traceback (most recent call last): ... - ValueError: libGAP: Error, must be a prime or a finite field + GAPError: Error, must be a prime or a finite field REFERENCE: diff --git a/src/sage/groups/class_function.py b/src/sage/groups/class_function.py index f509be5b79a..51ef300275e 100644 --- a/src/sage/groups/class_function.py +++ b/src/sage/groups/class_function.py @@ -74,7 +74,7 @@ class function on the conjugacy classes, in that order. ### GAP Interface-based Class Function ### ### This is old code that should be deleted once we have transitioned -### everything to libGAP. +### everything to using the library interface to GAP. ### ##################################################################### @@ -802,7 +802,7 @@ def adams_operation(self, k): ##################################################################### ### -### libGAP-based Class function +### Class function using the GAP library ### ##################################################################### diff --git a/src/sage/groups/finitely_presented.py b/src/sage/groups/finitely_presented.py index f27f334527c..982fa417704 100644 --- a/src/sage/groups/finitely_presented.py +++ b/src/sage/groups/finitely_presented.py @@ -1263,7 +1263,7 @@ def semidirect_product(self, H, hom, check=True, reduced=False): sage: D.semidirect_product(C, bad_hom) Traceback (most recent call last): ... - ValueError: libGAP: Error, and must be lists of same length + GAPError: Error, and must be lists of same length """ from sage.groups.free_group import FreeGroup, _lexi_gen diff --git a/src/sage/groups/libgap_mixin.py b/src/sage/groups/libgap_mixin.py index f91a66f6e47..57ed83d5598 100644 --- a/src/sage/groups/libgap_mixin.py +++ b/src/sage/groups/libgap_mixin.py @@ -1,8 +1,8 @@ """ -Mix-in Class for libGAP-based Groups +Mix-in Class for GAP-based Groups This class adds access to GAP functionality to groups such that parent -and element have a ``gap()`` method that returns a libGAP object for +and element have a ``gap()`` method that returns a GAP object for the parent/element. If your group implementation uses libgap, then you should add diff --git a/src/sage/groups/libgap_morphism.py b/src/sage/groups/libgap_morphism.py index 2545a20a412..430235115fb 100644 --- a/src/sage/groups/libgap_morphism.py +++ b/src/sage/groups/libgap_morphism.py @@ -1,5 +1,5 @@ r""" -Group homomorphisms for groups with a libGAP backend +Group homomorphisms for groups with a GAP backend EXAMPLES:: @@ -38,7 +38,7 @@ class GroupMorphism_libgap(Morphism): r""" - This wraps libGAP group homomorphisms. + This wraps GAP group homomorphisms. Checking if the input defines a group homomorphism can be expensive if the group is large. @@ -57,7 +57,7 @@ class GroupMorphism_libgap(Morphism): sage: A.hom([g^2 for g in A.gens()]) Group endomorphism of Abelian group with gap, generator orders (2, 4) - Homomorphisms can be defined between different kinds of libGAP groups:: + Homomorphisms can be defined between different kinds of GAP groups:: sage: G = MatrixGroup([Matrix(ZZ, 2, [0,1,1,0])]) sage: f = A.hom([G.0, G(1)]) @@ -76,7 +76,7 @@ class GroupMorphism_libgap(Morphism): From: Free Group on generators {a, b} To: Finitely presented group < a, b | a, b^3 > - Homomorphisms can be defined between libGAP groups and permutation groups:: + Homomorphisms can be defined between GAP groups and permutation groups:: sage: S = Sp(4,3) sage: P = PSp(4,3) @@ -543,7 +543,7 @@ def preimage(self, S): phi = self.gap() from sage.groups.perm_gps.permgroup import PermutationGroup_generic if not isinstance(S, (ParentLibGAP, PermutationGroup_generic)): - raise TypeError("%s must be a libGAP or permutation group of %s"%(S, self)) + raise TypeError("%s must be a GAP or permutation group of %s"%(S, self)) if not self.codomain().gap().IsSubgroup(S.gap()).sage(): raise ValueError("%s must be a subgroup of %s"%(S, self)) preimage = phi.PreImage(S.gap()) diff --git a/src/sage/groups/libgap_wrapper.pyx b/src/sage/groups/libgap_wrapper.pyx index 908f88d04e8..8396dd7f888 100644 --- a/src/sage/groups/libgap_wrapper.pyx +++ b/src/sage/groups/libgap_wrapper.pyx @@ -5,7 +5,7 @@ This module provides helper class for wrapping GAP groups via :mod:`~sage.libs.gap.libgap`. See :mod:`~sage.groups.free_group` for an example how they are used. -The parent class keeps track of the libGAP element object, to use it +The parent class keeps track of the GAP element object, to use it in your Python parent you have to derive both from the suitable group parent and :class:`ParentLibGAP` :: diff --git a/src/sage/interfaces/gap.py b/src/sage/interfaces/gap.py index b356dcff5d3..71ecfcddb93 100644 --- a/src/sage/interfaces/gap.py +++ b/src/sage/interfaces/gap.py @@ -226,8 +226,8 @@ def set_gap_memory_pool_size(size_in_bytes): """ Set the desired gap memory pool size. - Subsequently started GAP/libGAP instances will use this as - default. Currently running instances are unchanged. + Subsequently started GAP instances will use this as default. + Already running instances are unchanged. GAP will only reserve ``size_in_bytes`` address space. Unless you actually start a big GAP computation, the memory will not be diff --git a/src/sage/interfaces/gap_workspace.py b/src/sage/interfaces/gap_workspace.py index 3416cdfe466..f0f077d7269 100644 --- a/src/sage/interfaces/gap_workspace.py +++ b/src/sage/interfaces/gap_workspace.py @@ -27,7 +27,7 @@ def gap_workspace_file(system="gap", name="workspace", dir=None): ``"libgap"`` - ``name`` -- the kind of workspace, usually ``"workspace"`` but - libGAP also uses other files + the library interface also uses other files - ``dir`` -- the directory where the workspaces should be stored. By default, this is ``DOT_SAGE/gap`` diff --git a/src/sage/libs/gap/element.pyx b/src/sage/libs/gap/element.pyx index b016097e530..a74e12d4e65 100644 --- a/src/sage/libs/gap/element.pyx +++ b/src/sage/libs/gap/element.pyx @@ -1,8 +1,8 @@ """ -libGAP element wrapper +GAP element wrapper This document describes the individual wrappers for various GAP -elements. For general information about libGAP, you should read the +elements. For general information about GAP, you should read the :mod:`~sage.libs.gap.libgap` module documentation. """ @@ -23,6 +23,7 @@ from cysignals.signals cimport sig_on, sig_off from .gap_includes cimport * from .util cimport * +from .util import GAPError from sage.cpython.string cimport str_to_bytes, char_to_str from sage.misc.cachefunc import cached_method from sage.structure.sage_object cimport SageObject @@ -141,8 +142,8 @@ cdef char *crepr(Obj obj): stream = CALL_2ARGS(output_text_string, s, GAP_True) if not OpenOutputStream(stream): - raise RuntimeError("failed to open output capture stream for " - "representing GAP object") + raise GAPError("failed to open output capture stream for " + "representing GAP object") viewobj = GAP_ValueGlobalVariable("ViewObj") CALL_1ARGS(viewobj, obj) @@ -247,7 +248,7 @@ cdef Obj make_gap_string(sage_string) except NULL: cdef GapElement make_any_gap_element(parent, Obj obj): """ - Return the libGAP element wrapper of ``obj`` + Return the GAP element wrapper of ``obj`` The most suitable subclass of GapElement is determined automatically. Use this function to wrap GAP objects unless you @@ -382,19 +383,20 @@ cdef class GapElement(RingElement): sage: libgap(0) 0 - If Gap finds an error while evaluating, a corresponding assertion is raised:: + If Gap finds an error while evaluating, a :class:`GAPError` + exception is raised:: sage: libgap.eval('1/0') Traceback (most recent call last): ... - ValueError: libGAP: Error, Rational operations: must not be zero + GAPError: Error, Rational operations: must not be zero - Also, a ``ValueError`` is raised if the input is not a simple expression:: + Also, a ``GAPError`` is raised if the input is not a simple expression:: sage: libgap.eval('1; 2; 3') Traceback (most recent call last): ... - ValueError: can only evaluate a single statement + GAPError: can only evaluate a single statement """ def __cinit__(self): @@ -409,7 +411,6 @@ cdef class GapElement(RingElement): self.value = NULL self._compare_by_id = False - def __init__(self): """ The ``GapElement`` constructor @@ -457,7 +458,6 @@ cdef class GapElement(RingElement): return reference_obj(obj) - def __dealloc__(self): r""" The Cython destructor @@ -576,7 +576,7 @@ cdef class GapElement(RingElement): sage: libgap.eval('Integers') in libgap(1) Traceback (most recent call last): ... - ValueError: libGAP: Error, no method found! Error, no 1st choice method found for `in' on 2 arguments + GAPError: Error, no method found! Error, no 1st choice method found for `in' on 2 arguments """ from sage.libs.gap.libgap import libgap GAP_IN = libgap.eval(r'\in') @@ -647,24 +647,24 @@ cdef class GapElement(RingElement): sage: lst.Adddddd(1) Traceback (most recent call last): ... - AttributeError: name "Adddddd" is not defined in GAP. + AttributeError: 'Adddddd' is not defined in GAP sage: libgap.eval('some_name := 1') 1 sage: lst.some_name Traceback (most recent call last): ... - AttributeError: name "some_name" does not define a GAP function. + AttributeError: 'some_name' does not define a GAP function """ if name in ('__dict__', '_getAttributeNames', '__custom_name', 'keys'): raise AttributeError('Python special name, not a GAP function.') try: proxy = make_GapElement_MethodProxy\ (self.parent(), gap_eval(name), self) - except ValueError: - raise AttributeError('name "'+str(name)+'" is not defined in GAP.') + except GAPError: + raise AttributeError(f"'{name}' is not defined in GAP") if not proxy.is_function(): - raise AttributeError('name "'+str(name)+'" does not define a GAP function.') + raise AttributeError(f"'{name}' does not define a GAP function") return proxy def _repr_(self): @@ -691,15 +691,15 @@ cdef class GapElement(RingElement): """ Set comparison to compare by ``id`` - By default, GAP is used to compare libGAP objects. However, - this is not defined for all GAP objects. To have libGAP play + By default, GAP is used to compare GAP objects. However, + this is not defined for all GAP objects. To have GAP play nice with ``UniqueRepresentation``, comparison must always work. This method allows one to override the comparison to sort by the (unique) Python ``id``. Obviously it is a bad idea to change the comparison of objects after you have inserted them into a set/dict. You also must - not mix libGAP objects with different sort methods in the same + not mix GAP objects with different sort methods in the same container. EXAMPLES:: @@ -709,7 +709,7 @@ cdef class GapElement(RingElement): sage: F1 < F2 Traceback (most recent call last): ... - ValueError: libGAP: cannot compare less than: Error, no method found! + GAPError: Error, no method found! Error, no 1st choice method found for `<' on 2 arguments sage: F1._set_compare_by_id() @@ -743,13 +743,13 @@ cdef class GapElement(RingElement): sage: x._assert_compare_by_id() Traceback (most recent call last): ... - ValueError: requires a libGAP objects whose comparison is by "id" + ValueError: this requires a GAP object whose comparison is by "id" sage: x._set_compare_by_id() sage: x._assert_compare_by_id() """ if not self._compare_by_id: - raise ValueError('requires a libGAP objects whose comparison is by "id"') + raise ValueError('this requires a GAP object whose comparison is by "id"') def __hash__(self): """ @@ -798,7 +798,7 @@ cdef class GapElement(RingElement): sage: F1 < F2 Traceback (most recent call last): ... - ValueError: libGAP: cannot compare less than: Error, no method found! + GAPError: Error, no method found! Error, no 1st choice method found for `<' on 2 arguments sage: F1._set_compare_by_id() @@ -824,7 +824,7 @@ cdef class GapElement(RingElement): True """ if self._compare_by_id != (other)._compare_by_id: - raise ValueError('comparison style must be the same for both operands') + raise ValueError("comparison style must be the same for both operands") if op==Py_LT: return self._compare_less(other) elif op==Py_LE: @@ -858,8 +858,6 @@ cdef class GapElement(RingElement): try: GAP_Enter() return EQ(self.value, c_other.value) - except RuntimeError as msg: - raise ValueError('libGAP: cannot compare equality: '+str(msg)) finally: GAP_Leave() sig_off() @@ -882,8 +880,6 @@ cdef class GapElement(RingElement): try: GAP_Enter() return LT(self.value, c_other.value) - except RuntimeError as msg: - raise ValueError('libGAP: cannot compare less than: '+str(msg)) finally: GAP_Leave() sig_off() @@ -904,7 +900,7 @@ cdef class GapElement(RingElement): sage: libgap(1) + libgap.CyclicGroup(2) Traceback (most recent call last): ... - ValueError: libGAP: Error, no method found! + GAPError: Error, no method found! Error, no 1st choice method found for `+' on 2 arguments """ cdef Obj result @@ -913,13 +909,10 @@ cdef class GapElement(RingElement): GAP_Enter() result = SUM(self.value, (right).value) return make_any_gap_element(self.parent(), result) - except RuntimeError as msg: - raise ValueError('libGAP: '+str(msg)) finally: GAP_Leave() sig_off() - cpdef _sub_(self, right): r""" Subtract two GapElement objects. @@ -936,7 +929,7 @@ cdef class GapElement(RingElement): sage: libgap(1) - libgap.CyclicGroup(2) Traceback (most recent call last): ... - ValueError: libGAP: Error, no method found! ... + GAPError: Error, no method found! ... """ cdef Obj result sig_on() @@ -944,8 +937,6 @@ cdef class GapElement(RingElement): GAP_Enter() result = DIFF(self.value, (right).value) return make_any_gap_element(self.parent(), result) - except RuntimeError as msg: - raise ValueError('libGAP: {}'.format(msg)) finally: GAP_Leave() sig_off() @@ -967,7 +958,7 @@ cdef class GapElement(RingElement): sage: libgap(1) * libgap.CyclicGroup(2) Traceback (most recent call last): ... - ValueError: libGAP: Error, no method found! + GAPError: Error, no method found! Error, no 1st choice method found for `*' on 2 arguments """ cdef Obj result @@ -976,13 +967,10 @@ cdef class GapElement(RingElement): GAP_Enter() result = PROD(self.value, (right).value) return make_any_gap_element(self.parent(), result) - except RuntimeError as msg: - raise ValueError('libGAP: {}'.format(msg)) finally: GAP_Leave() sig_off() - cpdef _div_(self, right): r""" Divide two GapElement objects. @@ -999,13 +987,13 @@ cdef class GapElement(RingElement): sage: libgap(1) / libgap.CyclicGroup(2) Traceback (most recent call last): ... - ValueError: libGAP: Error, no method found! + GAPError: Error, no method found! Error, no 1st choice method found for `/' on 2 arguments sage: libgap(1) / libgap(0) Traceback (most recent call last): ... - ValueError: libGAP: Error, Rational operations: must not be zero + GAPError: Error, Rational operations: must not be zero """ cdef Obj result sig_on() @@ -1013,8 +1001,6 @@ cdef class GapElement(RingElement): GAP_Enter() result = QUO(self.value, (right).value) return make_any_gap_element(self.parent(), result) - except RuntimeError as msg: - raise ValueError('libGAP: '+str(msg)) finally: GAP_Leave() sig_off() @@ -1033,7 +1019,7 @@ cdef class GapElement(RingElement): sage: libgap(1) % libgap.CyclicGroup(2) Traceback (most recent call last): ... - ValueError: libGAP: Error, no method found! + GAPError: Error, no method found! Error, no 1st choice method found for `mod' on 2 arguments """ cdef Obj result @@ -1042,13 +1028,10 @@ cdef class GapElement(RingElement): GAP_Enter() result = MOD(self.value, (right).value) return make_any_gap_element(self.parent(), result) - except RuntimeError as msg: - raise ValueError('libGAP: '+str(msg)) finally: GAP_Leave() sig_off() - def __pow__(GapElement self, right, dummy): r""" Exponentiation of two GapElement objects. @@ -1063,13 +1046,13 @@ cdef class GapElement(RingElement): sage: libgap.CyclicGroup(2) ^ 2 Traceback (most recent call last): ... - ValueError: libGAP: Error, no method found! + GAPError: Error, no method found! Error, no 1st choice method found for `^' on 2 arguments sage: libgap(3) ^ Infinity Traceback (most recent call last): ... - ValueError: libGAP: Error, no method found! Error, no 1st choice + GAPError: Error, no method found! Error, no 1st choice method found for `InverseMutable' on 1 arguments """ if not isinstance(right, GapElement): @@ -1081,13 +1064,10 @@ cdef class GapElement(RingElement): GAP_Enter() result = POW(self.value, (right).value) return make_any_gap_element(self.parent(), result) - except RuntimeError as msg: - raise ValueError('libGAP: ' + str(msg)) finally: GAP_Leave() sig_off() - def is_function(self): """ Return whether the wrapped GAP object is a function. @@ -1107,7 +1087,6 @@ cdef class GapElement(RingElement): """ return IS_FUNC(self.value) - def is_list(self): r""" Return whether the wrapped GAP object is a GAP List. @@ -1125,7 +1104,6 @@ cdef class GapElement(RingElement): """ return IS_LIST(self.value) - def is_record(self): r""" Return whether the wrapped GAP object is a GAP record. @@ -1143,7 +1121,6 @@ cdef class GapElement(RingElement): """ return IS_REC(self.value) - cpdef is_bool(self): r""" Return whether the wrapped GAP object is a GAP boolean. @@ -1177,7 +1154,6 @@ cdef class GapElement(RingElement): """ return IS_STRING(self.value) - def is_permutation(self): r""" Return whether the wrapped GAP object is a GAP permutation. @@ -1198,7 +1174,6 @@ cdef class GapElement(RingElement): return (TNUM_OBJ(self.value) == T_PERM2 or TNUM_OBJ(self.value) == T_PERM4) - def sage(self): r""" Return the Sage equivalent of the :class:`GapElement` @@ -2384,7 +2359,7 @@ cdef class GapElement_Function(GapElement): sage: s(libgap(1), libgap(2)) Traceback (most recent call last): ... - ValueError: libGAP: Error, no method found! + GAPError: Error, no method found! Error, no 1st choice method found for `SumOp' on 2 arguments sage: for i in range(0,100): @@ -2457,8 +2432,6 @@ cdef class GapElement_Function(GapElement): return None return make_any_gap_element(self.parent(), result) - except RuntimeError as msg: - raise ValueError('libGAP: ' + str(msg)) finally: GAP_Leave() sig_off() @@ -3023,8 +2996,7 @@ cdef class GapElement_Record(GapElement): sage: rec['no_such_element'] Traceback (most recent call last): ... - IndexError: libGAP: Error, Record Element: '.no_such_element' must - have an assigned value + GAPError: Error, Record Element: '.no_such_element' must have an assigned value """ def __len__(self): @@ -3043,7 +3015,6 @@ cdef class GapElement_Record(GapElement): """ return LEN_PREC(self.value) - def __iter__(self): r""" Iterate over the elements of the record. @@ -3063,7 +3034,6 @@ cdef class GapElement_Record(GapElement): """ return GapElement_RecordIterator(self) - cpdef UInt record_name_to_index(self, name): r""" Convert string to GAP record index. @@ -3114,13 +3084,10 @@ cdef class GapElement_Record(GapElement): GAP_Enter() result = ELM_REC(self.value, i) return make_any_gap_element(self.parent(), result) - except RuntimeError as msg: - raise IndexError('libGAP: ' + str(msg)) finally: GAP_Leave() sig_off() - def sage(self): r""" Return the Sage equivalent of the :class:`GapElement` diff --git a/src/sage/libs/gap/libgap.pyx b/src/sage/libs/gap/libgap.pyx index be235f54c1c..e78b88a33bc 100644 --- a/src/sage/libs/gap/libgap.pyx +++ b/src/sage/libs/gap/libgap.pyx @@ -1,8 +1,8 @@ """ -libGAP shared library Interface to GAP +Library Interface to GAP -This module implements a fast C library interface to GAP. To use -libGAP you simply call ``libgap`` (the parent of all +This module implements a fast C library interface to GAP. +To use it, you simply call ``libgap`` (the parent of all :class:`~sage.libs.gap.element.GapElement` instances) and use it to convert Sage objects into GAP objects. @@ -157,12 +157,12 @@ using the recursive expansion of the [ 0 0 7 8] -Using the libGAP C library from Cython -====================================== +Using the GAP C library from Cython +=================================== -.. TODO:: Update the following text +.. TODO:: Expand the following text - We are using libgap API provided by the GAP project since + We are using the GAP API provided by the GAP project since GAP 4.10. AUTHORS: @@ -475,7 +475,7 @@ class Gap(Parent): sage: libgap.get_global('FooBar') Traceback (most recent call last): ... - ValueError: libGAP: Error, VAL_GVAR: No value bound to FooBar + GAPError: Error, VAL_GVAR: No value bound to FooBar """ is_bound = self.function_factory('IsBoundGlobal') bind_global = self.function_factory('BindGlobal') @@ -500,7 +500,7 @@ class Gap(Parent): sage: libgap.get_global('FooBar') Traceback (most recent call last): ... - ValueError: libGAP: Error, VAL_GVAR: No value bound to FooBar + GAPError: Error, VAL_GVAR: No value bound to FooBar """ is_readonlyglobal = self.function_factory('IsReadOnlyGlobal') make_readwrite = self.function_factory('MakeReadWriteGlobal') @@ -532,7 +532,7 @@ class Gap(Parent): sage: libgap.get_global('FooBar') Traceback (most recent call last): ... - ValueError: libGAP: Error, VAL_GVAR: No value bound to FooBar + GAPError: Error, VAL_GVAR: No value bound to FooBar """ value_global = self.function_factory('ValueGlobal') return value_global(variable) @@ -759,7 +759,7 @@ class Gap(Parent): def count_GAP_objects(self): """ Return the number of GAP objects that are being tracked by - libGAP + GAP. OUTPUT: diff --git a/src/sage/libs/gap/test.py b/src/sage/libs/gap/test.py index a5eb4c4b026..5f75ff3b16a 100644 --- a/src/sage/libs/gap/test.py +++ b/src/sage/libs/gap/test.py @@ -1,5 +1,5 @@ """ -Short tests for libGAP +Short tests for GAP """ from sage.libs.all import libgap diff --git a/src/sage/libs/gap/test/Makefile b/src/sage/libs/gap/test/Makefile deleted file mode 100644 index a8b0f4365f4..00000000000 --- a/src/sage/libs/gap/test/Makefile +++ /dev/null @@ -1,24 +0,0 @@ - - -GAPDIR="$(SAGE_LOCAL)/gap/latest" - -# VALGRIND=valgrind --leak-check=full -# VALGRIND=valgrind --db-attach=yes -#VALGRIND=valgrind --suppressions=libgap.supp --gen-suppressions=yes -#VALGRIND=valgrind --suppressions=libgap.supp --db-attach=yes -VALGRIND= - -all: main - $(VALGRIND) ./main - -main: main.o Makefile - gcc -L$(SAGE_LOCAL)/lib -o main main.o -lgap -lcsage -lntl -lstdc++ -lpari -lpython2.7 -lm -lgmp - -main.o: main.c - echo $(GAPDIR) - gcc -std=gnu99 -DGAPDIR="\"$(GAPDIR)\"" -I$(SAGE_LOCAL)/include -I$(SAGE_LOCAL)/include/python2.6 -c -g $^ - -clean: - rm main.o main *~ - -.PHONY: all clean diff --git a/src/sage/libs/gap/test/README.txt b/src/sage/libs/gap/test/README.txt deleted file mode 100644 index 0c596955983..00000000000 --- a/src/sage/libs/gap/test/README.txt +++ /dev/null @@ -1,5 +0,0 @@ -In this folder is a small stub program that uses libGAP library for -some simple computations for debugging purposes. It uses libGAP -essentially in the same as the Sage libgap interface, but without the -overhead. - diff --git a/src/sage/libs/gap/test/main.c b/src/sage/libs/gap/test/main.c deleted file mode 100644 index aa37b3cec21..00000000000 --- a/src/sage/libs/gap/test/main.c +++ /dev/null @@ -1,80 +0,0 @@ -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -extern char **environ; - -void error_handler(char* msg) -{ - printf("Error: %s\n", msg); -} - -void eval(char* cmd) { - printf("Input:\n%s", cmd); - libgap_start_interaction(cmd); - - libgap_enter(); - libGAP_ReadEvalCommand(libGAP_BottomLVars); - libGAP_ViewObjHandler(libGAP_ReadEvalResult); - char* out = libgap_get_output(); - libgap_exit(); - - printf("Output:\n%s", out); - libgap_finish_interaction(); -} - -int main() -{ - char* argv[8]; - argv[0] = "gap"; - argv[1] = "-l"; - argv[2] = GAPDIR; - argv[3] = "-m"; - argv[4] = "32M"; - argv[5] = "-q"; - argv[6] = "-T"; - argv[7] = NULL; - int argc=7; - // gap_main_loop(argc, argv, environ); - libgap_set_error_handler(&error_handler); - libgap_initialize(argc, argv); - printf("Initialized\n"); - - libgap_enter() - libGAP_CollectBags(0,1); // full GC - libgap_exit() - - eval("1+2+3;\n"); - eval("g:=FreeGroup(2);\n"); - eval("a:=g.1;\n"); - eval("b:=g.2;\n"); - eval("lis:=[a^2, a^2, b*a];\n"); - eval("h:=g/lis;\n"); - eval("c:=h.1;\n"); - eval("Set([1..1000000], i->Order(c));\n"); - - libgap_finalize(); - return 0; -} - - -/* - -g:=FreeGroup(2); -a:=g.1; -b:=g.2; -lis:=[a^2, a^2, b*a]; -h:=g/lis; -c:=h.1; -Set([1..300000], i->Order(c)); - - - */ diff --git a/src/sage/libs/gap/test_long.py b/src/sage/libs/gap/test_long.py index e091b11e9fe..33eb7c7eaf0 100644 --- a/src/sage/libs/gap/test_long.py +++ b/src/sage/libs/gap/test_long.py @@ -1,5 +1,5 @@ """ -Long tests for libGAP +Long tests for GAP These stress test the garbage collection inside GAP """ diff --git a/src/sage/libs/gap/util.pxd b/src/sage/libs/gap/util.pxd index 3ea72ebedc6..986a2db45c2 100644 --- a/src/sage/libs/gap/util.pxd +++ b/src/sage/libs/gap/util.pxd @@ -32,7 +32,7 @@ cdef void gasman_callback() with gil ############################################################################ -### Initialization of libGAP ############################################### +### Initialization of GAP ################################################## ############################################################################ cdef initialize() diff --git a/src/sage/libs/gap/util.pyx b/src/sage/libs/gap/util.pyx index 39026e10876..97c383fdbe0 100644 --- a/src/sage/libs/gap/util.pyx +++ b/src/sage/libs/gap/util.pyx @@ -1,5 +1,5 @@ """ -Utility functions for libGAP +Utility functions for GAP """ #***************************************************************************** @@ -20,11 +20,11 @@ import warnings from libc.string cimport strcpy, strlen -from cpython.exc cimport PyErr_SetObject, PyErr_Occurred, PyErr_Fetch +from cpython.exc cimport PyErr_Fetch, PyErr_Restore from cpython.object cimport Py_LT, Py_LE, Py_EQ, Py_NE, Py_GT, Py_GE -from cpython.ref cimport PyObject +from cpython.ref cimport PyObject, Py_XINCREF, Py_XDECREF from cysignals.memory cimport sig_malloc -from cysignals.pysignals import changesignal +from cysignals.pysignals import containsignals from cysignals.signals cimport sig_on, sig_off, sig_error import sage.env @@ -165,11 +165,8 @@ cdef void gasman_callback() with gil: MarkBag((obj).value) - - - ############################################################################ -### Initialization of libGAP ############################################### +### Initialization of GAP ################################################## ############################################################################ def gap_root(): @@ -311,21 +308,19 @@ cdef initialize(): env = copy_environ(environ) - # Initialize GAP and capture any error messages - # The initialization just prints error and does not use the error handler - sig_on() - try: - with changesignal(signal.SIGCHLD, signal.SIG_DFL), \ - changesignal(signal.SIGINT, signal.SIG_DFL): - # Need to save/restore current SIGINT handling since GAP_Initialize - # currently clobbers it; it doesn't matter what we set SIGINT to - # temporarily. + # Need to save/restore current SIGINT handling since GAP_Initialize + # currently clobbers it; it doesn't matter what we set SIGINT to + # temporarily. + with containsignals(): + sig_on() + try: + # Initialize GAP and capture any error messages. The + # initialization just prints any errors and does not + # use the error handler. GAP_Initialize(argc, argv, env, &gasman_callback, &error_handler) - except RuntimeError as msg: - raise RuntimeError('libGAP initialization failed\n' + msg) - finally: - sig_off() + finally: + sig_off() # Set the ERROR_OUTPUT global in GAP to an output stream in which to # receive error output @@ -374,7 +369,7 @@ cdef Obj gap_eval(str gap_string) except? NULL: sage: libgap.eval('if 4>3 thenPrint("hi");\nfi') Traceback (most recent call last): ... - ValueError: libGAP: Syntax error: then expected in stream:1 + GAPError: Syntax error: then expected in stream:1 if 4>3 thenPrint("hi"); ^^^^^^^^^ sage: libgap.eval('1+1') # testing that we have successfully recovered @@ -389,7 +384,7 @@ cdef Obj gap_eval(str gap_string) except? NULL: sage: libgap.eval('Complex Field with 53 bits of precision;') Traceback (most recent call last): ... - ValueError: libGAP: Error, Variable: 'Complex' must have a value + GAPError: Error, Variable: 'Complex' must have a value Syntax error: ; expected in stream:1 Complex Field with 53 bits of precision;; ^^^^^^^^^^^^ @@ -403,14 +398,13 @@ cdef Obj gap_eval(str gap_string) except? NULL: ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Error, Variable: 'precision' must have a value - Test that on a subsequent attemt we get the same message (no garbage was left in the error stream):: sage: libgap.eval('Complex Field with 53 bits of precision;') Traceback (most recent call last): ... - ValueError: libGAP: Error, Variable: 'Complex' must have a value + GAPError: Error, Variable: 'Complex' must have a value ... Error, Variable: 'precision' must have a value @@ -422,7 +416,7 @@ cdef Obj gap_eval(str gap_string) except? NULL: cdef int i, j, nresults # Careful: We need to keep a reference to the bytes object here - # so that Cython doesn't dereference it before libGAP is done with + # so that Cython doesn't deallocate it before GAP is done with # its contents. cmd = str_to_bytes(gap_string + ';\n') sig_on() @@ -439,7 +433,7 @@ cdef Obj gap_eval(str gap_string) except? NULL: nresults = LEN_LIST(result) if nresults > 1: # to mimick the old libGAP # TODO: Get rid of this restriction eventually? - raise ValueError('can only evaluate a single statement') + raise GAPError("can only evaluate a single statement") # Get the result of the first statement result = ELM0_LIST(result, 1) # 1-indexed! @@ -448,7 +442,7 @@ cdef Obj gap_eval(str gap_string) except? NULL: # An otherwise unhandled error occurred in GAP (such as a # syntax error). Try running the error handler manually # to capture the error output, if any. - # This should result in a RuntimeError being set. + # This should result in a GAPError being set. error_handler_check_exception() # The actual resultant object, if any, is in the second entry @@ -457,8 +451,6 @@ cdef Obj gap_eval(str gap_string) except? NULL: # this like returning None) return ELM0_LIST(result, 2) - except RuntimeError as msg: - raise ValueError(f'libGAP: {msg}') finally: GAP_Leave() sig_off() @@ -491,8 +483,13 @@ cdef void hold_reference(Obj obj): ### Error handler ########################################################## ############################################################################ +class GAPError(ValueError): # ValueError for historical reasons + """ + Exceptions raised by the GAP library + """ + -cdef object extract_libgap_errout(): +cdef str extract_libgap_errout(): """ Reads the global variable libgap_errout and returns a Python string containing the error message (with some boilerplate removed). @@ -520,43 +517,51 @@ cdef void error_handler() with gil: """ The libgap error handler. - If an error occurred we set a RuntimeError; when the original - GAP_EvalString returns this exception will be raised. + If an error occurred, we raise a ``GAPError``; when the original + ``GAP_EvalString`` returns, this exception will be seen. TODO: We should probably prevent re-entering this function if we are already handling an error; if there is an error in our stream handling code below it could result in a stack overflow. """ - cdef PyObject* exc_type + cdef PyObject* exc_type = NULL cdef PyObject* exc_val = NULL - cdef PyObject* exc_tb - cdef PyObject* err_occurred + cdef PyObject* exc_tb = NULL - # Close the error stream: This flushes any remaining output and closes - # the stream for further writing; reset ERROR_OUTPUT to something sane - # just in case (trying to print to a closed stream segfaults GAP) try: GAP_EnterStack() + + # Close the error stream: this flushes any remaining output and + # closes the stream for further writing; reset ERROR_OUTPUT to + # something sane just in case (trying to print to a closed + # stream segfaults GAP) GAP_EvalStringNoExcept(_close_error_output_cmd) - if PyErr_Occurred() != NULL: - # Sometimes error_handler() can be called multiple times from a - # single GAP_EvalString call before it returns; in this case we - # just update the exception by appending to the existing exception - # message - # - # Fetch any existing exception before calling - # extract_libgap_errout() so that the exception indicator is - # cleared - PyErr_Fetch(&exc_type, &exc_val, &exc_tb) + # Fetch any existing exception before calling + # extract_libgap_errout() so that the exception indicator is + # cleared + PyErr_Fetch(&exc_type, &exc_val, &exc_tb) msg = extract_libgap_errout() - if exc_val != NULL: + # Sometimes error_handler() can be called multiple times + # from a single GAP_EvalString call before it returns. + # In this case, we just update the exception by appending + # to the existing exception message + if exc_type is GAPError and exc_val is not NULL: msg = str(exc_val) + '\n' + msg elif not msg: - msg = "An unknown error occurred in libGAP" - - PyErr_SetObject(RuntimeError, msg) + msg = "an unknown error occurred in GAP" + + # Raise an exception using PyErr_Restore(). + # This way, we can keep any existing traceback object. + # Note that we manually need to deal with refcounts here. + Py_XDECREF(exc_type) + Py_XDECREF(exc_val) + exc_type = GAPError + exc_val = msg + Py_XINCREF(exc_type) + Py_XINCREF(exc_val) + PyErr_Restore(exc_type, exc_val, exc_tb) finally: # Reset ERROR_OUTPUT with a new text string stream GAP_EvalStringNoExcept(_reset_error_output_cmd) diff --git a/src/sage/rings/number_field/number_field.py b/src/sage/rings/number_field/number_field.py index 500f80cd035..58b0d210b97 100644 --- a/src/sage/rings/number_field/number_field.py +++ b/src/sage/rings/number_field/number_field.py @@ -10250,6 +10250,13 @@ def _coerce_from_gap(self, x): Matrices over cyclotomic fields are correctly dealt with it as well:: + sage: b = libgap.eval('[[E(4), 1], [0, 1+E(8)-E(8)^3]]') + sage: matrix(F, b) + [ zeta8^2 1] + [ 0 -zeta8^3 + zeta8 + 1] + + It also works with the old pexpect interface to GAP:: + sage: b = gap(Matrix(F,[[z^2,1],[0,a+1]])); b [ [ E(4), 1 ], [ 0, 1+E(8)-E(8)^3 ] ] sage: b[1,2] @@ -10259,13 +10266,6 @@ def _coerce_from_gap(self, x): sage: matrix(F, b) [ zeta8^2 1] [ 0 -zeta8^3 + zeta8 + 1] - - It also word with libGAP instead of GAP:: - - sage: b = libgap.eval('[[E(4), 1], [0, 1+E(8)-E(8)^3]]') - sage: matrix(F, b) - [ zeta8^2 1] - [ 0 -zeta8^3 + zeta8 + 1] """ if x.IsRat(): return self(QQ(x)) diff --git a/src/sage/rings/universal_cyclotomic_field.py b/src/sage/rings/universal_cyclotomic_field.py index 0e3d4880906..357ccdaab72 100644 --- a/src/sage/rings/universal_cyclotomic_field.py +++ b/src/sage/rings/universal_cyclotomic_field.py @@ -14,10 +14,10 @@ There used to be a native Sage version of the universal cyclotomic field written by Christian Stump (see :trac:`8327`). It was slower on most - operations and it was decided to use a version based on libGAP instead (see + operations and it was decided to use a version based on GAP instead (see :trac:`18152`). One main difference in the design choices is that GAP stores dense vectors whereas the native ones used Python dictionaries (storing only - nonzero coefficients). Most operations are faster with libGAP except some + nonzero coefficients). Most operations are faster with GAP except some operation on very sparse elements. All details can be found in :trac:`18152`. @@ -783,7 +783,7 @@ def multiplicative_order(self): sage: UniversalCyclotomicField().zero().multiplicative_order() Traceback (most recent call last): ... - ValueError: libGAP: Error, argument must be nonzero + GAPError: Error, argument must be nonzero """ return self._obj.Order().sage() diff --git a/src/sage_setup/docbuild/build_options.py b/src/sage_setup/docbuild/build_options.py index 1168f025110..ddc88a39f0e 100644 --- a/src/sage_setup/docbuild/build_options.py +++ b/src/sage_setup/docbuild/build_options.py @@ -23,7 +23,7 @@ # Number of threads to use for parallel-building the documentation. NUM_THREADS = int(os.environ.get('SAGE_NUM_THREADS', 1)) -# Minimize GAP/libGAP RAM usage in the builder, docbuild already uses too much +# Minimize GAP RAM usage in the builder, docbuild already uses too much from sage.interfaces.gap import set_gap_memory_pool_size set_gap_memory_pool_size(80 * 1024 * 1024)