summaryrefslogtreecommitdiff
path: root/giscanner
diff options
context:
space:
mode:
Diffstat (limited to 'giscanner')
-rw-r--r--giscanner/annotationmain.py1
-rw-r--r--giscanner/annotationparser.py556
-rw-r--r--giscanner/annotationpatterns.py808
-rw-r--r--giscanner/ast.py152
-rw-r--r--giscanner/cachestore.py21
-rw-r--r--giscanner/codegen.py12
-rw-r--r--giscanner/collections/__init__.py22
-rw-r--r--giscanner/collections/ordereddict.py120
-rw-r--r--giscanner/docmain.py26
-rw-r--r--giscanner/doctemplates/C/class.tmpl2
-rw-r--r--giscanner/doctemplates/C/constructor.tmpl1
-rw-r--r--giscanner/doctemplates/C/default.tmpl1
-rw-r--r--giscanner/doctemplates/C/enum.tmpl2
-rw-r--r--giscanner/doctemplates/C/function.tmpl61
-rw-r--r--giscanner/doctemplates/C/method.tmpl1
-rw-r--r--giscanner/doctemplates/C/namespace.tmpl1
-rw-r--r--giscanner/doctemplates/C/property.tmpl5
-rw-r--r--giscanner/doctemplates/C/record.tmpl1
-rw-r--r--giscanner/doctemplates/C/signal.tmpl5
-rw-r--r--giscanner/doctemplates/C/vfunc.tmpl4
-rw-r--r--giscanner/doctemplates/Gjs/class.tmpl18
-rw-r--r--giscanner/doctemplates/Gjs/constructor.tmpl1
-rw-r--r--giscanner/doctemplates/Gjs/default.tmpl1
-rw-r--r--giscanner/doctemplates/Gjs/enum.tmpl13
-rw-r--r--giscanner/doctemplates/Gjs/function.tmpl48
-rw-r--r--giscanner/doctemplates/Gjs/method.tmpl1
-rw-r--r--giscanner/doctemplates/Gjs/namespace.tmpl2
-rw-r--r--giscanner/doctemplates/Gjs/property.tmpl10
-rw-r--r--giscanner/doctemplates/Gjs/record.tmpl2
-rw-r--r--giscanner/doctemplates/Gjs/signal.tmpl37
-rw-r--r--giscanner/doctemplates/Gjs/vfunc.tmpl27
-rw-r--r--giscanner/doctemplates/Python/class.tmpl17
-rw-r--r--giscanner/doctemplates/Python/constructor.tmpl1
-rw-r--r--giscanner/doctemplates/Python/default.tmpl1
-rw-r--r--giscanner/doctemplates/Python/enum.tmpl13
-rw-r--r--giscanner/doctemplates/Python/function.tmpl53
-rw-r--r--giscanner/doctemplates/Python/method.tmpl1
-rw-r--r--giscanner/doctemplates/Python/namespace.tmpl2
-rw-r--r--giscanner/doctemplates/Python/property.tmpl10
-rw-r--r--giscanner/doctemplates/Python/record.tmpl2
-rw-r--r--giscanner/doctemplates/Python/signal.tmpl42
-rw-r--r--giscanner/doctemplates/Python/vfunc.tmpl33
-rw-r--r--giscanner/doctemplates/base.tmpl29
-rw-r--r--giscanner/doctemplates/class.tmpl40
-rw-r--r--giscanner/doctemplates/namespace.tmpl19
-rw-r--r--giscanner/docwriter.py644
-rw-r--r--giscanner/dumper.py117
-rw-r--r--giscanner/gdumpparser.py23
-rw-r--r--giscanner/girparser.py158
-rw-r--r--giscanner/girwriter.py81
-rw-r--r--giscanner/giscannermodule.c104
-rw-r--r--giscanner/introspectablepass.py56
-rw-r--r--giscanner/libtoolimporter.py2
-rw-r--r--giscanner/maintransformer.py247
-rw-r--r--giscanner/mallard-C-class.tmpl48
-rw-r--r--giscanner/mallard-C-default.tmpl11
-rw-r--r--giscanner/mallard-C-enum.tmpl12
-rw-r--r--giscanner/mallard-C-function.tmpl95
-rw-r--r--giscanner/mallard-C-namespace.tmpl19
-rw-r--r--giscanner/mallard-C-property.tmpl13
-rw-r--r--giscanner/mallard-C-record.tmpl12
-rw-r--r--giscanner/mallard-C-signal.tmpl13
-rw-r--r--giscanner/mallard-C-vfunc.tmpl35
-rw-r--r--giscanner/mallard-Python-class.tmpl66
-rw-r--r--giscanner/mallard-Python-default.tmpl11
-rw-r--r--giscanner/mallard-Python-enum.tmpl23
-rw-r--r--giscanner/mallard-Python-function.tmpl88
-rw-r--r--giscanner/mallard-Python-namespace.tmpl19
-rw-r--r--giscanner/mallard-Python-property.tmpl16
-rw-r--r--giscanner/mallard-Python-record.tmpl12
-rw-r--r--giscanner/mallard-Python-signal.tmpl52
-rw-r--r--giscanner/mallard-Python-vfunc.tmpl56
-rw-r--r--giscanner/mallardwriter.py392
-rw-r--r--giscanner/message.py18
-rw-r--r--giscanner/odict.py45
-rw-r--r--giscanner/scannerlexer.l36
-rwxr-xr-xgiscanner/scannermain.py102
-rw-r--r--giscanner/scannerparser.y11
-rw-r--r--giscanner/sectionparser.py152
-rw-r--r--giscanner/shlibs.py9
-rw-r--r--giscanner/sourcescanner.c25
-rw-r--r--giscanner/sourcescanner.h2
-rw-r--r--giscanner/sourcescanner.py35
-rw-r--r--giscanner/testcodegen.py3
-rw-r--r--giscanner/transformer.py177
-rw-r--r--giscanner/utils.py11
-rwxr-xr-xgiscanner/xmlwriter.py54
87 files changed, 2779 insertions, 2551 deletions
diff --git a/giscanner/annotationmain.py b/giscanner/annotationmain.py
index 4df6e831..304f5a32 100644
--- a/giscanner/annotationmain.py
+++ b/giscanner/annotationmain.py
@@ -26,6 +26,7 @@ from giscanner.scannermain import (get_preprocessor_option_group,
create_source_scanner,
process_packages)
+
def annotation_main(args):
parser = optparse.OptionParser('%prog [options] sources')
diff --git a/giscanner/annotationparser.py b/giscanner/annotationparser.py
index 2ac1b0eb..a0657dc4 100644
--- a/giscanner/annotationparser.py
+++ b/giscanner/annotationparser.py
@@ -20,18 +20,13 @@
#
-# AnnotationParser - extract annotations from gtk-doc comments
+# AnnotationParser - extract annotations from GTK-Doc comment blocks
import re
from . import message
-from .annotationpatterns import (COMMENT_START_RE, COMMENT_END_RE,
- COMMENT_ASTERISK_RE, EMPTY_LINE_RE,
- SECTION_RE, SYMBOL_RE, PROPERTY_RE, SIGNAL_RE,
- PARAMETER_RE, DESCRIPTION_TAG_RE, TAG_RE,
- MULTILINE_ANNOTATION_CONTINUATION_RE)
-from .odict import odict
+from .collections import OrderedDict
# GTK-Doc comment block parts
@@ -53,6 +48,7 @@ TAG_STABILITY = 'stability'
TAG_DEPRECATED = 'deprecated'
TAG_RETURNS = 'returns'
TAG_RETURNVALUE = 'return value'
+TAG_DESCRIPTION = 'description'
TAG_ATTRIBUTES = 'attributes'
TAG_RENAME_TO = 'rename to'
TAG_TYPE = 'type'
@@ -68,6 +64,7 @@ _ALL_TAGS = [TAG_VFUNC,
TAG_DEPRECATED,
TAG_RETURNS,
TAG_RETURNVALUE,
+ TAG_DESCRIPTION,
TAG_ATTRIBUTES,
TAG_RENAME_TO,
TAG_TYPE,
@@ -137,15 +134,254 @@ OPT_TRANSFER_FULL = 'full'
OPT_TRANSFER_FLOATING = 'floating'
+#The following regular expression programs are built to:
+# - match (or substitute) a single comment block line at a time;
+# - support (but remains untested) LOCALE and UNICODE modes.
+
+# Program matching the start of a comment block.
+#
+# Results in 0 symbolic groups.
+COMMENT_START_RE = re.compile(
+ r'''
+ ^ # start
+ [^\S\n\r]* # 0 or more whitespace characters
+ / # 1 forward slash character
+ \*{2} # exactly 2 asterisk characters
+ [^\S\n\r]* # 0 or more whitespace characters
+ $ # end
+ ''',
+ re.VERBOSE)
+
+# Program matching the end of a comment block. We need to take care
+# of comment ends that aren't on their own line for legacy support
+# reasons. See https://bugzilla.gnome.org/show_bug.cgi?id=689354
+#
+# Results in 1 symbolic group:
+# - group 1 = description
+COMMENT_END_RE = re.compile(
+ r'''
+ ^ # start
+ [^\S\n\r]* # 0 or more whitespace characters
+ (?P<description>.*?) # description text
+ [^\S\n\r]* # 0 or more whitespace characters
+ \*+ # 1 or more asterisk characters
+ / # 1 forward slash character
+ [^\S\n\r]* # 0 or more whitespace characters
+ $ # end
+ ''',
+ re.VERBOSE)
+
+# Program matching the ' * ' at the beginning of every
+# line inside a comment block.
+#
+# Results in 0 symbolic groups.
+COMMENT_ASTERISK_RE = re.compile(
+ r'''
+ ^ # start
+ [^\S\n\r]* # 0 or more whitespace characters
+ \* # 1 asterisk character
+ [^\S\n\r]? # 0 or 1 whitespace characters. Careful,
+ # removing more than 1 whitespace
+ # character would break embedded
+ # example program indentation
+ ''',
+ re.VERBOSE)
+
+# Program matching the indentation at the beginning of every
+# line (stripped from the ' * ') inside a comment block.
+#
+# Results in 1 symbolic group:
+# - group 1 = indentation
+COMMENT_INDENTATION_RE = re.compile(
+ r'''
+ ^
+ (?P<indentation>[^\S\n\r]*) # 0 or more whitespace characters
+ .*
+ $
+ ''',
+ re.VERBOSE)
+
+# Program matching an empty line.
+#
+# Results in 0 symbolic groups.
+EMPTY_LINE_RE = re.compile(
+ r'''
+ ^ # start
+ [^\S\n\r]* # 0 or more whitespace characters
+ $ # end
+ ''',
+ re.VERBOSE)
+
+# Program matching SECTION identifiers.
+#
+# Results in 2 symbolic groups:
+# - group 1 = colon
+# - group 2 = section_name
+SECTION_RE = re.compile(
+ r'''
+ ^ # start
+ [^\S\n\r]* # 0 or more whitespace characters
+ SECTION # SECTION
+ [^\S\n\r]* # 0 or more whitespace characters
+ (?P<colon>:?) # colon
+ [^\S\n\r]* # 0 or more whitespace characters
+ (?P<section_name>\w\S+)? # section name
+ [^\S\n\r]* # 0 or more whitespace characters
+ $
+ ''',
+ re.VERBOSE)
+
+# Program matching symbol (function, constant, struct and enum) identifiers.
+#
+# Results in 3 symbolic groups:
+# - group 1 = symbol_name
+# - group 2 = colon
+# - group 3 = annotations
+SYMBOL_RE = re.compile(
+ r'''
+ ^ # start
+ [^\S\n\r]* # 0 or more whitespace characters
+ (?P<symbol_name>[\w-]*\w) # symbol name
+ [^\S\n\r]* # 0 or more whitespace characters
+ (?P<colon>:?) # colon
+ [^\S\n\r]* # 0 or more whitespace characters
+ (?P<annotations>(?:\(.*?\)[^\S\n\r]*)*) # annotations
+ [^\S\n\r]* # 0 or more whitespace characters
+ $ # end
+ ''',
+ re.VERBOSE)
+
+# Program matching property identifiers.
+#
+# Results in 4 symbolic groups:
+# - group 1 = class_name
+# - group 2 = property_name
+# - group 3 = colon
+# - group 4 = annotations
+PROPERTY_RE = re.compile(
+ r'''
+ ^ # start
+ [^\S\n\r]* # 0 or more whitespace characters
+ (?P<class_name>[\w]+) # class name
+ [^\S\n\r]* # 0 or more whitespace characters
+ :{1} # required colon
+ [^\S\n\r]* # 0 or more whitespace characters
+ (?P<property_name>[\w-]*\w) # property name
+ [^\S\n\r]* # 0 or more whitespace characters
+ (?P<colon>:?) # colon
+ [^\S\n\r]* # 0 or more whitespace characters
+ (?P<annotations>(?:\(.*?\)[^\S\n\r]*)*) # annotations
+ [^\S\n\r]* # 0 or more whitespace characters
+ $ # end
+ ''',
+ re.VERBOSE)
+
+# Program matching signal identifiers.
+#
+# Results in 4 symbolic groups:
+# - group 1 = class_name
+# - group 2 = signal_name
+# - group 3 = colon
+# - group 4 = annotations
+SIGNAL_RE = re.compile(
+ r'''
+ ^ # start
+ [^\S\n\r]* # 0 or more whitespace characters
+ (?P<class_name>[\w]+) # class name
+ [^\S\n\r]* # 0 or more whitespace characters
+ :{2} # 2 required colons
+ [^\S\n\r]* # 0 or more whitespace characters
+ (?P<signal_name>[\w-]*\w) # signal name
+ [^\S\n\r]* # 0 or more whitespace characters
+ (?P<colon>:?) # colon
+ [^\S\n\r]* # 0 or more whitespace characters
+ (?P<annotations>(?:\(.*?\)[^\S\n\r]*)*) # annotations
+ [^\S\n\r]* # 0 or more whitespace characters
+ $ # end
+ ''',
+ re.VERBOSE)
+
+# Program matching parameters.
+#
+# Results in 4 symbolic groups:
+# - group 1 = parameter_name
+# - group 2 = annotations
+# - group 3 = colon
+# - group 4 = description
+PARAMETER_RE = re.compile(
+ r'''
+ ^ # start
+ [^\S\n\r]* # 0 or more whitespace characters
+ @ # @ character
+ (?P<parameter_name>[\w-]*\w|\.\.\.) # parameter name
+ [^\S\n\r]* # 0 or more whitespace characters
+ :{1} # required colon
+ [^\S\n\r]* # 0 or more whitespace characters
+ (?P<annotations>(?:\(.*?\)[^\S\n\r]*)*) # annotations
+ (?P<colon>:?) # colon
+ [^\S\n\r]* # 0 or more whitespace characters
+ (?P<description>.*?) # description
+ [^\S\n\r]* # 0 or more whitespace characters
+ $ # end
+ ''',
+ re.VERBOSE)
+
+# Program matching tags.
+#
+# Results in 4 symbolic groups:
+# - group 1 = tag_name
+# - group 2 = annotations
+# - group 3 = colon
+# - group 4 = description
+_all_tags = '|'.join(_ALL_TAGS).replace(' ', '\\ ')
+TAG_RE = re.compile(
+ r'''
+ ^ # start
+ [^\S\n\r]* # 0 or more whitespace characters
+ (?P<tag_name>''' + _all_tags + r''') # tag name
+ [^\S\n\r]* # 0 or more whitespace characters
+ :{1} # required colon
+ [^\S\n\r]* # 0 or more whitespace characters
+ (?P<annotations>(?:\(.*?\)[^\S\n\r]*)*) # annotations
+ (?P<colon>:?) # colon
+ [^\S\n\r]* # 0 or more whitespace characters
+ (?P<description>.*?) # description
+ [^\S\n\r]* # 0 or more whitespace characters
+ $ # end
+ ''',
+ re.VERBOSE | re.IGNORECASE)
+
+# Program matching multiline annotation continuations.
+# This is used on multiline parameters and tags (but not on the first line) to
+# generate warnings about invalid annotations spanning multiple lines.
+#
+# Results in 3 symbolic groups:
+# - group 2 = annotations
+# - group 3 = colon
+# - group 4 = description
+MULTILINE_ANNOTATION_CONTINUATION_RE = re.compile(
+ r'''
+ ^ # start
+ [^\S\n\r]* # 0 or more whitespace characters
+ (?P<annotations>(?:\(.*?\)[^\S\n\r]*)*) # annotations
+ (?P<colon>:) # colon
+ [^\S\n\r]* # 0 or more whitespace characters
+ (?P<description>.*?) # description
+ [^\S\n\r]* # 0 or more whitespace characters
+ $ # end
+ ''',
+ re.VERBOSE)
+
+
class DocBlock(object):
def __init__(self, name):
self.name = name
self.options = DocOptions()
self.value = None
- self.tags = odict()
+ self.tags = OrderedDict()
self.comment = None
- self.params = odict()
+ self.params = OrderedDict()
self.position = None
def __cmp__(self, other):
@@ -154,16 +390,6 @@ class DocBlock(object):
def __repr__(self):
return '<DocBlock %r %r>' % (self.name, self.options)
- def set_position(self, position):
- self.position = position
- self.options.position = position
-
- def get_tag(self, name):
- return self.tags.get(name)
-
- def get_param(self, name):
- return self.params.get(name)
-
def to_gtk_doc(self):
options = ''
if self.options:
@@ -175,9 +401,10 @@ class DocBlock(object):
lines[0] += options
for param in self.params.values():
lines.append(param.to_gtk_doc_param())
- lines.append('')
- for l in self.comment.split('\n'):
- lines.append(l)
+ if self.comment:
+ lines.append('')
+ for l in self.comment.split('\n'):
+ lines.append(l)
if self.tags:
lines.append('')
for tag in self.tags.values():
@@ -229,8 +456,8 @@ class DocTag(object):
s = 'one value'
else:
s = '%d values' % (n_params, )
- if ((n_params > 0 and (value is None or value.length() != n_params)) or
- n_params == 0 and value is not None):
+ if ((n_params > 0 and (value is None or value.length() != n_params))
+ or n_params == 0 and value is not None):
if value is None:
length = 0
else:
@@ -250,7 +477,7 @@ class DocTag(object):
if value is None:
return
- for name, v in value.all().iteritems():
+ for name, v in value.all().items():
if name in [OPT_ARRAY_ZERO_TERMINATED, OPT_ARRAY_FIXED_SIZE]:
try:
int(v)
@@ -279,7 +506,7 @@ class DocTag(object):
if value is not None and value.length() > 1:
message.warn(
'closure takes at most 1 value, %d given' % (
- value.length()), self.position)
+ value.length(), ), self.position)
def _validate_element_type(self, option, value):
self._validate_option(option, value, required=True)
@@ -291,7 +518,7 @@ class DocTag(object):
if value.length() > 2:
message.warn(
'element-type takes at most 2 values, %d given' % (
- value.length()), self.position)
+ value.length(), ), self.position)
return
def _validate_out(self, option, value):
@@ -300,30 +527,26 @@ class DocTag(object):
if value.length() > 1:
message.warn(
'out annotation takes at most 1 value, %d given' % (
- value.length()), self.position)
+ value.length(), ), self.position)
return
value_str = value.one()
if value_str not in [OPT_OUT_CALLEE_ALLOCATES,
OPT_OUT_CALLER_ALLOCATES]:
message.warn("out annotation value is invalid: %r" % (
- value_str), self.position)
+ value_str, ), self.position)
return
- def set_position(self, position):
- self.position = position
- self.options.position = position
-
def _get_gtk_doc_value(self):
def serialize_one(option, value, fmt, fmt2):
if value:
if type(value) != str:
value = ' '.join((serialize_one(k, v, '%s=%s', '%s')
- for k, v in value.all().iteritems()))
+ for k, v in value.all().items()))
return fmt % (option, value)
else:
return fmt2 % (option, )
annotations = []
- for option, value in self.options.iteritems():
+ for option, value in self.options.items():
annotations.append(
serialize_one(option, value, '(%s %s)', '(%s)'))
if annotations:
@@ -345,8 +568,7 @@ class DocTag(object):
# validation below is most certainly going to fail.
return
- for option in self.options:
- value = self.options[option]
+ for option, value in self.options.items():
if option == OPT_ALLOW_NONE:
self._validate_option(option, value, n_params=0)
elif option == OPT_ARRAY:
@@ -399,6 +621,7 @@ class DocTag(object):
class DocOptions(object):
def __init__(self):
self.values = []
+ self.position = None
def __repr__(self):
return '<DocOptions %r>' % (self.values, )
@@ -429,7 +652,7 @@ class DocOptions(object):
if key == item:
yield value
- def iteritems(self):
+ def items(self):
return iter(self.values)
@@ -438,7 +661,7 @@ class DocOption(object):
def __init__(self, tag, option):
self.tag = tag
self._array = []
- self._dict = {}
+ self._dict = OrderedDict()
# (annotation option1=value1 option2=value2) etc
for p in option.split(' '):
if '=' in p:
@@ -477,7 +700,8 @@ class AnnotationParser(object):
:class:`DocTag`, :class:`DocOptions` and :class:`DocOption` objects. This
parser tries to accept malformed input whenever possible and does not emit
syntax errors. However, it does emit warnings at the slightest indication
- of malformed input when possible.
+ of malformed input when possible. It is usually a good idea to heed these
+ warnings as malformed input is known to result in invalid GTK-Doc output.
A GTK-Doc comment block can be constructed out of multiple parts that can
be combined to write different types of documentation.
@@ -489,42 +713,52 @@ class AnnotationParser(object):
.. code-block:: c
/**
- * identifier_name: [annotations]
- * @parameter_name: [annotations:] [description]
+ * identifier_name [:annotations]
+ * @parameter_name [:annotations] [:description]
*
- * description
- * tag_name: [annotations:] [description]
+ * comment_block_description
+ * tag_name [:annotations] [:description]
*/
- - Parts and fields cannot span multiple lines, except for parameter descriptions,
- tag descriptions and comment block descriptions.
- - There has to be exactly 1 `identifier` part on the first line of the
- comment block which consists of:
- * an `identifier_name` field
- * an optional `annotations` field
- - There can be 0 or more `parameter` parts following the `identifier` part,
- each consisting of:
- * a `parameter_name` filed
- * an optional `annotations` field
- * an optional `description` field
- - An empty lines signals the end of the `parameter` parts and the beginning
- of the (free form) comment block `description` part.
- - There can be 0 or 1 `description` parts following the `description` part.
- - There can be 0 or more `tag` parts following the `description` part,
- each consisting of:
- * a `tag_name` field
- * an optional `annotations` field
- * an optional `description` field
+ The order in which the different parts have to be specified is important::
+
+ - There has to be exactly 1 `identifier` part on the first line of the
+ comment block which consists of:
+ * an `identifier_name` field
+ * an optional `annotations` field
+ - Followed by 0 or more `parameters` parts, each consisting of:
+ * a `parameter_name` field
+ * an optional `annotations` field
+ * an optional `description` field
+ - Followed by at least 1 empty line signaling the beginning of
+ the `comment_block_description` part
+ - Followed by an optional `comment block description` part.
+ - Followed by 0 or more `tag` parts, each consisting of:
+ * a `tag_name` field
+ * an optional `annotations` field
+ * an optional `description` field
+
+ Additionally, the following restrictions are in effect::
+
+ - Parts can optionally be separated by an empty line, except between
+ the `parameter` parts and the `comment block description` part where
+ an empty line is required (see above).
+ - Parts and fields cannot span multiple lines, except for
+ `parameter descriptions`, `tag descriptions` and the
+ `comment_block_description` fields.
+ - `parameter descriptions` fields can not span multiple paragraphs.
+ - `tag descriptions` and `comment block description` fields can
+ span multiple paragraphs.
.. NOTE:: :class:`AnnotationParser` functionality is heavily based on gtkdoc-mkdb's
- `ScanSourceFile()`_ function and is currently in sync with gtk-doc
- commit `b41641b`_.
+ `ScanSourceFile()`_ function and is currently in sync with GTK-Doc
+ commit `47abcd5`_.
- .. _types of documentation:
+ .. _GTK-Doc's documentation:
http://developer.gnome.org/gtk-doc-manual/1.18/documenting.html.en
.. _ScanSourceFile():
http://git.gnome.org/browse/gtk-doc/tree/gtkdoc-mkdb.in#n3722
- .. _b41641b: b41641bd75f870afff7561ceed8a08456da57565
+ .. _47abcd5: 47abcd53b8489ebceec9e394676512a181c1f1f6
"""
def parse(self, comments):
@@ -532,22 +766,32 @@ class AnnotationParser(object):
Parses multiple GTK-Doc comment blocks.
:param comments: a list of (comment, filename, lineno) tuples
- :returns: a list of :class:`DocBlock` or ``None`` objects
+ :returns: a dictionary mapping identifier names to :class:`DocBlock` objects
"""
comment_blocks = {}
for comment in comments:
- comment_block = self.parse_comment_block(comment)
+ try:
+ comment_block = self.parse_comment_block(comment)
+ except Exception:
+ message.warn('unrecoverable parse error, please file a GObject-Introspection '
+ 'bug report including the complete comment block at the '
+ 'indicated location.', message.Position(comment[1], comment[2]))
+ continue
if comment_block is not None:
+ # Note: previous versions of this parser did not check
+ # if an identifier was already stored in comment_blocks,
+ # so when multiple comment blocks where encountered documenting
+ # the same identifier the last one seen "wins".
+ # Keep this behavior for backwards compatibility, but
+ # emit a warning.
if comment_block.name in comment_blocks:
message.warn("multiple comment blocks documenting '%s:' identifier." %
- (comment_block.name),
+ (comment_block.name, ),
comment_block.position)
- # Always store the block even if it's a duplicate for
- # backward compatibility...
comment_blocks[comment_block.name] = comment_block
return comment_blocks
@@ -561,29 +805,48 @@ class AnnotationParser(object):
"""
comment, filename, lineno = comment
+
+ # Assign line numbers to each line of the comment block,
+ # which will later be used as the offset to calculate the
+ # real line number in the source file
comment_lines = list(enumerate(comment.split('\n')))
# Check for the start the comment block.
- if COMMENT_START_RE.search(comment_lines[0][1]):
+ if COMMENT_START_RE.match(comment_lines[0][1]):
del comment_lines[0]
else:
# Not a GTK-Doc comment block.
return None
# Check for the end the comment block.
- if COMMENT_END_RE.search(comment_lines[-1][1]):
- del comment_lines[-1]
+ line_offset, line = comment_lines[-1]
+ result = COMMENT_END_RE.match(line)
+ if result:
+ description = result.group('description')
+ if description:
+ comment_lines[-1] = (line_offset, description)
+ position = message.Position(filename, lineno + line_offset)
+ marker = ' ' * result.end('description') + '^'
+ message.warn("Comments should end with */ on a new line:\n%s\n%s" %
+ (line, marker),
+ position)
+ else:
+ del comment_lines[-1]
+ else:
+ # Not a GTK-Doc comment block.
+ return None
# If we get this far, we are inside a GTK-Doc comment block.
return self._parse_comment_block(comment_lines, filename, lineno)
def _parse_comment_block(self, comment_lines, filename, lineno):
"""
- Parses a single GTK-Doc comment block stripped from it's
+ Parses a single GTK-Doc comment block already stripped from its
comment start (/**) and comment end (*/) marker lines.
- :param comment_lines: GTK-Doc comment block stripped from it's comment
- start (/**) and comment end (*/) marker lines
+ :param comment_lines: list of (line_offset, line) tuples representing a
+ GTK-Doc comment block already stripped from it's
+ start (/**) and end (*/) marker lines
:param filename: source file name where the comment block originated from
:param lineno: line in the source file where the comment block starts
:returns: a :class:`DocBlock` object or ``None``
@@ -601,6 +864,8 @@ class AnnotationParser(object):
http://git.gnome.org/browse/gtk-doc/tree/gtkdoc-mkdb.in#n3722
"""
comment_block = None
+ part_indent = None
+ line_indent = None
in_part = None
identifier = None
current_param = None
@@ -621,61 +886,55 @@ class AnnotationParser(object):
column_offset = result.end(0)
line = line[result.end(0):]
+ # Store indentation level of the line.
+ result = COMMENT_INDENTATION_RE.match(line)
+ line_indent = len(result.group('indentation').replace('\t', ' '))
+
####################################################################
# Check for GTK-Doc comment block identifier.
####################################################################
if not comment_block:
- # The correct identifier name would have the colon at the end
- # but maintransformer.py does not expect us to do that. So
- # make sure to compute an identifier_name without the colon and
- # a real_identifier_name with the colon.
-
if not identifier:
- result = SECTION_RE.search(line)
+ result = SECTION_RE.match(line)
if result:
identifier = IDENTIFIER_SECTION
- real_identifier_name = 'SECTION:%s' % (result.group('section_name'))
- identifier_name = real_identifier_name
+ identifier_name = 'SECTION:%s' % (result.group('section_name'), )
column = result.start('section_name') + column_offset
if not identifier:
- result = SYMBOL_RE.search(line)
+ result = SYMBOL_RE.match(line)
if result:
identifier = IDENTIFIER_SYMBOL
- real_identifier_name = '%s:' % (result.group('symbol_name'))
- identifier_name = '%s' % (result.group('symbol_name'))
+ identifier_name = '%s' % (result.group('symbol_name'), )
column = result.start('symbol_name') + column_offset
if not identifier:
- result = PROPERTY_RE.search(line)
+ result = PROPERTY_RE.match(line)
if result:
identifier = IDENTIFIER_PROPERTY
- real_identifier_name = '%s:%s:' % (result.group('class_name'),
- result.group('property_name'))
identifier_name = '%s:%s' % (result.group('class_name'),
result.group('property_name'))
column = result.start('property_name') + column_offset
if not identifier:
- result = SIGNAL_RE.search(line)
+ result = SIGNAL_RE.match(line)
if result:
identifier = IDENTIFIER_SIGNAL
- real_identifier_name = '%s::%s:' % (result.group('class_name'),
- result.group('signal_name'))
identifier_name = '%s::%s' % (result.group('class_name'),
result.group('signal_name'))
column = result.start('signal_name') + column_offset
if identifier:
in_part = PART_IDENTIFIER
+ part_indent = line_indent
comment_block = DocBlock(identifier_name)
- comment_block.set_position(position)
+ comment_block.position = position
if 'colon' in result.groupdict() and result.group('colon') != ':':
colon_start = result.start('colon')
colon_column = column_offset + colon_start
- marker = ' '*colon_column + '^'
+ marker = ' ' * colon_column + '^'
message.warn("missing ':' at column %s:\n%s\n%s" %
(colon_column + 1, original_line, marker),
position)
@@ -695,7 +954,7 @@ class AnnotationParser(object):
# right thing to do because sooner or later some long
# descriptions will contain something matching an identifier
# pattern by accident.
- marker = ' '*column_offset + '^'
+ marker = ' ' * column_offset + '^'
message.warn('ignoring unrecognized GTK-Doc comment block, identifier not '
'found:\n%s\n%s' % (original_line, marker),
position)
@@ -705,7 +964,7 @@ class AnnotationParser(object):
####################################################################
# Check for comment block parameters.
####################################################################
- result = PARAMETER_RE.search(line)
+ result = PARAMETER_RE.match(line)
if result:
param_name = result.group('parameter_name')
param_annotations = result.group('annotations')
@@ -714,9 +973,11 @@ class AnnotationParser(object):
if in_part == PART_IDENTIFIER:
in_part = PART_PARAMETERS
+ part_indent = line_indent
+
if in_part != PART_PARAMETERS:
column = result.start('parameter_name') + column_offset
- marker = ' '*column + '^'
+ marker = ' ' * column + '^'
message.warn("'@%s' parameter unexpected at this location:\n%s\n%s" %
(param_name, original_line, marker),
position)
@@ -730,17 +991,17 @@ class AnnotationParser(object):
returns_seen = True
else:
message.warn("encountered multiple 'Returns' parameters or tags for "
- "'%s'." % (comment_block.name),
+ "'%s'." % (comment_block.name, ),
position)
elif param_name in comment_block.params.keys():
column = result.start('parameter_name') + column_offset
- marker = ' '*column + '^'
+ marker = ' ' * column + '^'
message.warn("multiple '@%s' parameters for identifier '%s':\n%s\n%s" %
(param_name, comment_block.name, original_line, marker),
position)
tag = DocTag(comment_block, param_name)
- tag.set_position(position)
+ tag.position = position
tag.comment = param_description
if param_annotations:
tag.options = self.parse_options(tag, param_annotations)
@@ -756,28 +1017,48 @@ class AnnotationParser(object):
#
# When we are parsing comment block parameters or the comment block
# identifier (when there are no parameters) and encounter an empty
- # line, we must be parsing the comment block description
+ # line, we must be parsing the comment block description.
####################################################################
- if (EMPTY_LINE_RE.search(line)
- and (in_part == PART_IDENTIFIER or in_part == PART_PARAMETERS)):
+ if (EMPTY_LINE_RE.match(line) and in_part in [PART_IDENTIFIER, PART_PARAMETERS]):
in_part = PART_DESCRIPTION
+ part_indent = line_indent
continue
####################################################################
# Check for GTK-Doc comment block tags.
####################################################################
- result = TAG_RE.search(line)
- if result:
+ result = TAG_RE.match(line)
+ if result and line_indent <= part_indent:
tag_name = result.group('tag_name')
tag_annotations = result.group('annotations')
tag_description = result.group('description')
+ marker = ' ' * (result.start('tag_name') + column_offset) + '^'
+
+ # Deprecated GTK-Doc Description: tag
+ if tag_name.lower() == TAG_DESCRIPTION:
+ message.warn("GTK-Doc tag \"Description:\" has been deprecated:\n%s\n%s" %
+ (original_line, marker),
+ position)
+
+ in_part = PART_DESCRIPTION
+ part_indent = line_indent
+
+ if not comment_block.comment:
+ comment_block.comment = tag_description
+ else:
+ comment_block.comment += '\n' + tag_description
+ continue
+
+ # Now that the deprecated stuff is out of the way, continue parsing real tags
if in_part == PART_DESCRIPTION:
in_part = PART_TAGS
+ part_indent = line_indent
+
if in_part != PART_TAGS:
column = result.start('tag_name') + column_offset
- marker = ' '*column + '^'
+ marker = ' ' * column + '^'
message.warn("'%s:' tag unexpected at this location:\n%s\n%s" %
(tag_name, original_line, marker),
position)
@@ -787,7 +1068,7 @@ class AnnotationParser(object):
returns_seen = True
else:
message.warn("encountered multiple 'Returns' parameters or tags for "
- "'%s'." % (comment_block.name),
+ "'%s'." % (comment_block.name, ),
position)
tag = DocTag(comment_block, TAG_RETURNS)
@@ -801,7 +1082,7 @@ class AnnotationParser(object):
else:
if tag_name.lower() in comment_block.tags.keys():
column = result.start('tag_name') + column_offset
- marker = ' '*column + '^'
+ marker = ' ' * column + '^'
message.warn("multiple '%s:' tags for identifier '%s':\n%s\n%s" %
(tag_name, comment_block.name, original_line, marker),
position)
@@ -814,7 +1095,7 @@ class AnnotationParser(object):
tag.options = self.parse_options(tag, tag_annotations)
else:
message.warn("annotations not supported for tag '%s:'." %
- (tag_name),
+ (tag_name, ),
position)
comment_block.tags[tag_name.lower()] = tag
current_tag = tag
@@ -824,12 +1105,8 @@ class AnnotationParser(object):
# If we get here, we must be in the middle of a multiline
# comment block, parameter or tag description.
####################################################################
- if in_part == PART_DESCRIPTION or in_part == PART_IDENTIFIER:
+ if in_part in [PART_IDENTIFIER, PART_DESCRIPTION]:
if not comment_block.comment:
- # Backwards compatibility with old style GTK-Doc
- # comment blocks where Description used to be a comment
- # block tag. Simply get rid of 'Description:'.
- line = re.sub(DESCRIPTION_TAG_RE, '', line)
comment_block.comment = line
else:
comment_block.comment += '\n' + line
@@ -837,38 +1114,39 @@ class AnnotationParser(object):
elif in_part == PART_PARAMETERS:
self._validate_multiline_annotation_continuation(line, original_line,
column_offset, position)
-
# Append to parameter description.
current_param.comment += ' ' + line.strip()
+ continue
elif in_part == PART_TAGS:
self._validate_multiline_annotation_continuation(line, original_line,
column_offset, position)
-
# Append to tag description.
if current_tag.name.lower() in [TAG_RETURNS, TAG_RETURNVALUE]:
current_tag.comment += ' ' + line.strip()
else:
current_tag.value += ' ' + line.strip()
+ continue
########################################################################
# Finished parsing this comment block.
########################################################################
- # We have picked up a couple of \n characters that where not
- # intended. Strip those.
- if comment_block.comment:
- comment_block.comment = comment_block.comment.strip()
- else:
- comment_block.comment = ''
+ if comment_block:
+ # We have picked up a couple of \n characters that where not
+ # intended. Strip those.
+ if comment_block.comment:
+ comment_block.comment = comment_block.comment.strip()
- for tag in comment_block.tags.itervalues():
- self._clean_comment_block_part(tag)
+ for tag in comment_block.tags.values():
+ self._clean_comment_block_part(tag)
- for param in comment_block.params.itervalues():
- self._clean_comment_block_part(param)
+ for param in comment_block.params.values():
+ self._clean_comment_block_part(param)
- # Validate and store block.
- comment_block.validate()
- return comment_block
+ # Validate and store block.
+ comment_block.validate()
+ return comment_block
+ else:
+ return None
def _clean_comment_block_part(self, part):
if part.comment:
@@ -882,7 +1160,7 @@ class AnnotationParser(object):
part.value = ''
def _validate_multiline_annotation_continuation(self, line, original_line,
- column_offset, position):
+ column_offset, position):
'''
Validate parameters and tags (except the first line) and generate
warnings about invalid annotations spanning multiple lines.
@@ -893,26 +1171,26 @@ class AnnotationParser(object):
:param position: position of `line` in the source file
'''
- result = MULTILINE_ANNOTATION_CONTINUATION_RE.search(line)
+ result = MULTILINE_ANNOTATION_CONTINUATION_RE.match(line)
if result:
- line = result.group('description')
column = result.start('annotations') + column_offset
- marker = ' '*column + '^'
+ marker = ' ' * column + '^'
message.warn('ignoring invalid multiline annotation continuation:\n'
'%s\n%s' % (original_line, marker),
position)
@classmethod
def parse_options(cls, tag, value):
- # (foo)
- # (bar opt1 opt2 ...)
+ # (annotation)
+ # (annotation opt1 opt2 ...)
+ # (annotation opt1=value1 opt2=value2 ...)
opened = -1
options = DocOptions()
options.position = tag.position
for i, c in enumerate(value):
if c == '(' and opened == -1:
- opened = i+1
+ opened = i + 1
if c == ')' and opened != -1:
segment = value[opened:i]
parts = segment.split(' ', 1)
diff --git a/giscanner/annotationpatterns.py b/giscanner/annotationpatterns.py
deleted file mode 100644
index a4030051..00000000
--- a/giscanner/annotationpatterns.py
+++ /dev/null
@@ -1,808 +0,0 @@
-# -*- Mode: Python -*-
-# GObject-Introspection - a framework for introspecting GObject libraries
-# Copyright (C) 2012 Dieter Verfaillie <dieterv@optionexplicit.be>
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301, USA.
-#
-
-
-'''
-This module provides regular expression programs that can be used to identify
-and extract useful information from different parts of GTK-Doc comment blocks.
-These programs are built to:
- - match (or substitute) a single comment block line at a time;
- - support MULTILINE mode and should support (but remains untested)
- LOCALE and UNICODE modes.
-'''
-
-
-import re
-
-
-# Program matching the start of a comment block.
-#
-# Results in 0 symbolic groups.
-COMMENT_START_RE = re.compile(r'''
- ^ # start
- [^\S\n\r]* # 0 or more whitespace characters
- / # 1 forward slash character
- \*{2} # exactly 2 asterisk characters
- [^\S\n\r]* # 0 or more whitespace characters
- $ # end
- ''',
- re.VERBOSE | re.MULTILINE)
-
-# Program matching the end of a comment block.
-#
-# Results in 0 symbolic groups.
-COMMENT_END_RE = re.compile(r'''
- ^ # start
- [^\S\n\r]* # 0 or more whitespace characters
- \*+ # 1 or more asterisk characters
- / # 1 forward slash character
- $ # end
- ''',
- re.VERBOSE | re.MULTILINE)
-
-# Program matching the "*" at the beginning of every
-# line inside a comment block.
-#
-# Results in 0 symbolic groups.
-COMMENT_ASTERISK_RE = re.compile(r'''
- ^ # start
- [^\S\n\r]* # 0 or more whitespace characters
- \* # 1 asterisk character
- [^\S\n\r]? # 0 or 1 whitespace characters
- # Carefull: removing more would
- # break embedded example program
- # indentation
- ''',
- re.VERBOSE)
-
-# Program matching an empty line.
-#
-# Results in 0 symbolic groups.
-EMPTY_LINE_RE = re.compile(r'''
- ^ # start
- [^\S\n\r]* # 0 or 1 whitespace characters
- $ # end
- ''',
- re.VERBOSE | re.MULTILINE)
-
-# Program matching SECTION identifiers.
-#
-# Results in 2 symbolic groups:
-# - group 1 = colon
-# - group 2 = section_name
-SECTION_RE = re.compile(r'''
- ^ # start
- [^\S\n\r]* # 0 or more whitespace characters
- SECTION # SECTION
- [^\S\n\r]* # 0 or more whitespace characters
- (?P<colon>:?) # colon
- [^\S\n\r]* # 0 or more whitespace characters
- (?P<section_name>\w\S+)? # section name
- [^\S\n\r]* # 0 or more whitespace characters
- $
- ''',
- re.VERBOSE | re.MULTILINE)
-
-# Program matching symbol (function, constant, struct and enum) identifiers.
-#
-# Results in 3 symbolic groups:
-# - group 1 = symbol_name
-# - group 2 = colon
-# - group 3 = annotations
-SYMBOL_RE = re.compile(r'''
- ^ # start
- [^\S\n\r]* # 0 or more whitespace characters
- (?P<symbol_name>[\w-]*\w) # symbol name
- [^\S\n\r]* # 0 or more whitespace characters
- (?P<colon>:?) # colon
- [^\S\n\r]* # 0 or more whitespace characters
- (?P<annotations>(?:\(.*?\)[^\S\n\r]*)*) # annotations
- [^\S\n\r]* # 0 or more whitespace characters
- $ # end
- ''',
- re.VERBOSE | re.MULTILINE)
-
-# Program matching property identifiers.
-#
-# Results in 4 symbolic groups:
-# - group 1 = class_name
-# - group 2 = property_name
-# - group 3 = colon
-# - group 4 = annotations
-PROPERTY_RE = re.compile(r'''
- ^ # start
- [^\S\n\r]* # 0 or more whitespace characters
- (?P<class_name>[\w]+) # class name
- [^\S\n\r]* # 0 or more whitespace characters
- :{1} # required colon
- [^\S\n\r]* # 0 or more whitespace characters
- (?P<property_name>[\w-]*\w) # property name
- [^\S\n\r]* # 0 or more whitespace characters
- (?P<colon>:?) # colon
- [^\S\n\r]* # 0 or more whitespace characters
- (?P<annotations>(?:\(.*?\)[^\S\n\r]*)*) # annotations
- [^\S\n\r]* # 0 or more whitespace characters
- $ # end
- ''',
- re.VERBOSE | re.MULTILINE)
-
-# Program matching signal identifiers.
-#
-# Results in 4 symbolic groups:
-# - group 1 = class_name
-# - group 2 = signal_name
-# - group 3 = colon
-# - group 4 = annotations
-SIGNAL_RE = re.compile(r'''
- ^ # start
- [^\S\n\r]* # 0 or more whitespace characters
- (?P<class_name>[\w]+) # class name
- [^\S\n\r]* # 0 or more whitespace characters
- :{2} # 2 required colons
- [^\S\n\r]* # 0 or more whitespace characters
- (?P<signal_name>[\w-]*\w) # signal name
- [^\S\n\r]* # 0 or more whitespace characters
- (?P<colon>:?) # colon
- [^\S\n\r]* # 0 or more whitespace characters
- (?P<annotations>(?:\(.*?\)[^\S\n\r]*)*) # annotations
- [^\S\n\r]* # 0 or more whitespace characters
- $ # end
- ''',
- re.VERBOSE | re.MULTILINE)
-
-# Program matching parameters.
-#
-# Results in 4 symbolic groups:
-# - group 1 = parameter_name
-# - group 2 = annotations
-# - group 3 = colon
-# - group 4 = description
-PARAMETER_RE = re.compile(r'''
- ^ # start
- [^\S\n\r]* # 0 or more whitespace characters
- @ # @ character
- (?P<parameter_name>[\w-]*\w|\.\.\.) # parameter name
- [^\S\n\r]* # 0 or more whitespace characters
- :{1} # required colon
- [^\S\n\r]* # 0 or more whitespace characters
- (?P<annotations>(?:\(.*?\)[^\S\n\r]*)*) # annotations
- (?P<colon>:?) # colon
- [^\S\n\r]* # 0 or more whitespace characters
- (?P<description>.*?) # description
- [^\S\n\r]* # 0 or more whitespace characters
- $ # end
- ''',
- re.VERBOSE | re.MULTILINE)
-
-# Program matching old style "Description:" tag.
-#
-# Results in 0 symbolic groups.
-DESCRIPTION_TAG_RE = re.compile(r'''
- ^ # start
- [^\S\n\r]* # 0 or more whitespace characters
- Description: # 'Description:' literal
- ''',
- re.VERBOSE | re.MULTILINE)
-
-# Program matching tags.
-#
-# Results in 4 symbolic groups:
-# - group 1 = tag_name
-# - group 2 = annotations
-# - group 3 = colon
-# - group 4 = description
-TAG_RE = re.compile(r'''
- ^ # start
- [^\S\n\r]* # 0 or more whitespace characters
- (?P<tag_name>virtual|since|stability|
- deprecated|returns|
- return\ value|attributes|
- rename\ to|type|
- unref\ func|ref\ func|
- set\ value\ func|
- get\ value\ func|
- transfer|value) # tag name
- [^\S\n\r]* # 0 or more whitespace characters
- :{1} # required colon
- [^\S\n\r]* # 0 or more whitespace characters
- (?P<annotations>(?:\(.*?\)[^\S\n\r]*)*) # annotations
- (?P<colon>:?) # colon
- [^\S\n\r]* # 0 or more whitespace characters
- (?P<description>.*?) # description
- [^\S\n\r]* # 0 or more whitespace characters
- $ # end
- ''',
- re.VERBOSE | re.MULTILINE | re.IGNORECASE)
-
-# Program matching multiline annotation continuations.
-# This is used on multiline parameters and tags (but not on the first line) to
-# generate warnings about invalid annotations spanning multiple lines.
-#
-# Results in 3 symbolic groups:
-# - group 2 = annotations
-# - group 3 = colon
-# - group 4 = description
-MULTILINE_ANNOTATION_CONTINUATION_RE = re.compile(r'''
- ^ # start
- [^\S\n\r]* # 0 or more whitespace characters
- (?P<annotations>(?:\(.*?\)[^\S\n\r]*)*) # annotations
- (?P<colon>:) # colon
- [^\S\n\r]* # 0 or more whitespace characters
- (?P<description>.*?) # description
- [^\S\n\r]* # 0 or more whitespace characters
- $ # end
- ''',
- re.VERBOSE | re.MULTILINE)
-
-
-if __name__ == '__main__':
- import unittest
-
- identifier_section_tests = [
- (SECTION_RE, 'TSIEOCN',
- None),
- (SECTION_RE, 'section',
- None),
- (SECTION_RE, 'section:',
- None),
- (SECTION_RE, 'section:test',
- None),
- (SECTION_RE, 'SECTION',
- {'colon': '',
- 'section_name': None}),
- (SECTION_RE, 'SECTION \t ',
- {'colon': '',
- 'section_name': None}),
- (SECTION_RE, ' \t SECTION \t ',
- {'colon': '',
- 'section_name': None}),
- (SECTION_RE, 'SECTION: \t ',
- {'colon': ':',
- 'section_name': None}),
- (SECTION_RE, 'SECTION : ',
- {'colon': ':',
- 'section_name': None}),
- (SECTION_RE, ' SECTION : ',
- {'colon': ':',
- 'section_name': None}),
- (SECTION_RE, 'SECTION:gtkwidget',
- {'colon': ':',
- 'section_name': 'gtkwidget'}),
- (SECTION_RE, 'SECTION:gtkwidget ',
- {'colon': ':',
- 'section_name': 'gtkwidget'}),
- (SECTION_RE, ' SECTION:gtkwidget',
- {'colon': ':',
- 'section_name': 'gtkwidget'}),
- (SECTION_RE, ' SECTION:gtkwidget\t ',
- {'colon': ':',
- 'section_name': 'gtkwidget'}),
- (SECTION_RE, 'SECTION: gtkwidget ',
- {'colon': ':',
- 'section_name': 'gtkwidget'}),
- (SECTION_RE, 'SECTION : gtkwidget',
- {'colon': ':',
- 'section_name': 'gtkwidget'}),
- (SECTION_RE, 'SECTION gtkwidget \f ',
- {'colon': '',
- 'section_name': 'gtkwidget'})]
-
- identifier_symbol_tests = [
- (SYMBOL_RE, 'GBaseFinalizeFunc:',
- {'colon': ':',
- 'symbol_name': 'GBaseFinalizeFunc',
- 'annotations': ''}),
- (SYMBOL_RE, 'gtk_widget_show ',
- {'colon': '',
- 'symbol_name': 'gtk_widget_show',
- 'annotations': ''}),
- (SYMBOL_RE, ' gtk_widget_show',
- {'colon': '',
- 'symbol_name': 'gtk_widget_show',
- 'annotations': ''}),
- (SYMBOL_RE, ' gtk_widget_show ',
- {'colon': '',
- 'symbol_name': 'gtk_widget_show',
- 'annotations': ''}),
- (SYMBOL_RE, 'gtk_widget_show:',
- {'colon': ':',
- 'symbol_name': 'gtk_widget_show',
- 'annotations': ''}),
- (SYMBOL_RE, 'gtk_widget_show :',
- {'colon': ':',
- 'symbol_name': 'gtk_widget_show',
- 'annotations': ''}),
- (SYMBOL_RE, 'gtk_widget_show: ',
- {'colon': ':',
- 'symbol_name': 'gtk_widget_show',
- 'annotations': ''}),
- (SYMBOL_RE, 'gtk_widget_show : ',
- {'colon': ':',
- 'symbol_name': 'gtk_widget_show',
- 'annotations': ''}),
- (SYMBOL_RE, ' gtk_widget_show:',
- {'colon': ':',
- 'symbol_name': 'gtk_widget_show',
- 'annotations': ''}),
- (SYMBOL_RE, ' gtk_widget_show :',
- {'colon': ':',
- 'symbol_name': 'gtk_widget_show',
- 'annotations': ''}),
- (SYMBOL_RE, ' gtk_widget_show: ',
- {'colon': ':',
- 'symbol_name': 'gtk_widget_show',
- 'annotations': ''}),
- (SYMBOL_RE, ' gtk_widget_show : ',
- {'colon': ':',
- 'symbol_name': 'gtk_widget_show',
- 'annotations': ''}),
- (SYMBOL_RE, 'gtk_widget_show (skip)',
- {'colon': '',
- 'symbol_name': 'gtk_widget_show',
- 'annotations': '(skip)'}),
- (SYMBOL_RE, 'gtk_widget_show: (skip)',
- {'colon': ':',
- 'symbol_name': 'gtk_widget_show',
- 'annotations': '(skip)'}),
- (SYMBOL_RE, 'gtk_widget_show : (skip)',
- {'colon': ':',
- 'symbol_name': 'gtk_widget_show',
- 'annotations': '(skip)'}),
- (SYMBOL_RE, 'gtk_widget_show: (skip)',
- {'colon': ':',
- 'symbol_name': 'gtk_widget_show',
- 'annotations': '(skip)'}),
- (SYMBOL_RE, 'gtk_widget_show : (skip)',
- {'colon': ':',
- 'symbol_name': 'gtk_widget_show',
- 'annotations': '(skip)'}),
- (SYMBOL_RE, ' gtk_widget_show:(skip)',
- {'colon': ':',
- 'symbol_name': 'gtk_widget_show',
- 'annotations': '(skip)'}),
- (SYMBOL_RE, ' gtk_widget_show :(skip)',
- {'colon': ':',
- 'symbol_name': 'gtk_widget_show',
- 'annotations': '(skip)'}),
- (SYMBOL_RE, ' gtk_widget_show: (skip)',
- {'colon': ':',
- 'symbol_name': 'gtk_widget_show',
- 'annotations': '(skip)'}),
- (SYMBOL_RE, ' gtk_widget_show : (skip) \t ',
- {'colon': ':',
- 'symbol_name': 'gtk_widget_show',
- 'annotations': '(skip) \t '}),
- (SYMBOL_RE, ' gtk_widget_show : (skip) \t ',
- {'colon': ':',
- 'symbol_name': 'gtk_widget_show',
- 'annotations': '(skip) \t '}),
- (SYMBOL_RE, 'gtk_widget_show:(skip)(test1)',
- {'colon': ':',
- 'symbol_name': 'gtk_widget_show',
- 'annotations': '(skip)(test1)'}),
- (SYMBOL_RE, 'gtk_widget_show (skip)(test1)',
- {'colon': '',
- 'symbol_name': 'gtk_widget_show',
- 'annotations': '(skip)(test1)'}),
- (SYMBOL_RE, 'gtk_widget_show: (skip) (test1)',
- {'colon': ':',
- 'symbol_name': 'gtk_widget_show',
- 'annotations': '(skip) (test1)'}),
- (SYMBOL_RE, 'gtk_widget_show : (skip) (test1)',
- {'colon': ':',
- 'symbol_name': 'gtk_widget_show',
- 'annotations': '(skip) (test1)'}),
- (SYMBOL_RE, 'gtk_widget_show: (skip) (test1)',
- {'colon': ':',
- 'symbol_name': 'gtk_widget_show',
- 'annotations': '(skip) (test1)'}),
- (SYMBOL_RE, 'gtk_widget_show : (skip) (test1)',
- {'colon': ':',
- 'symbol_name': 'gtk_widget_show',
- 'annotations': '(skip) (test1)'}),
- (SYMBOL_RE, ' gtk_widget_show:(skip) (test1)',
- {'colon': ':',
- 'symbol_name': 'gtk_widget_show',
- 'annotations': '(skip) (test1)'}),
- (SYMBOL_RE, ' gtk_widget_show :(skip) (test1)',
- {'colon': ':',
- 'symbol_name': 'gtk_widget_show',
- 'annotations': '(skip) (test1)'}),
- (SYMBOL_RE, ' gtk_widget_show: (skip) (test1)',
- {'colon': ':',
- 'symbol_name': 'gtk_widget_show',
- 'annotations': '(skip) (test1)'}),
- (SYMBOL_RE, ' gtk_widget_show : (skip) (test1) ',
- {'colon': ':',
- 'symbol_name': 'gtk_widget_show',
- 'annotations': '(skip) (test1) '}),
- (SYMBOL_RE, 'gtk_widget_show: (skip) (test1) (test-2)',
- {'colon': ':',
- 'symbol_name': 'gtk_widget_show',
- 'annotations': '(skip) (test1) (test-2)'}),
- (SYMBOL_RE, 'gtk_widget_show : (skip) (test1) (test-2)',
- {'colon': ':',
- 'symbol_name': 'gtk_widget_show',
- 'annotations': '(skip) (test1) (test-2)'}),
- (SYMBOL_RE, 'gtk_widget_show: (skip) (test1) (test-2)',
- {'colon': ':',
- 'symbol_name': 'gtk_widget_show',
- 'annotations': '(skip) (test1) (test-2)'}),
- (SYMBOL_RE, 'gtk_widget_show : (skip) (test1) (test-2)',
- {'colon': ':',
- 'symbol_name': 'gtk_widget_show',
- 'annotations': '(skip) (test1) (test-2)'}),
- (SYMBOL_RE, ' gtk_widget_show:(skip) (test1) (test-2)',
- {'colon': ':',
- 'symbol_name': 'gtk_widget_show',
- 'annotations': '(skip) (test1) (test-2)'}),
- (SYMBOL_RE, ' gtk_widget_show :(skip) (test1) (test-2)',
- {'colon': ':',
- 'symbol_name': 'gtk_widget_show',
- 'annotations': '(skip) (test1) (test-2)'}),
- (SYMBOL_RE, ' gtk_widget_show: (skip) (test1) (test-2)',
- {'colon': ':',
- 'symbol_name': 'gtk_widget_show',
- 'annotations': '(skip) (test1) (test-2)'}),
- (SYMBOL_RE, ' gtk_widget_show : (skip) (test1) (test-2) ',
- {'colon': ':',
- 'symbol_name': 'gtk_widget_show',
- 'annotations': '(skip) (test1) (test-2) '}),
- (SYMBOL_RE, ' gtk_widget_show : (skip) (test1) (test-2) ',
- {'colon': ':',
- 'symbol_name': 'gtk_widget_show',
- 'annotations': '(skip) (test1) (test-2) '}),
- # constants
- (SYMBOL_RE, 'MY_CONSTANT:',
- {'colon': ':',
- 'symbol_name': 'MY_CONSTANT',
- 'annotations': ''}),
- # structs
- (SYMBOL_RE, 'FooWidget:',
- {'colon': ':',
- 'symbol_name': 'FooWidget',
- 'annotations': ''}),
- # enums
- (SYMBOL_RE, 'Something:',
- {'colon': ':',
- 'symbol_name': 'Something',
- 'annotations': ''})]
-
- identifier_property_tests = [
- # simple property name
- (PROPERTY_RE, 'GtkWidget:name (skip)',
- {'class_name': 'GtkWidget',
- 'property_name': 'name',
- 'colon': '',
- 'annotations': '(skip)'}),
- (PROPERTY_RE, 'GtkWidget:name',
- {'class_name': 'GtkWidget',
- 'property_name': 'name',
- 'colon': '',
- 'annotations': ''}),
- (PROPERTY_RE, ' GtkWidget :name',
- {'class_name': 'GtkWidget',
- 'property_name': 'name',
- 'colon': '',
- 'annotations': ''}),
- (PROPERTY_RE, 'GtkWidget: name ',
- {'class_name': 'GtkWidget',
- 'property_name': 'name',
- 'colon': '',
- 'annotations': ''}),
- (PROPERTY_RE, ' GtkWidget : name ',
- {'class_name': 'GtkWidget',
- 'property_name': 'name',
- 'colon': '',
- 'annotations': ''}),
- (PROPERTY_RE, 'GtkWidget:name:',
- {'class_name': 'GtkWidget',
- 'property_name': 'name',
- 'colon': ':',
- 'annotations': ''}),
- (PROPERTY_RE, 'GtkWidget:name: ',
- {'class_name': 'GtkWidget',
- 'property_name': 'name',
- 'colon': ':',
- 'annotations': ''}),
- (PROPERTY_RE, ' GtkWidget:name:',
- {'class_name': 'GtkWidget',
- 'property_name': 'name',
- 'colon': ':',
- 'annotations': ''}),
- (PROPERTY_RE, 'Something:name:',
- {'class_name': 'Something',
- 'property_name': 'name',
- 'colon': ':',
- 'annotations': ''}),
- (PROPERTY_RE, 'Something:name: ',
- {'class_name': 'Something',
- 'property_name': 'name',
- 'colon': ':',
- 'annotations': ''}),
- (PROPERTY_RE, ' Something:name:',
- {'class_name': 'Something',
- 'property_name': 'name',
- 'colon': ':',
- 'annotations': ''}),
- (PROPERTY_RE, 'Weird-thing:name:',
- None),
- (PROPERTY_RE, 'really-weird_thing:name:',
- None),
- (PROPERTY_RE, 'GWin32InputStream:handle:',
- {'class_name': 'GWin32InputStream',
- 'property_name': 'handle',
- 'colon': ':',
- 'annotations': ''}),
- # property name that contains a dash
- (PROPERTY_RE, 'GtkWidget:double-buffered (skip)',
- {'class_name': 'GtkWidget',
- 'property_name': 'double-buffered',
- 'colon': '',
- 'annotations': '(skip)'}),
- (PROPERTY_RE, 'GtkWidget:double-buffered',
- {'class_name': 'GtkWidget',
- 'property_name': 'double-buffered',
- 'colon': '',
- 'annotations': ''}),
- (PROPERTY_RE, ' GtkWidget :double-buffered',
- {'class_name': 'GtkWidget',
- 'property_name': 'double-buffered',
- 'colon': '',
- 'annotations': ''}),
- (PROPERTY_RE, 'GtkWidget: double-buffered ',
- {'class_name': 'GtkWidget',
- 'property_name': 'double-buffered',
- 'colon': '',
- 'annotations': ''}),
- (PROPERTY_RE, ' GtkWidget : double-buffered ',
- {'class_name': 'GtkWidget',
- 'property_name': 'double-buffered',
- 'colon': '',
- 'annotations': ''}),
- (PROPERTY_RE, 'GtkWidget:double-buffered:',
- {'class_name': 'GtkWidget',
- 'property_name': 'double-buffered',
- 'colon': ':',
- 'annotations': ''}),
- (PROPERTY_RE, 'GtkWidget:double-buffered: ',
- {'class_name': 'GtkWidget',
- 'property_name': 'double-buffered',
- 'colon': ':',
- 'annotations': ''}),
- (PROPERTY_RE, ' GtkWidget:double-buffered:',
- {'class_name': 'GtkWidget',
- 'property_name': 'double-buffered',
- 'colon': ':',
- 'annotations': ''}),
- (PROPERTY_RE, 'Something:double-buffered:',
- {'class_name': 'Something',
- 'property_name': 'double-buffered',
- 'colon': ':',
- 'annotations': ''}),
- (PROPERTY_RE, 'Something:double-buffered: ',
- {'class_name': 'Something',
- 'property_name': 'double-buffered',
- 'colon': ':',
- 'annotations': ''}),
- (PROPERTY_RE, ' Something:double-buffered:',
- {'class_name': 'Something',
- 'property_name': 'double-buffered',
- 'colon': ':',
- 'annotations': ''}),
- (PROPERTY_RE, 'Weird-thing:double-buffered:',
- None),
- (PROPERTY_RE, 'really-weird_thing:double-buffered:',
- None),
- (PROPERTY_RE, ' GMemoryOutputStream:realloc-function: (skip)',
- {'class_name': 'GMemoryOutputStream',
- 'property_name': 'realloc-function',
- 'colon': ':',
- 'annotations': '(skip)'})]
-
- identifier_signal_tests = [
- # simple property name
- (SIGNAL_RE, 'GtkWidget::changed: (skip)',
- {'class_name': 'GtkWidget',
- 'signal_name': 'changed',
- 'colon': ':',
- 'annotations': '(skip)'}),
- (SIGNAL_RE, 'GtkWidget::changed:',
- {'class_name': 'GtkWidget',
- 'signal_name': 'changed',
- 'colon': ':',
- 'annotations': ''}),
- (SIGNAL_RE, 'Something::changed:',
- {'class_name': 'Something',
- 'signal_name': 'changed',
- 'colon': ':',
- 'annotations': ''}),
- (SIGNAL_RE, 'Weird-thing::changed:',
- None),
- (SIGNAL_RE, 'really-weird_thing::changed:',
- None),
- # property name that contains a dash
- (SIGNAL_RE, 'GtkWidget::hierarchy-changed: (skip)',
- {'class_name': 'GtkWidget',
- 'signal_name': 'hierarchy-changed',
- 'colon': ':',
- 'annotations': '(skip)'}),
- (SIGNAL_RE, 'GtkWidget::hierarchy-changed:',
- {'class_name': 'GtkWidget',
- 'signal_name': 'hierarchy-changed',
- 'colon': ':',
- 'annotations': ''}),
- (SIGNAL_RE, 'Something::hierarchy-changed:',
- {'class_name': 'Something',
- 'signal_name': 'hierarchy-changed',
- 'colon': ':',
- 'annotations': ''}),
- (SIGNAL_RE, 'Weird-thing::hierarchy-changed:',
- None),
- (SIGNAL_RE, 'really-weird_thing::hierarchy-changed:',
- None)]
-
- parameter_tests = [
- (PARAMETER_RE, '@Short_description: Base class for all widgets ',
- {'parameter_name': 'Short_description',
- 'annotations': '',
- 'colon': '',
- 'description': 'Base class for all widgets'}),
- (PARAMETER_RE, '@...: the value of the first property, followed optionally by more',
- {'parameter_name': '...',
- 'annotations': '',
- 'colon': '',
- 'description': 'the value of the first property, followed optionally by more'}),
- (PARAMETER_RE, '@widget: a #GtkWidget',
- {'parameter_name': 'widget',
- 'annotations': '',
- 'colon': '',
- 'description': 'a #GtkWidget'}),
- (PARAMETER_RE, '@widget_pointer: (inout) (transfer none): '
- 'address of a variable that contains @widget',
- {'parameter_name': 'widget_pointer',
- 'annotations': '(inout) (transfer none)',
- 'colon': ':',
- 'description': 'address of a variable that contains @widget'}),
- (PARAMETER_RE, '@weird_thing: (inout) (transfer none) (allow-none) (attribute) (destroy) '
- '(foreign) (inout) (out) (transfer) (skip) (method): some weird @thing',
- {'parameter_name': 'weird_thing',
- 'annotations': '(inout) (transfer none) (allow-none) (attribute) (destroy) '
- '(foreign) (inout) (out) (transfer) (skip) (method)',
- 'colon': ':',
- 'description': 'some weird @thing'}),
- (PARAMETER_RE, '@data: a pointer to the element data. The data may be moved as elements '
- 'are added to the #GByteArray.',
- {'parameter_name': 'data',
- 'annotations': '',
- 'colon': '',
- 'description': 'a pointer to the element data. The data may be moved as elements '
- 'are added to the #GByteArray.'}),
- (PARAMETER_RE, '@a: a #GSequenceIter',
- {'parameter_name': 'a',
- 'annotations': '',
- 'colon': '',
- 'description': 'a #GSequenceIter'}),
- (PARAMETER_RE, '@keys: (array length=n_keys) (element-type GQuark) (allow-none):',
- {'parameter_name': 'keys',
- 'annotations': '(array length=n_keys) (element-type GQuark) (allow-none)',
- 'colon': ':',
- 'description': ''})]
-
- tag_tests = [
- (TAG_RE, 'Since 3.0',
- None),
- (TAG_RE, 'Since: 3.0',
- {'tag_name': 'Since',
- 'annotations': '',
- 'colon': '',
- 'description': '3.0'}),
- (TAG_RE, 'Attributes: (inout) (transfer none): some note about attributes',
- {'tag_name': 'Attributes',
- 'annotations': '(inout) (transfer none)',
- 'colon': ':',
- 'description': 'some note about attributes'}),
- (TAG_RE, 'Rename to: something_else',
- {'tag_name': 'Rename to',
- 'annotations': '',
- 'colon': '',
- 'description': 'something_else'}),
- (TAG_RE, '@Deprecated: Since 2.8, reference counting is done atomically',
- None),
- (TAG_RE, 'Returns %TRUE and does weird things',
- None),
- (TAG_RE, 'Returns: a #GtkWidget',
- {'tag_name': 'Returns',
- 'annotations': '',
- 'colon': '',
- 'description': 'a #GtkWidget'}),
- (TAG_RE, 'Return value: (transfer none): The binary data that @text responds. '
- 'This pointer',
- {'tag_name': 'Return value',
- 'annotations': '(transfer none)',
- 'colon': ':',
- 'description': 'The binary data that @text responds. This pointer'}),
- (TAG_RE, 'Return value: (transfer full) (array length=out_len) (element-type guint8):',
- {'tag_name': 'Return value',
- 'annotations': '(transfer full) (array length=out_len) (element-type guint8)',
- 'colon': ':',
- 'description': ''}),
- (TAG_RE, 'Returns: A boolean value, but let me tell you a bit about this boolean. It',
- {'tag_name': 'Returns',
- 'annotations': '',
- 'colon': '',
- 'description': 'A boolean value, but let me tell you a bit about this boolean. '
- 'It'}),
- (TAG_RE, 'Returns: (transfer container) (element-type GObject.ParamSpec): a',
- {'tag_name': 'Returns',
- 'annotations': '(transfer container) (element-type GObject.ParamSpec)',
- 'colon': ':',
- 'description': 'a'}),
- (TAG_RE, 'Return value: (type GLib.HashTable<utf8,GLib.HashTable<utf8,utf8>>) '
- '(transfer full):',
- {'tag_name': 'Return value',
- 'annotations': '(type GLib.HashTable<utf8,GLib.HashTable<utf8,utf8>>) '
- '(transfer full)',
- 'colon': ':',
- 'description': ''})]
-
-
- def create_tests(cls, test_name, testcases):
- for (index, testcase) in enumerate(testcases):
- real_test_name = '%s_%03d' % (test_name, index)
-
- test_method = cls.__create_test__(testcase)
- test_method.__name__ = real_test_name
- setattr(cls, real_test_name, test_method)
-
-
- class TestProgram(unittest.TestCase):
- @classmethod
- def __create_test__(cls, testcase):
- def do_test(self):
- (program, text, expected) = testcase
-
- match = program.search(text)
-
- if expected is None:
- msg = 'Program matched text but shouldn\'t:\n"%s"'
- self.assertTrue(match is None, msg % (text))
- else:
- msg = 'Program should match text but didn\'t:\n"%s"'
- self.assertTrue(match is not None, msg % (text))
-
- for key, value in expected.items():
- msg = 'expected "%s" for "%s" but match returned "%s"'
- msg = msg % (value, key, match.group(key))
- self.assertEqual(match.group(key), value, msg)
-
- return do_test
-
-
- # Create tests from data
- create_tests(TestProgram, 'test_identifier_section', identifier_section_tests)
- create_tests(TestProgram, 'test_identifier_symbol', identifier_symbol_tests)
- create_tests(TestProgram, 'test_identifier_property', identifier_property_tests)
- create_tests(TestProgram, 'test_identifier_signal', identifier_signal_tests)
- create_tests(TestProgram, 'test_parameter', parameter_tests)
- create_tests(TestProgram, 'test_tag', tag_tests)
-
- # Run test suite
- unittest.main()
diff --git a/giscanner/ast.py b/giscanner/ast.py
index 654c68e2..4c54b548 100644
--- a/giscanner/ast.py
+++ b/giscanner/ast.py
@@ -20,22 +20,25 @@
#
import copy
+from itertools import chain
from . import message
+from .collections import OrderedDict
from .message import Position
-from .odict import odict
from .utils import to_underscores
+
class Type(object):
- """A Type can be either:
-* A reference to a node (target_giname)
-* A reference to a "fundamental" type like 'utf8'
-* A "foreign" type - this can be any string."
-If none are specified, then it's in an "unresolved" state. An
-unresolved type can have two data sources; a "ctype" which comes
-from a C type string, or a gtype_name (from g_type_name()).
-""" # '''
+ """
+ A Type can be either:
+ * A reference to a node (target_giname)
+ * A reference to a "fundamental" type like 'utf8'
+ * A "foreign" type - this can be any string."
+ If none are specified, then it's in an "unresolved" state. An
+ unresolved type can have two data sources; a "ctype" which comes
+ from a C type string, or a gtype_name (from g_type_name()).
+ """
def __init__(self,
ctype=None,
@@ -83,6 +86,8 @@ from a C type string, or a gtype_name (from g_type_name()).
return self.ctype
elif self.gtype_name:
return self.gtype_name
+ elif self.target_giname:
+ return self.target_giname
else:
assert False
@@ -94,7 +99,8 @@ in contrast to the other create_type() functions."""
# First, is it a fundamental?
fundamental = type_names.get(gtype_name)
if fundamental is not None:
- return cls(target_fundamental=fundamental.target_fundamental)
+ return cls(target_fundamental=fundamental.target_fundamental,
+ ctype=fundamental.ctype)
if gtype_name == 'GHashTable':
return Map(TYPE_ANY, TYPE_ANY, gtype_name=gtype_name)
elif gtype_name in ('GArray', 'GPtrArray', 'GByteArray'):
@@ -121,11 +127,12 @@ in contrast to the other create_type() functions."""
def __cmp__(self, other):
if self.target_fundamental:
return cmp(self.target_fundamental, other.target_fundamental)
- if self.target_giname:
+ elif self.target_giname:
return cmp(self.target_giname, other.target_giname)
- if self.target_foreign:
+ elif self.target_foreign:
return cmp(self.target_foreign, other.target_foreign)
- return cmp(self.ctype, other.ctype)
+ else:
+ return cmp(self.ctype, other.ctype)
def is_equiv(self, typeval):
"""Return True if the specified types are compatible at
@@ -166,6 +173,7 @@ in contrast to the other create_type() functions."""
data = ''
return '%s(%sctype=%s)' % (self.__class__.__name__, data, self.ctype)
+
class TypeUnknown(Type):
def __init__(self):
Type.__init__(self, _target_unknown=True)
@@ -347,9 +355,7 @@ SIGNAL_MUST_COLLECT = 'must-collect'
class Namespace(object):
- def __init__(self, name, version,
- identifier_prefixes=None,
- symbol_prefixes=None):
+ def __init__(self, name, version, identifier_prefixes=None, symbol_prefixes=None):
self.name = name
self.version = version
if identifier_prefixes is not None:
@@ -363,11 +369,15 @@ class Namespace(object):
self.symbol_prefixes = [to_underscores(p).lower() for p in ps]
# cache upper-cased versions
self._ucase_symbol_prefixes = [p.upper() for p in self.symbol_prefixes]
- self.names = odict() # Maps from GIName -> node
- self.aliases = {} # Maps from GIName -> GIName
- self.type_names = {} # Maps from GTName -> node
- self.ctypes = {} # Maps from CType -> node
- self.symbols = {} # Maps from function symbols -> Function
+ self.names = OrderedDict() # Maps from GIName -> node
+ self.aliases = {} # Maps from GIName -> GIName
+ self.type_names = {} # Maps from GTName -> node
+ self.ctypes = {} # Maps from CType -> node
+ self.symbols = {} # Maps from function symbols -> Function
+ self.includes = set() # Include
+ self.shared_libraries = [] # str
+ self.c_includes = [] # str
+ self.exported_packages = [] # str
def type_from_name(self, name, ctype=None):
"""Backwards compatibility method for older .gir files, which
@@ -399,6 +409,24 @@ but adds it to things like ctypes, symbols, and type_names.
self.type_names[node.gtype_name] = node
elif isinstance(node, Function):
self.symbols[node.symbol] = node
+ if isinstance(node, (Compound, Class, Interface)):
+ for fn in chain(node.methods, node.static_methods, node.constructors):
+ if not isinstance(fn, Function):
+ continue
+ fn.namespace = self
+ self.symbols[fn.symbol] = fn
+ if isinstance(node, (Class, Interface)):
+ for m in chain(node.signals, node.properties):
+ m.namespace = self
+ if isinstance(node, (Enum, Bitfield)):
+ for fn in node.static_methods:
+ if not isinstance(fn, Function):
+ continue
+ fn.namespace = self
+ self.symbols[fn.symbol] = fn
+ for member in node.members:
+ member.namespace = self
+ self.symbols[member.symbol] = member
if hasattr(node, 'ctype'):
self.ctypes[node.ctype] = node
@@ -456,6 +484,7 @@ functions via get_by_symbol()."""
for node in self.itervalues():
node.walk(callback, [])
+
class Include(object):
def __init__(self, name, version):
@@ -478,6 +507,7 @@ class Include(object):
def __str__(self):
return '%s-%s' % (self.name, self.version)
+
class Annotated(object):
"""An object which has a few generic metadata
properties."""
@@ -485,12 +515,13 @@ properties."""
self.version = None
self.skip = False
self.introspectable = True
- self.attributes = [] # (key, value)*
+ self.attributes = [] # (key, value)*
self.stability = None
self.deprecated = None
self.deprecated_version = None
self.doc = None
+
class Node(Annotated):
"""A node is a type of object which is uniquely identified by its
(namespace, name) pair. When combined with a ., this is called a
@@ -501,10 +532,21 @@ GIName. It's possible for nodes to contain or point to other nodes."""
def __init__(self, name=None):
Annotated.__init__(self)
- self.namespace = None # Should be set later by Namespace.append()
+ self.namespace = None # Should be set later by Namespace.append()
self.name = name
self.foreign = False
self.file_positions = set()
+ self._parent = None
+
+ def _get_parent(self):
+ if self._parent is not None:
+ return self._parent
+ else:
+ return self.namespace
+
+ def _set_parent(self, value):
+ self._parent = value
+ parent = property(_get_parent, _set_parent)
def create_type(self):
"""Create a Type object referencing this node."""
@@ -559,8 +601,16 @@ class Callable(Node):
self.retval = retval
self.parameters = parameters
self.throws = not not throws
- self.instance_parameter = None # Parameter
- self.parent = None # A Class or Interface
+ self.instance_parameter = None # Parameter
+ self.parent = None # A Class or Interface
+
+ # Returns all parameters, including the instance parameter
+ @property
+ def all_parameters(self):
+ if self.instance_parameter is not None:
+ return [self.instance_parameter] + self.parameters
+ else:
+ return self.parameters
def get_parameter_index(self, name):
for i, parameter in enumerate(self.parameters):
@@ -569,7 +619,7 @@ class Callable(Node):
raise ValueError("Unknown argument %s" % (name, ))
def get_parameter(self, name):
- for parameter in self.parameters:
+ for parameter in self.all_parameters:
if parameter.argname == name:
return parameter
raise ValueError("Unknown argument %s" % (name, ))
@@ -582,9 +632,10 @@ class Function(Callable):
self.symbol = symbol
self.is_method = False
self.is_constructor = False
- self.shadowed_by = None # C symbol string
- self.shadows = None # C symbol string
- self.moved_to = None # namespaced function name string
+ self.shadowed_by = None # C symbol string
+ self.shadows = None # C symbol string
+ self.moved_to = None # namespaced function name string
+ self.internal_skipped = False # if True, this func will not be written to GIR
def clone(self):
clone = copy.copy(self)
@@ -595,9 +646,8 @@ class Function(Callable):
def is_type_meta_function(self):
# Named correctly
- if not (self.name.endswith('_get_type') or
- self.name.endswith('_get_gtype')):
- return False
+ if not (self.name.endswith('_get_type') or self.name.endswith('_get_gtype')):
+ return False
# Doesn't have any parameters
if self.parameters:
@@ -605,14 +655,13 @@ class Function(Callable):
# Returns GType
rettype = self.retval.type
- if (not rettype.is_equiv(TYPE_GTYPE) and
- rettype.target_giname != 'Gtk.Type'):
- message.warn("function '%s' returns '%r', not a GType" %
- (self.name, rettype))
+ if (not rettype.is_equiv(TYPE_GTYPE) and rettype.target_giname != 'Gtk.Type'):
+ message.warn("function '%s' returns '%r', not a GType" % (self.name, rettype))
return False
return True
+
class ErrorQuarkFunction(Function):
def __init__(self, name, retval, parameters, throws, symbol, error_domain):
@@ -633,7 +682,6 @@ class VFunction(Callable):
return obj
-
class Varargs(Type):
def __init__(self):
@@ -669,6 +717,7 @@ class Array(Type):
arr.size = self.size
return arr
+
class List(Type):
def __init__(self, name, element_type, **kwargs):
@@ -681,6 +730,7 @@ class List(Type):
def clone(self):
return List(self.name, self.element_type)
+
class Map(Type):
def __init__(self, key_type, value_type, **kwargs):
@@ -693,6 +743,7 @@ class Map(Type):
def clone(self):
return Map(self.key_type, self.value_type)
+
class Alias(Node):
def __init__(self, name, target, ctype=None):
@@ -751,6 +802,8 @@ class Enum(Node, Registered):
self.c_symbol_prefix = c_symbol_prefix
self.ctype = ctype
self.members = members
+ for member in members:
+ member.parent = self
# Associated error domain name
self.error_domain = None
self.static_methods = []
@@ -772,6 +825,8 @@ class Bitfield(Node, Registered):
self.ctype = ctype
self.c_symbol_prefix = c_symbol_prefix
self.members = members
+ for member in members:
+ member.parent = self
self.static_methods = []
def _walk(self, callback, chain):
@@ -787,10 +842,13 @@ class Member(Annotated):
self.value = value
self.symbol = symbol
self.nick = nick
+ self.parent = None
def __cmp__(self, other):
return cmp(self.name, other.name)
+ def __repr__(self):
+ return '%s(%r)' % (self.__class__.__name__, self.name)
class Compound(Node, Registered):
@@ -828,6 +886,7 @@ class Compound(Node, Registered):
if field.anonymous_node is not None:
field.anonymous_node.walk(callback, chain)
+
class Field(Annotated):
def __init__(self, name, typenode, readable, writable, bits=None,
@@ -841,10 +900,14 @@ class Field(Annotated):
self.bits = bits
self.anonymous_node = anonymous_node
self.private = False
+ self.parent = None # a compound
def __cmp__(self, other):
return cmp(self.name, other.name)
+ def __repr__(self):
+ return '%s(%r)' % (self.__class__.__name__, self.name)
+
class Record(Compound):
@@ -922,7 +985,7 @@ class Signal(Callable):
class Class(Node, Registered):
- def __init__(self, name, parent,
+ def __init__(self, name, parent_type,
ctype=None,
gtype_name=None,
get_type=None,
@@ -932,7 +995,7 @@ class Class(Node, Registered):
Registered.__init__(self, gtype_name, get_type)
self.ctype = ctype
self.c_symbol_prefix = c_symbol_prefix
- self.parent = parent
+ self.parent_type = parent_type
self.fundamental = False
self.unref_func = None
self.ref_func = None
@@ -967,11 +1030,13 @@ class Class(Node, Registered):
field.anonymous_node.walk(callback, chain)
for sig in self.signals:
sig.walk(callback, chain)
+ for prop in self.properties:
+ prop.walk(callback, chain)
class Interface(Node, Registered):
- def __init__(self, name, parent,
+ def __init__(self, name, parent_type,
ctype=None,
gtype_name=None,
get_type=None,
@@ -980,7 +1045,7 @@ class Interface(Node, Registered):
Registered.__init__(self, gtype_name, get_type)
self.ctype = ctype
self.c_symbol_prefix = c_symbol_prefix
- self.parent = parent
+ self.parent_type = parent_type
self.parent_chain = []
self.methods = []
self.signals = []
@@ -990,6 +1055,9 @@ class Interface(Node, Registered):
self.properties = []
self.fields = []
self.prerequisites = []
+ # Not used yet, exists just to avoid an exception in
+ # Namespace.append()
+ self.constructors = []
def _walk(self, callback, chain):
for meth in self.methods:
@@ -1028,7 +1096,7 @@ class Property(Node):
self.transfer = PARAM_TRANSFER_NONE
else:
self.transfer = transfer
- self.parent = None # A Class or Interface
+ self.parent = None # A Class or Interface
class Callback(Callable):
diff --git a/giscanner/cachestore.py b/giscanner/cachestore.py
index 44e3b04c..ad4c7a36 100644
--- a/giscanner/cachestore.py
+++ b/giscanner/cachestore.py
@@ -31,6 +31,7 @@ import giscanner
_CACHE_VERSION_FILENAME = '.cache-version'
+
def _get_versionhash():
toplevel = os.path.dirname(giscanner.__file__)
# Use pyc instead of py to avoid extra IO
@@ -40,6 +41,7 @@ def _get_versionhash():
mtimes = (str(os.stat(source).st_mtime) for source in sources)
return hashlib.sha1(''.join(mtimes)).hexdigest()
+
def _get_cachedir():
if 'GI_SCANNER_DISABLE_CACHE' in os.environ:
return None
@@ -73,7 +75,7 @@ class CacheStore(object):
def __init__(self):
try:
self._directory = _get_cachedir()
- except OSError, e:
+ except OSError as e:
if e.errno != errno.EPERM:
raise
self._directory = None
@@ -88,7 +90,7 @@ class CacheStore(object):
version = os.path.join(self._directory, _CACHE_VERSION_FILENAME)
try:
cache_hash = open(version).read()
- except IOError, e:
+ except IOError as e:
# File does not exist
if e.errno == errno.ENOENT:
cache_hash = 0
@@ -101,7 +103,7 @@ class CacheStore(object):
self._clean()
try:
fp = open(version, 'w')
- except IOError, e:
+ except IOError as e:
# Permission denied
if e.errno == errno.EACCES:
return
@@ -126,13 +128,13 @@ class CacheStore(object):
def _remove_filename(self, filename):
try:
os.unlink(filename)
- except IOError, e:
+ except IOError as e:
# Permission denied
if e.errno == errno.EACCES:
return
else:
raise
- except OSError, e:
+ except OSError as e:
# File does not exist
if e.errno == errno.ENOENT:
return
@@ -150,14 +152,13 @@ class CacheStore(object):
if store_filename is None:
return
- if (os.path.exists(store_filename) and
- self._cache_is_valid(store_filename, filename)):
+ if (os.path.exists(store_filename) and self._cache_is_valid(store_filename, filename)):
return None
tmp_fd, tmp_filename = tempfile.mkstemp(prefix='g-ir-scanner-cache-')
try:
cPickle.dump(data, os.fdopen(tmp_fd, 'w'))
- except IOError, e:
+ except IOError as e:
# No space left on device
if e.errno == errno.ENOSPC:
self._remove_filename(tmp_filename)
@@ -167,7 +168,7 @@ class CacheStore(object):
try:
shutil.move(tmp_filename, store_filename)
- except IOError, e:
+ except IOError as e:
# Permission denied
if e.errno == errno.EACCES:
self._remove_filename(tmp_filename)
@@ -180,7 +181,7 @@ class CacheStore(object):
return
try:
fd = open(store_filename)
- except IOError, e:
+ except IOError as e:
if e.errno == errno.ENOENT:
return None
else:
diff --git a/giscanner/codegen.py b/giscanner/codegen.py
index b73a7da3..e9ed9415 100644
--- a/giscanner/codegen.py
+++ b/giscanner/codegen.py
@@ -24,6 +24,7 @@ from contextlib import contextmanager
from . import ast
+
class CCodeGenerator(object):
def __init__(self, namespace, out_h_filename, out_c_filename):
self.out_h_filename = out_h_filename
@@ -36,15 +37,16 @@ class CCodeGenerator(object):
return '%s_%s' % (self.namespace.symbol_prefixes[0], name)
def _typecontainer_to_ctype(self, param):
- if (isinstance(param, ast.Parameter) and
- param.direction in (ast.PARAM_DIRECTION_OUT,
- ast.PARAM_DIRECTION_INOUT)):
+ if (isinstance(param, ast.Parameter)
+ and param.direction in (ast.PARAM_DIRECTION_OUT, ast.PARAM_DIRECTION_INOUT)):
suffix = '*'
else:
suffix = ''
- if (param.type.is_equiv((ast.TYPE_STRING, ast.TYPE_FILENAME)) and
- param.transfer == ast.PARAM_TRANSFER_NONE):
+
+ if (param.type.is_equiv((ast.TYPE_STRING, ast.TYPE_FILENAME))
+ and param.transfer == ast.PARAM_TRANSFER_NONE):
return "const gchar*" + suffix
+
return param.type.ctype + suffix
def _write_prelude(self, out, func):
diff --git a/giscanner/collections/__init__.py b/giscanner/collections/__init__.py
new file mode 100644
index 00000000..29987a10
--- /dev/null
+++ b/giscanner/collections/__init__.py
@@ -0,0 +1,22 @@
+# -*- Mode: Python -*-
+# GObject-Introspection - a framework for introspecting GObject libraries
+# Copyright (C) 2013 Dieter Verfaillie <dieterv@optionexplicit.be>
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.
+#
+
+
+from .ordereddict import OrderedDict
diff --git a/giscanner/collections/ordereddict.py b/giscanner/collections/ordereddict.py
new file mode 100644
index 00000000..0cb4b956
--- /dev/null
+++ b/giscanner/collections/ordereddict.py
@@ -0,0 +1,120 @@
+# -*- Mode: Python -*-
+# GObject-Introspection - a framework for introspecting GObject libraries
+# Copyright (C) 2008 Johan Dahlin
+# Copyright (C) 2013 Dieter Verfaillie <dieterv@optionexplicit.be>
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+
+
+# Borrowed from:
+# http://hg.sqlalchemy.org/sqlalchemy/raw-file/77e2264283d4/lib/sqlalchemy/util/_collections.py
+# http://hg.sqlalchemy.org/sqlalchemy/raw-file/77e2264283d4/AUTHORS
+#
+# util/_collections.py
+# Copyright (C) 2005-2012 the SQLAlchemy authors and contributors <see AUTHORS file>
+#
+# This module is part of SQLAlchemy and is released under
+# the MIT License: http://www.opensource.org/licenses/mit-license.php
+
+
+class OrderedDict(dict):
+ """A dict that returns keys/values/items in the order they were added."""
+
+ def __init__(self, ____sequence=None, **kwargs):
+ self._list = []
+ if ____sequence is None:
+ if kwargs:
+ self.update(**kwargs)
+ else:
+ self.update(____sequence, **kwargs)
+
+ def clear(self):
+ self._list = []
+ dict.clear(self)
+
+ def copy(self):
+ return self.__copy__()
+
+ def __copy__(self):
+ return OrderedDict(self)
+
+ def sort(self, *arg, **kw):
+ self._list.sort(*arg, **kw)
+
+ def update(self, ____sequence=None, **kwargs):
+ if ____sequence is not None:
+ if hasattr(____sequence, 'keys'):
+ for key in ____sequence.keys():
+ self.__setitem__(key, ____sequence[key])
+ else:
+ for key, value in ____sequence:
+ self[key] = value
+ if kwargs:
+ self.update(kwargs)
+
+ def setdefault(self, key, value):
+ if key not in self:
+ self.__setitem__(key, value)
+ return value
+ else:
+ return self.__getitem__(key)
+
+ def __iter__(self):
+ return iter(self._list)
+
+ def values(self):
+ return [self[key] for key in self._list]
+
+ def itervalues(self):
+ return iter([self[key] for key in self._list])
+
+ def keys(self):
+ return list(self._list)
+
+ def iterkeys(self):
+ return iter(self.keys())
+
+ def items(self):
+ return [(key, self[key]) for key in self.keys()]
+
+ def iteritems(self):
+ return iter(self.items())
+
+ def __setitem__(self, key, obj):
+ if key not in self:
+ try:
+ self._list.append(key)
+ except AttributeError:
+ # work around Python pickle loads() with
+ # dict subclass (seems to ignore __setstate__?)
+ self._list = [key]
+ dict.__setitem__(self, key, obj)
+
+ def __delitem__(self, key):
+ dict.__delitem__(self, key)
+ self._list.remove(key)
+
+ def pop(self, key, *default):
+ present = key in self
+ value = dict.pop(self, key, *default)
+ if present:
+ self._list.remove(key)
+ return value
+
+ def popitem(self):
+ item = dict.popitem(self)
+ self._list.remove(item[0])
+ return item
diff --git a/giscanner/docmain.py b/giscanner/docmain.py
index 8089a6b3..e65b57a0 100644
--- a/giscanner/docmain.py
+++ b/giscanner/docmain.py
@@ -21,9 +21,11 @@
import os
import optparse
-from .mallardwriter import MallardWriter
+from .docwriter import DocWriter
+from .sectionparser import generate_sections_file, write_sections_file
from .transformer import Transformer
+
def doc_main(args):
parser = optparse.OptionParser('%prog [options] GIR-file')
@@ -32,14 +34,18 @@ def doc_main(args):
help="Directory to write output to")
parser.add_option("-l", "--language",
action="store", dest="language",
- default="Python",
+ default="c",
help="Output language")
+ parser.add_option("", "--add-include-path",
+ action="append", dest="include_paths", default=[],
+ help="include paths for other GIR files")
+ parser.add_option("", "--write-sections-file",
+ action="store_true", dest="write_sections",
+ help="Generate and write out a sections file")
options, args = parser.parse_args(args)
if not options.output:
raise SystemExit("missing output parameter")
- if not os.path.isdir(options.output):
- raise SystemExit("wrong output parameter: %s" % (options.output, ))
if len(args) < 2:
raise SystemExit("Need an input GIR filename")
@@ -50,9 +56,17 @@ def doc_main(args):
extra_include_dirs = [os.path.join(top_srcdir, 'gir'), top_builddir]
else:
extra_include_dirs = []
+ extra_include_dirs.extend(options.include_paths)
transformer = Transformer.parse_from_gir(args[1], extra_include_dirs)
- writer = MallardWriter(transformer, options.language)
- writer.write(options.output)
+ if options.write_sections:
+ sections_file = generate_sections_file(transformer)
+
+ fp = open(options.output, 'w')
+ write_sections_file(fp, sections_file)
+ fp.close()
+ else:
+ writer = DocWriter(transformer, options.language)
+ writer.write(options.output)
return 0
diff --git a/giscanner/doctemplates/C/class.tmpl b/giscanner/doctemplates/C/class.tmpl
new file mode 100644
index 00000000..3f18b021
--- /dev/null
+++ b/giscanner/doctemplates/C/class.tmpl
@@ -0,0 +1,2 @@
+<%! page_type="guide" %>\
+<%inherit file="/class.tmpl"/>
diff --git a/giscanner/doctemplates/C/constructor.tmpl b/giscanner/doctemplates/C/constructor.tmpl
new file mode 100644
index 00000000..a03d2825
--- /dev/null
+++ b/giscanner/doctemplates/C/constructor.tmpl
@@ -0,0 +1 @@
+<%inherit file="./function.tmpl"/>
diff --git a/giscanner/doctemplates/C/default.tmpl b/giscanner/doctemplates/C/default.tmpl
new file mode 100644
index 00000000..b66ae926
--- /dev/null
+++ b/giscanner/doctemplates/C/default.tmpl
@@ -0,0 +1 @@
+<%inherit file="/base.tmpl"/>
diff --git a/giscanner/doctemplates/C/enum.tmpl b/giscanner/doctemplates/C/enum.tmpl
new file mode 100644
index 00000000..1523e0df
--- /dev/null
+++ b/giscanner/doctemplates/C/enum.tmpl
@@ -0,0 +1,2 @@
+<%! page_type="guide" %>\
+<%inherit file="/base.tmpl"/>
diff --git a/giscanner/doctemplates/C/function.tmpl b/giscanner/doctemplates/C/function.tmpl
new file mode 100644
index 00000000..8d669438
--- /dev/null
+++ b/giscanner/doctemplates/C/function.tmpl
@@ -0,0 +1,61 @@
+<%inherit file="/base.tmpl"/>
+<%block name="info">
+ ${formatter.format_xref(node.parent, type="guide", group=page_kind)}
+ <api:function>
+ <api:returns>
+ <api:type>${formatter.format_type(node.retval.type) | x}</api:type>
+ </api:returns>
+ <api:name>${formatter.format_function_name(node)}</api:name>
+% for arg in formatter.get_parameters(node):
+% if arg.type.ctype == '<varargs>':
+ <api:varargs/>
+% else:
+ <api:arg>
+ <api:type>${formatter.format_type(arg.type) | x}</api:type>
+ <api:name>${formatter.format_parameter_name(node, arg)}</api:name>
+ </api:arg>
+% endif
+% endfor
+ </api:function>
+</%block>
+<%block name="synopsis">
+<synopsis><code mime="text/x-csrc">
+${node.retval.type.ctype} ${formatter.format_function_name(node)} (\
+% if not formatter.get_parameters(node):
+void\
+% else:
+% for ix, arg in enumerate(formatter.get_parameters(node)):
+% if ix != 0:
+${' ' * (len(formatter.format_type(node.retval.type)) + len(formatter.format_function_name(node)) + 3)}\
+% endif
+% if arg.type.ctype == '<varargs>':
+...\
+% else:
+${formatter.format_type(arg.type) | x} ${arg.argname}\
+% endif
+% if ix != len(formatter.get_parameters(node)) - 1:
+,
+% endif
+% endfor
+% endif
+);
+</code></synopsis>
+</%block>
+<%block name="details">
+% if formatter.get_parameters(node) or node.retval:
+<terms>
+% for arg in formatter.get_parameters(node):
+<item>
+<title><code>${arg.argname}</code></title>
+${formatter.format(node, arg.doc)}
+</item>
+% endfor
+% if node.retval:
+<item>
+<title><code>Returns</code></title>
+${formatter.format(node, node.retval.doc)}
+</item>
+% endif
+</terms>
+% endif
+</%block>
diff --git a/giscanner/doctemplates/C/method.tmpl b/giscanner/doctemplates/C/method.tmpl
new file mode 100644
index 00000000..a03d2825
--- /dev/null
+++ b/giscanner/doctemplates/C/method.tmpl
@@ -0,0 +1 @@
+<%inherit file="./function.tmpl"/>
diff --git a/giscanner/doctemplates/C/namespace.tmpl b/giscanner/doctemplates/C/namespace.tmpl
new file mode 100644
index 00000000..cb8195da
--- /dev/null
+++ b/giscanner/doctemplates/C/namespace.tmpl
@@ -0,0 +1 @@
+<%inherit file="/namespace.tmpl"/>
diff --git a/giscanner/doctemplates/C/property.tmpl b/giscanner/doctemplates/C/property.tmpl
new file mode 100644
index 00000000..6ec9e8e1
--- /dev/null
+++ b/giscanner/doctemplates/C/property.tmpl
@@ -0,0 +1,5 @@
+<%inherit file="/base.tmpl"/>
+<%block name="info">
+ ${formatter.format_xref(node.parent, type="guide", group=page_kind)}
+ <title type="link" role="topic">${node.name}</title>
+</%block>
diff --git a/giscanner/doctemplates/C/record.tmpl b/giscanner/doctemplates/C/record.tmpl
new file mode 100644
index 00000000..b66ae926
--- /dev/null
+++ b/giscanner/doctemplates/C/record.tmpl
@@ -0,0 +1 @@
+<%inherit file="/base.tmpl"/>
diff --git a/giscanner/doctemplates/C/signal.tmpl b/giscanner/doctemplates/C/signal.tmpl
new file mode 100644
index 00000000..28c0b740
--- /dev/null
+++ b/giscanner/doctemplates/C/signal.tmpl
@@ -0,0 +1,5 @@
+<%inherit file="./function.tmpl"/>
+<%block name="info">
+ ${formatter.format_xref(node.parent, type="guide", group=page_kind)}
+ <title type="link" role="topic">${node.name}</title>
+</%block>
diff --git a/giscanner/doctemplates/C/vfunc.tmpl b/giscanner/doctemplates/C/vfunc.tmpl
new file mode 100644
index 00000000..aa0394f4
--- /dev/null
+++ b/giscanner/doctemplates/C/vfunc.tmpl
@@ -0,0 +1,4 @@
+<%inherit file="./function.tmpl"/>
+<%block name="info">
+ ${formatter.format_xref(node.parent, type="guide", group=page_kind)}
+</%block>
diff --git a/giscanner/doctemplates/Gjs/class.tmpl b/giscanner/doctemplates/Gjs/class.tmpl
new file mode 100644
index 00000000..887c646b
--- /dev/null
+++ b/giscanner/doctemplates/Gjs/class.tmpl
@@ -0,0 +1,18 @@
+<%inherit file="/class.tmpl"/>
+<%block name="synopsis">
+ <synopsis><code>
+const ${namespace.name} = imports.gi.${namespace.name};
+
+let ${formatter.to_underscores(node.name).lower()} = new ${namespace.name}.${node.name}(\
+% if len(node.properties) > 0:
+{
+% for ix, property_ in enumerate(node.properties):
+% if property_.construct or property_.construct_only or property_.writable:
+ <link xref='${namespace.name}.${node.name}-${property_.name}'>${property_.name.replace('-', '_')}</link>: value,
+% endif
+% endfor
+}\
+% endif
+);
+ </code></synopsis>
+</%block>
diff --git a/giscanner/doctemplates/Gjs/constructor.tmpl b/giscanner/doctemplates/Gjs/constructor.tmpl
new file mode 100644
index 00000000..a03d2825
--- /dev/null
+++ b/giscanner/doctemplates/Gjs/constructor.tmpl
@@ -0,0 +1 @@
+<%inherit file="./function.tmpl"/>
diff --git a/giscanner/doctemplates/Gjs/default.tmpl b/giscanner/doctemplates/Gjs/default.tmpl
new file mode 100644
index 00000000..b66ae926
--- /dev/null
+++ b/giscanner/doctemplates/Gjs/default.tmpl
@@ -0,0 +1 @@
+<%inherit file="/base.tmpl"/>
diff --git a/giscanner/doctemplates/Gjs/enum.tmpl b/giscanner/doctemplates/Gjs/enum.tmpl
new file mode 100644
index 00000000..35cdd439
--- /dev/null
+++ b/giscanner/doctemplates/Gjs/enum.tmpl
@@ -0,0 +1,13 @@
+<%inherit file="/base.tmpl"/>
+<%block name="details">
+% if node.members:
+<terms>
+% for member in node.members:
+<item>
+<title><code>${node.name}.${member.name.upper()}</code></title>
+${formatter.format(node, member.doc)}
+</item>
+% endfor
+</terms>
+% endif
+</%block>
diff --git a/giscanner/doctemplates/Gjs/function.tmpl b/giscanner/doctemplates/Gjs/function.tmpl
new file mode 100644
index 00000000..e0fd9612
--- /dev/null
+++ b/giscanner/doctemplates/Gjs/function.tmpl
@@ -0,0 +1,48 @@
+<%inherit file="/base.tmpl"/>
+<%block name="info">
+ ${formatter.format_xref(node.parent, type="guide", group=page_kind)}
+ <api:function>
+ <api:returns>
+ <api:type>${formatter.format_type(node.retval.type) | x}</api:type>
+ </api:returns>
+ <api:name>${node.symbol}</api:name>
+% for arg in formatter.get_parameters(node):
+% if arg.type.ctype == '<varargs>':
+ <api:varargs/>
+% else:
+ <api:arg>
+ <api:type>${formatter.format_type(arg.type) | x}</api:type>
+ <api:name>${formatter.format_parameter_name(node, arg)}</api:name>
+ </api:arg>
+% endif
+% endfor
+ </api:function>
+</%block>
+<%block name="synopsis">
+<synopsis><code mime="text/x-gjs">
+function \
+${node.name}(\
+${', '.join('%s:%s' % (arg.argname, formatter.format_type(arg.type)) for arg in formatter.get_parameters(node))}\
+):${formatter.format_type(node.retval.type)} {
+ // Gjs wrapper for ${node.symbol}()
+}
+</code></synopsis>
+</%block>
+<%block name="details">
+% if formatter.get_parameters(node) or node.retval:
+<terms>
+% for arg in formatter.get_parameters(node):
+<item>
+<title><code>${arg.argname}</code></title>
+${formatter.format(node, arg.doc)}
+</item>
+% endfor
+% if node.retval and node.retval.type.ctype != 'void':
+<item>
+<title><code>Returns</code></title>
+${formatter.format(node, node.retval.doc)}
+</item>
+% endif
+</terms>
+% endif
+</%block>
diff --git a/giscanner/doctemplates/Gjs/method.tmpl b/giscanner/doctemplates/Gjs/method.tmpl
new file mode 100644
index 00000000..a03d2825
--- /dev/null
+++ b/giscanner/doctemplates/Gjs/method.tmpl
@@ -0,0 +1 @@
+<%inherit file="./function.tmpl"/>
diff --git a/giscanner/doctemplates/Gjs/namespace.tmpl b/giscanner/doctemplates/Gjs/namespace.tmpl
new file mode 100644
index 00000000..4d80c2a5
--- /dev/null
+++ b/giscanner/doctemplates/Gjs/namespace.tmpl
@@ -0,0 +1,2 @@
+<%! page_type="guide" %>\
+<%inherit file="/namespace.tmpl"/>
diff --git a/giscanner/doctemplates/Gjs/property.tmpl b/giscanner/doctemplates/Gjs/property.tmpl
new file mode 100644
index 00000000..3316a00c
--- /dev/null
+++ b/giscanner/doctemplates/Gjs/property.tmpl
@@ -0,0 +1,10 @@
+<%inherit file="/base.tmpl"/>
+<%block name="info">
+ ${formatter.format_xref(node.parent, type="guide", group=page_kind)}
+ <title type="link" role="topic">${node.name}</title>
+</%block>
+<%block name="synopsis">
+<synopsis><code mime="text/x-python">
+"${node.name}" ${formatter.format_type(node.type)} : ${formatter.format_property_flags(node)}
+</code></synopsis>
+</%block>
diff --git a/giscanner/doctemplates/Gjs/record.tmpl b/giscanner/doctemplates/Gjs/record.tmpl
new file mode 100644
index 00000000..1523e0df
--- /dev/null
+++ b/giscanner/doctemplates/Gjs/record.tmpl
@@ -0,0 +1,2 @@
+<%! page_type="guide" %>\
+<%inherit file="/base.tmpl"/>
diff --git a/giscanner/doctemplates/Gjs/signal.tmpl b/giscanner/doctemplates/Gjs/signal.tmpl
new file mode 100644
index 00000000..084d9743
--- /dev/null
+++ b/giscanner/doctemplates/Gjs/signal.tmpl
@@ -0,0 +1,37 @@
+<%inherit file="/base.tmpl"/>
+<%block name="info">
+ ${formatter.format_xref(node.parent, type="guide", group=page_kind)}
+ <title type="link" role="topic">${node.name}</title>
+</%block>
+<%block name="synopsis">
+<synopsis><code mime="text/x-python">
+function callback(${formatter.to_underscores(node.parent.name).lower()}, \
+% for arg in formatter.get_parameters(node):
+${arg.argname}:${formatter.format_type(arg.type)}, \
+% endfor
+):${formatter.format_type(node.retval.type)};
+</code></synopsis>
+</%block>
+<%block name="details">
+<terms>
+<item>
+<title><code>${formatter.to_underscores(node.parent.name).lower()}</code></title>
+<p>instance of ${formatter.format_xref(node.parent)} that is emitting the signal</p>
+</item>
+% for arg in formatter.get_parameters(node):
+<item>
+<title><code>${arg.argname}</code></title>
+${formatter.format(node, arg.doc)}
+</item>
+% endfor
+% if node.retval and \
+ node.retval.type.ctype != 'void' and \
+ node.retval.type.ctype is not None:
+<item>
+<title><code>Returns</code></title>
+${formatter.format(node, node.retval.doc)}
+</item>
+% endif
+</terms>
+</%block>
+
diff --git a/giscanner/doctemplates/Gjs/vfunc.tmpl b/giscanner/doctemplates/Gjs/vfunc.tmpl
new file mode 100644
index 00000000..1cbe511c
--- /dev/null
+++ b/giscanner/doctemplates/Gjs/vfunc.tmpl
@@ -0,0 +1,27 @@
+<%inherit file="/base.tmpl"/>
+<%block name="synopsis">
+<synopsis><code mime="text/x-gjs">
+function vfunc_${node.name}(\
+${', '.join('%s:%s' % (arg.argname, formatter.format_type(arg.type)) for arg in formatter.get_parameters(node))}\
+):${formatter.format_type(node.retval.type)} {
+}
+</code></synopsis>
+</%block>
+<%block name="details">
+% if formatter.get_parameters(node) or node.retval:
+<terms>
+% for arg in formatter.get_parameters(node):
+<item>
+<title><code>${arg.argname}</code></title>
+${formatter.format(node, arg.doc)}
+</item>
+% endfor
+% if node.retval and node.retval.type.ctype != 'void':
+<item>
+<title><code>Returns</code></title>
+${formatter.format(node, node.retval.doc)}
+</item>
+% endif
+</terms>
+% endif
+</%block>
diff --git a/giscanner/doctemplates/Python/class.tmpl b/giscanner/doctemplates/Python/class.tmpl
new file mode 100644
index 00000000..435b31a5
--- /dev/null
+++ b/giscanner/doctemplates/Python/class.tmpl
@@ -0,0 +1,17 @@
+<%inherit file="/class.tmpl"/>
+<%block name="synopsis">
+ <synopsis><code>
+from gi.repository import ${namespace.name}
+
+${formatter.to_underscores(node.name).lower()} = ${namespace.name}.${node.name}(\
+% for ix, property_ in enumerate(node.properties):
+% if property_.construct or property_.construct_only or property_.writable:
+<link xref='${namespace.name}.${node.name}-${property_.name}'>${property_.name.replace('-', '_')}</link>=value\
+% if ix != len(node.properties) - 1:
+, \
+% endif
+% endif
+% endfor
+)\
+ </code></synopsis>
+</%block>
diff --git a/giscanner/doctemplates/Python/constructor.tmpl b/giscanner/doctemplates/Python/constructor.tmpl
new file mode 100644
index 00000000..a03d2825
--- /dev/null
+++ b/giscanner/doctemplates/Python/constructor.tmpl
@@ -0,0 +1 @@
+<%inherit file="./function.tmpl"/>
diff --git a/giscanner/doctemplates/Python/default.tmpl b/giscanner/doctemplates/Python/default.tmpl
new file mode 100644
index 00000000..b66ae926
--- /dev/null
+++ b/giscanner/doctemplates/Python/default.tmpl
@@ -0,0 +1 @@
+<%inherit file="/base.tmpl"/>
diff --git a/giscanner/doctemplates/Python/enum.tmpl b/giscanner/doctemplates/Python/enum.tmpl
new file mode 100644
index 00000000..35cdd439
--- /dev/null
+++ b/giscanner/doctemplates/Python/enum.tmpl
@@ -0,0 +1,13 @@
+<%inherit file="/base.tmpl"/>
+<%block name="details">
+% if node.members:
+<terms>
+% for member in node.members:
+<item>
+<title><code>${node.name}.${member.name.upper()}</code></title>
+${formatter.format(node, member.doc)}
+</item>
+% endfor
+</terms>
+% endif
+</%block>
diff --git a/giscanner/doctemplates/Python/function.tmpl b/giscanner/doctemplates/Python/function.tmpl
new file mode 100644
index 00000000..072a1185
--- /dev/null
+++ b/giscanner/doctemplates/Python/function.tmpl
@@ -0,0 +1,53 @@
+<%inherit file="/base.tmpl"/>
+<%block name="info">
+ ${formatter.format_xref(node.parent, type="guide", group=page_kind)}
+ <api:function>
+ <api:returns>
+ <api:type>${formatter.format_type(node.retval.type) | x}</api:type>
+ </api:returns>
+ <api:name>${node.symbol}</api:name>
+% for arg in formatter.get_parameters(node):
+% if arg.type.ctype == '<varargs>':
+ <api:varargs/>
+% else:
+ <api:arg>
+ <api:type>${formatter.format_type(arg.type) | x}</api:type>
+ <api:name>${formatter.format_parameter_name(node, arg)}</api:name>
+ </api:arg>
+% endif
+% endfor
+ </api:function>
+</%block>
+<%block name="synopsis">
+<synopsis><code mime="text/x-python">
+% if formatter.get_parameters(node):
+@accepts(\
+${', '.join((formatter.format_type(arg.type) for arg in formatter.get_parameters(node)))}\
+)
+% endif
+@returns(${formatter.format_type(node.retval.type) | x})
+def \
+${node.name}(\
+${', '.join((formatter.format_parameter_name(node, arg) for arg in formatter.get_parameters(node)))}\
+):
+ # Python wrapper for ${node.symbol}()
+</code></synopsis>
+</%block>
+<%block name="details">
+% if formatter.get_parameters(node) or node.retval:
+<terms>
+% for ix, arg in enumerate(formatter.get_parameters(node)):
+<item>
+<title><code>${formatter.format_parameter_name(node, arg)}</code></title>
+${formatter.format(node, arg.doc)}
+</item>
+% endfor
+% if node.retval and node.retval.type.ctype != 'void':
+<item>
+<title><code>Returns</code></title>
+{formatter.format(node, node.retval.doc)}
+</item>
+% endif
+</terms>
+% endif
+</%block>
diff --git a/giscanner/doctemplates/Python/method.tmpl b/giscanner/doctemplates/Python/method.tmpl
new file mode 100644
index 00000000..a03d2825
--- /dev/null
+++ b/giscanner/doctemplates/Python/method.tmpl
@@ -0,0 +1 @@
+<%inherit file="./function.tmpl"/>
diff --git a/giscanner/doctemplates/Python/namespace.tmpl b/giscanner/doctemplates/Python/namespace.tmpl
new file mode 100644
index 00000000..4d80c2a5
--- /dev/null
+++ b/giscanner/doctemplates/Python/namespace.tmpl
@@ -0,0 +1,2 @@
+<%! page_type="guide" %>\
+<%inherit file="/namespace.tmpl"/>
diff --git a/giscanner/doctemplates/Python/property.tmpl b/giscanner/doctemplates/Python/property.tmpl
new file mode 100644
index 00000000..3316a00c
--- /dev/null
+++ b/giscanner/doctemplates/Python/property.tmpl
@@ -0,0 +1,10 @@
+<%inherit file="/base.tmpl"/>
+<%block name="info">
+ ${formatter.format_xref(node.parent, type="guide", group=page_kind)}
+ <title type="link" role="topic">${node.name}</title>
+</%block>
+<%block name="synopsis">
+<synopsis><code mime="text/x-python">
+"${node.name}" ${formatter.format_type(node.type)} : ${formatter.format_property_flags(node)}
+</code></synopsis>
+</%block>
diff --git a/giscanner/doctemplates/Python/record.tmpl b/giscanner/doctemplates/Python/record.tmpl
new file mode 100644
index 00000000..1523e0df
--- /dev/null
+++ b/giscanner/doctemplates/Python/record.tmpl
@@ -0,0 +1,2 @@
+<%! page_type="guide" %>\
+<%inherit file="/base.tmpl"/>
diff --git a/giscanner/doctemplates/Python/signal.tmpl b/giscanner/doctemplates/Python/signal.tmpl
new file mode 100644
index 00000000..dc931107
--- /dev/null
+++ b/giscanner/doctemplates/Python/signal.tmpl
@@ -0,0 +1,42 @@
+<%inherit file="/base.tmpl"/>
+<%block name="info">
+ ${formatter.format_xref(node.parent, type="guide", group=page_kind)}
+ <title type="link" role="topic">${node.name}</title>
+</%block>
+<%block name="synopsis">
+<synopsis><code mime="text/x-python">
+def callback(${formatter.to_underscores(node.parent.name).lower()}, \
+% for arg in formatter.get_parameters(node):
+${arg.argname}, \
+% endfor
+user_param1, ...)
+</code></synopsis>
+</%block>
+<%block name="details">
+<terms>
+<item>
+<title><code>${formatter.to_underscores(node.parent.name).lower()}</code></title>
+<p>instance of ${formatter.format_xref(node.parent)} that is emitting the signal</p>
+</item>
+% for arg in formatter.get_parameters(node):
+<item>
+<title><code>${arg.argname}</code></title>
+${formatter.format(node, arg.doc)}
+</item>
+% endfor
+<title><code>user_param1</code></title>
+<p>first user parameter (if any) specified with the connect() method</p>
+<item>
+<title><code>...</code></title>
+<p>additional user parameters (if any)</p>
+</item>
+% if node.retval and \
+ node.retval.type.ctype != 'void' and \
+ node.retval.type.ctype is not None:
+<item>
+<title><code>Returns</code></title>
+${formatter.format(node, node.retval.doc)}
+</item>
+% endif
+</terms>
+</%block>
diff --git a/giscanner/doctemplates/Python/vfunc.tmpl b/giscanner/doctemplates/Python/vfunc.tmpl
new file mode 100644
index 00000000..98a30932
--- /dev/null
+++ b/giscanner/doctemplates/Python/vfunc.tmpl
@@ -0,0 +1,33 @@
+<%inherit file="/base.tmpl"/>
+<%block name="synopsis">
+<synopsis><code mime="text/x-python">
+% if formatter.get_parameters(node):
+@accepts(\
+${', '.join((formatter.format_type(arg.type) for arg in formatter.get_parameters(node)))}\
+)
+% endif
+@returns(${formatter.format_type(node.retval.type) | x})
+def \
+do_${node.name}(\
+${', '.join((arg.argname for arg in formatter.get_parameters(node)))}\
+):
+</code></synopsis>
+</%block>
+<%block name="details">
+% if formatter.get_parameters(node) or node.retval:
+<terms>
+% for arg in formatter.get_parameters(node):
+<item>
+<title><code>${arg.argname}</code></title>
+${formatter.format(node, arg.doc)}
+</item>
+% endfor
+% if node.retval and node.retval.type.ctype != 'void':
+<item>
+<title><code>Returns</code></title>
+${formatter.format(node, node.retval.doc)}
+</item>
+% endif
+</terms>
+% endif
+</%block>
diff --git a/giscanner/doctemplates/base.tmpl b/giscanner/doctemplates/base.tmpl
new file mode 100644
index 00000000..78980773
--- /dev/null
+++ b/giscanner/doctemplates/base.tmpl
@@ -0,0 +1,29 @@
+<%! page_type="topic" %>\
+<?xml version="1.0"?>
+<page id="${page_id}"
+ type="${self.attr.page_type}"
+ style="${page_kind}"
+ xmlns="http://projectmallard.org/1.0/"
+ xmlns:api="http://projectmallard.org/experimental/api/"
+ xmlns:ui="http://projectmallard.org/1.0/ui/">
+ <info>
+ <%block name="info">
+ ${formatter.format_xref(node.parent, type="guide", group=page_kind)}
+ </%block>
+ </info>
+ <title><%block name="title">${formatter.format_page_name(node)}</%block></title>
+ <%block name="synopsis">
+ </%block>
+ <%block name="doc">
+ ${formatter.format(node, node.doc)}
+ </%block>
+ <%block name="since_version">
+ % if node.version:
+ <p>Since ${node.version}</p>
+ % endif
+ </%block>
+ <%block name="details">
+ </%block>
+ <%block name="links">
+ </%block>
+</page>
diff --git a/giscanner/doctemplates/class.tmpl b/giscanner/doctemplates/class.tmpl
new file mode 100644
index 00000000..7f8b6869
--- /dev/null
+++ b/giscanner/doctemplates/class.tmpl
@@ -0,0 +1,40 @@
+<%! page_type="guide" %>\
+<%inherit file="/base.tmpl"/>
+<%block name="details">
+ <synopsis>
+ <title>Hierarchy</title>
+ <tree>
+% for class_ in formatter.get_class_hierarchy(node):
+ <item>
+ <code>${class_.namespace.name}.${class_.name}</code>
+% endfor
+% for class_ in formatter.get_class_hierarchy(node):
+ </item>
+% endfor
+ </tree>
+ </synopsis>
+</%block>
+<%block name="links">
+ <links type="topic" ui:expanded="true"
+ api:type="function" api:mime="${formatter.mime_type}"
+ groups="method" style="linklist">
+ <title>Methods</title>
+ </links>
+ <links type="topic" ui:expanded="true"
+ api:type="function" api:mime="${formatter.mime_type}"
+ groups="function" style="linklist">
+ <title>Functions</title>
+ </links>
+ <links type="topic" ui:expanded="true" groups="property" style="linklist">
+ <title>Properties</title>
+ </links>
+ <links type="topic" ui:expanded="true" groups="signal" style="linklist">
+ <title>Signals</title>
+ </links>
+ <links type="topic" ui:expanded="true" groups="vfunc" style="linklist">
+ <title>Virtual functions</title>
+ </links>
+ <links type="topic" ui:expanded="true" groups="#first #default #last" style="linklist">
+ <title>Other</title>
+ </links>
+</%block>
diff --git a/giscanner/doctemplates/namespace.tmpl b/giscanner/doctemplates/namespace.tmpl
new file mode 100644
index 00000000..bb58bb16
--- /dev/null
+++ b/giscanner/doctemplates/namespace.tmpl
@@ -0,0 +1,19 @@
+<%! page_type="guide" %>\
+<%inherit file="/base.tmpl"/>
+<%block name="doc">
+</%block>
+<%block name="info">
+</%block>
+<%block name="links">
+ <links type="topic" ui:expanded="true" groups="class" style="linklist">
+ <title>Classes</title>
+ </links>
+ <links type="topic" ui:expanded="true" groups="function" style="linklist">
+ <title>Functions</title>
+ </links>
+ <links type="topic" ui:expanded="true" groups="#first #default #last" style="linklist">
+ <title>Other</title>
+ </links>
+</%block>
+<%block name="since_version">
+</%block>
diff --git a/giscanner/docwriter.py b/giscanner/docwriter.py
new file mode 100644
index 00000000..982ab37c
--- /dev/null
+++ b/giscanner/docwriter.py
@@ -0,0 +1,644 @@
+#!/usr/bin/env python
+# -*- Mode: Python -*-
+# GObject-Introspection - a framework for introspecting GObject libraries
+# Copyright (C) 2010 Zach Goldberg
+# Copyright (C) 2011 Johan Dahlin
+# Copyright (C) 2011 Shaun McCance
+#
+# This program is free software; you can redistribute it and/or
+# modify it under the terms of the GNU General Public License
+# as published by the Free Software Foundation; either version 2
+# of the License, or (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.
+#
+
+import os
+import re
+import tempfile
+
+from xml.sax import saxutils
+from mako.lookup import TemplateLookup
+
+from . import ast, xmlwriter
+from .utils import to_underscores
+
+
+def make_page_id(node, recursive=False):
+ if isinstance(node, ast.Namespace):
+ if recursive:
+ return node.name
+ else:
+ return 'index'
+
+ if hasattr(node, '_chain') and node._chain:
+ parent = node._chain[-1]
+ else:
+ parent = None
+
+ if parent is None:
+ return '%s.%s' % (node.namespace.name, node.name)
+
+ if isinstance(node, (ast.Property, ast.Signal, ast.VFunction)):
+ return '%s-%s' % (make_page_id(parent, recursive=True), node.name)
+ else:
+ return '%s.%s' % (make_page_id(parent, recursive=True), node.name)
+
+
+def get_node_kind(node):
+ if isinstance(node, ast.Namespace):
+ node_kind = 'namespace'
+ elif isinstance(node, (ast.Class, ast.Interface)):
+ node_kind = 'class'
+ elif isinstance(node, ast.Record):
+ node_kind = 'record'
+ elif isinstance(node, ast.Function):
+ if node.is_method:
+ node_kind = 'method'
+ elif node.is_constructor:
+ node_kind = 'constructor'
+ else:
+ node_kind = 'function'
+ elif isinstance(node, ast.Enum):
+ node_kind = 'enum'
+ elif isinstance(node, ast.Property) and node.parent is not None:
+ node_kind = 'property'
+ elif isinstance(node, ast.Signal) and node.parent is not None:
+ node_kind = 'signal'
+ elif isinstance(node, ast.VFunction) and node.parent is not None:
+ node_kind = 'vfunc'
+ else:
+ node_kind = 'default'
+
+ return node_kind
+
+
+class TemplatedScanner(object):
+ def __init__(self, specs):
+ self.specs = self.unmangle_specs(specs)
+ self.regex = self.make_regex(self.specs)
+
+ def unmangle_specs(self, specs):
+ mangled = re.compile('<<([a-zA-Z_:]+)>>')
+ specdict = dict((name.lstrip('!'), spec) for name, spec in specs)
+
+ def unmangle(spec, name=None):
+ def replace_func(match):
+ child_spec_name = match.group(1)
+
+ if ':' in child_spec_name:
+ pattern_name, child_spec_name = child_spec_name.split(':', 1)
+ else:
+ pattern_name = None
+
+ child_spec = specdict[child_spec_name]
+ # Force all child specs of this one to be unnamed
+ unmangled = unmangle(child_spec, None)
+ if pattern_name and name:
+ return '(?P<%s_%s>%s)' % (name, pattern_name, unmangled)
+ else:
+ return unmangled
+
+ return mangled.sub(replace_func, spec)
+
+ return [(name, unmangle(spec, name)) for name, spec in specs]
+
+ def make_regex(self, specs):
+ regex = '|'.join('(?P<%s>%s)' % (name, spec) for name, spec in specs
+ if not name.startswith('!'))
+ return re.compile(regex)
+
+ def get_properties(self, name, match):
+ groupdict = match.groupdict()
+ properties = {name: groupdict.pop(name)}
+ name = name + "_"
+ for group, value in groupdict.iteritems():
+ if group.startswith(name):
+ key = group[len(name):]
+ properties[key] = value
+ return properties
+
+ def scan(self, text):
+ pos = 0
+ while True:
+ match = self.regex.search(text, pos)
+ if match is None:
+ break
+
+ start = match.start()
+ if start > pos:
+ yield ('other', text[pos:start], None)
+
+ pos = match.end()
+ name = match.lastgroup
+ yield (name, match.group(0), self.get_properties(name, match))
+
+ if pos < len(text):
+ yield ('other', text[pos:], None)
+
+
+class DocstringScanner(TemplatedScanner):
+ def __init__(self):
+ specs = [
+ ('!alpha', r'[a-zA-Z0-9_]+'),
+ ('!alpha_dash', r'[a-zA-Z0-9_-]+'),
+ ('property', r'#<<type_name:alpha>>:(<<property_name:alpha_dash>>)'),
+ ('signal', r'#<<type_name:alpha>>::(<<signal_name:alpha_dash>>)'),
+ ('type_name', r'#(<<type_name:alpha>>)'),
+ ('enum_value', r'%(<<member_name:alpha>>)'),
+ ('parameter', r'@<<param_name:alpha>>'),
+ ('function_call', r'<<symbol_name:alpha>>\(\)'),
+ ]
+
+ super(DocstringScanner, self).__init__(specs)
+
+
+class DocFormatter(object):
+ def __init__(self, transformer):
+ self._transformer = transformer
+ self._scanner = DocstringScanner()
+
+ def escape(self, text):
+ return saxutils.escape(text)
+
+ def should_render_node(self, node):
+ if isinstance(node, ast.Constant):
+ return False
+
+ return True
+
+ def format(self, node, doc):
+ if doc is None:
+ return ''
+
+ result = ''
+ for para in doc.split('\n\n'):
+ result += '<p>'
+ result += self.format_inline(node, para)
+ result += '</p>'
+ return result
+
+ def _resolve_type(self, ident):
+ try:
+ matches = self._transformer.split_ctype_namespaces(ident)
+ except ValueError:
+ return None
+ for namespace, name in matches:
+ node = namespace.get(name)
+ if node:
+ return node
+ return None
+
+ def _resolve_symbol(self, symbol):
+ try:
+ matches = self._transformer.split_csymbol_namespaces(symbol)
+ except ValueError:
+ return None
+ for namespace, name in matches:
+ node = namespace.get_by_symbol(symbol)
+ if node:
+ return node
+ return None
+
+ def _find_thing(self, list_, name):
+ for item in list_:
+ if item.name == name:
+ return item
+ raise KeyError("Could not find %s" % (name, ))
+
+ def _process_other(self, node, match, props):
+ return self.escape(match)
+
+ def _process_property(self, node, match, props):
+ type_node = self._resolve_type(props['type_name'])
+ if type_node is None:
+ return match
+
+ try:
+ prop = self._find_thing(type_node.properties, props['property_name'])
+ except (AttributeError, KeyError):
+ return match
+
+ return self.format_xref(prop)
+
+ def _process_signal(self, node, match, props):
+ type_node = self._resolve_type(props['type_name'])
+ if type_node is None:
+ return match
+
+ try:
+ signal = self._find_thing(type_node.signals, props['signal_name'])
+ except (AttributeError, KeyError):
+ return match
+
+ return self.format_xref(signal)
+
+ def _process_type_name(self, node, match, props):
+ type_ = self._resolve_type(props['type_name'])
+ if type_ is None:
+ return match
+
+ return self.format_xref(type_)
+
+ def _process_enum_value(self, node, match, props):
+ member_name = props['member_name']
+
+ try:
+ return '<code>%s</code>' % (self.fundamentals[member_name], )
+ except KeyError:
+ pass
+
+ enum_value = self._resolve_symbol(member_name)
+ if enum_value:
+ return self.format_xref(enum_value)
+
+ return match
+
+ def _process_parameter(self, node, match, props):
+ try:
+ parameter = node.get_parameter(props['param_name'])
+ except (AttributeError, ValueError):
+ return match
+
+ return '<code>%s</code>' % (self.format_parameter_name(node, parameter), )
+
+ def _process_function_call(self, node, match, props):
+ func = self._resolve_symbol(props['symbol_name'])
+ if func is None:
+ return match
+
+ return self.format_xref(func)
+
+ def _process_token(self, node, tok):
+ kind, match, props = tok
+
+ dispatch = {
+ 'other': self._process_other,
+ 'property': self._process_property,
+ 'signal': self._process_signal,
+ 'type_name': self._process_type_name,
+ 'enum_value': self._process_enum_value,
+ 'parameter': self._process_parameter,
+ 'function_call': self._process_function_call,
+ }
+
+ return dispatch[kind](node, match, props)
+
+ def get_parameters(self, node):
+ raise NotImplementedError
+
+ def format_inline(self, node, para):
+ tokens = self._scanner.scan(para)
+ words = [self._process_token(node, tok) for tok in tokens]
+ return ''.join(words)
+
+ def format_parameter_name(self, node, parameter):
+ if isinstance(parameter.type, ast.Varargs):
+ return "..."
+ else:
+ return parameter.argname
+
+ def format_function_name(self, func):
+ raise NotImplementedError
+
+ def format_type(self, type_):
+ raise NotImplementedError
+
+ def format_page_name(self, node):
+ if isinstance(node, ast.Namespace):
+ return 'Index'
+ elif isinstance(node, ast.Function):
+ return self.format_function_name(node)
+ elif isinstance(node, ast.Property) and node.parent is not None:
+ return '%s:%s' % (self.format_page_name(node.parent), node.name)
+ elif isinstance(node, ast.Signal) and node.parent is not None:
+ return '%s::%s' % (self.format_page_name(node.parent), node.name)
+ elif isinstance(node, ast.VFunction) and node.parent is not None:
+ return '%s::%s' % (self.format_page_name(node.parent), node.name)
+ else:
+ return make_page_id(node)
+
+ def format_xref(self, node, **attrdict):
+ if node is None:
+ attrs = [('xref', 'index')] + attrdict.items()
+ return xmlwriter.build_xml_tag('link', attrs)
+ elif isinstance(node, ast.Member):
+ # Enum/BitField members are linked to the main enum page.
+ return self.format_xref(node.parent, **attrdict) + '.' + node.name
+ else:
+ attrs = [('xref', make_page_id(node))] + attrdict.items()
+ return xmlwriter.build_xml_tag('link', attrs)
+
+ def format_property_flags(self, property_, construct_only=False):
+ flags = []
+ if property_.readable and not construct_only:
+ flags.append("Read")
+ if property_.writable and not construct_only:
+ flags.append("Write")
+ if property_.construct:
+ flags.append("Construct")
+ if property_.construct_only:
+ flags.append("Construct Only")
+
+ return " / ".join(flags)
+
+ def to_underscores(self, string):
+ return to_underscores(string)
+
+ def get_class_hierarchy(self, node):
+ parent_chain = [node]
+
+ while node.parent_type:
+ node = self._transformer.lookup_typenode(node.parent_type)
+ parent_chain.append(node)
+
+ parent_chain.reverse()
+ return parent_chain
+
+
+class DocFormatterC(DocFormatter):
+ language = "C"
+ mime_type = "text/x-csrc"
+
+ fundamentals = {
+ "TRUE": "TRUE",
+ "FALSE": "FALSE",
+ "NULL": "NULL",
+ }
+
+ def format_type(self, type_):
+ if isinstance(type_, ast.Array):
+ return self.format_type(type_.element_type) + '*'
+ elif type_.ctype is not None:
+ return type_.ctype
+ elif type_.target_fundamental:
+ return type_.target_fundamental
+ else:
+ node = self._transformer.lookup_typenode(type_)
+ return getattr(node, 'ctype')
+
+ def format_function_name(self, func):
+ if isinstance(func, ast.Function):
+ return func.symbol
+ else:
+ return func.name
+
+ def get_parameters(self, node):
+ return node.all_parameters
+
+
+class DocFormatterIntrospectableBase(DocFormatter):
+ def should_render_node(self, node):
+ if isinstance(node, ast.Record) and node.is_gtype_struct_for is not None:
+ return False
+
+ if not getattr(node, "introspectable", True):
+ return False
+
+ return super(DocFormatterIntrospectableBase, self).should_render_node(node)
+
+
+class DocFormatterPython(DocFormatterIntrospectableBase):
+ language = "Python"
+ mime_type = "text/python"
+
+ fundamentals = {
+ "TRUE": "True",
+ "FALSE": "False",
+ "NULL": "None",
+ }
+
+ def should_render_node(self, node):
+ if getattr(node, "is_constructor", False):
+ return False
+
+ return super(DocFormatterPython, self).should_render_node(node)
+
+ def is_method(self, node):
+ if getattr(node, "is_method", False):
+ return True
+
+ if isinstance(node, ast.VFunction):
+ return True
+
+ return False
+
+ def format_parameter_name(self, node, parameter):
+ # Force "self" for the first parameter of a method
+ if self.is_method(node) and parameter is node.instance_parameter:
+ return "self"
+ elif isinstance(parameter.type, ast.Varargs):
+ return "..."
+ else:
+ return parameter.argname
+
+ def format_fundamental_type(self, name):
+ fundamental_types = {
+ "utf8": "unicode",
+ "gunichar": "unicode",
+ "gchar": "str",
+ "guchar": "str",
+ "gboolean": "bool",
+ "gint": "int",
+ "guint": "int",
+ "glong": "int",
+ "gulong": "int",
+ "gint64": "int",
+ "guint64": "int",
+ "gfloat": "float",
+ "gdouble": "float",
+ "gchararray": "str",
+ "GParam": "GLib.Param",
+ "PyObject": "object",
+ "GStrv": "[str]",
+ "GVariant": "GLib.Variant"}
+
+ return fundamental_types.get(name, name)
+
+ def format_type(self, type_):
+ if isinstance(type_, (ast.List, ast.Array)):
+ return '[' + self.format_type(type_.element_type) + ']'
+ elif isinstance(type_, ast.Map):
+ return '{%s: %s}' % (self.format_type(type_.key_type),
+ self.format_type(type_.value_type))
+ elif type_.target_giname is not None:
+ return type_.target_giname
+ else:
+ return self.format_fundamental_type(type_.target_fundamental)
+
+ def format_function_name(self, func):
+ if func.parent is not None:
+ return "%s.%s" % (self.format_page_name(func.parent), func.name)
+ else:
+ return func.name
+
+ def get_parameters(self, node):
+ return node.all_parameters
+
+
+class DocFormatterGjs(DocFormatterIntrospectableBase):
+ language = "Gjs"
+ mime_type = "text/x-gjs"
+
+ fundamentals = {
+ "TRUE": "true",
+ "FALSE": "false",
+ "NULL": "null",
+ }
+
+ def is_method(self, node):
+ if getattr(node, "is_method", False):
+ return True
+
+ if isinstance(node, ast.VFunction):
+ return True
+
+ return False
+
+ def format_fundamental_type(self, name):
+ fundamental_types = {
+ "utf8": "String",
+ "gunichar": "String",
+ "gchar": "String",
+ "guchar": "String",
+ "gboolean": "Boolean",
+ "gint": "Number",
+ "guint": "Number",
+ "glong": "Number",
+ "gulong": "Number",
+ "gint64": "Number",
+ "guint64": "Number",
+ "gfloat": "Number",
+ "gdouble": "Number",
+ "gchararray": "String",
+ "GParam": "GLib.Param",
+ "PyObject": "Object",
+ "GStrv": "[String]",
+ "GVariant": "GLib.Variant"}
+
+ return fundamental_types.get(name, name)
+
+ def format_type(self, type_):
+ if isinstance(type_, (ast.List, ast.Array)):
+ return '[' + self.format_type(type_.element_type) + ']'
+ elif isinstance(type_, ast.Map):
+ return '{%s: %s}' % (self.format_type(type_.key_type),
+ self.format_type(type_.value_type))
+ elif type_.target_fundamental == "none":
+ return "void"
+ elif type_.target_giname is not None:
+ return type_.target_giname
+ else:
+ return self.format_fundamental_type(type_.target_fundamental)
+
+ def format_function_name(self, func):
+ if func.is_method:
+ return "%s.prototype.%s" % (self.format_page_name(func.parent), func.name)
+ elif func.is_constructor:
+ return "%s.%s" % (self.format_page_name(func.parent), func.name)
+ else:
+ return func.name
+
+ def get_parameters(self, node):
+ skip = []
+ for param in node.parameters:
+ if param.direction == ast.PARAM_DIRECTION_OUT:
+ skip.append(param)
+ if param.closure_name is not None:
+ skip.append(node.get_parameter(param.closure_name))
+ if param.destroy_name is not None:
+ skip.append(node.get_parameter(param.destroy_name))
+ if isinstance(param.type, ast.Array) and param.type.length_param_name is not None:
+ skip.append(node.get_parameter(param.type.length_param_name))
+
+ params = []
+ for param in node.parameters:
+ if param not in skip:
+ params.append(param)
+ return params
+
+
+LANGUAGES = {
+ "c": DocFormatterC,
+ "python": DocFormatterPython,
+ "gjs": DocFormatterGjs,
+}
+
+
+class DocWriter(object):
+ def __init__(self, transformer, language):
+ self._transformer = transformer
+
+ try:
+ formatter_class = LANGUAGES[language.lower()]
+ except KeyError:
+ raise SystemExit("Unsupported language: %s" % (language, ))
+
+ self._formatter = formatter_class(self._transformer)
+ self._language = self._formatter.language
+
+ self._lookup = self._get_template_lookup()
+
+ def _get_template_lookup(self):
+ if 'UNINSTALLED_INTROSPECTION_SRCDIR' in os.environ:
+ top_srcdir = os.environ['UNINSTALLED_INTROSPECTION_SRCDIR']
+ srcdir = os.path.join(top_srcdir, 'giscanner')
+ else:
+ srcdir = os.path.dirname(__file__)
+
+ template_dir = os.path.join(srcdir, 'doctemplates')
+
+ return TemplateLookup(directories=[template_dir],
+ module_directory=tempfile.mkdtemp(),
+ output_encoding='utf-8')
+
+ def write(self, output):
+ try:
+ os.makedirs(output)
+ except OSError:
+ # directory already made
+ pass
+
+ self._walk_node(output, self._transformer.namespace, [])
+ self._transformer.namespace.walk(lambda node, chain: self._walk_node(output, node, chain))
+
+ def _walk_node(self, output, node, chain):
+ if isinstance(node, ast.Function) and node.moved_to is not None:
+ return False
+ if getattr(node, 'disguised', False):
+ return False
+ if self._formatter.should_render_node(node):
+ self._render_node(node, chain, output)
+ return True
+ return False
+
+ def _render_node(self, node, chain, output):
+ namespace = self._transformer.namespace
+
+ # A bit of a hack...maybe this should be an official API
+ node._chain = list(chain)
+
+ page_kind = get_node_kind(node)
+ template_name = '%s/%s.tmpl' % (self._language, page_kind)
+ page_id = make_page_id(node)
+
+ template = self._lookup.get_template(template_name)
+ result = template.render(namespace=namespace,
+ node=node,
+ page_id=page_id,
+ page_kind=page_kind,
+ formatter=self._formatter)
+
+ output_file_name = os.path.join(os.path.abspath(output),
+ page_id + '.page')
+ fp = open(output_file_name, 'w')
+ fp.write(result)
+ fp.close()
diff --git a/giscanner/dumper.py b/giscanner/dumper.py
index f78d2ae8..157b24da 100644
--- a/giscanner/dumper.py
+++ b/giscanner/dumper.py
@@ -45,7 +45,9 @@ main(int argc, char **argv)
GError *error = NULL;
const char *introspect_dump_prefix = "--introspect-dump=";
+#if !GLIB_CHECK_VERSION(2,35,0)
g_type_init ();
+#endif
%(init_sections)s
@@ -83,7 +85,13 @@ class DumpCompiler(object):
self._compiler_cmd = os.environ.get('CC', 'gcc')
self._linker_cmd = os.environ.get('CC', self._compiler_cmd)
self._pkgconfig_cmd = os.environ.get('PKG_CONFIG', 'pkg-config')
-
+ self._pkgconfig_msvc_flags = ''
+ # Enable the --msvc-syntax pkg-config flag when
+ # the Microsoft compiler is used
+ # (This is the other way to check whether Visual C++ is used subsequently)
+ if 'clang' not in self._compiler_cmd:
+ if 'cl' in self._compiler_cmd:
+ self._pkgconfig_msvc_flags = '--msvc-syntax'
self._uninst_srcdir = os.environ.get(
'UNINSTALLED_INTROSPECTION_SRCDIR')
self._packages = ['gio-2.0 gmodule-2.0']
@@ -143,7 +151,12 @@ class DumpCompiler(object):
f.write("\n};\n")
f.close()
- o_path = self._generate_tempfile(tmpdir, '.o')
+ # Microsoft compilers generate intermediate .obj files
+ # during compilation, unlike .o files like GCC and others
+ if self._pkgconfig_msvc_flags:
+ o_path = self._generate_tempfile(tmpdir, '.obj')
+ else:
+ o_path = self._generate_tempfile(tmpdir, '.o')
if os.name == 'nt':
ext = 'exe'
@@ -154,14 +167,14 @@ class DumpCompiler(object):
try:
self._compile(o_path, c_path)
- except CompilerError, e:
+ except CompilerError as e:
if not utils.have_debug_flag('save-temps'):
shutil.rmtree(tmpdir)
raise SystemExit('compilation of temporary binary failed:' + str(e))
try:
self._link(bin_path, o_path)
- except LinkerError, e:
+ except LinkerError as e:
if not utils.have_debug_flag('save-temps'):
shutil.rmtree(tmpdir)
raise SystemExit('linking of temporary binary failed: ' + str(e))
@@ -176,8 +189,14 @@ class DumpCompiler(object):
return os.path.join(tmpdir, tmpl)
def _run_pkgconfig(self, flag):
+ # Enable the --msvc-syntax pkg-config flag when
+ # the Microsoft compiler is used
+ if self._pkgconfig_msvc_flags:
+ cmd = [self._pkgconfig_cmd, self._pkgconfig_msvc_flags, flag]
+ else:
+ cmd = [self._pkgconfig_cmd, flag]
proc = subprocess.Popen(
- [self._pkgconfig_cmd, flag] + self._packages,
+ cmd + self._packages,
stdout=subprocess.PIPE)
return proc.communicate()[0].split()
@@ -188,6 +207,12 @@ class DumpCompiler(object):
# header of the library being introspected
if self._compiler_cmd == 'gcc' and not self._options.init_sections:
args.append('-Wall')
+ # The Microsoft compiler uses different option flags for
+ # silencing warnings on deprecated function usage
+ if self._pkgconfig_msvc_flags:
+ args.append("-wd4996")
+ else:
+ args.append("-Wno-deprecated-declarations")
pkgconfig_flags = self._run_pkgconfig('--cflags')
args.extend(pkgconfig_flags)
cflags = os.environ.get('CFLAGS', '')
@@ -195,7 +220,12 @@ class DumpCompiler(object):
args.append(cflag)
for include in self._options.cpp_includes:
args.append('-I' + include)
- args.extend(['-c', '-o', output])
+ # The Microsoft compiler uses different option flags for
+ # compilation result output
+ if self._pkgconfig_msvc_flags:
+ args.extend(['-c', '-Fe' + output, '-Fo' + output])
+ else:
+ args.extend(['-c', '-o', output])
for source in sources:
if not os.path.exists(source):
raise CompilerError(
@@ -207,7 +237,7 @@ class DumpCompiler(object):
sys.stdout.flush()
try:
subprocess.check_call(args)
- except subprocess.CalledProcessError, e:
+ except subprocess.CalledProcessError as e:
raise CompilerError(e)
def _link(self, output, *sources):
@@ -221,7 +251,12 @@ class DumpCompiler(object):
args.append('--silent')
args.extend(self._linker_cmd.split())
- args.extend(['-o', output])
+ # We can use -o for the Microsoft compiler/linker,
+ # but it is considered deprecated usage with that
+ if self._pkgconfig_msvc_flags:
+ args.extend(['-Fe' + output])
+ else:
+ args.extend(['-o', output])
if libtool:
if os.name == 'nt':
args.append('-export-all-symbols')
@@ -256,7 +291,7 @@ class DumpCompiler(object):
sys.stdout.flush()
try:
subprocess.check_call(args)
- except subprocess.CalledProcessError, e:
+ except subprocess.CalledProcessError as e:
raise LinkerError(e)
def _add_link_internal_args(self, args, libtool):
@@ -264,26 +299,52 @@ class DumpCompiler(object):
# is being built in the current directory.
# Search the current directory first
- args.append('-L.')
+ # (This flag is not supported nor needed for Visual C++)
+ if self._pkgconfig_msvc_flags == '':
+ args.append('-L.')
# https://bugzilla.gnome.org/show_bug.cgi?id=625195
if not libtool:
- args.append('-Wl,-rpath=.')
+ # We don't have -Wl,-rpath for Visual C++, and that's
+ # going to cause a problem. Instead, link to internal
+ # libraries by deducing the .lib file name using
+ # the namespace name and version
+ if self._pkgconfig_msvc_flags:
+ if self._options.namespace_version:
+ args.append(str.lower(self._options.namespace_name) +
+ '-' +
+ self._options.namespace_version + '.lib')
+ else:
+ args.append(str.lower(self._options.namespace_name) + '.lib')
+ else:
+ args.append('-Wl,-rpath=.')
+
+ # Ensure libraries are always linked as we are going to use ldd to work
+ # out their names later
+ if not libtool and self._pkgconfig_msvc_flags == '':
+ args.append('-Wl,--no-as-needed')
for library in self._options.libraries:
- if library.endswith(".la"): # explicitly specified libtool library
- args.append(library)
- else:
- args.append('-l' + library)
+ # Visual C++: We have the needed .lib files now, and we need to link
+ # to .lib files, not the .dll as the --library option specifies the
+ # .dll(s) the .gir file refers to
+ if self._pkgconfig_msvc_flags == '':
+ if library.endswith(".la"): # explicitly specified libtool library
+ args.append(library)
+ else:
+ args.append('-l' + library)
for library_path in self._options.library_paths:
- args.append('-L' + library_path)
- if os.path.isabs(library_path):
- if libtool:
- args.append('-rpath')
- args.append(library_path)
- else:
- args.append('-Wl,-rpath=' + library_path)
+ # Not used/needed on Visual C++, and -Wl,-rpath options
+ # will cause grief
+ if self._pkgconfig_msvc_flags == '':
+ args.append('-L' + library_path)
+ if os.path.isabs(library_path):
+ if libtool:
+ args.append('-rpath')
+ args.append(library_path)
+ else:
+ args.append('-Wl,-rpath=' + library_path)
args.extend(self._run_pkgconfig('--libs'))
@@ -294,10 +355,14 @@ class DumpCompiler(object):
args.extend(self._run_pkgconfig('--libs'))
for library in self._options.libraries:
- if library.endswith(".la"): # explicitly specified libtool library
- args.append(library)
- else:
- args.append('-l' + library)
+ # The --library option on Windows pass in the .dll file(s) the
+ # .gir files refer to, so don't link to them on Visual C++
+ if self._pkgconfig_msvc_flags == '':
+ if library.endswith(".la"): # explicitly specified libtool library
+ args.append(library)
+ else:
+ args.append('-l' + library)
+
def compile_introspection_binary(options, get_type_functions,
error_quark_functions):
diff --git a/giscanner/gdumpparser.py b/giscanner/gdumpparser.py
index c0b13f4a..568777bd 100644
--- a/giscanner/gdumpparser.py
+++ b/giscanner/gdumpparser.py
@@ -165,7 +165,7 @@ blob containing data gleaned from GObject's primitive introspection."""
try:
try:
subprocess.check_call(args, stdout=sys.stdout, stderr=sys.stderr)
- except subprocess.CalledProcessError, e:
+ except subprocess.CalledProcessError as e:
# Clean up temporaries
raise SystemExit(e)
return parse(out_path)
@@ -203,8 +203,7 @@ blob containing data gleaned from GObject's primitive introspection."""
def _initparse_gobject_record(self, record):
if (record.name.startswith('ParamSpec')
- and not record.name in ('ParamSpecPool', 'ParamSpecClass',
- 'ParamSpecTypeInfo')):
+ and not record.name in ('ParamSpecPool', 'ParamSpecClass', 'ParamSpecTypeInfo')):
parent = None
if record.name != 'ParamSpec':
parent = ast.Type(target_giname='GObject.ParamSpec')
@@ -251,7 +250,7 @@ blob containing data gleaned from GObject's primitive introspection."""
(get_type, c_symbol_prefix) = self._split_type_and_symbol_prefix(xmlnode)
try:
enum_name = self._transformer.strip_identifier(type_name)
- except TransformerException, e:
+ except TransformerException as e:
message.fatal(e)
# The scanned member values are more accurate than the values that the
@@ -280,7 +279,6 @@ blob containing data gleaned from GObject's primitive introspection."""
member.attrib['name'],
member.attrib['nick']))
-
if xmlnode.tag == 'flags':
klass = ast.Bitfield
else:
@@ -316,7 +314,7 @@ different --identifier-prefix.""" % (xmlnode.attrib['name'], self._namespace.ide
(get_type, c_symbol_prefix) = self._split_type_and_symbol_prefix(xmlnode)
try:
object_name = self._transformer.strip_identifier(type_name)
- except TransformerException, e:
+ except TransformerException as e:
message.fatal(e)
node = ast.Class(object_name, None,
gtype_name=type_name,
@@ -346,7 +344,7 @@ different --identifier-prefix.""" % (xmlnode.attrib['name'], self._namespace.ide
(get_type, c_symbol_prefix) = self._split_type_and_symbol_prefix(xmlnode)
try:
interface_name = self._transformer.strip_identifier(type_name)
- except TransformerException, e:
+ except TransformerException as e:
message.fatal(e)
node = ast.Interface(interface_name, None,
gtype_name=type_name,
@@ -391,7 +389,7 @@ different --identifier-prefix.""" % (xmlnode.attrib['name'], self._namespace.ide
try:
name = self._transformer.strip_identifier(type_name)
- except TransformerException, e:
+ except TransformerException as e:
message.fatal(e)
# This one doesn't go in the main namespace; we associate it with
# the struct or union
@@ -437,7 +435,7 @@ different --identifier-prefix.""" % (xmlnode.attrib['name'], self._namespace.ide
if i == 0:
argname = 'object'
else:
- argname = 'p%s' % (i-1, )
+ argname = 'p%s' % (i - 1, )
pctype = parameter.attrib['type']
ptype = ast.Type.create_from_gtype_name(pctype)
param = ast.Parameter(argname, ptype)
@@ -465,7 +463,7 @@ different --identifier-prefix.""" % (xmlnode.attrib['name'], self._namespace.ide
(get_type, c_symbol_prefix) = self._split_type_and_symbol_prefix(xmlnode)
try:
fundamental_name = self._transformer.strip_identifier(type_name)
- except TransformerException, e:
+ except TransformerException as e:
message.warn(e)
return
@@ -509,7 +507,7 @@ different --identifier-prefix.""" % (xmlnode.attrib['name'], self._namespace.ide
def _pair_boxed_type(self, boxed):
try:
name = self._transformer.strip_identifier(boxed.gtype_name)
- except TransformerException, e:
+ except TransformerException as e:
message.fatal(e)
pair_node = self._namespace.get(name)
if not pair_node:
@@ -526,8 +524,7 @@ different --identifier-prefix.""" % (xmlnode.attrib['name'], self._namespace.ide
return False
def _strip_class_suffix(self, name):
- if (name.endswith('Class') or
- name.endswith('Iface')):
+ if (name.endswith('Class') or name.endswith('Iface')):
return name[:-5]
elif name.endswith('Interface'):
return name[:-9]
diff --git a/giscanner/girparser.py b/giscanner/girparser.py
index eb53a3c4..2538036a 100644
--- a/giscanner/girparser.py
+++ b/giscanner/girparser.py
@@ -46,9 +46,6 @@ class GIRParser(object):
def __init__(self, types_only=False):
self._types_only = types_only
- self._shared_libraries = []
- self._includes = set()
- self._pkgconfig_packages = set()
self._namespace = None
self._filename_stack = []
@@ -62,10 +59,9 @@ class GIRParser(object):
self._filename_stack.pop()
def parse_tree(self, tree):
- self._includes.clear()
self._namespace = None
- self._shared_libraries = []
self._pkgconfig_packages = set()
+ self._includes = set()
self._c_includes = set()
self._c_prefix = None
self._parse_api(tree.getroot())
@@ -73,26 +69,9 @@ class GIRParser(object):
def get_namespace(self):
return self._namespace
- def get_shared_libraries(self):
- return self._shared_libraries
-
- def get_includes(self):
- return self._includes
-
- def get_c_includes(self):
- return self._c_includes
-
def get_c_prefix(self):
return self._c_prefix
- def get_pkgconfig_packages(self):
- if not hasattr(self, '_pkgconfig_packages'):
- self._pkgconfig_packages = []
- return self._pkgconfig_packages
-
- def get_doc(self):
- return parse(self._filename)
-
# Private
def _find_first_child(self, node, name_or_names):
@@ -122,9 +101,8 @@ class GIRParser(object):
assert root.tag == _corens('repository')
version = root.attrib['version']
if version != COMPATIBLE_GIR_VERSION:
- raise SystemExit("%s: Incompatible version %s (supported: %s)" \
- % (self._get_current_file(),
- version, COMPATIBLE_GIR_VERSION))
+ raise SystemExit("%s: Incompatible version %s (supported: %s)" %
+ (self._get_current_file(), version, COMPATIBLE_GIR_VERSION))
for node in root.getchildren():
if node.tag == _corens('include'):
@@ -143,12 +121,14 @@ class GIRParser(object):
if symbol_prefixes:
symbol_prefixes = symbol_prefixes.split(',')
self._namespace = ast.Namespace(ns.attrib['name'],
- ns.attrib['version'],
- identifier_prefixes=identifier_prefixes,
- symbol_prefixes=symbol_prefixes)
+ ns.attrib['version'],
+ identifier_prefixes=identifier_prefixes,
+ symbol_prefixes=symbol_prefixes)
if 'shared-library' in ns.attrib:
- self._shared_libraries.extend(
- ns.attrib['shared-library'].split(','))
+ self._namespace.shared_libraries = ns.attrib['shared-library'].split(',')
+ self._namespace.includes = self._includes
+ self._namespace.c_includes = self._c_includes
+ self._namespace.exported_packages = self._pkgconfig_packages
parser_methods = {
_corens('alias'): self._parse_alias,
@@ -159,8 +139,7 @@ class GIRParser(object):
_corens('interface'): self._parse_object_interface,
_corens('record'): self._parse_record,
_corens('union'): self._parse_union,
- _glibns('boxed'): self._parse_boxed,
- }
+ _glibns('boxed'): self._parse_boxed}
if not self._types_only:
parser_methods[_corens('constant')] = self._parse_constant
@@ -172,8 +151,7 @@ class GIRParser(object):
method(node)
def _parse_include(self, node):
- include = ast.Include(node.attrib['name'],
- node.attrib['version'])
+ include = ast.Include(node.attrib['name'], node.attrib['version'])
self._includes.add(include)
def _parse_pkgconfig_package(self, node):
@@ -184,9 +162,7 @@ class GIRParser(object):
def _parse_alias(self, node):
typeval = self._parse_type(node)
- alias = ast.Alias(node.attrib['name'],
- typeval,
- node.attrib.get(_cns('type')))
+ alias = ast.Alias(node.attrib['name'], typeval, node.attrib.get(_cns('type')))
self._parse_generic_attribs(node, alias)
self._namespace.append(alias)
@@ -218,9 +194,9 @@ class GIRParser(object):
else:
parent_type = None
- ctor_args = [node.attrib['name'],
- parent_type]
- ctor_kwargs = {'gtype_name': node.attrib[_glibns('type-name')],
+ ctor_kwargs = {'name': node.attrib['name'],
+ 'parent_type': parent_type,
+ 'gtype_name': node.attrib[_glibns('type-name')],
'get_type': node.attrib[_glibns('get-type')],
'c_symbol_prefix': node.attrib.get(_cns('symbol-prefix')),
'ctype': node.attrib.get(_cns('type'))}
@@ -234,7 +210,7 @@ class GIRParser(object):
else:
raise AssertionError(node)
- obj = klass(*ctor_args, **ctor_kwargs)
+ obj = klass(**ctor_kwargs)
self._parse_generic_attribs(node, obj)
type_struct = node.attrib.get(_glibns('type-struct'))
if type_struct:
@@ -247,10 +223,11 @@ class GIRParser(object):
'set-value-func', 'get-value-func']:
func_name = node.attrib.get(_glibns(func_id))
obj.__dict__[func_id.replace('-', '_')] = func_name
- self._namespace.append(obj)
if self._types_only:
+ self._namespace.append(obj)
return
+
for iface in self._find_children(node, _corens('implements')):
obj.interfaces.append(self._namespace.type_from_name(iface.attrib['name']))
for iface in self._find_children(node, _corens('prerequisite')):
@@ -272,12 +249,14 @@ class GIRParser(object):
func = self._parse_function_common(ctor, ast.Function, obj)
func.is_constructor = True
obj.constructors.append(func)
- obj.fields.extend(self._parse_fields(node))
+ obj.fields.extend(self._parse_fields(node, obj))
for prop in self._find_children(node, _corens('property')):
obj.properties.append(self._parse_property(prop, obj))
for signal in self._find_children(node, _glibns('signal')):
obj.signals.append(self._parse_function_common(signal, ast.Signal, obj))
+ self._namespace.append(obj)
+
def _parse_callback(self, node):
callback = self._parse_function_common(node, ast.Callback)
self._namespace.append(callback)
@@ -286,6 +265,18 @@ class GIRParser(object):
function = self._parse_function_common(node, ast.Function)
self._namespace.append(function)
+ def _parse_parameter(self, node):
+ typeval = self._parse_type(node)
+ param = ast.Parameter(node.attrib.get('name'),
+ typeval,
+ node.attrib.get('direction') or ast.PARAM_DIRECTION_IN,
+ node.attrib.get('transfer-ownership'),
+ node.attrib.get('allow-none') == '1',
+ node.attrib.get('scope'),
+ node.attrib.get('caller-allocates') == '1')
+ self._parse_generic_attribs(node, param)
+ return param
+
def _parse_function_common(self, node, klass, parent=None):
name = node.attrib['name']
returnnode = node.find(_corens('return-value'))
@@ -323,17 +314,11 @@ class GIRParser(object):
parameters_node = node.find(_corens('parameters'))
if (parameters_node is not None):
+ paramnode = self._find_first_child(parameters_node, _corens('instance-parameter'))
+ if paramnode:
+ func.instance_parameter = self._parse_parameter(paramnode)
for paramnode in self._find_children(parameters_node, _corens('parameter')):
- typeval = self._parse_type(paramnode)
- param = ast.Parameter(paramnode.attrib.get('name'),
- typeval,
- paramnode.attrib.get('direction') or ast.PARAM_DIRECTION_IN,
- paramnode.attrib.get('transfer-ownership'),
- paramnode.attrib.get('allow-none') == '1',
- paramnode.attrib.get('scope'),
- paramnode.attrib.get('caller-allocates') == '1')
- self._parse_generic_attribs(paramnode, param)
- parameters.append(param)
+ parameters.append(self._parse_parameter(paramnode))
for i, paramnode in enumerate(self._find_children(parameters_node,
_corens('parameter'))):
param = parameters[i]
@@ -356,12 +341,12 @@ class GIRParser(object):
self._namespace.track(func)
return func
- def _parse_fields(self, node):
+ def _parse_fields(self, node, obj):
res = []
names = (_corens('field'), _corens('record'), _corens('union'), _corens('callback'))
for child in node.getchildren():
if child.tag in names:
- fieldobj = self._parse_field(child)
+ fieldobj = self._parse_field(child, obj)
res.append(fieldobj)
return res
@@ -376,16 +361,18 @@ class GIRParser(object):
compound.foreign = True
self._parse_generic_attribs(node, compound)
if not self._types_only:
- compound.fields.extend(self._parse_fields(node))
+ compound.fields.extend(self._parse_fields(node, compound))
for method in self._find_children(node, _corens('method')):
- compound.methods.append(
- self._parse_function_common(method, ast.Function, compound))
+ func = self._parse_function_common(method, ast.Function, compound)
+ func.is_method = True
+ compound.methods.append(func)
for func in self._find_children(node, _corens('function')):
compound.static_methods.append(
self._parse_function_common(func, ast.Function, compound))
for ctor in self._find_children(node, _corens('constructor')):
- compound.constructors.append(
- self._parse_function_common(ctor, ast.Function, compound))
+ func = self._parse_function_common(ctor, ast.Function, compound)
+ func.is_constructor = True
+ compound.constructors.append(func)
return compound
def _parse_record(self, node, anonymous=False):
@@ -435,7 +422,8 @@ class GIRParser(object):
return ast.Type(ctype=ctype)
elif name in ['GLib.List', 'GLib.SList']:
subchild = self._find_first_child(typenode,
- map(_corens, ('callback', 'array', 'varargs', 'type')))
+ map(_corens, ('callback', 'array',
+ 'varargs', 'type')))
if subchild is not None:
element_type = self._parse_type(typenode)
else:
@@ -446,9 +434,7 @@ class GIRParser(object):
subchildren_types = map(self._parse_type_simple, subchildren)
while len(subchildren_types) < 2:
subchildren_types.append(ast.TYPE_ANY)
- return ast.Map(subchildren_types[0],
- subchildren_types[1],
- ctype=ctype)
+ return ast.Map(subchildren_types[0], subchildren_types[1], ctype=ctype)
else:
return self._namespace.type_from_name(name, ctype)
else:
@@ -470,8 +456,8 @@ class GIRParser(object):
lenidx = typenode.attrib.get('length')
if lenidx is not None:
idx = int(lenidx)
- assert idx < len(parent.parameters), "%r %d >= %d" \
- % (parent, idx, len(parent.parameters))
+ assert idx < len(parent.parameters), "%r %d >= %d" % (parent, idx,
+ len(parent.parameters))
typeval.length_param_name = parent.parameters[idx].argname
def _parse_boxed(self, node):
@@ -480,9 +466,11 @@ class GIRParser(object):
get_type=node.attrib[_glibns('get-type')],
c_symbol_prefix=node.attrib.get(_cns('symbol-prefix')))
self._parse_generic_attribs(node, obj)
- self._namespace.append(obj)
+
if self._types_only:
+ self._namespace.append(obj)
return
+
for method in self._find_children(node, _corens('method')):
func = self._parse_function_common(method, ast.Function, obj)
func.is_method = True
@@ -493,8 +481,9 @@ class GIRParser(object):
for callback in self._find_children(node, _corens('callback')):
obj.fields.append(
self._parse_function_common(callback, ast.Callback, obj))
+ self._namespace.append(obj)
- def _parse_field(self, node):
+ def _parse_field(self, node, parent):
type_node = None
anonymous_node = None
if node.tag in map(_corens, ('record', 'union')):
@@ -514,23 +503,24 @@ class GIRParser(object):
assert node.tag == _corens('field'), node.tag
type_node = self._parse_type(node)
field = ast.Field(node.attrib.get('name'),
- type_node,
- node.attrib.get('readable') != '0',
- node.attrib.get('writable') == '1',
- node.attrib.get('bits'),
- anonymous_node=anonymous_node)
+ type_node,
+ node.attrib.get('readable') != '0',
+ node.attrib.get('writable') == '1',
+ node.attrib.get('bits'),
+ anonymous_node=anonymous_node)
field.private = node.attrib.get('private') == '1'
+ field.parent = parent
self._parse_generic_attribs(node, field)
return field
def _parse_property(self, node, parent):
prop = ast.Property(node.attrib['name'],
- self._parse_type(node),
- node.attrib.get('readable') != '0',
- node.attrib.get('writable') == '1',
- node.attrib.get('construct') == '1',
- node.attrib.get('construct-only') == '1',
- node.attrib.get('transfer-ownership'))
+ self._parse_type(node),
+ node.attrib.get('readable') != '0',
+ node.attrib.get('writable') == '1',
+ node.attrib.get('construct') == '1',
+ node.attrib.get('construct-only') == '1',
+ node.attrib.get('transfer-ownership'))
self._parse_generic_attribs(node, prop)
prop.parent = parent
return prop
@@ -570,12 +560,16 @@ class GIRParser(object):
obj.error_domain = glib_error_domain
obj.ctype = ctype
self._parse_generic_attribs(node, obj)
- self._namespace.append(obj)
if self._types_only:
+ self._namespace.append(obj)
return
- for member in self._find_children(node, _corens('member')):
- members.append(self._parse_member(member))
+
+ for member_node in self._find_children(node, _corens('member')):
+ member = self._parse_member(member_node)
+ member.parent = obj
+ members.append(member)
for func_node in self._find_children(node, _corens('function')):
func = self._parse_function_common(func_node, ast.Function)
obj.static_methods.append(func)
+ self._namespace.append(obj)
diff --git a/giscanner/girwriter.py b/giscanner/girwriter.py
index 97f81616..e7af2533 100644
--- a/giscanner/girwriter.py
+++ b/giscanner/girwriter.py
@@ -28,40 +28,32 @@ from .xmlwriter import XMLWriter
# Compatible changes we just make inline
COMPATIBLE_GIR_VERSION = '1.2'
+
class GIRWriter(XMLWriter):
- def __init__(self, namespace, shlibs, includes, pkgs, c_includes):
+ def __init__(self, namespace):
super(GIRWriter, self).__init__()
self.write_comment(
-'''This file was automatically generated from C sources - DO NOT EDIT!
-To affect the contents of this file, edit the original C definitions,
-and/or use gtk-doc annotations. ''')
- self._write_repository(namespace, shlibs, includes, pkgs,
- c_includes)
-
- def _write_repository(self, namespace, shlibs, includes=None,
- packages=None, c_includes=None):
- if includes is None:
- includes = frozenset()
- if packages is None:
- packages = frozenset()
- if c_includes is None:
- c_includes = frozenset()
+ 'This file was automatically generated from C sources - DO NOT EDIT!\n'
+ 'To affect the contents of this file, edit the original C definitions,\n'
+ 'and/or use gtk-doc annotations. ')
+ self._write_repository(namespace)
+
+ def _write_repository(self, namespace):
attrs = [
('version', COMPATIBLE_GIR_VERSION),
('xmlns', 'http://www.gtk.org/introspection/core/1.0'),
('xmlns:c', 'http://www.gtk.org/introspection/c/1.0'),
- ('xmlns:glib', 'http://www.gtk.org/introspection/glib/1.0'),
- ]
+ ('xmlns:glib', 'http://www.gtk.org/introspection/glib/1.0')]
with self.tagcontext('repository', attrs):
- for include in sorted(includes):
+ for include in sorted(namespace.includes):
self._write_include(include)
- for pkg in sorted(set(packages)):
+ for pkg in sorted(set(namespace.exported_packages)):
self._write_pkgconfig_pkg(pkg)
- for c_include in sorted(set(c_includes)):
+ for c_include in sorted(set(namespace.c_includes)):
self._write_c_include(c_include)
self._namespace = namespace
- self._write_namespace(namespace, shlibs)
+ self._write_namespace(namespace)
self._namespace = None
def _write_include(self, include):
@@ -76,10 +68,10 @@ and/or use gtk-doc annotations. ''')
attrs = [('name', c_include)]
self.write_tag('c:include', attrs)
- def _write_namespace(self, namespace, shlibs):
+ def _write_namespace(self, namespace):
attrs = [('name', namespace.name),
('version', namespace.version),
- ('shared-library', ','.join(shlibs)),
+ ('shared-library', ','.join(namespace.shared_libraries)),
('c:identifier-prefixes', ','.join(namespace.identifier_prefixes)),
('c:symbol-prefixes', ','.join(namespace.symbol_prefixes))]
with self.tagcontext('namespace', attrs):
@@ -134,7 +126,7 @@ and/or use gtk-doc annotations. ''')
for key, value in node.attributes:
self.write_tag('attribute', [('name', key), ('value', value)])
if hasattr(node, 'doc') and node.doc:
- self.write_tag('doc', [('xml:whitespace', 'preserve')],
+ self.write_tag('doc', [('xml:space', 'preserve')],
node.doc)
def _append_node_generic(self, node, attrs):
@@ -170,9 +162,11 @@ and/or use gtk-doc annotations. ''')
with self.tagcontext(tag_name, attrs):
self._write_generic(callable)
self._write_return_type(callable.retval, parent=callable)
- self._write_parameters(callable, callable.parameters)
+ self._write_parameters(callable)
def _write_function(self, func, tag_name='function'):
+ if func.internal_skipped:
+ return
attrs = []
if hasattr(func, 'symbol'):
attrs.append(('c:identifier', func.symbol))
@@ -206,14 +200,16 @@ and/or use gtk-doc annotations. ''')
self._write_generic(return_)
self._write_type(return_.type, function=parent)
- def _write_parameters(self, parent, parameters):
- if not parameters:
+ def _write_parameters(self, callable):
+ if not callable.parameters and callable.instance_parameter is None:
return
with self.tagcontext('parameters'):
- for parameter in parameters:
- self._write_parameter(parent, parameter)
+ if callable.instance_parameter:
+ self._write_parameter(callable, callable.instance_parameter, 'instance-parameter')
+ for parameter in callable.parameters:
+ self._write_parameter(callable, parameter)
- def _write_parameter(self, parent, parameter):
+ def _write_parameter(self, parent, parameter, nodename='parameter'):
attrs = []
if parameter.argname is not None:
attrs.append(('name', parameter.argname))
@@ -236,7 +232,7 @@ and/or use gtk-doc annotations. ''')
attrs.append(('destroy', '%d' % (idx, )))
if parameter.skip:
attrs.append(('skip', '1'))
- with self.tagcontext('parameter', attrs):
+ with self.tagcontext(nodename, attrs):
self._write_generic(parameter)
self._write_type(parameter.type, function=parent)
@@ -296,8 +292,8 @@ and/or use gtk-doc annotations. ''')
attrs.append(('fixed-size', '%d' % (ntype.size, )))
if ntype.length_param_name is not None:
assert function
- attrs.insert(0, ('length', '%d'
- % (function.get_parameter_index(ntype.length_param_name, ))))
+ length = function.get_parameter_index(ntype.length_param_name)
+ attrs.insert(0, ('length', '%d' % (length, )))
with self.tagcontext('array', attrs):
self._write_type(ntype.element_type)
@@ -363,13 +359,17 @@ and/or use gtk-doc annotations. ''')
('c:identifier', member.symbol)]
if member.nick is not None:
attrs.append(('glib:nick', member.nick))
- self.write_tag('member', attrs)
+ with self.tagcontext('member', attrs):
+ self._write_generic(member)
def _write_constant(self, constant):
attrs = [('name', constant.name),
('value', constant.value),
('c:type', constant.ctype)]
+ self._append_version(constant, attrs)
+ self._append_node_generic(constant, attrs)
with self.tagcontext('constant', attrs):
+ self._write_generic(constant)
self._write_type(constant.value_type)
def _write_class(self, node):
@@ -380,9 +380,9 @@ and/or use gtk-doc annotations. ''')
self._append_node_generic(node, attrs)
if isinstance(node, ast.Class):
tag_name = 'class'
- if node.parent is not None:
+ if node.parent_type is not None:
attrs.append(('parent',
- self._type_to_name(node.parent)))
+ self._type_to_name(node.parent_type)))
if node.is_abstract:
attrs.append(('abstract', '1'))
else:
@@ -482,7 +482,7 @@ and/or use gtk-doc annotations. ''')
attrs = list(extra_attrs)
if record.name is not None:
attrs.append(('name', record.name))
- if record.ctype is not None: # the record might be anonymous
+ if record.ctype is not None: # the record might be anonymous
attrs.append(('c:type', record.ctype))
if record.disguised:
attrs.append(('disguised', '1'))
@@ -513,7 +513,7 @@ and/or use gtk-doc annotations. ''')
attrs = []
if union.name is not None:
attrs.append(('name', union.name))
- if union.ctype is not None: # the union might be anonymous
+ if union.ctype is not None: # the union might be anonymous
attrs.append(('c:type', union.ctype))
self._append_version(union, attrs)
self._append_node_generic(union, attrs)
@@ -544,8 +544,7 @@ and/or use gtk-doc annotations. ''')
elif isinstance(field.anonymous_node, ast.Union):
self._write_union(field.anonymous_node)
else:
- raise AssertionError("Unknown field anonymous: %r" \
- % (field.anonymous_node, ))
+ raise AssertionError("Unknown field anonymous: %r" % (field.anonymous_node, ))
else:
attrs = [('name', field.name)]
self._append_node_generic(field, attrs)
@@ -581,4 +580,4 @@ and/or use gtk-doc annotations. ''')
with self.tagcontext('glib:signal', attrs):
self._write_generic(signal)
self._write_return_type(signal.retval)
- self._write_parameters(signal, signal.parameters)
+ self._write_parameters(signal)
diff --git a/giscanner/giscannermodule.c b/giscanner/giscannermodule.c
index 4a854413..182d8438 100644
--- a/giscanner/giscannermodule.c
+++ b/giscanner/giscannermodule.c
@@ -42,8 +42,8 @@
DL_EXPORT(void) init_giscanner(void);
-#define NEW_CLASS(ctype, name, cname) \
-static const PyMethodDef _Py##cname##_methods[]; \
+#define NEW_CLASS(ctype, name, cname, num_methods) \
+static const PyMethodDef _Py##cname##_methods[num_methods]; \
PyTypeObject Py##cname##_Type = { \
PyObject_HEAD_INIT(NULL) \
0, \
@@ -86,9 +86,9 @@ typedef struct {
GISourceScanner *scanner;
} PyGISourceScanner;
-NEW_CLASS (PyGISourceSymbol, "SourceSymbol", GISourceSymbol);
-NEW_CLASS (PyGISourceType, "SourceType", GISourceType);
-NEW_CLASS (PyGISourceScanner, "SourceScanner", GISourceScanner);
+NEW_CLASS (PyGISourceSymbol, "SourceSymbol", GISourceSymbol, 10);
+NEW_CLASS (PyGISourceType, "SourceType", GISourceType, 9);
+NEW_CLASS (PyGISourceScanner, "SourceScanner", GISourceScanner, 8);
/* Symbol */
@@ -414,44 +414,80 @@ pygi_source_scanner_parse_file (PyGISourceScanner *self,
#ifdef _WIN32
/* The file descriptor passed to us is from the C library Python
- * uses. That is msvcr71.dll at least for Python 2.5. This code, at
- * least if compiled with mingw, uses msvcrt.dll, so we cannot use
- * the file descriptor directly. So perform appropriate magic.
+ * uses. That is msvcr71.dll for Python 2.5 and msvcr90.dll for
+ * Python 2.6, 2.7, 3.2 etc; and msvcr100.dll for Python 3.3 and later.
+ * This code, at least if compiled with mingw, uses
+ * msvcrt.dll, so we cannot use the file descriptor directly. So
+ * perform appropriate magic.
*/
+
+ /* If we are using the following combinations,
+ * we can use the file descriptors directly
+ * (Not if a build using WDK is used):
+ * Python 2.6.x/2.7.x with Visual C++ 2008
+ * Python 3.1.x/3.2.x with Visual C++ 2008
+ * Python 3.3+ with Visual C++ 2010
+ */
+
+#if (defined(_MSC_VER) && !defined(USE_WIN_DDK))
+#if (PY_MAJOR_VERSION==2 && PY_MINOR_VERSION>=6 && (_MSC_VER >= 1500 && _MSC_VER < 1600))
+#define MSVC_USE_FD_DIRECTLY 1
+#elif (PY_MAJOR_VERSION==3 && PY_MINOR_VERSION<=2 && (_MSC_VER >= 1500 && _MSC_VER < 1600))
+#define MSVC_USE_FD_DIRECTLY 1
+#elif (PY_MAJOR_VERSION==3 && PY_MINOR_VERSION>=3 && (_MSC_VER >= 1600 && _MSC_VER < 1700))
+#define MSVC_USE_FD_DIRECTLY 1
+#endif
+#endif
+
+#ifndef MSVC_USE_FD_DIRECTLY
{
- HMODULE msvcr71;
- int (*p__get_osfhandle) (int);
+#if defined(PY_MAJOR_VERSION) && PY_MAJOR_VERSION==2 && PY_MINOR_VERSION==5
+#define PYTHON_MSVCRXX_DLL "msvcr71.dll"
+#elif defined(PY_MAJOR_VERSION) && PY_MAJOR_VERSION==2 && PY_MINOR_VERSION==6
+#define PYTHON_MSVCRXX_DLL "msvcr90.dll"
+#elif defined(PY_MAJOR_VERSION) && PY_MAJOR_VERSION==2 && PY_MINOR_VERSION==7
+#define PYTHON_MSVCRXX_DLL "msvcr90.dll"
+#elif defined(PY_MAJOR_VERSION) && PY_MAJOR_VERSION==3 && PY_MINOR_VERSION==2
+#define PYTHON_MSVCRXX_DLL "msvcr90.dll"
+#elif defined(PY_MAJOR_VERSION) && PY_MAJOR_VERSION==3 && PY_MINOR_VERSION>=3
+#define PYTHON_MSVCRXX_DLL "msvcr100.dll"
+#else
+#error This Python version not handled
+#endif
+ HMODULE msvcrxx;
+ intptr_t (*p__get_osfhandle) (int);
HANDLE handle;
- msvcr71 = GetModuleHandle ("msvcr71.dll");
- if (!msvcr71)
- {
- g_print ("No msvcr71.dll loaded.\n");
- return NULL;
- }
+ msvcrxx = GetModuleHandle (PYTHON_MSVCRXX_DLL);
+ if (!msvcrxx)
+ {
+ g_print ("No " PYTHON_MSVCRXX_DLL " loaded.\n");
+ return NULL;
+ }
- p__get_osfhandle = GetProcAddress (msvcr71, "_get_osfhandle");
+ p__get_osfhandle = (intptr_t (*) (int)) GetProcAddress (msvcrxx, "_get_osfhandle");
if (!p__get_osfhandle)
- {
- g_print ("No _get_osfhandle found in msvcr71.dll.\n");
- return NULL;
- }
+ {
+ g_print ("No _get_osfhandle found in " PYTHON_MSVCRXX_DLL ".\n");
+ return NULL;
+ }
- handle = p__get_osfhandle (fd);
+ handle = (HANDLE) p__get_osfhandle (fd);
if (!p__get_osfhandle)
- {
- g_print ("Could not get OS handle from msvcr71 fd.\n");
- return NULL;
- }
+ {
+ g_print ("Could not get OS handle from " PYTHON_MSVCRXX_DLL " fd.\n");
+ return NULL;
+ }
- fd = _open_osfhandle (handle, _O_RDONLY);
+ fd = _open_osfhandle ((intptr_t) handle, _O_RDONLY);
if (fd == -1)
- {
- g_print ("Could not open C fd from OS handle.\n");
- return NULL;
- }
+ {
+ g_print ("Could not open C fd from OS handle.\n");
+ return NULL;
+ }
}
#endif
+#endif
fp = fdopen (fd, "r");
if (!fp)
@@ -479,7 +515,7 @@ pygi_source_scanner_lex_filename (PyGISourceScanner *self,
if (!PyArg_ParseTuple (args, "s:SourceScanner.lex_filename", &filename))
return NULL;
- self->scanner->current_filename = g_strdup (filename);
+ self->scanner->current_filename = g_realpath (filename);
if (!gi_source_scanner_lex_filename (self->scanner, filename))
{
g_print ("Something went wrong during lexing.\n");
@@ -659,7 +695,7 @@ pygi_collect_attributes (PyObject *self,
goto out;
}
- if (!PyTuple_Size (tuple) == 2)
+ if (PyTuple_Size (tuple) != 2)
{
PyErr_SetString(PyExc_IndexError,
"attribute item must be a tuple of length 2");
@@ -725,8 +761,6 @@ init_giscanner(void)
PyObject *m, *d;
gboolean is_uninstalled;
- g_type_init ();
-
/* Hack to avoid having to create a fake directory structure; when
* running uninstalled, the module will be in the top builddir,
* with no _giscanner prefix.
diff --git a/giscanner/introspectablepass.py b/giscanner/introspectablepass.py
index 97ccfe71..3d67c73e 100644
--- a/giscanner/introspectablepass.py
+++ b/giscanner/introspectablepass.py
@@ -21,6 +21,7 @@ from . import ast
from . import message
from .annotationparser import TAG_RETURNS
+
class IntrospectablePass(object):
def __init__(self, transformer, blocks):
@@ -58,7 +59,7 @@ class IntrospectablePass(object):
else:
context = "return value: "
if block:
- return_tag = block.get_tag(TAG_RETURNS)
+ return_tag = block.tags.get(TAG_RETURNS)
if return_tag:
position = return_tag.position
message.warn_node(parent, prefix + context + text,
@@ -79,7 +80,7 @@ class IntrospectablePass(object):
if not node.type.resolved:
self._parameter_warning(parent, node,
-"Unresolved type: %r" % (node.type.unresolved_string, ))
+ "Unresolved type: %r" % (node.type.unresolved_string, ))
parent.introspectable = False
return
@@ -87,23 +88,24 @@ class IntrospectablePass(object):
parent.introspectable = False
return
- if (isinstance(node.type, ast.List)
- and node.type.element_type == ast.TYPE_ANY):
+ if (isinstance(node.type, (ast.List, ast.Array))
+ and node.type.element_type == ast.TYPE_ANY):
self._parameter_warning(parent, node, "Missing (element-type) annotation")
parent.introspectable = False
return
if (is_parameter
- and isinstance(target, ast.Callback)
- and not node.type.target_giname in ('GLib.DestroyNotify',
- 'Gio.AsyncReadyCallback')
- and node.scope is None):
- self._parameter_warning(parent, node,
- ("Missing (scope) annotation for callback" +
- " without GDestroyNotify (valid: %s, %s)")
- % (ast.PARAM_SCOPE_CALL, ast.PARAM_SCOPE_ASYNC))
- parent.introspectable = False
- return
+ and isinstance(target, ast.Callback)
+ and not node.type.target_giname in ('GLib.DestroyNotify', 'Gio.AsyncReadyCallback')
+ and node.scope is None):
+ self._parameter_warning(
+ parent,
+ node,
+ "Missing (scope) annotation for callback without "
+ "GDestroyNotify (valid: %s, %s)" % (ast.PARAM_SCOPE_CALL, ast.PARAM_SCOPE_ASYNC))
+
+ parent.introspectable = False
+ return
if is_return and isinstance(target, ast.Callback):
self._parameter_warning(parent, node, "Callbacks cannot be return values; use (skip)")
@@ -111,12 +113,14 @@ class IntrospectablePass(object):
return
if (is_return
- and isinstance(target, (ast.Record, ast.Union))
- and target.get_type is None
- and not target.foreign):
+ and isinstance(target, (ast.Record, ast.Union))
+ and target.get_type is None
+ and not target.foreign):
if node.transfer != ast.PARAM_TRANSFER_NONE:
- self._parameter_warning(parent, node,
-"Invalid non-constant return of bare structure or union; register as boxed type or (skip)")
+ self._parameter_warning(
+ parent, node,
+ "Invalid non-constant return of bare structure or union; "
+ "register as boxed type or (skip)")
parent.introspectable = False
return
@@ -143,10 +147,10 @@ class IntrospectablePass(object):
# These are not introspectable pending us adding
# larger type tags to the typelib (in theory these could
# be 128 bit or larger)
- if typeval.is_equiv((ast.TYPE_LONG_LONG, ast.TYPE_LONG_ULONG,
- ast.TYPE_LONG_DOUBLE)):
+ elif typeval.is_equiv((ast.TYPE_LONG_LONG, ast.TYPE_LONG_ULONG, ast.TYPE_LONG_DOUBLE)):
return False
- return True
+ else:
+ return True
target = self._transformer.lookup_typenode(typeval)
if not target:
return False
@@ -229,8 +233,8 @@ class IntrospectablePass(object):
def _remove_non_reachable_backcompat_copies(self, obj, stack):
if obj.skip:
return False
- if (isinstance(obj, ast.Function)
- and not obj.introspectable
- and obj.moved_to is not None):
- self._namespace.remove(obj)
+ if (isinstance(obj, ast.Function) and obj.moved_to is not None):
+ # remove functions that are not introspectable
+ if not obj.introspectable:
+ obj.internal_skipped = True
return True
diff --git a/giscanner/libtoolimporter.py b/giscanner/libtoolimporter.py
index 20bd0053..0d26b0c5 100644
--- a/giscanner/libtoolimporter.py
+++ b/giscanner/libtoolimporter.py
@@ -72,5 +72,5 @@ class LibtoolImporter(object):
sys.meta_path.append(cls)
@classmethod
- def __exit__(cls, type, value, traceback):
+ def __exit__(cls, exc_type, exc_val, exc_tb):
sys.meta_path.remove(cls)
diff --git a/giscanner/maintransformer.py b/giscanner/maintransformer.py
index d4163fae..11bfb4cb 100644
--- a/giscanner/maintransformer.py
+++ b/giscanner/maintransformer.py
@@ -35,9 +35,8 @@ from .annotationparser import (OPT_ALLOW_NONE, OPT_ARRAY, OPT_ATTRIBUTE,
OPT_ARRAY_LENGTH, OPT_ARRAY_ZERO_TERMINATED,
OPT_CONSTRUCTOR, OPT_METHOD,
OPT_TRANSFER_NONE, OPT_TRANSFER_FLOATING)
-from .annotationparser import AnnotationParser
-from .transformer import TransformerException
-from .utils import to_underscores, to_underscores_noprefix
+from .utils import to_underscores_noprefix
+
class MainTransformer(object):
@@ -50,12 +49,10 @@ class MainTransformer(object):
# Public API
def transform(self):
- contents = list(self._namespace.itervalues())
- if len(contents) == 0:
- message.fatal("""Namespace is empty; likely causes are:
-* Not including .h files to be scanned
-* Broken --identifier-prefix
-""")
+ if not self._namespace.names:
+ message.fatal('Namespace is empty; likely causes are:\n'
+ '* Not including .h files to be scanned\n'
+ '* Broken --identifier-prefix')
# Some initial namespace surgery
self._namespace.walk(self._pass_fixup_hidden_fields)
@@ -109,23 +106,20 @@ class MainTransformer(object):
def _pass_fixup_hidden_fields(self, node, chain):
"""Hide all callbacks starting with _; the typical
-usage is void (*_gtk_reserved1)(void);"""
- if not isinstance(node, (ast.Class, ast.Interface,
- ast.Record, ast.Union)):
- return True
- for field in node.fields:
- if field is None:
- continue
- if (field.name.startswith('_')
+ usage is void (*_gtk_reserved1)(void);"""
+ if isinstance(node, (ast.Class, ast.Interface, ast.Record, ast.Union)):
+ for field in node.fields:
+ if (field
+ and field.name.startswith('_')
and field.anonymous_node is not None
and isinstance(field.anonymous_node, ast.Callback)):
- field.introspectable = False
+ field.introspectable = False
return True
def _get_validate_parameter_name(self, parent, param_name, origin):
try:
param = parent.get_parameter(param_name)
- except ValueError, e:
+ except ValueError:
param = None
if param is None:
if isinstance(origin, ast.Parameter):
@@ -142,7 +136,7 @@ usage is void (*_gtk_reserved1)(void);"""
def _apply_annotation_rename_to(self, node, chain, block):
if not block:
return
- rename_to = block.get_tag(TAG_RENAME_TO)
+ rename_to = block.tags.get(TAG_RENAME_TO)
if not rename_to:
return
rename_to = rename_to.value
@@ -192,7 +186,7 @@ usage is void (*_gtk_reserved1)(void);"""
def _get_annotation_name(self, node):
if isinstance(node, (ast.Class, ast.Interface, ast.Record,
ast.Union, ast.Enum, ast.Bitfield,
- ast.Callback, ast.Alias)):
+ ast.Callback, ast.Alias, ast.Constant)):
if node.ctype is not None:
return node.ctype
elif isinstance(node, ast.Registered) and node.gtype_name is not None:
@@ -211,10 +205,12 @@ usage is void (*_gtk_reserved1)(void);"""
if isinstance(node, ast.Function):
self._apply_annotations_function(node, chain)
if isinstance(node, ast.Callback):
- self._apply_annotations_callable(node, chain, block = self._get_block(node))
+ self._apply_annotations_callable(node, chain, block=self._get_block(node))
if isinstance(node, (ast.Class, ast.Interface, ast.Union, ast.Enum,
ast.Bitfield, ast.Callback)):
self._apply_annotations_annotated(node, self._get_block(node))
+ if isinstance(node, (ast.Enum, ast.Bitfield)):
+ self._apply_annotations_enum_members(node, self._get_block(node))
if isinstance(node, (ast.Class, ast.Interface, ast.Record, ast.Union)):
block = self._get_block(node)
for field in node.fields:
@@ -223,7 +219,7 @@ usage is void (*_gtk_reserved1)(void);"""
section_name = 'SECTION:' + name.lower()
block = self._blocks.get(section_name)
if block:
- node.doc = block.comment
+ node.doc = block.comment if block.comment else ''
if isinstance(node, (ast.Class, ast.Interface)):
for prop in node.properties:
self._apply_annotations_property(node, prop)
@@ -232,13 +228,13 @@ usage is void (*_gtk_reserved1)(void);"""
if isinstance(node, ast.Class):
block = self._get_block(node)
if block:
- tag = block.get_tag(TAG_UNREF_FUNC)
+ tag = block.tags.get(TAG_UNREF_FUNC)
node.unref_func = tag.value if tag else None
- tag = block.get_tag(TAG_REF_FUNC)
+ tag = block.tags.get(TAG_REF_FUNC)
node.ref_func = tag.value if tag else None
- tag = block.get_tag(TAG_SET_VALUE_FUNC)
+ tag = block.tags.get(TAG_SET_VALUE_FUNC)
node.set_value_func = tag.value if tag else None
- tag = block.get_tag(TAG_GET_VALUE_FUNC)
+ tag = block.tags.get(TAG_GET_VALUE_FUNC)
node.get_value_func = tag.value if tag else None
if isinstance(node, ast.Constant):
self._apply_annotations_constant(node)
@@ -262,7 +258,7 @@ usage is void (*_gtk_reserved1)(void);"""
Use resolver() on each identifier, and combiner() on the parts of
each complete type. (top_combiner is used on the top-most type.)"""
bits = re.split(r'([,<>()])', type_str, 1)
- first, sep, rest = [bits[0], '', ''] if (len(bits)==1) else bits
+ first, sep, rest = [bits[0], '', ''] if (len(bits) == 1) else bits
args = [resolver(first)]
if sep == '<' or sep == '(':
lastsep = '>' if (sep == '<') else ')'
@@ -273,9 +269,11 @@ usage is void (*_gtk_reserved1)(void);"""
else:
rest = sep + rest
return top_combiner(*args), rest
+
def resolver(ident):
res = self._transformer.create_type_from_user_string(ident)
return res
+
def combiner(base, *rest):
if not rest:
return base
@@ -286,6 +284,7 @@ usage is void (*_gtk_reserved1)(void);"""
message.warn(
"Too many parameters in type specification %r" % (type_str, ))
return base
+
def top_combiner(base, *rest):
if type_node is not None and isinstance(type_node, ast.Type):
base.is_const = type_node.is_const
@@ -304,7 +303,7 @@ usage is void (*_gtk_reserved1)(void);"""
else:
text = type_str
message.warn_node(parent, "%s: Unknown type: %r" %
- (text, result.ctype), positions=position)
+ (text, type_str), positions=position)
return result
def _resolve_toplevel(self, type_str, type_node=None, node=None, parent=None):
@@ -332,24 +331,23 @@ usage is void (*_gtk_reserved1)(void);"""
return block.position
def _check_array_element_type(self, array, options):
+ array_type = array.array_type
+ element_type = array.element_type
+
# GPtrArrays are allowed to contain non basic types
# (except enums and flags) or basic types that are
# as big as a gpointer
- if array.array_type == ast.Array.GLIB_PTRARRAY and \
- ((array.element_type in ast.BASIC_GIR_TYPES
- and not array.element_type in ast.POINTER_TYPES) or
- isinstance(array.element_type, ast.Enum) or
- isinstance(array.element_type, ast.Bitfield)):
- message.warn("invalid (element-type) for a GPtrArray, "
- "must be a pointer", options.position)
+ if array_type == ast.Array.GLIB_PTRARRAY:
+ if ((element_type in ast.BASIC_GIR_TYPES and not element_type in ast.POINTER_TYPES)
+ or isinstance(element_type, (ast.Enum, ast.Bitfield))):
+ message.warn("invalid (element-type) for a GPtrArray, "
+ "must be a pointer", options.position)
# GByteArrays have (element-type) guint8 by default
- if array.array_type == ast.Array.GLIB_BYTEARRAY:
- if array.element_type == ast.TYPE_ANY:
+ if array_type == ast.Array.GLIB_BYTEARRAY:
+ if element_type == ast.TYPE_ANY:
array.element_type = ast.TYPE_UINT8
- elif not array.element_type in [ast.TYPE_UINT8,
- ast.TYPE_INT8,
- ast.TYPE_CHAR]:
+ elif not element_type in [ast.TYPE_UINT8, ast.TYPE_INT8, ast.TYPE_CHAR]:
message.warn("invalid (element-type) for a GByteArray, "
"must be one of guint8, gint8 or gchar",
options.position)
@@ -459,8 +457,8 @@ usage is void (*_gtk_reserved1)(void);"""
def _get_transfer_default_returntype_basic(self, typeval):
if (typeval.is_equiv(ast.BASIC_GIR_TYPES)
- or typeval.is_const
- or typeval.is_equiv(ast.TYPE_NONE)):
+ or typeval.is_const
+ or typeval.is_equiv(ast.TYPE_NONE)):
return ast.PARAM_TRANSFER_NONE
elif typeval.is_equiv(ast.TYPE_STRING):
# Non-const strings default to FULL
@@ -477,8 +475,8 @@ usage is void (*_gtk_reserved1)(void);"""
assert supercls
if cls is supercls:
return True
- if cls.parent and cls.parent.target_giname != 'GObject.Object':
- return self._is_gi_subclass(cls.parent, supercls_type)
+ if cls.parent_type and cls.parent_type.target_giname != 'GObject.Object':
+ return self._is_gi_subclass(cls.parent_type, supercls_type)
return False
def _get_transfer_default_return(self, parent, node):
@@ -540,8 +538,7 @@ usage is void (*_gtk_reserved1)(void);"""
caller_allocates = False
annotated_direction = None
- if (OPT_INOUT in options or
- OPT_INOUT_ALT in options):
+ if (OPT_INOUT in options or OPT_INOUT_ALT in options):
annotated_direction = ast.PARAM_DIRECTION_INOUT
elif OPT_OUT in options:
subtype = options[OPT_OUT]
@@ -579,9 +576,9 @@ usage is void (*_gtk_reserved1)(void);"""
self._adjust_container_type(parent, node, options)
- if (OPT_ALLOW_NONE in options or
- node.type.target_giname == 'Gio.AsyncReadyCallback' or
- node.type.target_giname == 'Gio.Cancellable'):
+ if (OPT_ALLOW_NONE in options
+ or node.type.target_giname == 'Gio.AsyncReadyCallback'
+ or node.type.target_giname == 'Gio.Cancellable'):
node.allow_none = True
if tag is not None and tag.comment is not None:
@@ -598,19 +595,19 @@ usage is void (*_gtk_reserved1)(void);"""
if block is None:
return
- node.doc = block.comment
+ node.doc = block.comment if block.comment else ''
- since_tag = block.get_tag(TAG_SINCE)
+ since_tag = block.tags.get(TAG_SINCE)
if since_tag is not None:
node.version = since_tag.value
- deprecated_tag = block.get_tag(TAG_DEPRECATED)
+ deprecated_tag = block.tags.get(TAG_DEPRECATED)
if deprecated_tag is not None:
value = deprecated_tag.value
if ': ' in value:
colon = value.find(': ')
version = value[:colon]
- desc = value[colon+2:]
+ desc = value[colon + 2:]
else:
desc = value
version = None
@@ -618,7 +615,7 @@ usage is void (*_gtk_reserved1)(void);"""
if version is not None:
node.deprecated_version = version
- stability_tag = block.get_tag(TAG_STABILITY)
+ stability_tag = block.tags.get(TAG_STABILITY)
if stability_tag is not None:
stability = stability_tag.value.capitalize()
if stability in ["Stable", "Unstable", "Private", "Internal"]:
@@ -627,9 +624,9 @@ usage is void (*_gtk_reserved1)(void);"""
message.warn('unknown value "%s" for Stability tag' % (
stability_tag.value), stability_tag.position)
- annos_tag = block.get_tag(TAG_ATTRIBUTES)
+ annos_tag = block.tags.get(TAG_ATTRIBUTES)
if annos_tag is not None:
- for key, value in annos_tag.options.iteritems():
+ for key, value in annos_tag.options.items():
if value:
node.attributes.append((key, value.one()))
@@ -689,7 +686,7 @@ usage is void (*_gtk_reserved1)(void);"""
def _apply_annotations_return(self, parent, return_, block):
if block:
- tag = block.get_tag(TAG_RETURNS)
+ tag = block.tags.get(TAG_RETURNS)
else:
tag = None
self._apply_annotations_param_ret_common(parent, return_, tag)
@@ -700,7 +697,7 @@ usage is void (*_gtk_reserved1)(void);"""
declparams.add(parent.instance_parameter.argname)
for param in params:
if block:
- tag = block.get_param(param.argname)
+ tag = block.params.get(param.argname)
else:
tag = None
self._apply_annotations_param(parent, param, tag)
@@ -723,10 +720,9 @@ usage is void (*_gtk_reserved1)(void);"""
(param, ) = unused
text = ', should be %r' % (param, )
else:
- text = ', should be one of %s' % (
- ', '.join(repr(p) for p in unused), )
+ text = ', should be one of %s' % (', '.join(repr(p) for p in unused), )
- tag = block.get_param(doc_name)
+ tag = block.params.get(doc_name)
message.warn(
'%s: unknown parameter %r in documentation comment%s' % (
block.name, doc_name, text),
@@ -754,7 +750,7 @@ usage is void (*_gtk_reserved1)(void);"""
def _apply_annotations_field(self, parent, block, field):
if not block:
return
- tag = block.get_param(field.name)
+ tag = block.params.get(field.name)
if not tag:
return
t = tag.options.get(OPT_TYPE)
@@ -772,7 +768,7 @@ usage is void (*_gtk_reserved1)(void);"""
self._apply_annotations_annotated(prop, block)
if not block:
return
- transfer_tag = block.get_tag(TAG_TRANSFER)
+ transfer_tag = block.tags.get(TAG_TRANSFER)
if transfer_tag is not None:
transfer = transfer_tag.value
if transfer == OPT_TRANSFER_FLOATING:
@@ -780,28 +776,36 @@ usage is void (*_gtk_reserved1)(void);"""
prop.transfer = transfer
else:
prop.transfer = self._get_transfer_default(parent, prop)
- type_tag = block.get_tag(TAG_TYPE)
+ type_tag = block.tags.get(TAG_TYPE)
if type_tag:
prop.type = self._resolve_toplevel(type_tag.value, prop.type, prop, parent)
def _apply_annotations_signal(self, parent, signal):
+ names = []
prefix = self._get_annotation_name(parent)
block = self._blocks.get('%s::%s' % (prefix, signal.name))
- self._apply_annotations_annotated(signal, block)
- # We're only attempting to name the signal parameters if
- # the number of parameter tags (@foo) is the same or greater
- # than the number of signal parameters
- if block and len(block.params) > len(signal.parameters):
- names = block.params.items()
- # Resolve real parameter names early, so that in later
- # phase we can refer to them while resolving annotations.
- for i, param in enumerate(signal.parameters):
- param.argname, tag = names[i+1]
- else:
- names = []
+
+ if block:
+ self._apply_annotations_annotated(signal, block)
+
+ # We're only attempting to name the signal parameters if
+ # the number of parameters (@foo) is the same or greater
+ # than the number of signal parameters
+ if len(block.params) > len(signal.parameters):
+ names = block.params.items()
+ # Resolve real parameter names early, so that in later
+ # phase we can refer to them while resolving annotations.
+ for i, param in enumerate(signal.parameters):
+ param.argname, tag = names[i + 1]
+ elif len(signal.parameters) != 0:
+ # Only warn about missing params if there are actually parameters
+ # besides implicit self.
+ message.warn("incorrect number of parameters in comment block, "
+ "parameter annotations will be ignored.", block.position)
+
for i, param in enumerate(signal.parameters):
if names:
- name, tag = names[i+1]
+ name, tag = names[i + 1]
options = getattr(tag, 'options', {})
param_type = options.get(OPT_TYPE)
if param_type:
@@ -813,43 +817,51 @@ usage is void (*_gtk_reserved1)(void);"""
self._apply_annotations_return(signal, signal.retval, block)
def _apply_annotations_constant(self, node):
- block = self._blocks.get(node.ctype)
- if not block:
+ block = self._get_block(node)
+ if block is None:
return
- tag = block.get_tag(TAG_VALUE)
+
+ self._apply_annotations_annotated(node, block)
+
+ tag = block.tags.get(TAG_VALUE)
if tag:
node.value = tag.value
+ def _apply_annotations_enum_members(self, node, block):
+ if block is None:
+ return
+
+ for m in node.members:
+ tag = block.params.get(m.symbol, None)
+ if tag is not None:
+ m.doc = tag.comment
+
def _pass_read_annotations2(self, node, chain):
if isinstance(node, ast.Function):
- self._apply_annotations2_function(node, chain)
+ block = self._blocks.get(node.symbol)
+
+ self._apply_annotation_rename_to(node, chain, block)
+
+ # Handle virtual invokers
+ parent = chain[-1] if chain else None
+ if (block and parent):
+ virtual_annotation = block.tags.get(TAG_VFUNC)
+ if virtual_annotation:
+ invoker_name = virtual_annotation.value
+ matched = False
+ for vfunc in parent.virtual_methods:
+ if vfunc.name == invoker_name:
+ matched = True
+ vfunc.invoker = node.name
+ # Also merge in annotations
+ self._apply_annotations_callable(vfunc, [parent], block)
+ break
+ if not matched:
+ message.warn_node(node,
+ "Virtual slot %r not found for %r annotation" % (invoker_name,
+ TAG_VFUNC))
return True
- def _apply_annotations2_function(self, node, chain):
- block = self._blocks.get(node.symbol)
-
- self._apply_annotation_rename_to(node, chain, block)
-
- # Handle virtual invokers
- parent = chain[-1] if chain else None
- if not (block and parent):
- return
- virtual = block.get_tag(TAG_VFUNC)
- if not virtual:
- return
- invoker_name = virtual.value
- matched = False
- for vfunc in parent.virtual_methods:
- if vfunc.name == invoker_name:
- matched = True
- vfunc.invoker = node.name
- # Also merge in annotations
- self._apply_annotations_callable(vfunc, [parent], block)
- break
- if not matched:
- message.warn_node(node,
- "Virtual slot %r not found for %r annotation" % (invoker_name, TAG_VFUNC))
-
def _resolve_and_filter_type_list(self, typelist):
"""Given a list of Type instances, return a new list of types with
the ones that failed to resolve removed."""
@@ -877,19 +889,18 @@ the ones that failed to resolve removed."""
else:
self._transformer.resolve_type(field.type)
if isinstance(node, (ast.Class, ast.Interface)):
- resolved_parent = None
for parent in node.parent_chain:
try:
self._transformer.resolve_type(parent)
- except ValueError, e:
+ except ValueError:
continue
target = self._transformer.lookup_typenode(parent)
if target:
- node.parent = parent
+ node.parent_type = parent
break
else:
if isinstance(node, ast.Interface):
- node.parent = ast.Type(target_giname='GObject.Object')
+ node.parent_type = ast.Type(target_giname='GObject.Object')
for prop in node.properties:
self._transformer.resolve_type(prop.type)
for sig in node.signals:
@@ -1156,9 +1167,9 @@ method or constructor of some type."""
origin_node = self._get_constructor_class(func, subsymbol)
if origin_node is None:
if func.is_constructor:
- message.warn_node(func,
- "Can't find matching type for constructor; symbol=%r" \
- % (func.symbol, ))
+ message.warn_node(
+ func,
+ "Can't find matching type for constructor; symbol=%r" % (func.symbol, ))
return False
# Some sanity checks; only objects and boxeds can have ctors
@@ -1184,8 +1195,8 @@ method or constructor of some type."""
while parent and (not parent.gi_name == 'GObject.Object'):
if parent == target:
break
- if parent.parent:
- parent = self._transformer.lookup_typenode(parent.parent)
+ if parent.parent_type:
+ parent = self._transformer.lookup_typenode(parent.parent_type)
else:
parent = None
if parent is None:
@@ -1282,7 +1293,7 @@ method or constructor of some type."""
params = node.parameters
# First, do defaults for well-known callback types
- for i, param in enumerate(params):
+ for param in params:
argnode = self._transformer.lookup_typenode(param.type)
if isinstance(argnode, ast.Callback):
if param.type.target_giname in ('Gio.AsyncReadyCallback',
@@ -1291,7 +1302,7 @@ method or constructor of some type."""
param.transfer = ast.PARAM_TRANSFER_NONE
callback_param = None
- for i, param in enumerate(params):
+ for param in params:
argnode = self._transformer.lookup_typenode(param.type)
is_destroynotify = False
if isinstance(argnode, ast.Callback):
diff --git a/giscanner/mallard-C-class.tmpl b/giscanner/mallard-C-class.tmpl
deleted file mode 100644
index 2d739043..00000000
--- a/giscanner/mallard-C-class.tmpl
+++ /dev/null
@@ -1,48 +0,0 @@
-<?xml version="1.0"?>
-<page id="${node.namespace.name}.${node.name}"
- type="guide"
- style="class"
- xmlns="http://projectmallard.org/1.0/"
- xmlns:api="http://projectmallard.org/experimental/api/"
- xmlns:ui="http://projectmallard.org/experimental/ui/">
- <info>
- <link type="guide" xref="index" group="class"/>
- </info>
- <title>${node.ctype}</title>
-${formatter.format(node.doc)}
-% if node.version:
-<p>Since ${node.version}</p>
-% endif
- <synopsis ui:expanded="no">
- <title>Hierarchy</title>
- <tree>
- <item>
- <code>GObjectObject</code>
- </item>
- </tree>
- </synopsis>
- <links type="topic" ui:expanded="yes"
- api:type="function" api:mime="text/x-csrc"
- groups="constructor" style="linklist">
- <title>Constructors</title>
- </links>
- <links type="topic" ui:expanded="yes"
- api:type="function" api:mime="text/x-csrc"
- groups="method" style="linklist">
- <title>Methods</title>
- </links>
- <links type="topic" ui:expanded="yes"
- api:type="function" api:mime="text/x-csrc"
- groups="function" style="linklist">
- <title>Functions</title>
- </links>
- <links type="topic" ui:expanded="yes" groups="property" style="linklist">
- <title>Properties</title>
- </links>
- <links type="topic" ui:expanded="yes" groups="signal" style="linklist">
- <title>Signals</title>
- </links>
- <links type="topic" ui:expanded="yes" groups="#first #default #last" style="linklist">
- <title>Other</title>
- </links>
-</page>
diff --git a/giscanner/mallard-C-default.tmpl b/giscanner/mallard-C-default.tmpl
deleted file mode 100644
index 577fa566..00000000
--- a/giscanner/mallard-C-default.tmpl
+++ /dev/null
@@ -1,11 +0,0 @@
-<?xml version="1.0"?>
-<page id="${namespace.name}.${node.name}"
- type="topic"
- style=""
- xmlns="http://projectmallard.org/1.0/"
- xmlns:ui="http://projectmallard.org/experimental/ui/">
- <info>
- </info>
- <title>${namespace.name}.${node.name}</title>
-${formatter.format(node.doc)}
-</page>
diff --git a/giscanner/mallard-C-enum.tmpl b/giscanner/mallard-C-enum.tmpl
deleted file mode 100644
index 20cd0894..00000000
--- a/giscanner/mallard-C-enum.tmpl
+++ /dev/null
@@ -1,12 +0,0 @@
-<?xml version="1.0"?>
-<page id="${node.namespace.name}.${node.name}"
- type="guide"
- style="enum"
- xmlns="http://projectmallard.org/1.0/"
- xmlns:ui="http://projectmallard.org/experimental/ui/">
- <info>
- <link type="guide" xref="index"/>
- </info>
- <title>${node.namespace.name}.${node.name}</title>
-${formatter.format(node.doc)}
-</page>
diff --git a/giscanner/mallard-C-function.tmpl b/giscanner/mallard-C-function.tmpl
deleted file mode 100644
index 2da4710f..00000000
--- a/giscanner/mallard-C-function.tmpl
+++ /dev/null
@@ -1,95 +0,0 @@
-<?xml version="1.0"?>
-<%
-page_style = 'function'
-if node.is_constructor:
- page_style = 'constructor'
-elif node.is_method:
- page_style = 'method'
-%>
-<page id="${page_id}"
- type="topic"
- style="${page_style}"
- xmlns="http://projectmallard.org/1.0/"
- xmlns:api="http://projectmallard.org/experimental/api/"
- xmlns:ui="http://projectmallard.org/experimental/ui/">
- <info>
-% if node.parent is not None:
- <link type="guide" xref="${namespace.name}.${node.parent.name}" group="${page_style}"/>
-% else:
- <link type="guide" xref="index" group="${page_style}"/>
-% endif
- <api:function>
- <api:returns>
- <api:type>${formatter.format_type(node.retval.type) | x}</api:type>
- </api:returns>
- <api:name>${node.symbol}</api:name>
-% if node.is_method:
- <api:arg>
- <api:type>${node.parent.ctype} *</api:type>
- <api:name>self</api:name>
- </api:arg>
-% endif
-% for arg in node.parameters:
-% if arg.type.ctype == '<varargs>':
- <api:varargs/>
-% else:
- <api:arg>
- <api:type>${formatter.format_type(arg.type) | x}</api:type>
- <api:name>${arg.argname}</api:name>
- </api:arg>
-% endif
-% endfor
- </api:function>
- </info>
- <title>${node.symbol}</title>
-<synopsis><code mime="text/x-csrc">
-${node.retval.type.ctype} ${node.symbol} (\
-% if node.is_method:
-${node.parent.ctype} *self\
-%endif
-% if len(node.parameters) == 0:
-% if not node.is_method:
-void\
-%endif
-);
-% elif node.is_method:
-,
-% endif
-% for arg, ix in zip(node.parameters, range(len(node.parameters))):
-% if ix != 0:
-${' ' * (len(formatter.format_type(node.retval.type)) + len(node.symbol) + 3)}\
-% endif
-% if arg.type.ctype == '<varargs>':
-...\
-% else:
-${formatter.format_type(arg.type) | x} ${arg.argname}\
-% endif
-% if ix == len(node.parameters) - 1:
-);
-% else:
-,
-%endif
-% endfor
-</code></synopsis>
-${formatter.format(node.doc)}
-
-% if node.parameters or node.retval:
-<table>
-% for arg, ix in zip(node.parameters, range(len(node.parameters))):
-<tr>
-<td><p>${arg.argname} :</p></td>
-<td>${formatter.format(arg.doc)}</td>
-</tr>
-% endfor
-% if node.retval:
-<tr>
-<td><p>Returns :</p></td>
-<td>${formatter.format(node.retval.doc)}</td>
-</tr>
-% endif
-</table>
-% endif
-% if node.version:
-<p>Since ${node.version}</p>
-% endif
-</page>
diff --git a/giscanner/mallard-C-namespace.tmpl b/giscanner/mallard-C-namespace.tmpl
deleted file mode 100644
index 284ba238..00000000
--- a/giscanner/mallard-C-namespace.tmpl
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0"?>
-<page id="index"
- type="guide"
- style="namespace"
- xmlns="http://projectmallard.org/1.0/"
- xmlns:ui="http://projectmallard.org/experimental/ui/">
- <info>
- </info>
- <title>${node.name} Documentation</title>
- <links type="topic" ui:expanded="yes" groups="class" style="linklist">
- <title>Classes</title>
- </links>
- <links type="topic" ui:expanded="yes" groups="function" style="linklist">
- <title>Functions</title>
- </links>
- <links type="topic" ui:expanded="yes" groups="#first #default #last" style="linklist">
- <title>Other</title>
- </links>
-</page>
diff --git a/giscanner/mallard-C-property.tmpl b/giscanner/mallard-C-property.tmpl
deleted file mode 100644
index 2d37ba10..00000000
--- a/giscanner/mallard-C-property.tmpl
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0"?>
-<page id="${namespace.name}.${node.name}"
- type="topic"
- style="property"
- xmlns="http://projectmallard.org/1.0/"
- xmlns:ui="http://projectmallard.org/experimental/ui/">
- <info>
- <link type="guide" xref="${namespace.name}.${node.parent.name}" group="property"/>
- <title type="link" role="topic">${node.name}</title>
- </info>
- <title>${node.parent.ctype}:${node.name}</title>
-${formatter.format(node.doc)}
-</page>
diff --git a/giscanner/mallard-C-record.tmpl b/giscanner/mallard-C-record.tmpl
deleted file mode 100644
index a173e77a..00000000
--- a/giscanner/mallard-C-record.tmpl
+++ /dev/null
@@ -1,12 +0,0 @@
-<?xml version="1.0"?>
-<page id="${node.namespace.name}.${node.name}"
- type="guide"
- style="record"
- xmlns="http://projectmallard.org/1.0/"
- xmlns:ui="http://projectmallard.org/experimental/ui/">
- <info>
- <link type="guide" xref="index"/>
- </info>
- <title>${node.namespace.name}${node.name}</title>
-${formatter.format(node.doc)}
-</page>
diff --git a/giscanner/mallard-C-signal.tmpl b/giscanner/mallard-C-signal.tmpl
deleted file mode 100644
index 7aae3ae4..00000000
--- a/giscanner/mallard-C-signal.tmpl
+++ /dev/null
@@ -1,13 +0,0 @@
-<?xml version="1.0"?>
-<page id="${namespace.name}.${node.name}"
- type="topic"
- style="signal"
- xmlns="http://projectmallard.org/1.0/"
- xmlns:ui="http://projectmallard.org/experimental/ui/">
- <info>
- <link type="guide" xref="${namespace.name}.${node.parent.name}" group="signal"/>
- <title type="link" role="topic">${node.name}</title>
- </info>
- <title>${node.parent.ctype}::${node.name}</title>
-${formatter.format(node.doc)}
-</page>
diff --git a/giscanner/mallard-C-vfunc.tmpl b/giscanner/mallard-C-vfunc.tmpl
deleted file mode 100644
index 5b5bbfb1..00000000
--- a/giscanner/mallard-C-vfunc.tmpl
+++ /dev/null
@@ -1,35 +0,0 @@
-<?xml version="1.0"?>
-<page id="${page_id}"
- type="topic"
- style="vfunc"
- xmlns="http://projectmallard.org/1.0/"
- xmlns:api="http://projectmallard.org/experimental/api/"
- xmlns:ui="http://projectmallard.org/experimental/ui/">
- <info>
- <link type="guide" xref="${namespace.name}.${node.parent.name}" group="vfunc"/>
- </info>
- <title>${node.name}</title>
-<synopsis><code mime="text/x-csrc">
-</code></synopsis>
-${formatter.format(node.doc)}
-
-% if node.parameters or node.retval:
-<table>
-% for arg, ix in zip(node.parameters, range(len(node.parameters))):
-<tr>
-<td><p>${arg.argname} :</p></td>
-<td>${formatter.format(arg.doc)}</td>
-</tr>
-% endfor
-% if node.retval:
-<tr>
-<td><p>Returns :</p></td>
-<td>${formatter.format(node.retval.doc)}</td>
-</tr>
-% endif
-</table>
-% endif
-% if node.version:
-<p>Since ${node.version}</p>
-% endif
-</page>
diff --git a/giscanner/mallard-Python-class.tmpl b/giscanner/mallard-Python-class.tmpl
deleted file mode 100644
index 04e5fc72..00000000
--- a/giscanner/mallard-Python-class.tmpl
+++ /dev/null
@@ -1,66 +0,0 @@
-<?xml version="1.0"?>
-<page id="${node.namespace.name}.${node.name}"
- type="guide"
- style="class"
- xmlns="http://projectmallard.org/1.0/"
- xmlns:api="http://projectmallard.org/experimental/api/"
- xmlns:ui="http://projectmallard.org/experimental/ui/">
- <info>
- <link type="guide" xref="index" group="class"/>
- </info>
- <title>${namespace.name}.${node.name}</title>
-${formatter.format(node.doc)}
-
- <synopsis><code>
-from gi.repository import ${namespace.name}
-
-${formatter.to_underscores(node.name).lower()} = ${namespace.name}.${node.name}(\
-% for property_, ix in zip(node.properties, range(len(node.properties))):
-% if property_.construct or property_.construct_only or property_.writable:
-<link xref='${namespace.name}.${node.name}-${property_.name}'>${property_.name.replace('-', '_')}</link>=value\
-% if ix != len(node.properties) - 1:
-, \
-% endif
-% endif
-% endfor
-)\
- </code></synopsis>
-
-% if node.version:
-<p>Since ${node.version}</p>
-% endif
- <synopsis>
- <title>Hierarchy</title>
- <tree>
-% for class_ in formatter.get_class_hierarchy(node):
- <item>
- <code>${class_.namespace.name}.${class_.name}</code>
-% endfor
-% for class_ in formatter.get_class_hierarchy(node):
- </item>
-% endfor
- </tree>
- </synopsis>
- <links type="topic" ui:expanded="yes"
- api:type="function" api:mime="text/x-python"
- groups="method" style="linklist">
- <title>Methods</title>
- </links>
- <links type="topic" ui:expanded="yes"
- api:type="function" api:mime="text/x-python"
- groups="function" style="linklist">
- <title>Functions</title>
- </links>
- <links type="topic" ui:expanded="yes" groups="property" style="linklist">
- <title>Properties</title>
- </links>
- <links type="topic" ui:expanded="yes" groups="signal" style="linklist">
- <title>Signals</title>
- </links>
- <links type="topic" ui:expanded="yes" groups="vfunc" style="linklist">
- <title>Virtual functions</title>
- </links>
- <links type="topic" ui:expanded="yes" groups="#first #default #last" style="linklist">
- <title>Other</title>
- </links>
-</page>
diff --git a/giscanner/mallard-Python-default.tmpl b/giscanner/mallard-Python-default.tmpl
deleted file mode 100644
index 683adf6a..00000000
--- a/giscanner/mallard-Python-default.tmpl
+++ /dev/null
@@ -1,11 +0,0 @@
-<?xml version="1.0"?>
-<page id="${page_id}"
- type="topic"
- style=""
- xmlns="http://projectmallard.org/1.0/"
- xmlns:ui="http://projectmallard.org/experimental/ui/">
- <info>
- </info>
- <title>${namespace.name}.${node.name}</title>
-${formatter.format(node.doc)}
-</page>
diff --git a/giscanner/mallard-Python-enum.tmpl b/giscanner/mallard-Python-enum.tmpl
deleted file mode 100644
index fd6ca0fb..00000000
--- a/giscanner/mallard-Python-enum.tmpl
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0"?>
-<page id="${node.namespace.name}.${node.name}"
- type="guide"
- style="enum"
- xmlns="http://projectmallard.org/1.0/"
- xmlns:ui="http://projectmallard.org/experimental/ui/">
- <info>
- <link type="guide" xref="index"/>
- </info>
- <title>${node.namespace.name}.${node.name}</title>
- ${formatter.format(node.doc)}
-% if node.members:
-<table>
-% for member, ix in zip(node.members, range(len(node.members))):
-<tr>
-<td><p>${node.name}.${member.name.upper()} :</p></td>
-<td>${formatter.format(member.doc)}</td>
-</tr>
-% endfor
-</table>
-% endif
-
-</page>
diff --git a/giscanner/mallard-Python-function.tmpl b/giscanner/mallard-Python-function.tmpl
deleted file mode 100644
index 7aa25e8e..00000000
--- a/giscanner/mallard-Python-function.tmpl
+++ /dev/null
@@ -1,88 +0,0 @@
-<?xml version="1.0"?>
-<%
-page_style = 'function'
-if node.is_constructor:
- page_style = 'constructor'
-elif node.is_method:
- page_style = 'method'
-%>
-<page id="${page_id}"
- type="topic"
- style="${page_style}"
- xmlns="http://projectmallard.org/1.0/"
- xmlns:api="http://projectmallard.org/experimental/api/"
- xmlns:ui="http://projectmallard.org/experimental/ui/">
- <info>
-% if node.parent is not None:
- <link type="guide" xref="${namespace.name}.${node.parent.name}" group="${page_style}"/>
-% else:
- <link type="guide" xref="index" group="${page_style}"/>
-% endif
- <api:function>
- <api:returns>
- <api:type>${formatter.format_type(node.retval.type) | x}</api:type>
- </api:returns>
- <api:name>${node.symbol}</api:name>
-% if node.is_method:
- <api:arg>
- <api:type>${node.parent.ctype} *</api:type>
- <api:name>self</api:name>
- </api:arg>
-% endif
-% for arg in node.parameters:
-% if arg.type.ctype == '<varargs>':
- <api:varargs/>
-% else:
- <api:arg>
- <api:type>${formatter.format_type(arg.type) | x}</api:type>
- <api:name>${arg.argname}</api:name>
- </api:arg>
-% endif
-% endfor
- </api:function>
- </info>
- <title>${node.name}</title>
-<synopsis><code mime="text/x-python">
-% if len(node.parameters) != 0:
-@accepts(\
-% for arg, ix in zip(node.parameters, range(len(node.parameters))):
-${formatter.format_type(arg.type) | x}\
-% if ix != len(node.parameters) - 1:
-, \
-%endif
-% endfor
-)
-% endif
-@returns(${formatter.format_type(node.retval.type) | x})
-def \
-${node.name}(\
-% for arg, ix in zip(node.parameters, range(len(node.parameters))):
-${arg.argname}\
-% if ix != len(node.parameters) - 1:
-, \
-%endif
-% endfor
-)
-</code></synopsis>
-${formatter.format(node.doc)}
-
-% if node.parameters or node.retval:
-<table>
-% for arg, ix in zip(node.parameters, range(len(node.parameters))):
-<tr>
-<td><p>${arg.argname} :</p></td>
-<td>${formatter.format(arg.doc)}</td>
-</tr>
-% endfor
-% if node.retval and node.retval.type.ctype != 'void':
-<tr>
-<td><p>Returns :</p></td>
-<td>${formatter.format(node.retval.doc)}</td>
-</tr>
-% endif
-</table>
-% endif
-% if node.version:
-<p>Since ${node.version}</p>
-% endif
-</page>
diff --git a/giscanner/mallard-Python-namespace.tmpl b/giscanner/mallard-Python-namespace.tmpl
deleted file mode 100644
index 935cd440..00000000
--- a/giscanner/mallard-Python-namespace.tmpl
+++ /dev/null
@@ -1,19 +0,0 @@
-<?xml version="1.0"?>
-<page id="index"
- type="guide"
- style="namespace"
- xmlns="http://projectmallard.org/1.0/"
- xmlns:ui="http://projectmallard.org/experimental/ui/">
- <info>
- </info>
- <title>${node.name} Documentation</title>
- <links type="topic" ui:expanded="yes" groups="class">
- <title>Classes</title>
- </links>
- <links type="topic" ui:expanded="yes" groups="function">
- <title>Functions</title>
- </links>
- <links type="topic" ui:expanded="yes" groups="#first #default #last">
- <title>Other</title>
- </links>
-</page>
diff --git a/giscanner/mallard-Python-property.tmpl b/giscanner/mallard-Python-property.tmpl
deleted file mode 100644
index c4d2229e..00000000
--- a/giscanner/mallard-Python-property.tmpl
+++ /dev/null
@@ -1,16 +0,0 @@
-<?xml version="1.0"?>
-<page id="${namespace.name}.${node.parent.name}-${node.name}"
- type="topic"
- style="property"
- xmlns="http://projectmallard.org/1.0/"
- xmlns:ui="http://projectmallard.org/experimental/ui/">
- <info>
- <link type="guide" xref="${namespace.name}.${node.parent.name}" group="property"/>
- <title type="link" role="topic">${node.name}</title>
- </info>
- <title>${namespace.name}.${node.parent.name}:${node.name}</title>
-<synopsis><code mime="text/x-python">
-"${node.name}" ${formatter.format_type(node.type)} : ${formatter.format_property_flags(node)}
-</code></synopsis>
-${formatter.format(node.doc)}
-</page>
diff --git a/giscanner/mallard-Python-record.tmpl b/giscanner/mallard-Python-record.tmpl
deleted file mode 100644
index 1b00e3be..00000000
--- a/giscanner/mallard-Python-record.tmpl
+++ /dev/null
@@ -1,12 +0,0 @@
-<?xml version="1.0"?>
-<page id="${node.namespace.name}.${node.name}"
- type="guide"
- style="record"
- xmlns="http://projectmallard.org/1.0/"
- xmlns:ui="http://projectmallard.org/experimental/ui/">
- <info>
- <link type="guide" xref="index"/>
- </info>
- <title>${node.namespace.name}${node.name}</title>
- <p>${node.doc}</p>
-</page>
diff --git a/giscanner/mallard-Python-signal.tmpl b/giscanner/mallard-Python-signal.tmpl
deleted file mode 100644
index fed0659f..00000000
--- a/giscanner/mallard-Python-signal.tmpl
+++ /dev/null
@@ -1,52 +0,0 @@
-<?xml version="1.0"?>
-<page id="${namespace.name}.${node.parent.name}-${node.name}"
- type="topic"
- style="signal"
- xmlns="http://projectmallard.org/1.0/"
- xmlns:ui="http://projectmallard.org/experimental/ui/">
- <info>
- <link type="guide" xref="${namespace.name}.${node.parent.name}" group="signal"/>
- <title type="link" role="topic">${node.name}</title>
- </info>
- <title>${namespace.name}.${node.parent.name}::${node.name}</title>
-<synopsis><code mime="text/x-python">
-def callback(${formatter.to_underscores(node.parent.name).lower()}, \
-% for arg, ix in zip(node.parameters, range(len(node.parameters))):
-${arg.argname}, \
-% endfor
-user_param1, ...)
-</code></synopsis>
-${formatter.format(node.doc)}
-
-<table>
-<tr>
-<td><p>${formatter.to_underscores(node.parent.name).lower()} :</p></td>
-<td><p>instance of ${namespace.name}.${node.parent.name} that is emitting the signal</p></td>
-</tr>
-% for arg, ix in zip(node.parameters, range(len(node.parameters))):
-<tr>
-<td><p>${arg.argname} :</p></td>
-<td>${formatter.format(arg.doc)}</td>
-</tr>
-% endfor
-<tr>
-<td><p>user_param1 :</p></td>
-<td><p>first user parameter (if any) specified with the connect() method</p></td>
-</tr>
-<tr>
-<td><p>... :</p></td>
-<td><p>additional user parameters (if any)</p></td>
-</tr>
-% if node.retval and \
- node.retval.type.ctype != 'void' and \
- node.retval.type.ctype is not None:
-<tr>
-<td><p>Returns :</p></td>
-<td>${node.retval.type.ctype} ${formatter.format(node.retval.doc)}</td>
-</tr>
-% endif
-</table>
-% if node.version:
-<p>Since ${node.version}</p>
-% endif
-</page>
diff --git a/giscanner/mallard-Python-vfunc.tmpl b/giscanner/mallard-Python-vfunc.tmpl
deleted file mode 100644
index 7e95bc85..00000000
--- a/giscanner/mallard-Python-vfunc.tmpl
+++ /dev/null
@@ -1,56 +0,0 @@
-<?xml version="1.0"?>
-<page id="${page_id}"
- type="topic"
- style="vfunc"
- xmlns="http://projectmallard.org/1.0/"
- xmlns:api="http://projectmallard.org/experimental/api/"
- xmlns:ui="http://projectmallard.org/experimental/ui/">
- <info>
- <link type="guide" xref="${namespace.name}.${node.parent.name}" group="vfunc"/>
- <title type="link" role="topic">${node.name}</title>
- </info>
- <title>${namespace.name}.${node.parent.name}.${node.name}</title>
-<synopsis><code mime="text/x-python">
-% if len(node.parameters) != 0:
-@accepts(\
-% for arg, ix in zip(node.parameters, range(len(node.parameters))):
-${formatter.format_type(arg.type) | x}\
-% if ix != len(node.parameters) - 1:
-, \
-%endif
-% endfor
-)
-% endif
-@returns(${formatter.format_type(node.retval.type) | x})
-def \
-do_${node.name}(self, \
-% for arg, ix in zip(node.parameters, range(len(node.parameters))):
-${arg.argname}\
-% if ix != len(node.parameters) - 1:
-, \
-%endif
-% endfor
-):
-</code></synopsis>
-${formatter.format(node.doc)}
-
-% if node.parameters or node.retval:
-<table>
-% for arg, ix in zip(node.parameters, range(len(node.parameters))):
-<tr>
-<td><p>${arg.argname} :</p></td>
-<td>${formatter.format(arg.doc)}</td>
-</tr>
-% endfor
-% if node.retval and node.retval.type.ctype != 'void':
-<tr>
-<td><p>Returns :</p></td>
-<td>${formatter.format(node.retval.doc)}</td>
-</tr>
-% endif
-</table>
-% endif
-% if node.version:
-<p>Since ${node.version}</p>
-% endif
-</page>
diff --git a/giscanner/mallardwriter.py b/giscanner/mallardwriter.py
deleted file mode 100644
index 9c833843..00000000
--- a/giscanner/mallardwriter.py
+++ /dev/null
@@ -1,392 +0,0 @@
-#!/usr/bin/env python
-# -*- Mode: Python -*-
-# GObject-Introspection - a framework for introspecting GObject libraries
-# Copyright (C) 2010 Zach Goldberg
-# Copyright (C) 2011 Johan Dahlin
-# Copyright (C) 2011 Shaun McCance
-#
-# This program is free software; you can redistribute it and/or
-# modify it under the terms of the GNU General Public License
-# as published by the Free Software Foundation; either version 2
-# of the License, or (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program; if not, write to the Free Software
-# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
-# 02110-1301, USA.
-#
-
-import os
-import re
-import tempfile
-
-from xml.sax import saxutils
-from mako.template import Template
-
-from . import ast
-from .utils import to_underscores
-
-def make_page_id(namespace, node):
- if isinstance(node, ast.Namespace):
- return 'index'
- elif isinstance(node, (ast.Class, ast.Interface)):
- return '%s.%s' % (namespace.name, node.name)
- elif isinstance(node, ast.Record):
- return '%s.%s' % (namespace.name, node.name)
- elif isinstance(node, ast.Function):
- if node.parent is not None:
- return '%s.%s.%s' % (namespace.name, node.parent.name, node.name)
- else:
- return '%s.%s' % (namespace.name, node.name)
- elif isinstance(node, ast.Enum):
- return '%s.%s' % (namespace.name, node.name)
- elif isinstance(node, ast.Property) and node.parent is not None:
- return '%s.%s-%s' % (namespace.name, node.parent.name, node.name)
- elif isinstance(node, ast.Signal) and node.parent is not None:
- return '%s.%s-%s' % (namespace.name, node.parent.name, node.name)
- elif isinstance(node, ast.VFunction) and node.parent is not None:
- return '%s.%s-%s' % (namespace.name, node.parent.name, node.name)
- else:
- return '%s.%s' % (namespace.name, node.name)
-
-def make_template_name(node, language):
- if isinstance(node, ast.Namespace):
- node_kind = 'namespace'
- elif isinstance(node, (ast.Class, ast.Interface)):
- node_kind = 'class'
- elif isinstance(node, ast.Record):
- node_kind = 'record'
- elif isinstance(node, ast.Function):
- node_kind = 'function'
- elif isinstance(node, ast.Enum):
- node_kind = 'enum'
- elif isinstance(node, ast.Property) and node.parent is not None:
- node_kind = 'property'
- elif isinstance(node, ast.Signal) and node.parent is not None:
- node_kind = 'signal'
- elif isinstance(node, ast.VFunction) and node.parent is not None:
- node_kind = 'vfunc'
- else:
- node_kind = 'default'
-
- return 'mallard-%s-%s.tmpl' % (language, node_kind)
-
-class TemplatedScanner(object):
- def __init__(self, specs):
- self.specs = self.unmangle_specs(specs)
- self.regex = self.make_regex(self.specs)
-
- def unmangle_specs(self, specs):
- mangled = re.compile('<<([a-zA-Z_:]+)>>')
- specdict = dict((name.lstrip('!'), spec) for name, spec in specs)
-
- def unmangle(spec, name=None):
- def replace_func(match):
- child_spec_name = match.group(1)
-
- if ':' in child_spec_name:
- pattern_name, child_spec_name = child_spec_name.split(':', 1)
- else:
- pattern_name = None
-
- child_spec = specdict[child_spec_name]
- # Force all child specs of this one to be unnamed
- unmangled = unmangle(child_spec, None)
- if pattern_name and name:
- return '(?P<%s_%s>%s)' % (name, pattern_name, unmangled)
- else:
- return unmangled
-
- return mangled.sub(replace_func, spec)
-
- return [(name, unmangle(spec, name)) for name, spec in specs]
-
- def make_regex(self, specs):
- regex = '|'.join('(?P<%s>%s)' % (name, spec) for name, spec in specs
- if not name.startswith('!'))
- return re.compile(regex)
-
- def get_properties(self, name, match):
- groupdict = match.groupdict()
- properties = {name: groupdict.pop(name)}
- name = name + "_"
- for group, value in groupdict.iteritems():
- if group.startswith(name):
- key = group[len(name):]
- properties[key] = value
- return properties
-
- def scan(self, text):
- pos = 0
- while True:
- match = self.regex.search(text, pos)
- if match is None:
- break
-
- start = match.start()
- if start > pos:
- yield ('other', text[pos:start], None)
-
- pos = match.end()
- name = match.lastgroup
- yield (name, match.group(0), self.get_properties(name, match))
-
- if pos < len(text):
- yield ('other', text[pos:], None)
-
-class DocstringScanner(TemplatedScanner):
- def __init__(self):
- specs = [
- ('!alpha', r'[a-zA-Z0-9_]+'),
- ('!alpha_dash', r'[a-zA-Z0-9_-]+'),
- ('property', r'#<<type_name:alpha>>:(<<property_name:alpha_dash>>)'),
- ('signal', r'#<<type_name:alpha>>::(<<signal_name:alpha_dash>>)'),
- ('type_name', r'#(<<type_name:alpha>>)'),
- ('fundamental', r'%(<<fundamental:alpha>>)'),
- ('function_call', r'<<symbol_name:alpha>>\(\)'),
- ]
-
- super(DocstringScanner, self).__init__(specs)
-
-class MallardFormatter(object):
- def __init__(self, transformer):
- self._transformer = transformer
- self._scanner = DocstringScanner()
-
- def escape(self, text):
- return saxutils.escape(text)
-
- def format(self, doc):
- if doc is None:
- return ''
-
- result = ''
- for para in doc.split('\n\n'):
- result += '<p>'
- result += self.format_inline(para)
- result += '</p>'
- return result
-
- def _process_other(self, namespace, match, props):
- return self.escape(match)
-
- def _find_thing(self, list_, name):
- for item in list_:
- if item.name == name:
- return item
- raise KeyError("Could not find %s" % (name, ))
-
- def _process_property(self, namespace, match, props):
- type_node = namespace.get_by_ctype(props['type_name'])
- if type_node is None:
- return match
-
- try:
- node = self._find_thing(type_node.properties, props['property_name'])
- except (AttributeError, KeyError), e:
- return match
-
- xref_name = "%s.%s:%s" % (namespace.name, type_node.name, node.name)
- return '<link xref="%s">%s</link>' % (make_page_id(namespace, node), xref_name)
-
- def _process_signal(self, namespace, match, props):
- type_node = namespace.get_by_ctype(props['type_name'])
- if type_node is None:
- return match
-
- try:
- node = self._find_thing(type_node.signals, props['signal_name'])
- except (AttributeError, KeyError), e:
- return match
-
- xref_name = "%s.%s::%s" % (namespace.name, type_node.name, node.name)
- return '<link xref="%s">%s</link>' % (make_page_id(namespace, node), xref_name)
-
- def _process_type_name(self, namespace, match, props):
- node = namespace.get_by_ctype(props['type_name'])
- if node is None:
- return match
- xref_name = "%s.%s" % (namespace.name, node.name)
- return '<link xref="%s">%s</link>' % (make_page_id(namespace, node), xref_name)
-
- def _process_function_call(self, namespace, match, props):
- node = namespace.get_by_symbol(props['symbol_name'])
- if node is None:
- return match
-
- return '<link xref="%s">%s</link>' % (make_page_id(namespace, node),
- self.format_function_name(node))
-
- def _process_fundamental(self, namespace, match, props):
- return self.fundamentals.get(props['fundamental'], match)
-
- def _process_token(self, tok):
- namespace = self._transformer.namespace
-
- kind, match, props = tok
-
- dispatch = {
- 'other': self._process_other,
- 'property': self._process_property,
- 'signal': self._process_signal,
- 'type_name': self._process_type_name,
- 'function_call': self._process_function_call,
- 'fundamental': self._process_fundamental,
- }
-
- return dispatch[kind](namespace, match, props)
-
- def format_inline(self, para):
- tokens = self._scanner.scan(para)
- words = [self._process_token(tok) for tok in tokens]
- return ''.join(words)
-
- def format_function_name(self, func):
- raise NotImplementedError
-
- def format_type(self, type_):
- raise NotImplementedError
-
- def format_property_flags(self, property_):
- flags = []
- if property_.readable:
- flags.append("Read")
- if property_.writable:
- flags.append("Write")
- if property_.construct:
- flags.append("Construct")
- if property_.construct_only:
- flags.append("Construct Only")
-
- return " / ".join(flags)
-
- def to_underscores(self, string):
- return to_underscores(string)
-
- def get_class_hierarchy(self, node):
- parent_chain = [node]
-
- while node.parent:
- node = self._transformer.lookup_giname(str(node.parent))
- parent_chain.append(node)
-
- parent_chain.reverse()
- return parent_chain
-
-class MallardFormatterC(MallardFormatter):
- language = "C"
-
- fundamentals = {
- "TRUE": "TRUE",
- "FALSE": "FALSE",
- "NULL": "NULL",
- }
-
- def format_type(self, type_):
- if isinstance(type_, ast.Array):
- return self.format_type(type_.element_type) + '*'
- elif type_.ctype is not None:
- return type_.ctype
- else:
- return type_.target_fundamental
-
- def format_function_name(self, func):
- return func.symbol
-
-class MallardFormatterPython(MallardFormatter):
- language = "Python"
-
- fundamentals = {
- "TRUE": "True",
- "FALSE": "False",
- "NULL": "None",
- }
-
- def format_type(self, type_):
- if isinstance(type_, ast.Array):
- return '[' + self.format_type(type_.element_type) + ']'
- elif isinstance(type_, ast.Map):
- return '{%s: %s}' % (self.format_type(type_.key_type),
- self.format_type(type_.value_type))
- elif type_.target_giname is not None:
- return type_.target_giname
- else:
- return type_.target_fundamental
-
- def format_function_name(self, func):
- if func.parent is not None:
- return "%s.%s" % (func.parent.name, func.name)
- else:
- return func.name
-
-LANGUAGES = {
- "c": MallardFormatterC,
- "python": MallardFormatterPython,
-}
-
-class MallardWriter(object):
- def __init__(self, transformer, language):
- self._transformer = transformer
-
- try:
- formatter_class = LANGUAGES[language.lower()]
- except KeyError:
- raise SystemExit("Unsupported language: %s" % (language, ))
-
- self._formatter = formatter_class(self._transformer)
- self._language = self._formatter.language
-
- def write(self, output):
- nodes = [self._transformer.namespace]
- for node in self._transformer.namespace.itervalues():
- if isinstance(node, ast.Function) and node.moved_to is not None:
- continue
- if getattr(node, 'disguised', False):
- continue
- if isinstance(node, ast.Record) and \
- self._language == 'Python' and \
- node.is_gtype_struct_for is not None:
- continue
- nodes.append(node)
- if isinstance(node, (ast.Class, ast.Interface, ast.Record)):
- nodes += getattr(node, 'methods', [])
- nodes += getattr(node, 'static_methods', [])
- nodes += getattr(node, 'virtual_methods', [])
- nodes += getattr(node, 'properties', [])
- nodes += getattr(node, 'signals', [])
- if self._language == 'C':
- nodes += getattr(node, 'constructors', [])
- for node in nodes:
- self._render_node(node, output)
-
- def _render_node(self, node, output):
- namespace = self._transformer.namespace
-
- if 'UNINSTALLED_INTROSPECTION_SRCDIR' in os.environ:
- top_srcdir = os.environ['UNINSTALLED_INTROSPECTION_SRCDIR']
- template_dir = os.path.join(top_srcdir, 'giscanner')
- else:
- template_dir = os.path.dirname(__file__)
-
- template_name = make_template_name(node, self._language)
- page_id = make_page_id(namespace, node)
-
- file_name = os.path.join(template_dir, template_name)
- file_name = os.path.abspath(file_name)
- template = Template(filename=file_name, output_encoding='utf-8',
- module_directory=tempfile.gettempdir())
- result = template.render(namespace=namespace,
- node=node,
- page_id=page_id,
- formatter=self._formatter)
-
- output_file_name = os.path.join(os.path.abspath(output),
- page_id + '.page')
- fp = open(output_file_name, 'w')
- fp.write(result)
- fp.close()
diff --git a/giscanner/message.py b/giscanner/message.py
index 8a948cd3..3a330afe 100644
--- a/giscanner/message.py
+++ b/giscanner/message.py
@@ -61,7 +61,7 @@ class Position(object):
return '%s:' % (filename, )
def offset(self, offset):
- return Position(self.filename, self.line+offset, self.column)
+ return Position(self.filename, self.line + offset, self.column)
class MessageLogger(object):
@@ -119,16 +119,14 @@ If the warning is related to a ast.Node type, see log_node()."""
elif log_type == FATAL:
error_type = "Fatal"
if prefix:
- text = (
-'''%s: %s: %s: %s: %s\n''' % (last_position, error_type, self._namespace.name,
- prefix, text))
+ text = ('%s: %s: %s: %s: %s\n' % (last_position, error_type,
+ self._namespace.name, prefix, text))
else:
if self._namespace:
- text = (
-'''%s: %s: %s: %s\n''' % (last_position, error_type, self._namespace.name, text))
+ text = ('%s: %s: %s: %s\n' % (last_position, error_type,
+ self._namespace.name, text))
else:
- text = (
-'''%s: %s: %s\n''' % (last_position, error_type, text))
+ text = ('%s: %s: %s\n' % (last_position, error_type, text))
self._output.write(text)
if log_type == FATAL:
@@ -169,17 +167,21 @@ def log_node(log_type, node, text, context=None, positions=None):
ml = MessageLogger.get()
ml.log_node(log_type, node, text, context=context, positions=positions)
+
def warn(text, positions=None, prefix=None):
ml = MessageLogger.get()
ml.log(WARNING, text, positions, prefix)
+
def warn_node(node, text, context=None, positions=None):
log_node(WARNING, node, text, context=context, positions=positions)
+
def warn_symbol(symbol, text):
ml = MessageLogger.get()
ml.log_symbol(WARNING, symbol, text)
+
def fatal(text, positions=None, prefix=None):
ml = MessageLogger.get()
ml.log(FATAL, text, positions, prefix)
diff --git a/giscanner/odict.py b/giscanner/odict.py
deleted file mode 100644
index df703cbb..00000000
--- a/giscanner/odict.py
+++ /dev/null
@@ -1,45 +0,0 @@
-# -*- Mode: Python -*-
-# GObject-Introspection - a framework for introspecting GObject libraries
-# Copyright (C) 2008 Johan Dahlin
-#
-# This library is free software; you can redistribute it and/or
-# modify it under the terms of the GNU Lesser General Public
-# License as published by the Free Software Foundation; either
-# version 2 of the License, or (at your option) any later version.
-#
-# This library is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
-# Lesser General Public License for more details.
-#
-# You should have received a copy of the GNU Lesser General Public
-# License along with this library; if not, write to the
-# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
-# Boston, MA 02111-1307, USA.
-#
-
-"""odict - an ordered dictionary"""
-
-from UserDict import DictMixin
-
-
-class odict(DictMixin):
-
- def __init__(self):
- self._items = {}
- self._keys = []
-
- def __setitem__(self, key, value):
- if key not in self._items:
- self._keys.append(key)
- self._items[key] = value
-
- def __getitem__(self, key):
- return self._items[key]
-
- def __delitem__(self, key):
- del self._items[key]
- self._keys.remove(key)
-
- def keys(self):
- return self._keys[:]
diff --git a/giscanner/scannerlexer.l b/giscanner/scannerlexer.l
index a783ec06..554e2da7 100644
--- a/giscanner/scannerlexer.l
+++ b/giscanner/scannerlexer.l
@@ -46,6 +46,7 @@ char linebuf[2000];
extern int yylex (GISourceScanner *scanner);
#define YY_DECL int yylex (GISourceScanner *scanner)
static int yywrap (void);
+static void parse_gtk_doc_comment (GISourceScanner *scanner);
static void parse_comment (GISourceScanner *scanner);
static void parse_trigraph (GISourceScanner *scanner);
static void process_linemarks (GISourceScanner *scanner);
@@ -71,8 +72,10 @@ stringtext ([^\\\"])|(\\.)
++lineno;
}
"\\\n" { ++lineno; }
+
[\t\f\v\r ]+ { /* Ignore whitespace. */ }
+"/**" { parse_gtk_doc_comment(scanner); }
"/*" { parse_comment(scanner); }
"/*"[\t ]?<[\t ,=A-Za-z0-9_]+>[\t ]?"*/" { parse_trigraph(scanner); }
"//".* { /* Ignore C++ style comments. */ }
@@ -134,6 +137,8 @@ stringtext ([^\\\"])|(\\.)
"," { return ','; }
"->" { return ARROW; }
+"__asm"[\t\f\v\r ]+"volatile" { if (!parse_ignored_macro()) REJECT; }
+"__asm__"[\t\f\v\r ]+"volatile" { if (!parse_ignored_macro()) REJECT; }
"__asm" { if (!parse_ignored_macro()) REJECT; }
"__asm__" { if (!parse_ignored_macro()) REJECT; }
"__attribute__" { if (!parse_ignored_macro()) REJECT; }
@@ -145,6 +150,8 @@ stringtext ([^\\\"])|(\\.)
"__signed__" { return SIGNED; }
"__restrict" { return RESTRICT; }
"__typeof" { if (!parse_ignored_macro()) REJECT; }
+"__volatile" { if (!parse_ignored_macro()) REJECT; }
+"__volatile__" { if (!parse_ignored_macro()) REJECT; }
"_Bool" { return BOOL; }
"G_GINT64_CONSTANT" { return INTL_CONST; }
@@ -170,6 +177,10 @@ stringtext ([^\\\"])|(\\.)
"if" { return IF; }
"inline" { return INLINE; }
"int" { return INT; }
+"__uint128_t" { return INT; }
+"__int128_t" { return INT; }
+"__uint128" { return INT; }
+"__int128" { return INT; }
"long" { return LONG; }
"register" { return REGISTER; }
"restrict" { return RESTRICT; }
@@ -212,9 +223,8 @@ yywrap (void)
return 1;
}
-
static void
-parse_comment (GISourceScanner *scanner)
+parse_gtk_doc_comment (GISourceScanner *scanner)
{
GString *string = NULL;
int c1, c2;
@@ -227,7 +237,7 @@ parse_comment (GISourceScanner *scanner)
(GCompareFunc)g_strcmp0)) {
skip = TRUE;
} else {
- string = g_string_new ("/*");
+ string = g_string_new (yytext);
}
c1 = input();
@@ -262,6 +272,26 @@ parse_comment (GISourceScanner *scanner)
comment);
}
+static void
+parse_comment (GISourceScanner *scanner)
+{
+ int c1, c2;
+
+ c1 = input();
+ c2 = input();
+
+ while (c2 != EOF && !(c1 == '*' && c2 == '/'))
+ {
+ if (c1 == '\n')
+ lineno++;
+
+ c1 = c2;
+ c2 = input();
+ }
+
+ return;
+}
+
static int
check_identifier (GISourceScanner *scanner,
const char *s)
diff --git a/giscanner/scannermain.py b/giscanner/scannermain.py
index 794cede3..00dc30d6 100755
--- a/giscanner/scannermain.py
+++ b/giscanner/scannermain.py
@@ -38,12 +38,30 @@ from giscanner.girparser import GIRParser
from giscanner.girwriter import GIRWriter
from giscanner.maintransformer import MainTransformer
from giscanner.shlibs import resolve_shlibs
-from giscanner.sourcescanner import SourceScanner
+from giscanner.sourcescanner import SourceScanner, ALL_EXTS
from giscanner.transformer import Transformer
from . import utils
+
+def process_cflags_begin(option, opt, value, parser):
+ cflags = getattr(parser.values, option.dest)
+ while len(parser.rargs) > 0 and parser.rargs[0] != '--cflags-end':
+ cflags.append(parser.rargs.pop(0))
+
+
+def process_cflags_end(option, opt, value, parser):
+ pass
+
+
def get_preprocessor_option_group(parser):
group = optparse.OptionGroup(parser, "Preprocessor options")
+ group.add_option("", "--cflags-begin",
+ help="Start preprocessor/compiler flags",
+ dest="cflags", default=[],
+ action="callback", callback=process_cflags_begin)
+ group.add_option("", "--cflags-end",
+ help="End preprocessor/compiler flags",
+ action="callback", callback=process_cflags_end)
group.add_option("-I", help="Pre-processor include file",
action="append", dest="cpp_includes",
default=[])
@@ -56,6 +74,7 @@ def get_preprocessor_option_group(parser):
group.add_option("-p", dest="", help="Ignored")
return group
+
def get_windows_option_group(parser):
group = optparse.OptionGroup(parser, "Machine Dependent Options")
group.add_option("-m", help="some machine dependent option",
@@ -64,13 +83,13 @@ def get_windows_option_group(parser):
return group
+
def _get_option_parser():
parser = optparse.OptionParser('%prog [options] sources')
parser.add_option('', "--quiet",
action="store_true", dest="quiet",
default=False,
- help="If passed, do not print details of normal" \
- + " operation")
+ help="If passed, do not print details of normal operation")
parser.add_option("", "--format",
action="store", dest="format",
default="gir",
@@ -158,6 +177,9 @@ match the namespace prefix.""")
parser.add_option("", "--c-include",
action="append", dest="c_includes", default=[],
help="headers which should be included in C programs")
+ parser.add_option("", "--filelist",
+ action="store", dest="filelist", default=[],
+ help="file containing headers and sources to be scanned")
group = get_preprocessor_option_group(parser)
parser.add_option_group(group)
@@ -186,17 +208,15 @@ match the namespace prefix.""")
def _error(msg):
raise SystemExit('ERROR: %s' % (msg, ))
+
def passthrough_gir(path, f):
parser = GIRParser()
parser.parse(path)
- writer = GIRWriter(parser.get_namespace(),
- parser.get_shared_libraries(),
- parser.get_includes(),
- parser.get_pkgconfig_packages(),
- parser.get_c_includes())
+ writer = GIRWriter(parser.get_namespace())
f.write(writer.get_xml())
+
def test_codegen(optstring):
(namespace, out_h_filename, out_c_filename) = optstring.split(',')
if namespace == 'Everything':
@@ -207,6 +227,7 @@ def test_codegen(optstring):
_error("Invaild namespace %r" % (namespace, ))
return 0
+
def process_options(output, allowed_flags):
for option in output.split():
for flag in allowed_flags:
@@ -215,6 +236,7 @@ def process_options(output, allowed_flags):
yield option
break
+
def process_packages(options, packages):
args = ['pkg-config', '--cflags']
args.extend(packages)
@@ -234,15 +256,13 @@ def process_packages(options, packages):
options.cpp_defines.extend(pkg_options.cpp_defines)
options.cpp_undefines.extend(pkg_options.cpp_undefines)
+
def extract_filenames(args):
filenames = []
for arg in args:
# We don't support real C++ parsing yet, but we should be able
# to understand C API implemented in C++ files.
- if (arg.endswith('.c') or arg.endswith('.cpp') or
- arg.endswith('.cc') or arg.endswith('.cxx') or
- arg.endswith('.h') or arg.endswith('.hpp') or
- arg.endswith('.hxx')):
+ if os.path.splitext(arg)[1] in ALL_EXTS:
if not os.path.exists(arg):
_error('%s: no such a file or directory' % (arg, ))
# Make absolute, because we do comparisons inside scannerparser.c
@@ -250,6 +270,29 @@ def extract_filenames(args):
filenames.append(os.path.abspath(arg))
return filenames
+
+def extract_filelist(options):
+ filenames = []
+ if not os.path.exists(options.filelist):
+ _error('%s: no such filelist file' % (options.filelist, ))
+ filelist_file = open(options.filelist, "r")
+ lines = filelist_file.readlines()
+ for line in lines:
+ # We don't support real C++ parsing yet, but we should be able
+ # to understand C API implemented in C++ files.
+ filename = line.strip()
+ if (filename.endswith('.c') or filename.endswith('.cpp')
+ or filename.endswith('.cc') or filename.endswith('.cxx')
+ or filename.endswith('.h') or filename.endswith('.hpp')
+ or filename.endswith('.hxx')):
+ if not os.path.exists(filename):
+ _error('%s: Invalid filelist entry-no such file or directory' % (line, ))
+ # Make absolute, because we do comparisons inside scannerparser.c
+ # against the absolute path that cpp will give us
+ filenames.append(os.path.abspath(filename))
+ return filenames
+
+
def create_namespace(options):
if options.strip_prefix:
print """g-ir-scanner: warning: Option --strip-prefix has been deprecated;
@@ -278,6 +321,7 @@ see --identifier-prefix and --symbol-prefix."""
identifier_prefixes=identifier_prefixes,
symbol_prefixes=symbol_prefixes)
+
def create_transformer(namespace, options):
transformer = Transformer(namespace,
accept_unprefixed=options.accept_unprefixed)
@@ -286,7 +330,6 @@ def create_transformer(namespace, options):
transformer.disable_cache()
transformer.set_passthrough_mode()
- shown_include_warning = False
for include in options.includes:
if os.sep in include:
_error("Invalid include path %r" % (include, ))
@@ -300,6 +343,7 @@ def create_transformer(namespace, options):
return transformer
+
def create_binary(transformer, options, args):
# Transform the C AST nodes into higher level
# GLib/GObject nodes
@@ -310,7 +354,7 @@ def create_binary(transformer, options, args):
gdump_parser.init_parse()
if options.program:
- args=[options.program]
+ args = [options.program]
args.extend(options.program_args)
binary = IntrospectionBinary(args)
else:
@@ -323,19 +367,25 @@ def create_binary(transformer, options, args):
gdump_parser.parse()
return shlibs
+
def create_source_scanner(options, args):
- filenames = extract_filenames(args)
+ if hasattr(options, 'filelist') and options.filelist:
+ filenames = extract_filelist(options)
+ else:
+ filenames = extract_filenames(args)
# Run the preprocessor, tokenize and construct simple
# objects representing the raw C symbols
ss = SourceScanner()
ss.set_cpp_options(options.cpp_includes,
options.cpp_defines,
- options.cpp_undefines)
+ options.cpp_undefines,
+ cflags=options.cflags)
ss.parse_files(filenames)
ss.parse_macros(filenames)
return ss
+
def write_output(data, options):
if options.output == "-":
output = sys.stdout
@@ -355,7 +405,7 @@ def write_output(data, options):
os.unlink(temp_f_name)
try:
shutil.move(main_f_name, options.output)
- except OSError, e:
+ except OSError as e:
if e.errno == errno.EPERM:
os.unlink(main_f_name)
return 0
@@ -364,14 +414,15 @@ def write_output(data, options):
else:
try:
output = open(options.output, "w")
- except IOError, e:
+ except IOError as e:
_error("opening output for writing: %s" % (e.strerror, ))
try:
output.write(data)
- except IOError, e:
+ except IOError as e:
_error("while writing output: %s" % (e.strerror, ))
+
def scanner_main(args):
parser = _get_option_parser()
(options, args) = parser.parse_args(args)
@@ -381,8 +432,9 @@ def scanner_main(args):
if options.test_codegen:
return test_codegen(options.test_codegen)
- if len(args) <= 1:
- _error('Need at least one filename')
+ if hasattr(options, 'filelist') and not options.filelist:
+ if len(args) <= 1:
+ _error('Need at least one filename')
if not options.namespace_name:
_error('Namespace name missing')
@@ -416,7 +468,6 @@ def scanner_main(args):
blocks = ap.parse(ss.get_comments())
# Transform the C symbols into AST nodes
- transformer.set_annotations(blocks)
transformer.parse(ss.get_symbols())
if not options.header_only:
@@ -424,6 +475,8 @@ def scanner_main(args):
else:
shlibs = []
+ transformer.namespace.shared_libraries = shlibs
+
main = MainTransformer(transformer, blocks)
main.transform()
@@ -446,8 +499,9 @@ def scanner_main(args):
else:
exported_packages = options.packages
- writer = Writer(transformer.namespace, shlibs, transformer.get_includes(),
- exported_packages, options.c_includes)
+ transformer.namespace.c_includes = options.c_includes
+ transformer.namespace.exported_packages = exported_packages
+ writer = Writer(transformer.namespace)
data = writer.get_xml()
write_output(data, options)
diff --git a/giscanner/scannerparser.y b/giscanner/scannerparser.y
index cc495cc3..3457b499 100644
--- a/giscanner/scannerparser.y
+++ b/giscanner/scannerparser.y
@@ -366,15 +366,15 @@ unary_expression
$$ = $2;
break;
case UNARY_MINUS:
- $$ = $2;
+ $$ = gi_source_symbol_copy ($2);
$$->const_int = -$2->const_int;
break;
case UNARY_BITWISE_COMPLEMENT:
- $$ = $2;
+ $$ = gi_source_symbol_copy ($2);
$$->const_int = ~$2->const_int;
break;
case UNARY_LOGICAL_NEGATION:
- $$ = $2;
+ $$ = gi_source_symbol_copy ($2);
$$->const_int = !gi_source_symbol_get_const_boolean ($2);
break;
default:
@@ -818,11 +818,12 @@ type_specifier
struct_or_union_specifier
: struct_or_union identifier_or_typedef_name '{' struct_declaration_list '}'
{
+ GISourceSymbol *sym;
$$ = $1;
$$->name = $2;
$$->child_list = $4;
- GISourceSymbol *sym = gi_source_symbol_new (CSYMBOL_TYPE_INVALID, scanner->current_filename, lineno);
+ sym = gi_source_symbol_new (CSYMBOL_TYPE_INVALID, scanner->current_filename, lineno);
if ($$->type == CTYPE_STRUCT) {
sym->type = CSYMBOL_TYPE_STRUCT;
} else if ($$->type == CTYPE_UNION) {
@@ -1472,9 +1473,9 @@ gi_source_scanner_parse_macros (GISourceScanner *scanner, GList *filenames)
FILE *fmacros =
fdopen (g_file_open_tmp ("gen-introspect-XXXXXX.h", &tmp_name, &error),
"w+");
+ GList *l;
g_unlink (tmp_name);
- GList *l;
for (l = filenames; l != NULL; l = l->next)
{
FILE *f = fopen (l->data, "r");
diff --git a/giscanner/sectionparser.py b/giscanner/sectionparser.py
new file mode 100644
index 00000000..ffe41afc
--- /dev/null
+++ b/giscanner/sectionparser.py
@@ -0,0 +1,152 @@
+# -*- Mode: Python -*-
+# Copyright (C) 2013 Hat, Inc.
+#
+# This library is free software; you can redistribute it and/or
+# modify it under the terms of the GNU Lesser General Public
+# License as published by the Free Software Foundation; either
+# version 2 of the License, or (at your option) any later version.
+#
+# This library is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+# Lesser General Public License for more details.
+#
+# You should have received a copy of the GNU Lesser General Public
+# License along with this library; if not, write to the
+# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+# Boston, MA 02111-1307, USA.
+#
+
+import re
+from . import ast
+from .utils import to_underscores
+
+
+class SectionsFile(object):
+ def __init__(self, sections):
+ self.sections = sections
+
+
+class Section(object):
+ def __init__(self):
+ self.file = None
+ self.title = None
+ self.includes = None
+ self.subsections = []
+
+
+class Subsection(object):
+ def __init__(self, name):
+ self.name = name
+ self.symbols = []
+
+
+def parse_sections_file(lines):
+ sections = []
+ current_section = None
+ current_subsection = None
+
+ for line in lines:
+ line = line.rstrip()
+
+ if not line or line.isspace():
+ continue
+
+ if line == "<SECTION>":
+ current_section = Section()
+ sections.append(current_section)
+ current_subsection = Subsection(None)
+ current_section.subsections.append(current_subsection)
+ continue
+
+ if line == "</SECTION>":
+ current_section = None
+ continue
+
+ match = re.match(r"<FILE>(?P<contents>.*)</FILE>", line)
+ if match:
+ current_section.file = match.groupdict['contents']
+ continue
+
+ match = re.match(r"<TITLE>(?P<contents>.*)</TITLE>", line)
+ if match:
+ current_section.title = match.groupdict['contents']
+ continue
+
+ match = re.match(r"<INCLUDE>(?P<contents>.*)</INCLUDE>", line)
+ if match:
+ current_section.includes = match.groupdict['contents']
+ continue
+
+ match = re.match(r"<SUBSECTION(?: (?P<name>.*))?>", line)
+ if match:
+ current_subsection = Subsection(match.groupdict.get('name', None))
+ current_section.subsections.append(current_subsection)
+ continue
+
+ if line.startswith("<") and line.endswith(">"):
+ # Other directive to gtk-doc, not a symbol.
+ continue
+
+ current_subsection.symbols.append(line)
+
+ return SectionsFile(sections)
+
+
+def write_sections_file(f, sections_file):
+ for section in sections_file.sections:
+ f.write("\n<SECTION>\n")
+ if section.file is not None:
+ f.write("<FILE>%s</FILE>\n" % (section.file, ))
+ if section.title is not None:
+ f.write("<TITLE>%s</TITLE>\n" % (section.title, ))
+ if section.includes is not None:
+ f.write("<INCLUDE>%s</INCLUDE>\n" % (section.includes, ))
+
+ is_first_subsection = True
+ for subsection in section.subsections:
+ if subsection.name is not None:
+ f.write("<SUBSECTION %s>\n" % (subsection.name, ))
+ elif not is_first_subsection:
+ f.write("\n<SUBSECTION>\n")
+
+ is_first_subsection = False
+
+ for symbol in subsection.symbols:
+ f.write(symbol + "\n")
+
+
+def generate_sections_file(transformer):
+ ns = transformer.namespace
+
+ sections = []
+
+ def new_section(file_, title):
+ section = Section()
+ section.file = file_
+ section.title = title
+ section.subsections.append(Subsection(None))
+ sections.append(section)
+ return section
+
+ def append_symbol(section, sym):
+ section.subsections[0].symbols.append(sym)
+
+ general_section = new_section("main", "Main")
+
+ for node in ns.itervalues():
+ if isinstance(node, ast.Function):
+ append_symbol(general_section, node.symbol)
+ elif isinstance(node, (ast.Class, ast.Interface)):
+ gtype_name = node.gtype_name
+ file_name = to_underscores(gtype_name).replace('_', '-').lower()
+ section = new_section(file_name, gtype_name)
+ append_symbol(section, gtype_name)
+ append_symbol(section, node.glib_type_struct.target_giname.replace('.', ''))
+
+ for meth in node.methods:
+ append_symbol(section, meth.symbol)
+ for meth in node.static_methods:
+ append_symbol(section, meth.symbol)
+
+ return SectionsFile(sections)
diff --git a/giscanner/shlibs.py b/giscanner/shlibs.py
index 9579e7e6..1241827d 100644
--- a/giscanner/shlibs.py
+++ b/giscanner/shlibs.py
@@ -20,13 +20,13 @@
#
import os
-import re
import platform
+import re
import subprocess
-import os
from .utils import get_libtool_command, extract_libtool_shlib
+
# For .la files, the situation is easy.
def _resolve_libtool(options, binary, libraries):
shlibs = []
@@ -37,6 +37,7 @@ def _resolve_libtool(options, binary, libraries):
return shlibs
+
# Assume ldd output is something vaguely like
#
# libpangoft2-1.0.so.0 => /usr/lib/libpangoft2-1.0.so.0 (0x006c1000)
@@ -52,6 +53,7 @@ def _ldd_library_pattern(library_name):
return re.compile("(?<![A-Za-z0-9_-])(lib*%s[^A-Za-z0-9_-][^\s\(\)]*)"
% re.escape(library_name))
+
# This is a what we do for non-la files. We assume that we are on an
# ELF-like system where ldd exists and the soname extracted with ldd is
# a filename that can be opened with dlopen().
@@ -69,7 +71,7 @@ def _resolve_non_libtool(options, binary, libraries):
if not libraries:
return []
- if os.uname()[0] == 'OpenBSD':
+ if platform.platform().startswith('OpenBSD'):
# Hack for OpenBSD when using the ports' libtool which uses slightly
# different directories to store the libraries in. So rewite binary.args[0]
# by inserting '.libs/'.
@@ -119,6 +121,7 @@ def _resolve_non_libtool(options, binary, libraries):
return shlibs
+
# We want to resolve a set of library names (the <foo> of -l<foo>)
# against a library to find the shared library name. The shared
# library name is suppose to be what you pass to dlopen() (or
diff --git a/giscanner/sourcescanner.c b/giscanner/sourcescanner.c
index 1b775b46..90db2946 100644
--- a/giscanner/sourcescanner.c
+++ b/giscanner/sourcescanner.c
@@ -45,6 +45,31 @@ ctype_free (GISourceType * type)
}
GISourceSymbol *
+gi_source_symbol_copy (GISourceSymbol * symbol)
+{
+ GISourceSymbol *new_symbol = gi_source_symbol_new (symbol->type,
+ symbol->source_filename,
+ symbol->line);
+ new_symbol->ident = g_strdup (symbol->ident);
+
+ if (symbol->base_type)
+ new_symbol->base_type = gi_source_type_copy (symbol->base_type);
+
+ if (symbol->const_int_set) {
+ new_symbol->const_int = symbol->const_int;
+ new_symbol->const_int_is_unsigned = symbol->const_int_is_unsigned;
+ new_symbol->const_int_set = TRUE;
+ } else if (symbol->const_double_set) {
+ new_symbol->const_double = symbol->const_double;
+ new_symbol->const_double_set = TRUE;
+ } else if (symbol->const_string != NULL) {
+ new_symbol->const_string = g_strdup (symbol->const_string);
+ }
+
+ return new_symbol;
+}
+
+GISourceSymbol *
gi_source_symbol_ref (GISourceSymbol * symbol)
{
symbol->ref_count++;
diff --git a/giscanner/sourcescanner.h b/giscanner/sourcescanner.h
index a2834caf..f67ae6bd 100644
--- a/giscanner/sourcescanner.h
+++ b/giscanner/sourcescanner.h
@@ -120,7 +120,6 @@ struct _GISourceSymbol
{
int ref_count;
GISourceSymbolType type;
- int id;
char *ident;
GISourceType *base_type;
gboolean const_int_set;
@@ -163,6 +162,7 @@ GISourceSymbol * gi_source_symbol_new (GISourceSymbolType type
gboolean gi_source_symbol_get_const_boolean (GISourceSymbol *symbol);
GISourceSymbol * gi_source_symbol_ref (GISourceSymbol *symbol);
void gi_source_symbol_unref (GISourceSymbol *symbol);
+GISourceSymbol * gi_source_symbol_copy (GISourceSymbol *symbol);
/* Private */
void gi_source_scanner_add_symbol (GISourceScanner *scanner,
diff --git a/giscanner/sourcescanner.py b/giscanner/sourcescanner.py
index db282f84..d5c43926 100644
--- a/giscanner/sourcescanner.py
+++ b/giscanner/sourcescanner.py
@@ -32,6 +32,10 @@ with LibtoolImporter(None, None):
else:
from giscanner._giscanner import SourceScanner as CSourceScanner
+HEADER_EXTS = ['.h', '.hpp', '.hxx']
+SOURCE_EXTS = ['.c', '.cpp', '.cc', '.cxx']
+ALL_EXTS = SOURCE_EXTS + HEADER_EXTS
+
(CSYMBOL_TYPE_INVALID,
CSYMBOL_TYPE_ELLIPSIS,
CSYMBOL_TYPE_CONST,
@@ -89,8 +93,7 @@ def symbol_type_name(symbol_type):
CSYMBOL_TYPE_UNION: 'union',
CSYMBOL_TYPE_ENUM: 'enum',
CSYMBOL_TYPE_TYPEDEF: 'typedef',
- CSYMBOL_TYPE_MEMBER: 'member',
- }.get(symbol_type)
+ CSYMBOL_TYPE_MEMBER: 'member'}.get(symbol_type)
def ctype_name(ctype):
@@ -104,8 +107,7 @@ def ctype_name(ctype):
CTYPE_ENUM: 'enum',
CTYPE_POINTER: 'pointer',
CTYPE_ARRAY: 'array',
- CTYPE_FUNCTION: 'function',
- }.get(ctype)
+ CTYPE_FUNCTION: 'function'}.get(ctype)
class SourceType(object):
@@ -223,7 +225,8 @@ class SourceScanner(object):
# Public API
- def set_cpp_options(self, includes, defines, undefines):
+ def set_cpp_options(self, includes, defines, undefines, cflags=[]):
+ self._cpp_options.extend(cflags)
for prefix, args in [('-I', includes),
('-D', defines),
('-U', undefines)]:
@@ -240,8 +243,7 @@ class SourceScanner(object):
headers = []
for filename in filenames:
- if (filename.endswith('.c') or filename.endswith('.cpp') or
- filename.endswith('.cc') or filename.endswith('.cxx')):
+ if os.path.splitext(filename)[1] in SOURCE_EXTS:
filename = os.path.abspath(filename)
self._scanner.lex_filename(filename)
else:
@@ -262,7 +264,7 @@ class SourceScanner(object):
return self._scanner.get_comments()
def dump(self):
- print '-'*30
+ print '-' * 30
for symbol in self._scanner.get_symbols():
print symbol.ident, symbol.base_type.name, symbol.type
@@ -274,10 +276,17 @@ class SourceScanner(object):
defines = ['__GI_SCANNER__']
undefs = []
- cpp_args = os.environ.get('CC', 'cc').split()
+ cpp_args = os.environ.get('CC', 'cc').split() # support CC="ccache gcc"
+ if 'cl' in cpp_args:
+ # The Microsoft compiler/preprocessor (cl) does not accept
+ # source input from stdin (the '-' flag), so we need
+ # some help from gcc from MinGW/Cygwin or so.
+ # Note that the generated dumper program is
+ # still built and linked by Visual C++.
+ cpp_args = ['gcc']
cpp_args += ['-E', '-C', '-I.', '-']
-
cpp_args += self._cpp_options
+
proc = subprocess.Popen(cpp_args,
stdin=subprocess.PIPE,
stdout=subprocess.PIPE)
@@ -293,8 +302,8 @@ class SourceScanner(object):
proc.stdin.write('#include <%s>\n' % (filename, ))
proc.stdin.close()
- tmp = tempfile.mktemp()
- fp = open(tmp, 'w+')
+ tmp_fd, tmp_name = tempfile.mkstemp()
+ fp = os.fdopen(tmp_fd, 'w+b')
while True:
data = proc.stdout.read(4096)
if data is None:
@@ -311,4 +320,4 @@ class SourceScanner(object):
self._scanner.parse_file(fp.fileno())
fp.close()
- os.unlink(tmp)
+ os.unlink(tmp_name)
diff --git a/giscanner/testcodegen.py b/giscanner/testcodegen.py
index f304dc7a..1ed247c7 100644
--- a/giscanner/testcodegen.py
+++ b/giscanner/testcodegen.py
@@ -27,12 +27,14 @@ DEFAULT_C_VALUES = {ast.TYPE_ANY: 'NULL',
ast.TYPE_FILENAME: '""',
ast.TYPE_GTYPE: 'g_object_get_type ()'}
+
def get_default_for_typeval(typeval):
default = DEFAULT_C_VALUES.get(typeval)
if default:
return default
return "0"
+
def uscore_from_type(typeval):
if typeval.target_fundamental:
return typeval.target_fundamental.replace(' ', '_')
@@ -41,6 +43,7 @@ def uscore_from_type(typeval):
else:
assert False, typeval
+
class EverythingCodeGenerator(object):
def __init__(self, out_h_filename, out_c_filename):
diff --git a/giscanner/transformer.py b/giscanner/transformer.py
index 6afad889..f70d756a 100644
--- a/giscanner/transformer.py
+++ b/giscanner/transformer.py
@@ -34,6 +34,7 @@ from .sourcescanner import (
CSYMBOL_TYPE_MEMBER, CSYMBOL_TYPE_ELLIPSIS, CSYMBOL_TYPE_CONST,
TYPE_QUALIFIER_CONST, TYPE_QUALIFIER_VOLATILE)
+
class TransformerException(Exception):
pass
@@ -54,14 +55,9 @@ class Transformer(object):
self._namespace = namespace
self._pkg_config_packages = set()
self._typedefs_ns = {}
- self._includes = {} # <string namespace -> Namespace>
- self._include_names = set() # string namespace
+ self._parsed_includes = {} # <string namespace -> Namespace>
self._includepaths = []
self._passthrough_mode = False
- self._annotations = {}
-
- def get_includes(self):
- return self._include_names
def get_pkgconfig_packages(self):
return self._pkg_config_packages
@@ -72,9 +68,6 @@ class Transformer(object):
def set_passthrough_mode(self):
self._passthrough_mode = True
- def set_annotations(self, annotations):
- self._annotations = annotations
-
def _append_new_node(self, node):
original = self._namespace.get(node.name)
# Special case constants here; we allow duplication to sort-of
@@ -112,7 +105,7 @@ class Transformer(object):
if not ns_compound:
ns_compound = self._namespace.get('_' + compound.name)
if (not ns_compound and isinstance(compound, (ast.Record, ast.Union))
- and len(compound.fields) == 0):
+ and len(compound.fields) == 0):
disguised = ast.Record(compound.name, typedef, disguised=True)
self._namespace.append(disguised)
elif not ns_compound:
@@ -125,23 +118,23 @@ class Transformer(object):
self._includepaths = list(paths)
def register_include(self, include):
- if include in self._include_names:
+ if include in self._namespace.includes:
return
+ self._namespace.includes.add(include)
filename = self._find_include(include)
self._parse_include(filename)
- self._include_names.add(include)
def register_include_uninstalled(self, include_path):
basename = os.path.basename(include_path)
if not basename.endswith('.gir'):
- raise SystemExit(
-"Include path %r must be a filename path ending in .gir" % (include_path, ))
+ raise SystemExit("Include path %r must be a filename path "
+ "ending in .gir" % (include_path, ))
girname = basename[:-4]
include = ast.Include.from_string(girname)
- if include in self._include_names:
+ if include in self._namespace.includes:
return
+ self._namespace.includes.add(include)
self._parse_include(include_path, uninstalled=True)
- self._include_names.add(include)
def lookup_giname(self, name):
"""Given a name of the form Foo or Bar.Foo,
@@ -151,11 +144,16 @@ namespaces."""
if '.' not in name:
return self._namespace.get(name)
else:
- (ns, name) = name.split('.', 1)
+ (ns, giname) = name.split('.', 1)
if ns == self._namespace.name:
- return self._namespace.get(name)
- include = self._includes[ns]
- return include.get(name)
+ return self._namespace.get(giname)
+ # Fallback to the main namespace if not a dependency and matches a prefix
+ if ns in self._namespace.identifier_prefixes and not ns in self._parsed_includes:
+ message.warn(("Deprecated reference to identifier " +
+ "prefix %s in GIName %s") % (ns, name))
+ return self._namespace.get(giname)
+ include = self._parsed_includes[ns]
+ return include.get(giname)
def lookup_typenode(self, typeobj):
"""Given a Type object, if it points to a giname,
@@ -178,8 +176,7 @@ None."""
path = os.path.join(d, girname)
if os.path.exists(path):
return path
- sys.stderr.write("Couldn't find include %r (search path: %r)\n"\
- % (girname, searchdirs))
+ sys.stderr.write("Couldn't find include %r (search path: %r)\n" % (girname, searchdirs))
sys.exit(1)
@classmethod
@@ -191,7 +188,7 @@ None."""
self._parse_include(filename)
parser = self._cachestore.load(filename)
self._namespace = parser.get_namespace()
- del self._includes[self._namespace.name]
+ del self._parsed_includes[self._namespace.name]
return self
def _parse_include(self, filename, uninstalled=False):
@@ -204,20 +201,22 @@ None."""
if self._cachestore is not None:
self._cachestore.store(filename, parser)
- for include in parser.get_includes():
- self.register_include(include)
+ for include in parser.get_namespace().includes:
+ if include.name not in self._parsed_includes:
+ dep_filename = self._find_include(include)
+ self._parse_include(dep_filename)
if not uninstalled:
- for pkg in parser.get_pkgconfig_packages():
+ for pkg in parser.get_namespace().exported_packages:
self._pkg_config_packages.add(pkg)
namespace = parser.get_namespace()
- self._includes[namespace.name] = namespace
+ self._parsed_includes[namespace.name] = namespace
def _iter_namespaces(self):
"""Return an iterator over all included namespaces; the
currently-scanned namespace is first."""
yield self._namespace
- for ns in self._includes.itervalues():
+ for ns in self._parsed_includes.itervalues():
yield ns
def _sort_matches(self, x, y):
@@ -229,7 +228,7 @@ currently-scanned namespace is first."""
def _split_c_string_for_namespace_matches(self, name, is_identifier=False):
matches = [] # Namespaces which might contain this name
- unprefixed_namespaces = [] # Namespaces with no prefix, last resort
+ unprefixed_namespaces = [] # Namespaces with no prefix, last resort
for ns in self._iter_namespaces():
if is_identifier:
prefixes = ns.identifier_prefixes
@@ -286,7 +285,7 @@ raise ValueError."""
ident = ident[1:]
try:
matches = self.split_ctype_namespaces(ident)
- except ValueError, e:
+ except ValueError as e:
raise TransformerException(str(e))
for ns, name in matches:
if ns is self._namespace:
@@ -306,7 +305,7 @@ raise ValueError."""
ident = ident[1:]
try:
(ns, name) = self.split_csymbol(ident)
- except ValueError, e:
+ except ValueError as e:
raise TransformerException(str(e))
if ns != self._namespace:
raise TransformerException(
@@ -333,10 +332,7 @@ raise ValueError."""
elif stype == CSYMBOL_TYPE_UNION:
return self._create_union(symbol)
elif stype == CSYMBOL_TYPE_CONST:
- # Don't parse constants which are marked (skip)
- docblock = self._annotations.get(symbol.ident)
- if not docblock or not 'skip' in docblock.options:
- return self._create_const(symbol)
+ return self._create_const(symbol)
# Ignore variable declarations in the header
elif stype == CSYMBOL_TYPE_OBJECT:
pass
@@ -383,7 +379,7 @@ raise ValueError."""
# prefix.
try:
name = self._strip_symbol(child)
- except TransformerException, e:
+ except TransformerException as e:
message.warn_symbol(symbol, e)
return None
members.append(ast.Member(name.lower(),
@@ -393,7 +389,7 @@ raise ValueError."""
try:
enum_name = self.strip_identifier(symbol.ident)
- except TransformerException, e:
+ except TransformerException as e:
message.warn_symbol(symbol, e)
return None
if symbol.base_type.is_bitfield:
@@ -408,11 +404,11 @@ raise ValueError."""
# Drop functions that start with _ very early on here
if symbol.ident.startswith('_'):
return None
- parameters = list(self._create_parameters(symbol.base_type))
+ parameters = list(self._create_parameters(symbol, symbol.base_type))
return_ = self._create_return(symbol.base_type.base_type)
try:
name = self._strip_symbol(symbol)
- except TransformerException, e:
+ except TransformerException as e:
message.warn_symbol(symbol, e)
return None
func = ast.Function(name, return_, parameters, False, symbol.ident)
@@ -476,11 +472,11 @@ raise ValueError."""
return value
- def _create_parameters(self, base_type):
+ def _create_parameters(self, symbol, base_type):
# warn if we see annotations for unknown parameters
param_names = set(child.ident for child in base_type.child_list)
- for child in base_type.child_list:
- yield self._create_parameter(child)
+ for i, child in enumerate(base_type.child_list):
+ yield self._create_parameter(symbol, i, child)
def _synthesize_union_type(self, symbol, parent_symbol):
# Synthesize a named union so that it can be referenced.
@@ -506,8 +502,8 @@ raise ValueError."""
def _create_member(self, symbol, parent_symbol=None):
source_type = symbol.base_type
- if (source_type.type == CTYPE_POINTER and
- symbol.base_type.base_type.type == CTYPE_FUNCTION):
+ if (source_type.type == CTYPE_POINTER
+ and symbol.base_type.base_type.type == CTYPE_FUNCTION):
node = self._create_callback(symbol, member=True)
elif source_type.type == CTYPE_STRUCT and source_type.name is None:
node = self._create_struct(symbol, anonymous=True)
@@ -523,8 +519,8 @@ raise ValueError."""
# to be able to properly calculate the size of the compound
# type (e.g. GValue) that contains this array, see
# <https://bugzilla.gnome.org/show_bug.cgi?id=657040>.
- if (source_type.base_type.type == CTYPE_UNION and
- source_type.base_type.name is None):
+ if (source_type.base_type.type == CTYPE_UNION
+ and source_type.base_type.name is None):
synthesized_type = self._synthesize_union_type(symbol, parent_symbol)
ftype = ast.Array(None, synthesized_type, complete_ctype=complete_ctype)
else:
@@ -562,11 +558,9 @@ raise ValueError."""
def _create_typedef(self, symbol):
ctype = symbol.base_type.type
- if (ctype == CTYPE_POINTER and
- symbol.base_type.base_type.type == CTYPE_FUNCTION):
+ if (ctype == CTYPE_POINTER and symbol.base_type.base_type.type == CTYPE_FUNCTION):
node = self._create_typedef_callback(symbol)
- elif (ctype == CTYPE_POINTER and
- symbol.base_type.base_type.type == CTYPE_STRUCT):
+ elif (ctype == CTYPE_POINTER and symbol.base_type.base_type.type == CTYPE_STRUCT):
node = self._create_typedef_struct(symbol, disguised=True)
elif ctype == CTYPE_STRUCT:
node = self._create_typedef_struct(symbol)
@@ -580,7 +574,7 @@ raise ValueError."""
CTYPE_VOID):
try:
name = self.strip_identifier(symbol.ident)
- except TransformerException, e:
+ except TransformerException as e:
message.warn(e)
return None
if symbol.base_type.name:
@@ -631,8 +625,7 @@ raise ValueError."""
derefed_typename = canonical.replace('*', '')
# Preserve "pointerness" of struct/union members
- if (is_member and canonical.endswith('*') and
- derefed_typename in ast.basic_type_names):
+ if (is_member and canonical.endswith('*') and derefed_typename in ast.basic_type_names):
return 'gpointer'
else:
return derefed_typename
@@ -693,26 +686,36 @@ raise ValueError."""
return container
return ast.Type(ctype=ctype, is_const=is_const, complete_ctype=complete_ctype)
- def _create_parameter(self, symbol):
+ def _create_parameter(self, parent_symbol, index, symbol):
if symbol.type == CSYMBOL_TYPE_ELLIPSIS:
ptype = ast.Varargs()
else:
ptype = self._create_type_from_base(symbol.base_type, is_parameter=True)
- return ast.Parameter(symbol.ident, ptype)
+
+ if symbol.ident is None:
+ if symbol.base_type and symbol.base_type.type != CTYPE_VOID:
+ message.warn_symbol(parent_symbol, "missing parameter name; undocumentable")
+ ident = 'arg%d' % (index, )
+ else:
+ ident = symbol.ident
+
+ return ast.Parameter(ident, ptype)
def _create_return(self, source_type):
typeval = self._create_type_from_base(source_type, is_return=True)
return ast.Return(typeval)
def _create_const(self, symbol):
+ if symbol.ident.startswith('_'):
+ return None
+
# Don't create constants for non-public things
# http://bugzilla.gnome.org/show_bug.cgi?id=572790
- if (symbol.source_filename is None or
- not symbol.source_filename.endswith('.h')):
+ if (symbol.source_filename is None or not symbol.source_filename.endswith('.h')):
return None
try:
name = self._strip_symbol(symbol)
- except TransformerException, e:
+ except TransformerException as e:
message.warn_symbol(symbol, e)
return None
if symbol.const_string is not None:
@@ -731,13 +734,13 @@ raise ValueError."""
if isinstance(target, ast.Type):
unaliased = target
if unaliased == ast.TYPE_UINT64:
- value = str(symbol.const_int % 2**64)
+ value = str(symbol.const_int % 2 ** 64)
elif unaliased == ast.TYPE_UINT32:
- value = str(symbol.const_int % 2**32)
+ value = str(symbol.const_int % 2 ** 32)
elif unaliased == ast.TYPE_UINT16:
- value = str(symbol.const_int % 2**16)
+ value = str(symbol.const_int % 2 ** 16)
elif unaliased == ast.TYPE_UINT8:
- value = str(symbol.const_int % 2**16)
+ value = str(symbol.const_int % 2 ** 16)
else:
value = str(symbol.const_int)
elif symbol.const_double is not None:
@@ -754,7 +757,7 @@ raise ValueError."""
def _create_typedef_struct(self, symbol, disguised=False):
try:
name = self.strip_identifier(symbol.ident)
- except TransformerException, e:
+ except TransformerException as e:
message.warn_symbol(symbol, e)
return None
struct = ast.Record(name, symbol.ident, disguised=disguised)
@@ -766,7 +769,7 @@ raise ValueError."""
def _create_typedef_union(self, symbol):
try:
name = self.strip_identifier(symbol.ident)
- except TransformerException, e:
+ except TransformerException as e:
message.warn(e)
return None
union = ast.Union(name, symbol.ident)
@@ -814,7 +817,7 @@ raise ValueError."""
else:
try:
name = self.strip_identifier(symbol.ident)
- except TransformerException, e:
+ except TransformerException as e:
message.warn(e)
return None
compound = klass(name, symbol.ident)
@@ -830,13 +833,12 @@ raise ValueError."""
return self._create_compound(ast.Union, symbol, anonymous)
def _create_callback(self, symbol, member=False):
- parameters = list(self._create_parameters(symbol.base_type.base_type))
+ parameters = list(self._create_parameters(symbol, symbol.base_type.base_type))
retval = self._create_return(symbol.base_type.base_type.base_type)
# Mark the 'user_data' arguments
for i, param in enumerate(parameters):
- if (param.type.target_fundamental == 'gpointer' and
- param.argname == 'user_data'):
+ if (param.type.target_fundamental == 'gpointer' and param.argname == 'user_data'):
param.closure_name = param.argname
if member:
@@ -844,13 +846,13 @@ raise ValueError."""
elif symbol.ident.find('_') > 0:
try:
name = self._strip_symbol(symbol)
- except TransformerException, e:
+ except TransformerException as e:
message.warn_symbol(symbol, e)
return None
else:
try:
name = self.strip_identifier(symbol.ident)
- except TransformerException, e:
+ except TransformerException as e:
message.warn(e)
return None
callback = ast.Callback(name, retval, parameters, False,
@@ -868,9 +870,12 @@ Note that type resolution may not succeed."""
if '.' in typestr:
container = self._create_bare_container_type(typestr)
if container:
- return container
- return self._namespace.type_from_name(typestr)
- typeval = self.create_type_from_ctype_string(typestr)
+ typeval = container
+ else:
+ typeval = self._namespace.type_from_name(typestr)
+ else:
+ typeval = self.create_type_from_ctype_string(typestr)
+
self.resolve_type(typeval)
if typeval.resolved:
# Explicitly clear out the c_type; there isn't one in this case.
@@ -883,7 +888,7 @@ Note that type resolution may not succeed."""
# which has nominal namespace of "Meta", but a few classes are
# "Mutter". We don't export that data in introspection currently.
# Basically the library should be fixed, but we'll hack around it here.
- for namespace in self._includes.itervalues():
+ for namespace in self._parsed_includes.itervalues():
target = namespace.get_by_ctype(pointer_stripped)
if target:
typeval.target_giname = '%s.%s' % (namespace.name, target.name)
@@ -895,9 +900,8 @@ Note that type resolution may not succeed."""
pointer_stripped = typeval.ctype.replace('*', '')
try:
matches = self.split_ctype_namespaces(pointer_stripped)
- except ValueError, e:
+ except ValueError:
return self._resolve_type_from_ctype_all_namespaces(typeval, pointer_stripped)
- target_giname = None
for namespace, name in matches:
target = namespace.get(name)
if not target:
@@ -916,7 +920,7 @@ Note that type resolution may not succeed."""
return True
return False
- def resolve_type(self, typeval):
+ def _resolve_type_internal(self, typeval):
if isinstance(typeval, (ast.Array, ast.List)):
return self.resolve_type(typeval.element_type)
elif isinstance(typeval, ast.Map):
@@ -930,6 +934,25 @@ Note that type resolution may not succeed."""
elif typeval.gtype_name:
return self._resolve_type_from_gtype_name(typeval)
+ def resolve_type(self, typeval):
+ if not self._resolve_type_internal(typeval):
+ return False
+
+ if typeval.target_fundamental or typeval.target_foreign:
+ return True
+
+ assert typeval.target_giname is not None
+
+ try:
+ type_ = self.lookup_giname(typeval.target_giname)
+ except KeyError:
+ type_ = None
+
+ if type_ is None:
+ typeval.target_giname = None
+
+ return typeval.resolved
+
def _typepair_to_str(self, item):
nsname, item = item
if nsname is None:
diff --git a/giscanner/utils.py b/giscanner/utils.py
index 642da362..77d05b9e 100644
--- a/giscanner/utils.py
+++ b/giscanner/utils.py
@@ -22,7 +22,10 @@ import re
import os
import subprocess
+
_debugflags = None
+
+
def have_debug_flag(flag):
"""Check for whether a specific debugging feature is enabled.
Well-known flags:
@@ -38,6 +41,7 @@ Well-known flags:
_debugflags.remove('')
return flag in _debugflags
+
def break_on_debug_flag(flag):
if have_debug_flag(flag):
import pdb
@@ -69,8 +73,10 @@ def to_underscores_noprefix(name):
name = _upperstr_pat2.sub(r'\1_\2', name)
return name
+
_libtool_pat = re.compile("dlname='([A-z0-9\.\-\+]+)'\n")
+
def _extract_dlname_field(la_file):
f = open(la_file)
data = f.read()
@@ -81,6 +87,7 @@ def _extract_dlname_field(la_file):
else:
return None
+
# Returns the name that we would pass to dlopen() the library
# corresponding to this .la file
def extract_libtool_shlib(la_file):
@@ -92,6 +99,7 @@ def extract_libtool_shlib(la_file):
# a path rather than the raw dlname
return os.path.basename(dlname)
+
def extract_libtool(la_file):
dlname = _extract_dlname_field(la_file)
if dlname is None:
@@ -104,6 +112,7 @@ def extract_libtool(la_file):
libname = libname.replace('.libs/.libs', '.libs')
return libname
+
# Returns arguments for invoking libtool, if applicable, otherwise None
def get_libtool_command(options):
libtool_infection = not options.nolibtool
@@ -121,7 +130,7 @@ def get_libtool_command(options):
try:
subprocess.check_call(['libtool', '--version'],
stdout=open(os.devnull))
- except (subprocess.CalledProcessError, OSError), e:
+ except (subprocess.CalledProcessError, OSError):
# If libtool's not installed, assume we don't need it
return None
diff --git a/giscanner/xmlwriter.py b/giscanner/xmlwriter.py
index fb34adf1..6efe684d 100755
--- a/giscanner/xmlwriter.py
+++ b/giscanner/xmlwriter.py
@@ -44,8 +44,7 @@ def _calc_attrs_length(attributes, indent, self_indent):
return attr_length + indent + self_indent
-def collect_attributes(tag_name, attributes, self_indent,
- self_indent_char, indent=-1):
+def collect_attributes(tag_name, attributes, self_indent, self_indent_char, indent=-1):
if not attributes:
return ''
if _calc_attrs_length(attributes, indent, self_indent) > 79:
@@ -69,6 +68,25 @@ def collect_attributes(tag_name, attributes, self_indent,
return attr_value
+def build_xml_tag(tag_name, attributes=None, data=None, self_indent=0,
+ self_indent_char=' '):
+ if attributes is None:
+ attributes = []
+ prefix = u'<%s' % (tag_name, )
+ if data is not None:
+ if isinstance(data, str):
+ data = data.decode('UTF-8')
+ suffix = u'>%s</%s>' % (escape(data), tag_name)
+ else:
+ suffix = u'/>'
+ attrs = collect_attributes(
+ tag_name, attributes,
+ self_indent,
+ self_indent_char,
+ len(prefix) + len(suffix))
+ return prefix + attrs + suffix
+
+
with LibtoolImporter(None, None):
if 'UNINSTALLED_INTROSPECTION_SRCDIR' in os.environ:
from _giscanner import collect_attributes
@@ -91,10 +109,8 @@ class XMLWriter(object):
def _open_tag(self, tag_name, attributes=None):
if attributes is None:
attributes = []
- attrs = collect_attributes(
- tag_name, attributes, self._indent,
- self._indent_char,
- len(tag_name) + 2)
+ attrs = collect_attributes(tag_name, attributes,
+ self._indent, self._indent_char, len(tag_name) + 2)
self.write_line(u'<%s%s>' % (tag_name, attrs))
def _close_tag(self, tag_name):
@@ -118,12 +134,11 @@ class XMLWriter(object):
line = line.decode('utf-8')
assert isinstance(line, unicode)
if do_escape:
- line = escape(line.encode('utf-8')).decode('utf-8')
+ line = escape(line)
if indent:
- self._data.write('%s%s%s' % (
- self._indent_char * self._indent,
- line.encode('utf-8'),
- self._newline_char))
+ self._data.write('%s%s%s' % (self._indent_char * self._indent,
+ line.encode('utf-8'),
+ self._newline_char))
else:
self._data.write('%s%s' % (line.encode('utf-8'), self._newline_char))
@@ -131,21 +146,8 @@ class XMLWriter(object):
self.write_line('<!-- %s -->' % (text, ))
def write_tag(self, tag_name, attributes, data=None):
- if attributes is None:
- attributes = []
- prefix = u'<%s' % (tag_name, )
- if data is not None:
- if isinstance(data, str):
- data = data.decode('UTF-8')
- suffix = u'>%s</%s>' % (escape(data), tag_name)
- else:
- suffix = u'/>'
- attrs = collect_attributes(
- tag_name, attributes,
- self._indent,
- self._indent_char,
- len(prefix) + len(suffix))
- self.write_line(prefix + attrs + suffix)
+ self.write_line(build_xml_tag(tag_name, attributes, data,
+ self._indent, self._indent_char))
def push_tag(self, tag_name, attributes=None):
if attributes is None: