2 # GObject-Introspection - a framework for introspecting GObject libraries
3 # Copyright (C) 2008-2010 Johan Dahlin
4 # Copyright (C) 2012 Dieter Verfaillie <dieterv@optionexplicit.be>
6 # This program is free software; you can redistribute it and/or
7 # modify it under the terms of the GNU General Public License
8 # as published by the Free Software Foundation; either version 2
9 # of the License, or (at your option) any later version.
11 # This program is distributed in the hope that it will be useful,
12 # but WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 # GNU General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
23 # AnnotationParser - extract annotations from GTK-Doc comment blocks
29 from .annotationpatterns import (COMMENT_START_RE, COMMENT_END_RE,
30 COMMENT_ASTERISK_RE, EMPTY_LINE_RE,
31 SECTION_RE, SYMBOL_RE, PROPERTY_RE, SIGNAL_RE,
32 PARAMETER_RE, DESCRIPTION_TAG_RE, TAG_RE,
33 MULTILINE_ANNOTATION_CONTINUATION_RE)
34 from .odict import odict
37 # GTK-Doc comment block parts
38 PART_IDENTIFIER = 'identifier'
39 PART_PARAMETERS = 'parameters'
40 PART_DESCRIPTION = 'description'
44 IDENTIFIER_SECTION = 'section'
45 IDENTIFIER_SYMBOL = 'symbol'
46 IDENTIFIER_PROPERTY = 'property'
47 IDENTIFIER_SIGNAL = 'signal'
49 # Tags - annotations applied to comment blocks
52 TAG_STABILITY = 'stability'
53 TAG_DEPRECATED = 'deprecated'
54 TAG_RETURNS = 'returns'
55 TAG_RETURNVALUE = 'return value'
56 TAG_ATTRIBUTES = 'attributes'
57 TAG_RENAME_TO = 'rename to'
59 TAG_UNREF_FUNC = 'unref func'
60 TAG_REF_FUNC = 'ref func'
61 TAG_SET_VALUE_FUNC = 'set value func'
62 TAG_GET_VALUE_FUNC = 'get value func'
63 TAG_TRANSFER = 'transfer'
65 _ALL_TAGS = [TAG_VFUNC,
81 # Options - annotations for parameters and return values
82 OPT_ALLOW_NONE = 'allow-none'
84 OPT_ATTRIBUTE = 'attribute'
85 OPT_CLOSURE = 'closure'
86 OPT_DESTROY = 'destroy'
87 OPT_ELEMENT_TYPE = 'element-type'
88 OPT_FOREIGN = 'foreign'
91 OPT_INOUT_ALT = 'in-out'
94 OPT_TRANSFER = 'transfer'
97 OPT_CONSTRUCTOR = 'constructor'
119 # Array options - array specific annotations
120 OPT_ARRAY_FIXED_SIZE = 'fixed-size'
121 OPT_ARRAY_LENGTH = 'length'
122 OPT_ARRAY_ZERO_TERMINATED = 'zero-terminated'
125 OPT_OUT_CALLER_ALLOCATES = 'caller-allocates'
126 OPT_OUT_CALLEE_ALLOCATES = 'callee-allocates'
129 OPT_SCOPE_ASYNC = 'async'
130 OPT_SCOPE_CALL = 'call'
131 OPT_SCOPE_NOTIFIED = 'notified'
134 OPT_TRANSFER_NONE = 'none'
135 OPT_TRANSFER_CONTAINER = 'container'
136 OPT_TRANSFER_FULL = 'full'
137 OPT_TRANSFER_FLOATING = 'floating'
140 class DocBlock(object):
142 def __init__(self, name):
144 self.options = DocOptions()
148 self.params = odict()
151 def __cmp__(self, other):
152 return cmp(self.name, other.name)
155 return '<DocBlock %r %r>' % (self.name, self.options)
157 def set_position(self, position):
158 self.position = position
159 self.options.position = position
161 def get_tag(self, name):
162 return self.tags.get(name)
164 def get_param(self, name):
165 return self.params.get(name)
167 def to_gtk_doc(self):
171 options += ' '.join('(%s)' % o for o in self.options)
173 if 'SECTION' not in self.name:
176 for param in self.params.values():
177 lines.append(param.to_gtk_doc_param())
179 for l in self.comment.split('\n'):
183 for tag in self.tags.values():
184 lines.append(tag.to_gtk_doc_tag())
191 comment += ' * %s\n' % (line, )
198 for param in self.params.values():
201 for tag in self.tags.values():
205 class DocTag(object):
207 def __init__(self, block, name):
210 self.options = DocOptions()
216 return '<DocTag %r %r>' % (self.name, self.options)
218 def _validate_option(self, name, value, required=False,
219 n_params=None, choices=None):
220 if required and value is None:
221 message.warn('%s annotation needs a value' % (
222 name, ), self.position)
225 if n_params is not None:
231 s = '%d values' % (n_params, )
232 if ((n_params > 0 and (value is None or value.length() != n_params)) or
233 n_params == 0 and value is not None):
237 length = value.length()
238 message.warn('%s annotation needs %s, not %d' % (
239 name, s, length), self.position)
242 if choices is not None:
243 valuestr = value.one()
244 if valuestr not in choices:
245 message.warn('invalid %s annotation value: %r' % (
246 name, valuestr, ), self.position)
249 def _validate_array(self, option, value):
253 for name, v in value.all().iteritems():
254 if name in [OPT_ARRAY_ZERO_TERMINATED, OPT_ARRAY_FIXED_SIZE]:
257 except (TypeError, ValueError):
260 'array option %s needs a value' % (
262 positions=self.position)
265 'invalid array %s option value %r, '
266 'must be an integer' % (name, v, ),
267 positions=self.position)
268 elif name == OPT_ARRAY_LENGTH:
271 'array option length needs a value',
272 positions=self.position)
275 'invalid array annotation value: %r' % (
276 name, ), self.position)
278 def _validate_closure(self, option, value):
279 if value is not None and value.length() > 1:
281 'closure takes at most 1 value, %d given' % (
282 value.length()), self.position)
284 def _validate_element_type(self, option, value):
285 self._validate_option(option, value, required=True)
288 'element-type takes at least one value, none given',
291 if value.length() > 2:
293 'element-type takes at most 2 values, %d given' % (
294 value.length()), self.position)
297 def _validate_out(self, option, value):
300 if value.length() > 1:
302 'out annotation takes at most 1 value, %d given' % (
303 value.length()), self.position)
305 value_str = value.one()
306 if value_str not in [OPT_OUT_CALLEE_ALLOCATES,
307 OPT_OUT_CALLER_ALLOCATES]:
308 message.warn("out annotation value is invalid: %r" % (
309 value_str), self.position)
312 def set_position(self, position):
313 self.position = position
314 self.options.position = position
316 def _get_gtk_doc_value(self):
317 def serialize_one(option, value, fmt, fmt2):
319 if type(value) != str:
320 value = ' '.join((serialize_one(k, v, '%s=%s', '%s')
321 for k, v in value.all().iteritems()))
322 return fmt % (option, value)
324 return fmt2 % (option, )
326 for option, value in self.options.iteritems():
328 serialize_one(option, value, '(%s %s)', '(%s)'))
330 return ' '.join(annotations) + ': '
334 def to_gtk_doc_param(self):
335 return '@%s: %s%s' % (self.name, self._get_gtk_doc_value(), self.comment)
337 def to_gtk_doc_tag(self):
338 return '%s: %s%s' % (self.name.capitalize(),
339 self._get_gtk_doc_value(),
343 if self.name == TAG_ATTRIBUTES:
344 # The 'Attributes:' tag allows free form annotations so the
345 # validation below is most certainly going to fail.
348 for option in self.options:
349 value = self.options[option]
350 if option == OPT_ALLOW_NONE:
351 self._validate_option(option, value, n_params=0)
352 elif option == OPT_ARRAY:
353 self._validate_array(option, value)
354 elif option == OPT_ATTRIBUTE:
355 self._validate_option(option, value, n_params=2)
356 elif option == OPT_CLOSURE:
357 self._validate_closure(option, value)
358 elif option == OPT_DESTROY:
359 self._validate_option(option, value, n_params=1)
360 elif option == OPT_ELEMENT_TYPE:
361 self._validate_element_type(option, value)
362 elif option == OPT_FOREIGN:
363 self._validate_option(option, value, n_params=0)
364 elif option == OPT_IN:
365 self._validate_option(option, value, n_params=0)
366 elif option in [OPT_INOUT, OPT_INOUT_ALT]:
367 self._validate_option(option, value, n_params=0)
368 elif option == OPT_OUT:
369 self._validate_out(option, value)
370 elif option == OPT_SCOPE:
371 self._validate_option(
372 option, value, required=True,
374 choices=[OPT_SCOPE_ASYNC,
377 elif option == OPT_SKIP:
378 self._validate_option(option, value, n_params=0)
379 elif option == OPT_TRANSFER:
380 self._validate_option(
381 option, value, required=True,
383 choices=[OPT_TRANSFER_FULL,
384 OPT_TRANSFER_CONTAINER,
386 OPT_TRANSFER_FLOATING])
387 elif option == OPT_TYPE:
388 self._validate_option(option, value, required=True,
390 elif option == OPT_CONSTRUCTOR:
391 self._validate_option(option, value, n_params=0)
392 elif option == OPT_METHOD:
393 self._validate_option(option, value, n_params=0)
395 message.warn('invalid annotation option: %s' % (option, ),
399 class DocOptions(object):
404 return '<DocOptions %r>' % (self.values, )
406 def __getitem__(self, item):
407 for key, value in self.values:
412 def __nonzero__(self):
413 return bool(self.values)
416 return (k for k, v in self.values)
418 def add(self, name, value):
419 self.values.append((name, value))
421 def get(self, item, default=None):
422 for key, value in self.values:
427 def getall(self, item):
428 for key, value in self.values:
433 return iter(self.values)
436 class DocOption(object):
438 def __init__(self, tag, option):
442 # (annotation option1=value1 option2=value2) etc
443 for p in option.split(' '):
445 name, value = p.split('=', 1)
449 self._dict[name] = value
451 self._array.append(name)
453 self._array.append((name, value))
456 return '<DocOption %r>' % (self._array, )
459 return len(self._array)
462 assert len(self._array) == 1
463 return self._array[0]
472 class AnnotationParser(object):
474 GTK-Doc comment block parser.
476 Parses GTK-Doc comment blocks into a parse tree built out of :class:`DockBlock`,
477 :class:`DocTag`, :class:`DocOptions` and :class:`DocOption` objects. This
478 parser tries to accept malformed input whenever possible and does not emit
479 syntax errors. However, it does emit warnings at the slightest indication
480 of malformed input when possible. It is usually a good idea to heed these
481 warnings as malformed input is known to result in invalid GTK-Doc output.
483 A GTK-Doc comment block can be constructed out of multiple parts that can
484 be combined to write different types of documentation.
485 See `GTK-Doc's documentation`_ to learn more about possible valid combinations.
486 Each part can be further divided into fields which are separated by `:` characters.
488 Possible parts and the fields they are constructed from look like the
489 following (optional fields are enclosed in square brackets):
493 * identifier_name [:annotations]
494 * @parameter_name [:annotations] [:description]
496 * comment_block_description
497 * tag_name [:annotations] [:description]
500 The order in which the different parts have to be specified is important::
502 - There has to be exactly 1 `identifier` part on the first line of the
503 comment block which consists of:
504 * an `identifier_name` field
505 * an optional `annotations` field
506 - Followed by 0 or more `parameters` parts, each consisting of:
507 * a `parameter_name` field
508 * an optional `annotations` field
509 * an optional `description` field
510 - Followed by at least 1 empty line signaling the beginning of
511 the `comment_block_description` part
512 - Followed by an optional `comment block description` part.
513 - Followed by 0 or more `tag` parts, each consisting of:
515 * an optional `annotations` field
516 * an optional `description` field
518 Additionally, the following restrictions are in effect::
520 - Parts can optionally be separated by an empty line, except between
521 the `parameter` parts and the `comment block description` part where
522 an empty line is required (see above).
523 - Parts and fields cannot span multiple lines, except for
524 `parameter descriptions`, `tag descriptions` and the
525 `comment_block_description` fields.
526 - `parameter descriptions` fields can not span multiple paragraphs.
527 - `tag descriptions` and `comment block description` fields can
528 span multiple paragraphs.
530 .. NOTE:: :class:`AnnotationParser` functionality is heavily based on gtkdoc-mkdb's
531 `ScanSourceFile()`_ function and is currently in sync with GTK-Doc
534 .. _GTK-Doc's documentation:
535 http://developer.gnome.org/gtk-doc-manual/1.18/documenting.html.en
536 .. _ScanSourceFile():
537 http://git.gnome.org/browse/gtk-doc/tree/gtkdoc-mkdb.in#n3722
538 .. _b41641b: b41641bd75f870afff7561ceed8a08456da57565
541 def parse(self, comments):
543 Parses multiple GTK-Doc comment blocks.
545 :param comments: a list of (comment, filename, lineno) tuples
546 :returns: a dictionary mapping identifier names to :class:`DocBlock` objects
551 for comment in comments:
552 comment_block = self.parse_comment_block(comment)
554 if comment_block is not None:
555 # Note: previous versions of this parser did not check
556 # if an identifier was already stored in comment_blocks,
557 # so when multiple comment blocks where encountered documenting
558 # the same identifier the last one seen "wins".
559 # Keep this behavior for backwards compatibility, but
561 if comment_block.name in comment_blocks:
562 message.warn("multiple comment blocks documenting '%s:' identifier." %
563 (comment_block.name),
564 comment_block.position)
566 comment_blocks[comment_block.name] = comment_block
568 return comment_blocks
570 def parse_comment_block(self, comment):
572 Parses a single GTK-Doc comment block.
574 :param comment: a (comment, filename, lineno) tuple
575 :returns: a :class:`DocBlock` object or ``None``
578 comment, filename, lineno = comment
580 # Assign line numbers to each line of the comment block,
581 # which will later be used as the offset to calculate the
582 # real line number in the source file
583 comment_lines = list(enumerate(comment.split('\n')))
585 # Check for the start the comment block.
586 if COMMENT_START_RE.search(comment_lines[0][1]):
589 # Not a GTK-Doc comment block.
592 # Check for the end the comment block.
593 if COMMENT_END_RE.search(comment_lines[-1][1]):
594 del comment_lines[-1]
596 # If we get this far, we are inside a GTK-Doc comment block.
597 return self._parse_comment_block(comment_lines, filename, lineno)
599 def _parse_comment_block(self, comment_lines, filename, lineno):
601 Parses a single GTK-Doc comment block already stripped from its
602 comment start (/**) and comment end (*/) marker lines.
604 :param comment_lines: list of (line_offset, line) tuples representing a
605 GTK-Doc comment block already stripped from it's
606 start (/**) and end (*/) marker lines
607 :param filename: source file name where the comment block originated from
608 :param lineno: line in the source file where the comment block starts
609 :returns: a :class:`DocBlock` object or ``None``
611 .. NOTE:: If you are tempted to refactor this method and split it
612 further up (for example into _parse_identifier(), _parse_parameters(),
613 _parse_description(), _parse_tags() methods) then please resist the
614 urge. It is considered important that this method should be more or
615 less easily comparable with gtkdoc-mkdb's `ScanSourceFile()`_ function.
617 The different parsing steps are marked with a comment surrounded
618 by `#` characters in an attempt to make it clear what is going on.
620 .. _ScanSourceFile():
621 http://git.gnome.org/browse/gtk-doc/tree/gtkdoc-mkdb.in#n3722
630 for line_offset, line in comment_lines:
631 position = message.Position(filename, line_offset + lineno)
633 # Store the original line (without \n) and column offset
634 # so we can generate meaningful warnings later on.
638 # Get rid of ' * ' at start of the line.
639 result = COMMENT_ASTERISK_RE.match(line)
641 column_offset = result.end(0)
642 line = line[result.end(0):]
644 ####################################################################
645 # Check for GTK-Doc comment block identifier.
646 ####################################################################
647 if not comment_block:
648 # The correct identifier name would have the colon at the end
649 # but maintransformer.py does not expect us to do that. So
650 # make sure to compute an identifier_name without the colon and
651 # a real_identifier_name with the colon.
654 result = SECTION_RE.search(line)
656 identifier = IDENTIFIER_SECTION
657 real_identifier_name = 'SECTION:%s' % (result.group('section_name'))
658 identifier_name = real_identifier_name
659 column = result.start('section_name') + column_offset
662 result = SYMBOL_RE.search(line)
664 identifier = IDENTIFIER_SYMBOL
665 real_identifier_name = '%s:' % (result.group('symbol_name'))
666 identifier_name = '%s' % (result.group('symbol_name'))
667 column = result.start('symbol_name') + column_offset
670 result = PROPERTY_RE.search(line)
672 identifier = IDENTIFIER_PROPERTY
673 real_identifier_name = '%s:%s:' % (result.group('class_name'),
674 result.group('property_name'))
675 identifier_name = '%s:%s' % (result.group('class_name'),
676 result.group('property_name'))
677 column = result.start('property_name') + column_offset
680 result = SIGNAL_RE.search(line)
682 identifier = IDENTIFIER_SIGNAL
683 real_identifier_name = '%s::%s:' % (result.group('class_name'),
684 result.group('signal_name'))
685 identifier_name = '%s::%s' % (result.group('class_name'),
686 result.group('signal_name'))
687 column = result.start('signal_name') + column_offset
690 in_part = PART_IDENTIFIER
692 comment_block = DocBlock(identifier_name)
693 comment_block.set_position(position)
695 if 'colon' in result.groupdict() and result.group('colon') != ':':
696 colon_start = result.start('colon')
697 colon_column = column_offset + colon_start
698 marker = ' '*colon_column + '^'
699 message.warn("missing ':' at column %s:\n%s\n%s" %
700 (colon_column + 1, original_line, marker),
703 if 'annotations' in result.groupdict():
704 comment_block.options = self.parse_options(comment_block,
705 result.group('annotations'))
709 # If we get here, the identifier was not recognized, so
710 # ignore the rest of the block just like the old annotation
711 # parser did. Doing this is a bit more strict than
712 # gtkdoc-mkdb (which continues to search for the identifier
713 # until either it is found or the end of the block is
714 # reached). In practice, however, ignoring the block is the
715 # right thing to do because sooner or later some long
716 # descriptions will contain something matching an identifier
717 # pattern by accident.
718 marker = ' '*column_offset + '^'
719 message.warn('ignoring unrecognized GTK-Doc comment block, identifier not '
720 'found:\n%s\n%s' % (original_line, marker),
725 ####################################################################
726 # Check for comment block parameters.
727 ####################################################################
728 result = PARAMETER_RE.search(line)
730 param_name = result.group('parameter_name')
731 param_annotations = result.group('annotations')
732 param_description = result.group('description')
734 if in_part == PART_IDENTIFIER:
735 in_part = PART_PARAMETERS
737 if in_part != PART_PARAMETERS:
738 column = result.start('parameter_name') + column_offset
739 marker = ' '*column + '^'
740 message.warn("'@%s' parameter unexpected at this location:\n%s\n%s" %
741 (param_name, original_line, marker),
744 # Old style GTK-Doc allowed return values to be specified as
745 # parameters instead of tags.
746 if param_name.lower() == TAG_RETURNS:
747 param_name = TAG_RETURNS
752 message.warn("encountered multiple 'Returns' parameters or tags for "
753 "'%s'." % (comment_block.name),
755 elif param_name in comment_block.params.keys():
756 column = result.start('parameter_name') + column_offset
757 marker = ' '*column + '^'
758 message.warn("multiple '@%s' parameters for identifier '%s':\n%s\n%s" %
759 (param_name, comment_block.name, original_line, marker),
762 tag = DocTag(comment_block, param_name)
763 tag.set_position(position)
764 tag.comment = param_description
765 if param_annotations:
766 tag.options = self.parse_options(tag, param_annotations)
767 if param_name == TAG_RETURNS:
768 comment_block.tags[param_name] = tag
770 comment_block.params[param_name] = tag
774 ####################################################################
775 # Check for comment block description.
777 # When we are parsing comment block parameters or the comment block
778 # identifier (when there are no parameters) and encounter an empty
779 # line, we must be parsing the comment block description.
780 ####################################################################
781 if (EMPTY_LINE_RE.search(line)
782 and in_part in [PART_IDENTIFIER, PART_PARAMETERS]):
783 in_part = PART_DESCRIPTION
786 ####################################################################
787 # Check for GTK-Doc comment block tags.
788 ####################################################################
789 result = TAG_RE.search(line)
791 tag_name = result.group('tag_name')
792 tag_annotations = result.group('annotations')
793 tag_description = result.group('description')
795 if in_part == PART_DESCRIPTION:
798 if in_part != PART_TAGS:
799 column = result.start('tag_name') + column_offset
800 marker = ' '*column + '^'
801 message.warn("'%s:' tag unexpected at this location:\n%s\n%s" %
802 (tag_name, original_line, marker),
805 if tag_name.lower() in [TAG_RETURNS, TAG_RETURNVALUE]:
809 message.warn("encountered multiple 'Returns' parameters or tags for "
810 "'%s'." % (comment_block.name),
813 tag = DocTag(comment_block, TAG_RETURNS)
814 tag.position = position
815 tag.comment = tag_description
817 tag.options = self.parse_options(tag, tag_annotations)
818 comment_block.tags[TAG_RETURNS] = tag
822 if tag_name.lower() in comment_block.tags.keys():
823 column = result.start('tag_name') + column_offset
824 marker = ' '*column + '^'
825 message.warn("multiple '%s:' tags for identifier '%s':\n%s\n%s" %
826 (tag_name, comment_block.name, original_line, marker),
829 tag = DocTag(comment_block, tag_name.lower())
830 tag.position = position
831 tag.value = tag_description
833 if tag_name.lower() == TAG_ATTRIBUTES:
834 tag.options = self.parse_options(tag, tag_annotations)
836 message.warn("annotations not supported for tag '%s:'." %
839 comment_block.tags[tag_name.lower()] = tag
843 ####################################################################
844 # If we get here, we must be in the middle of a multiline
845 # comment block, parameter or tag description.
846 ####################################################################
847 if in_part in [PART_IDENTIFIER, PART_DESCRIPTION]:
848 if not comment_block.comment:
849 # Backwards compatibility with old style GTK-Doc
850 # comment blocks where Description used to be a comment
851 # block tag. Simply get rid of 'Description:'.
852 line = re.sub(DESCRIPTION_TAG_RE, '', line)
853 comment_block.comment = line
855 comment_block.comment += '\n' + line
857 elif in_part == PART_PARAMETERS:
858 self._validate_multiline_annotation_continuation(line, original_line,
859 column_offset, position)
861 # Append to parameter description.
862 current_param.comment += ' ' + line.strip()
863 elif in_part == PART_TAGS:
864 self._validate_multiline_annotation_continuation(line, original_line,
865 column_offset, position)
867 # Append to tag description.
868 if current_tag.name.lower() in [TAG_RETURNS, TAG_RETURNVALUE]:
869 current_tag.comment += ' ' + line.strip()
871 current_tag.value += ' ' + line.strip()
873 ########################################################################
874 # Finished parsing this comment block.
875 ########################################################################
876 # We have picked up a couple of \n characters that where not
877 # intended. Strip those.
878 if comment_block.comment:
879 comment_block.comment = comment_block.comment.strip()
881 comment_block.comment = ''
883 for tag in comment_block.tags.itervalues():
884 self._clean_comment_block_part(tag)
886 for param in comment_block.params.itervalues():
887 self._clean_comment_block_part(param)
889 # Validate and store block.
890 comment_block.validate()
893 def _clean_comment_block_part(self, part):
895 part.comment = part.comment.strip()
900 part.value = part.value.strip()
904 def _validate_multiline_annotation_continuation(self, line, original_line,
905 column_offset, position):
907 Validate parameters and tags (except the first line) and generate
908 warnings about invalid annotations spanning multiple lines.
910 :param line: line to validate, stripped from ' * ' at start of the line.
911 :param original_line: original line to validate (used in warning messages)
912 :param column_offset: column width of ' * ' at the time it was stripped from `line`
913 :param position: position of `line` in the source file
916 result = MULTILINE_ANNOTATION_CONTINUATION_RE.search(line)
918 line = result.group('description')
919 column = result.start('annotations') + column_offset
920 marker = ' '*column + '^'
921 message.warn('ignoring invalid multiline annotation continuation:\n'
922 '%s\n%s' % (original_line, marker),
926 def parse_options(cls, tag, value):
928 # (annotation opt1 opt2 ...)
929 # (annotation opt1=value1 opt2=value2 ...)
931 options = DocOptions()
932 options.position = tag.position
934 for i, c in enumerate(value):
935 if c == '(' and opened == -1:
937 if c == ')' and opened != -1:
938 segment = value[opened:i]
939 parts = segment.split(' ', 1)
942 elif len(parts) == 1:
947 if option is not None:
948 option = DocOption(tag, option)
949 options.add(name, option)