Skip to content

Commit

Permalink
Trac #19917: Libgap tab completion
Browse files Browse the repository at this point in the history
This ticket fixes tab completion on the libgap object
{{{
sage: libgap.[TAB]
sage: n = libgap(123)
sage: n.[TAB]
}}}

URL: http://trac.sagemath.org/19917
Reported by: vbraun
Ticket author(s): Volker Braun
Reviewer(s): Dima Pasechnik
  • Loading branch information
Release Manager authored and vbraun committed Feb 14, 2016
2 parents b026105 + 47bc138 commit c084c13
Show file tree
Hide file tree
Showing 6 changed files with 345 additions and 38 deletions.
24 changes: 24 additions & 0 deletions src/sage/libs/gap/all_documented_functions.py
@@ -0,0 +1,24 @@
"""
All Documented GAP Functions
This Python module contains all documented GAP functions, they can be
thought of as the official API of GAP.
EXAMPLES::
sage: from sage.libs.gap.all_documented_functions import *
sage: DihedralGroup(8)
<pc group of size 8 with 3 generators>
sage: GeneratorsOfGroup(_)
[ f1, f2, f3 ]
sage: List(_, Order)
[ 2, 4, 2 ]
"""

from sage.libs.gap.libgap import libgap
from sage.libs.gap.assigned_names import FUNCTIONS as _FUNCTIONS



for _f in _FUNCTIONS:
globals()[_f] = libgap.function_factory(_f)
147 changes: 147 additions & 0 deletions src/sage/libs/gap/assigned_names.py
@@ -0,0 +1,147 @@
"""
List of assigned names in GAP
EXAMPLES::
sage: from sage.libs.gap.assigned_names import KEYWORDS, GLOBALS, FUNCTIONS
sage: 'fi' in KEYWORDS
True
sage: 'ZassenhausIntersection' in GLOBALS
True
sage: 'SubdirectProduct' in FUNCTIONS
True
"""

###############################################################################
# Copyright (C) 2016, Volker Braun <vbraun.name@gmail.com>
#
# Distributed under the terms of the GNU General Public License (GPL)
# as published by the Free Software Foundation; either version 2 of
# the License, or (at your option) any later version.
# http://www.gnu.org/licenses/
###############################################################################


import cPickle
import string
from sage.libs.gap.libgap import libgap
from sage.libs.gap.saved_workspace import workspace


NamesGVars = libgap.function_factory('NamesGVars')
Filtered =libgap.function_factory('Filtered')
ValueGlobal = libgap.function_factory('ValueGlobal')
IsBoundGlobal = libgap.function_factory('IsBoundGlobal')
IsFunction = libgap.function_factory('IsFunction')
IsDocumentedWord = libgap.function_factory('IsDocumentedWord')


def load_or_compute(name, function):
"""
Helper to load a cached value or compute it
INPUT:
- ``name`` -- string. Part of the cache filename
- ``function`` -- function. To compute the value if not cached.
OUTPUT:
The value of ``function``, possibly cached.
EXAMPLES::
sage: from sage.libs.gap.assigned_names import GLOBALS
sage: len(GLOBALS) > 1000 # indirect doctest
True
sage: from sage.libs.gap.saved_workspace import workspace
sage: workspace(name='globals')
('...', True)
"""
filename, up_to_date = workspace(name=name)
if up_to_date:
with open(filename, 'rb') as f:
return cPickle.load(f)
else:
value = function()
from sage.misc.temporary_file import atomic_write
with atomic_write(filename) as f:
cPickle.dump(value, f)
return value


def list_keywords():
"""
Return the GAP reserved keywords
OUTPUT:
Tuple of strings.
EXAMPLES::
sage: from sage.libs.gap.assigned_names import KEYWORDS
sage: 'fi' in KEYWORDS # indirect doctest
True
"""
keywords = libgap.get_global('GAPInfo')['Keywords'].sage()
return tuple(sorted(keywords))


KEYWORDS = list_keywords()


def list_globals():
"""
Return the GAP reserved keywords
OUTPUT:
Tuple of strings.
EXAMPLES::
sage: from sage.libs.gap.assigned_names import GLOBALS
sage: 'ZassenhausIntersection' in GLOBALS # indirect doctest
True
"""
gvars = set(
name.sage() for name in NamesGVars()
if IsBoundGlobal(name)
)
gvars.difference_update(KEYWORDS)
return tuple(sorted(gvars))


GLOBALS = load_or_compute('globals', list_globals)


def list_functions():
"""
Return the GAP documented global functions
OUTPUT:
Tuple of strings.
EXAMPLES::
sage: from sage.libs.gap.assigned_names import FUNCTIONS
sage: 'IsBound' in FUNCTIONS # is a keyword
False
sage: 'SubdirectProduct' in FUNCTIONS # indirect doctest
True
"""
fnames = set(GLOBALS).difference(KEYWORDS)
documented = Filtered(list(fnames), IsDocumentedWord)
return tuple(sorted(documented.sage()))


FUNCTIONS = load_or_compute('functions', list_functions)






29 changes: 14 additions & 15 deletions src/sage/libs/gap/element.pyx
Expand Up @@ -18,6 +18,7 @@ elements. For general information about libGAP, you should read the
include "sage/ext/interrupt.pxi"
from cpython.object cimport *

from sage.misc.cachefunc import cached_method
from sage.structure.sage_object cimport SageObject
from sage.structure.parent import Parent
from sage.rings.all import ZZ
Expand Down Expand Up @@ -373,7 +374,6 @@ cdef class GapElement(RingElement):
return
dereference_obj(self.value)


cpdef _type_number(self):
"""
Return the GAP internal type number.
Expand All @@ -395,24 +395,24 @@ cdef class GapElement(RingElement):
name = decode_type_number.get(n, 'unknown')
return (n, name)


def trait_names(self):
def __dir__(self):
"""
Return all Gap function names.
OUTPUT:
A list of strings.
Customize tab completion
EXAMPLES::
sage: G = libgap.DihedralGroup(4)
sage: 'GeneratorsOfMagmaWithInverses' in dir(G)
True
sage: 'GeneratorsOfGroup' in dir(G) # known bug
False
sage: x = libgap(1)
sage: len(x.trait_names()) > 1000
sage: len(dir(x)) > 100
True
"""
import gap_functions
return gap_functions.common_gap_functions

from sage.libs.gap.operations import OperationInspector
ops = OperationInspector(self).op_names()
return dir(self.__class__) + ops

def __getattr__(self, name):
r"""
Expand All @@ -421,6 +421,8 @@ cdef class GapElement(RingElement):
EXAMPLES::
sage: lst = libgap([])
sage: 'Add' in dir(lst) # This is why tab-completion works
True
sage: lst.Add(1) # this is the syntactic sugar
sage: lst
[ 1 ]
Expand Down Expand Up @@ -458,7 +460,6 @@ cdef class GapElement(RingElement):
raise AttributeError('name "'+str(name)+'" does not define a GAP function.')
return proxy


def _repr_(self):
r"""
Return a string representation of ``self``.
Expand Down Expand Up @@ -486,7 +487,6 @@ cdef class GapElement(RingElement):
libgap_finish_interaction()
libgap_exit()


cpdef _set_compare_by_id(self):
"""
Set comparison to compare by ``id``
Expand Down Expand Up @@ -525,7 +525,6 @@ cdef class GapElement(RingElement):
"""
self._compare_by_id = True


cpdef _assert_compare_by_id(self):
"""
Ensure that comparison is by ``id``
Expand Down
32 changes: 12 additions & 20 deletions src/sage/libs/gap/libgap.pyx
Expand Up @@ -630,7 +630,6 @@ class Gap(Parent):
from sage.rings.integer_ring import ZZ
Parent.__init__(self, base=ZZ)


def __repr__(self):
r"""
Return a string representation of ``self``.
Expand All @@ -646,23 +645,18 @@ class Gap(Parent):
"""
return 'C library interface to GAP'


def trait_names(self):
@cached_method
def __dir__(self):
"""
Return all Gap function names.
OUTPUT:
A list of strings.
Customize tab completion
EXAMPLES::
sage: len(libgap.trait_names()) > 1000
True
sage: 'OctaveAlgebra' in dir(libgap)
True
"""
import gap_functions
return gap_functions.common_gap_functions

from sage.libs.gap.gap_functions import common_gap_functions
return dir(self.__class__) + list(common_gap_functions)

def __getattr__(self, name):
r"""
Expand All @@ -683,15 +677,17 @@ class Gap(Parent):
sage: libgap.List
<Gap function "List">
"""
if name in self.trait_names():
if name in dir(self.__class__):
return getattr(self.__class__, name)
from sage.libs.gap.gap_functions import common_gap_functions
if name in common_gap_functions:
f = make_GapElement_Function(self, gap_eval(str(name)))
assert f.is_function()
self.__dict__[name] = f
return f
else:
raise AttributeError, 'No such attribute: '+name+'.'


def show(self):
"""
Print statistics about the GAP owned object list
Expand All @@ -717,7 +713,6 @@ class Gap(Parent):
print self.eval('GasmanStatistics()')
# print_gasman_objects()


def count_GAP_objects(self):
"""
Return the number of GAP objects that are being tracked by
Expand All @@ -734,7 +729,6 @@ class Gap(Parent):
"""
return sum([1 for obj in get_owned_objects()])


def mem(self):
"""
Return information about libGAP memory usage
Expand Down Expand Up @@ -785,7 +779,6 @@ class Gap(Parent):
"""
return memory_usage()


def collect(self):
"""
Manually run the garbage collector
Expand All @@ -797,11 +790,10 @@ class Gap(Parent):
sage: libgap.collect()
"""
libgap_enter()
rc = libGAP_CollectBags(0,1)
rc = libGAP_CollectBags(0, 1)
libgap_exit()
if rc != 1:
raise RuntimeError('Garbage collection failed.')



libgap = Gap()

0 comments on commit c084c13

Please sign in to comment.