SF.net SVN: geany:[4510] trunk
eht16 at users.sourceforge.net
eht16 at xxxxx
Tue Dec 29 18:37:50 UTC 2009
Revision: 4510
http://geany.svn.sourceforge.net/geany/?rev=4510&view=rev
Author: eht16
Date: 2009-12-29 18:37:47 +0000 (Tue, 29 Dec 2009)
Log Message:
-----------
Rewrite and extend the Python tags parsing script to use Python's inspect module to read symbols from Python modules including scope information.
Modified Paths:
--------------
trunk/ChangeLog
trunk/scripts/create_py_tags.py
Modified: trunk/ChangeLog
===================================================================
--- trunk/ChangeLog 2009-12-29 18:35:30 UTC (rev 4509)
+++ trunk/ChangeLog 2009-12-29 18:37:47 UTC (rev 4510)
@@ -7,6 +7,10 @@
* src/callbacks.c, src/plugins.c, src/plugins.h:
Fix sensitivity of the Edit->Plugin Preferences menu item
if plugins are loaded which do not provide a configuration dialog.
+ * scripts/create_py_tags.py:
+ Rewrite and extend the Python tags parsing script to use
+ Python's inspect module to read symbols from Python modules
+ including scope information.
2009-12-26 Frank Lanitz <frank at frank.uvena.de>
Modified: trunk/scripts/create_py_tags.py
===================================================================
--- trunk/scripts/create_py_tags.py 2009-12-29 18:35:30 UTC (rev 4509)
+++ trunk/scripts/create_py_tags.py 2009-12-29 18:37:47 UTC (rev 4510)
@@ -12,48 +12,115 @@
# them into data/python.tags (internal tagmanager format).
# If called without command line arguments, a preset of common Python libs is used.
-import sys, re, os, datetime
+import datetime
+import imp
+import inspect
+import os
+import re
+import string
+import sys
+import types
# (from tagmanager/tm_tag.c:32)
TA_NAME = '%c' % 200,
TA_TYPE = '%c' % 204
TA_ARGLIST = '%c' % 205
+TA_SCOPE = '%c' % 206
# TMTagType (tagmanager/tm_tag.h:47)
TYPE_CLASS = '%d' % 1
TYPE_FUNCTION = '%d' % 128
tag_filename = 'data/python.tags'
-matcher = re.compile('^[ \t]*(def|class)[ \t]+([a-zA-Z0-9_]+)[ \t]*(\(.*\))[:]')
+tag_regexp = '^[ \t]*(def|class)[ \t]+([a-zA-Z0-9_]+)[ \t]*(\(.*\))[:]'
class Parser:
#----------------------------------------------------------------------
def __init__(self):
- self.tags = []
+ self.tags = {}
+ self.re_matcher = re.compile(tag_regexp)
#----------------------------------------------------------------------
- def add_tag(self, tag):
+ def _get_superclass(self, c):
"""
+ Python class base-finder
+ (found on http://mail.python.org/pipermail/python-list/2002-November/173949.html)
+
+ @param c (object)
+ @return superclass (object)
+ """
+ try:
+ #~ TODO print inspect.getmro(c)
+ if type(c) == types.ClassType:
+ return c.__bases__[0].__name__
+ else:
+ return c.__mro__[1].__name__
+ except Exception, e:
+ return ''
+
+ #----------------------------------------------------------------------
+ def _formatargspec(self, args, varargs=None, varkw=None, defaults=None,
+ formatarg=str,
+ formatvarargs=lambda name: '*' + name,
+ formatvarkw=lambda name: '**' + name,
+ formatvalue=lambda value: '=' + repr(value),
+ join=inspect.joinseq):
+ """Format an argument spec from the 4 values returned by getargspec.
+
+ The first four arguments are (args, varargs, varkw, defaults). The
+ other four arguments are the corresponding optional formatting functions
+ that are called to turn names and values into strings. The ninth
+ argument is an optional function to format the sequence of arguments."""
+ specs = []
+ if defaults:
+ firstdefault = len(args) - len(defaults)
+ for i in range(len(args)):
+ spec = inspect.strseq(args[i], formatarg, join)
+ if defaults and i >= firstdefault:
+ d = defaults[i - firstdefault]
+ # this is the difference from the original formatargspec() function
+ # to use nicer names then the default repr() output
+ if hasattr(d, '__name__'):
+ d = d.__name__
+ spec = spec + formatvalue(d)
+ specs.append(spec)
+ if varargs is not None:
+ specs.append(formatvarargs(varargs))
+ if varkw is not None:
+ specs.append(formatvarkw(varkw))
+ return '(' + string.join(specs, ', ') + ')'
+
+ #----------------------------------------------------------------------
+ def _add_tag(self, obj, tag_type, parent=''):
+ """
Verify the found tag name and if it is valid, add it to the list
- @param tag (str)
+ @param obj (instance)
+ @param tag_type (str)
+ @param parent (str)
"""
- end_pos = tag.find(TA_TYPE)
- tagname = tag[0:(end_pos+1)]
+ args = ''
+ scope = ''
+ try:
+ args = apply(self._formatargspec, inspect.getargspec(obj))
+ except TypeError, KeyError:
+ pass
+ if parent:
+ if tag_type == TYPE_CLASS:
+ args = '(%s)' % parent
+ else:
+ scope = '%s%s' % (TA_SCOPE, parent)
+ tagname = obj.__name__
# check for duplicates
- if len(tagname) < 5:
+ if len(tagname) < 4:
# skip short tags
return
- for test in self.tags:
- if test.startswith(tagname):
- # check whether we find a tag line which starts with the same name,
- # include the separating TA_TYPE character to ensure we don't match
- # writelines() and write()
- return
- self.tags.append(tag)
+ tag = '%s%s%s%s%s%s\n' % (tagname, TA_TYPE, tag_type, TA_ARGLIST, args, scope)
+ if not tagname in self.tags:
+ self.tags[tagname] = tag
#----------------------------------------------------------------------
def process_file(self, filename):
@@ -63,46 +130,66 @@
@param filename (str)
"""
try:
- fp = open(filename, 'r')
- except:
- sys.stderr.write('Cannot open %s\n' % filename)
- return
- for line in fp:
- m = matcher.match(line)
- if m:
- tag_type_str, name, args = m.groups()
- if not name or name[0] == '_':
+ module = imp.load_source('tags_file_module', filename)
+ except Exception, e:
+ module = None
+
+ if module:
+ symbols = inspect.getmembers(module, callable)
+ for obj_name, obj in symbols:
+ try:
+ name = obj.__name__
+ except AttributeError:
+ name = obj_name
+ if not name or name.startswith('_'):
# skip non-public tags
continue;
- if tag_type_str == 'class':
- tag_type = TYPE_CLASS
- else:
- tag_type = TYPE_FUNCTION
- args = args.strip()
- # tagnameTA_TYPEtypeTA_ARGLISTarglist\n
- s = name + TA_TYPE + tag_type + TA_ARGLIST + args + '\n'
- self.add_tag(s)
- #~ # maybe for later use, with a more sophisticated algo to retrieve the API
- #~ scope = ''
- #~ return_value = ''
- #~ # tagnameTA_TYPEtypeTA_ARGLISTarglistTA_SCOPEscopeTA_VARTYPEreturn_value\n
- #~ s = name + TA_TYPE + type + TA_ARGLIST + args + TA_SCOPE + scope + TA_VARTYPE + return_value + '\n'
+ if inspect.isfunction(obj):
+ self._add_tag(obj, TYPE_FUNCTION)
+ elif inspect.isclass(obj):
+ self._add_tag(obj, TYPE_CLASS, self._get_superclass(obj))
+ methods = inspect.getmembers(obj, inspect.ismethod)
+ for m_name, m_obj in methods:
+ # skip non-public tags
+ if m_name.startswith('_') or not inspect.ismethod(m_obj):
+ continue
+ self._add_tag(m_obj, TYPE_FUNCTION, name)
+ else:
+ # plain regular expression based parsing
+ fp = open(filename)
+ for line in fp:
+ m = self.re_matcher.match(line)
+ if m:
+ tag_type_str, tagname, args = m.groups()
+ if not tagname or tagname.startswith('_'):
+ # skip non-public tags
+ continue;
+ if tag_type_str == 'class':
+ tag_type = TYPE_CLASS
+ else:
+ tag_type = TYPE_FUNCTION
+ args = args.strip()
+ tag = '%s%s%s%s%s\n' % (tagname, TA_TYPE, tag_type, TA_ARGLIST, args)
+ if not tagname in self.tags:
+ self.tags[tagname] = tag
+ fp.close()
#----------------------------------------------------------------------
- def tags_to_file(self, filename):
+ def write_to_file(self, filename):
"""
Sort the found tags and write them into the file specified by filename
@param filename (str)
"""
+ result = self.tags.values()
# sort the tags
- self.tags.sort()
+ result.sort()
# write them
fp = open(filename, 'wb')
fp.write(
'# format=tagmanager - Automatically generated file - do not edit (created on %s)\n' % \
datetime.datetime.now().ctime())
- for s in self.tags:
+ for s in result:
if not s == '\n': # skip empty lines
fp.write(s)
fp.close()
@@ -151,7 +238,7 @@
for filename in args:
parser.process_file(filename)
- parser.tags_to_file(tag_filename)
+ parser.write_to_file(tag_filename)
if __name__ == '__main__':
This was sent by the SourceForge.net collaborative development platform, the world's largest Open Source development site.
More information about the Commits
mailing list