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().items():
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().items()))
322 return fmt % (option, value)
324 return fmt2 % (option, )
326 for option, value in self.options.items():
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, value in self.options.items():
349 if option == OPT_ALLOW_NONE:
350 self._validate_option(option, value, n_params=0)
351 elif option == OPT_ARRAY:
352 self._validate_array(option, value)
353 elif option == OPT_ATTRIBUTE:
354 self._validate_option(option, value, n_params=2)
355 elif option == OPT_CLOSURE:
356 self._validate_closure(option, value)
357 elif option == OPT_DESTROY:
358 self._validate_option(option, value, n_params=1)
359 elif option == OPT_ELEMENT_TYPE:
360 self._validate_element_type(option, value)
361 elif option == OPT_FOREIGN:
362 self._validate_option(option, value, n_params=0)
363 elif option == OPT_IN:
364 self._validate_option(option, value, n_params=0)
365 elif option in [OPT_INOUT, OPT_INOUT_ALT]:
366 self._validate_option(option, value, n_params=0)
367 elif option == OPT_OUT:
368 self._validate_out(option, value)
369 elif option == OPT_SCOPE:
370 self._validate_option(
371 option, value, required=True,
373 choices=[OPT_SCOPE_ASYNC,
376 elif option == OPT_SKIP:
377 self._validate_option(option, value, n_params=0)
378 elif option == OPT_TRANSFER:
379 self._validate_option(
380 option, value, required=True,
382 choices=[OPT_TRANSFER_FULL,
383 OPT_TRANSFER_CONTAINER,
385 OPT_TRANSFER_FLOATING])
386 elif option == OPT_TYPE:
387 self._validate_option(option, value, required=True,
389 elif option == OPT_CONSTRUCTOR:
390 self._validate_option(option, value, n_params=0)
391 elif option == OPT_METHOD:
392 self._validate_option(option, value, n_params=0)
394 message.warn('invalid annotation option: %s' % (option, ),
398 class DocOptions(object):
403 return '<DocOptions %r>' % (self.values, )
405 def __getitem__(self, item):
406 for key, value in self.values:
411 def __nonzero__(self):
412 return bool(self.values)
415 return (k for k, v in self.values)
417 def add(self, name, value):
418 self.values.append((name, value))
420 def get(self, item, default=None):
421 for key, value in self.values:
426 def getall(self, item):
427 for key, value in self.values:
432 return iter(self.values)
435 class DocOption(object):
437 def __init__(self, tag, option):
441 # (annotation option1=value1 option2=value2) etc
442 for p in option.split(' '):
444 name, value = p.split('=', 1)
448 self._dict[name] = value
450 self._array.append(name)
452 self._array.append((name, value))
455 return '<DocOption %r>' % (self._array, )
458 return len(self._array)
461 assert len(self._array) == 1
462 return self._array[0]
471 class AnnotationParser(object):
473 GTK-Doc comment block parser.
475 Parses GTK-Doc comment blocks into a parse tree built out of :class:`DockBlock`,
476 :class:`DocTag`, :class:`DocOptions` and :class:`DocOption` objects. This
477 parser tries to accept malformed input whenever possible and does not emit
478 syntax errors. However, it does emit warnings at the slightest indication
479 of malformed input when possible. It is usually a good idea to heed these
480 warnings as malformed input is known to result in invalid GTK-Doc output.
482 A GTK-Doc comment block can be constructed out of multiple parts that can
483 be combined to write different types of documentation.
484 See `GTK-Doc's documentation`_ to learn more about possible valid combinations.
485 Each part can be further divided into fields which are separated by `:` characters.
487 Possible parts and the fields they are constructed from look like the
488 following (optional fields are enclosed in square brackets):
492 * identifier_name [:annotations]
493 * @parameter_name [:annotations] [:description]
495 * comment_block_description
496 * tag_name [:annotations] [:description]
499 The order in which the different parts have to be specified is important::
501 - There has to be exactly 1 `identifier` part on the first line of the
502 comment block which consists of:
503 * an `identifier_name` field
504 * an optional `annotations` field
505 - Followed by 0 or more `parameters` parts, each consisting of:
506 * a `parameter_name` field
507 * an optional `annotations` field
508 * an optional `description` field
509 - Followed by at least 1 empty line signaling the beginning of
510 the `comment_block_description` part
511 - Followed by an optional `comment block description` part.
512 - Followed by 0 or more `tag` parts, each consisting of:
514 * an optional `annotations` field
515 * an optional `description` field
517 Additionally, the following restrictions are in effect::
519 - Parts can optionally be separated by an empty line, except between
520 the `parameter` parts and the `comment block description` part where
521 an empty line is required (see above).
522 - Parts and fields cannot span multiple lines, except for
523 `parameter descriptions`, `tag descriptions` and the
524 `comment_block_description` fields.
525 - `parameter descriptions` fields can not span multiple paragraphs.
526 - `tag descriptions` and `comment block description` fields can
527 span multiple paragraphs.
529 .. NOTE:: :class:`AnnotationParser` functionality is heavily based on gtkdoc-mkdb's
530 `ScanSourceFile()`_ function and is currently in sync with GTK-Doc
533 .. _GTK-Doc's documentation:
534 http://developer.gnome.org/gtk-doc-manual/1.18/documenting.html.en
535 .. _ScanSourceFile():
536 http://git.gnome.org/browse/gtk-doc/tree/gtkdoc-mkdb.in#n3722
537 .. _b41641b: b41641bd75f870afff7561ceed8a08456da57565
540 def parse(self, comments):
542 Parses multiple GTK-Doc comment blocks.
544 :param comments: a list of (comment, filename, lineno) tuples
545 :returns: a dictionary mapping identifier names to :class:`DocBlock` objects
550 for comment in comments:
551 comment_block = self.parse_comment_block(comment)
553 if comment_block is not None:
554 # Note: previous versions of this parser did not check
555 # if an identifier was already stored in comment_blocks,
556 # so when multiple comment blocks where encountered documenting
557 # the same identifier the last one seen "wins".
558 # Keep this behavior for backwards compatibility, but
560 if comment_block.name in comment_blocks:
561 message.warn("multiple comment blocks documenting '%s:' identifier." %
562 (comment_block.name),
563 comment_block.position)
565 comment_blocks[comment_block.name] = comment_block
567 return comment_blocks
569 def parse_comment_block(self, comment):
571 Parses a single GTK-Doc comment block.
573 :param comment: a (comment, filename, lineno) tuple
574 :returns: a :class:`DocBlock` object or ``None``
577 comment, filename, lineno = comment
579 # Assign line numbers to each line of the comment block,
580 # which will later be used as the offset to calculate the
581 # real line number in the source file
582 comment_lines = list(enumerate(comment.split('\n')))
584 # Check for the start the comment block.
585 if COMMENT_START_RE.search(comment_lines[0][1]):
588 # Not a GTK-Doc comment block.
591 # Check for the end the comment block.
592 if COMMENT_END_RE.search(comment_lines[-1][1]):
593 del comment_lines[-1]
595 # If we get this far, we are inside a GTK-Doc comment block.
596 return self._parse_comment_block(comment_lines, filename, lineno)
598 def _parse_comment_block(self, comment_lines, filename, lineno):
600 Parses a single GTK-Doc comment block already stripped from its
601 comment start (/**) and comment end (*/) marker lines.
603 :param comment_lines: list of (line_offset, line) tuples representing a
604 GTK-Doc comment block already stripped from it's
605 start (/**) and end (*/) marker lines
606 :param filename: source file name where the comment block originated from
607 :param lineno: line in the source file where the comment block starts
608 :returns: a :class:`DocBlock` object or ``None``
610 .. NOTE:: If you are tempted to refactor this method and split it
611 further up (for example into _parse_identifier(), _parse_parameters(),
612 _parse_description(), _parse_tags() methods) then please resist the
613 urge. It is considered important that this method should be more or
614 less easily comparable with gtkdoc-mkdb's `ScanSourceFile()`_ function.
616 The different parsing steps are marked with a comment surrounded
617 by `#` characters in an attempt to make it clear what is going on.
619 .. _ScanSourceFile():
620 http://git.gnome.org/browse/gtk-doc/tree/gtkdoc-mkdb.in#n3722
629 for line_offset, line in comment_lines:
630 position = message.Position(filename, line_offset + lineno)
632 # Store the original line (without \n) and column offset
633 # so we can generate meaningful warnings later on.
637 # Get rid of ' * ' at start of the line.
638 result = COMMENT_ASTERISK_RE.match(line)
640 column_offset = result.end(0)
641 line = line[result.end(0):]
643 ####################################################################
644 # Check for GTK-Doc comment block identifier.
645 ####################################################################
646 if not comment_block:
647 # The correct identifier name would have the colon at the end
648 # but maintransformer.py does not expect us to do that. So
649 # make sure to compute an identifier_name without the colon and
650 # a real_identifier_name with the colon.
653 result = SECTION_RE.search(line)
655 identifier = IDENTIFIER_SECTION
656 real_identifier_name = 'SECTION:%s' % (result.group('section_name'))
657 identifier_name = real_identifier_name
658 column = result.start('section_name') + column_offset
661 result = SYMBOL_RE.search(line)
663 identifier = IDENTIFIER_SYMBOL
664 real_identifier_name = '%s:' % (result.group('symbol_name'))
665 identifier_name = '%s' % (result.group('symbol_name'))
666 column = result.start('symbol_name') + column_offset
669 result = PROPERTY_RE.search(line)
671 identifier = IDENTIFIER_PROPERTY
672 real_identifier_name = '%s:%s:' % (result.group('class_name'),
673 result.group('property_name'))
674 identifier_name = '%s:%s' % (result.group('class_name'),
675 result.group('property_name'))
676 column = result.start('property_name') + column_offset
679 result = SIGNAL_RE.search(line)
681 identifier = IDENTIFIER_SIGNAL
682 real_identifier_name = '%s::%s:' % (result.group('class_name'),
683 result.group('signal_name'))
684 identifier_name = '%s::%s' % (result.group('class_name'),
685 result.group('signal_name'))
686 column = result.start('signal_name') + column_offset
689 in_part = PART_IDENTIFIER
691 comment_block = DocBlock(identifier_name)
692 comment_block.set_position(position)
694 if 'colon' in result.groupdict() and result.group('colon') != ':':
695 colon_start = result.start('colon')
696 colon_column = column_offset + colon_start
697 marker = ' '*colon_column + '^'
698 message.warn("missing ':' at column %s:\n%s\n%s" %
699 (colon_column + 1, original_line, marker),
702 if 'annotations' in result.groupdict():
703 comment_block.options = self.parse_options(comment_block,
704 result.group('annotations'))
708 # If we get here, the identifier was not recognized, so
709 # ignore the rest of the block just like the old annotation
710 # parser did. Doing this is a bit more strict than
711 # gtkdoc-mkdb (which continues to search for the identifier
712 # until either it is found or the end of the block is
713 # reached). In practice, however, ignoring the block is the
714 # right thing to do because sooner or later some long
715 # descriptions will contain something matching an identifier
716 # pattern by accident.
717 marker = ' '*column_offset + '^'
718 message.warn('ignoring unrecognized GTK-Doc comment block, identifier not '
719 'found:\n%s\n%s' % (original_line, marker),
724 ####################################################################
725 # Check for comment block parameters.
726 ####################################################################
727 result = PARAMETER_RE.search(line)
729 param_name = result.group('parameter_name')
730 param_annotations = result.group('annotations')
731 param_description = result.group('description')
733 if in_part == PART_IDENTIFIER:
734 in_part = PART_PARAMETERS
736 if in_part != PART_PARAMETERS:
737 column = result.start('parameter_name') + column_offset
738 marker = ' '*column + '^'
739 message.warn("'@%s' parameter unexpected at this location:\n%s\n%s" %
740 (param_name, original_line, marker),
743 # Old style GTK-Doc allowed return values to be specified as
744 # parameters instead of tags.
745 if param_name.lower() == TAG_RETURNS:
746 param_name = TAG_RETURNS
751 message.warn("encountered multiple 'Returns' parameters or tags for "
752 "'%s'." % (comment_block.name),
754 elif param_name in comment_block.params.keys():
755 column = result.start('parameter_name') + column_offset
756 marker = ' '*column + '^'
757 message.warn("multiple '@%s' parameters for identifier '%s':\n%s\n%s" %
758 (param_name, comment_block.name, original_line, marker),
761 tag = DocTag(comment_block, param_name)
762 tag.set_position(position)
763 tag.comment = param_description
764 if param_annotations:
765 tag.options = self.parse_options(tag, param_annotations)
766 if param_name == TAG_RETURNS:
767 comment_block.tags[param_name] = tag
769 comment_block.params[param_name] = tag
773 ####################################################################
774 # Check for comment block description.
776 # When we are parsing comment block parameters or the comment block
777 # identifier (when there are no parameters) and encounter an empty
778 # line, we must be parsing the comment block description.
779 ####################################################################
780 if (EMPTY_LINE_RE.search(line)
781 and in_part in [PART_IDENTIFIER, PART_PARAMETERS]):
782 in_part = PART_DESCRIPTION
785 ####################################################################
786 # Check for GTK-Doc comment block tags.
787 ####################################################################
788 result = TAG_RE.search(line)
790 tag_name = result.group('tag_name')
791 tag_annotations = result.group('annotations')
792 tag_description = result.group('description')
794 if in_part == PART_DESCRIPTION:
797 if in_part != PART_TAGS:
798 column = result.start('tag_name') + column_offset
799 marker = ' '*column + '^'
800 message.warn("'%s:' tag unexpected at this location:\n%s\n%s" %
801 (tag_name, original_line, marker),
804 if tag_name.lower() in [TAG_RETURNS, TAG_RETURNVALUE]:
808 message.warn("encountered multiple 'Returns' parameters or tags for "
809 "'%s'." % (comment_block.name),
812 tag = DocTag(comment_block, TAG_RETURNS)
813 tag.position = position
814 tag.comment = tag_description
816 tag.options = self.parse_options(tag, tag_annotations)
817 comment_block.tags[TAG_RETURNS] = tag
821 if tag_name.lower() in comment_block.tags.keys():
822 column = result.start('tag_name') + column_offset
823 marker = ' '*column + '^'
824 message.warn("multiple '%s:' tags for identifier '%s':\n%s\n%s" %
825 (tag_name, comment_block.name, original_line, marker),
828 tag = DocTag(comment_block, tag_name.lower())
829 tag.position = position
830 tag.value = tag_description
832 if tag_name.lower() == TAG_ATTRIBUTES:
833 tag.options = self.parse_options(tag, tag_annotations)
835 message.warn("annotations not supported for tag '%s:'." %
838 comment_block.tags[tag_name.lower()] = tag
842 ####################################################################
843 # If we get here, we must be in the middle of a multiline
844 # comment block, parameter or tag description.
845 ####################################################################
846 if in_part in [PART_IDENTIFIER, PART_DESCRIPTION]:
847 if not comment_block.comment:
848 # Backwards compatibility with old style GTK-Doc
849 # comment blocks where Description used to be a comment
850 # block tag. Simply get rid of 'Description:'.
851 line = re.sub(DESCRIPTION_TAG_RE, '', line)
852 comment_block.comment = line
854 comment_block.comment += '\n' + line
856 elif in_part == PART_PARAMETERS:
857 self._validate_multiline_annotation_continuation(line, original_line,
858 column_offset, position)
860 # Append to parameter description.
861 current_param.comment += ' ' + line.strip()
862 elif in_part == PART_TAGS:
863 self._validate_multiline_annotation_continuation(line, original_line,
864 column_offset, position)
866 # Append to tag description.
867 if current_tag.name.lower() in [TAG_RETURNS, TAG_RETURNVALUE]:
868 current_tag.comment += ' ' + line.strip()
870 current_tag.value += ' ' + line.strip()
872 ########################################################################
873 # Finished parsing this comment block.
874 ########################################################################
875 # We have picked up a couple of \n characters that where not
876 # intended. Strip those.
877 if comment_block.comment:
878 comment_block.comment = comment_block.comment.strip()
880 comment_block.comment = ''
882 for tag in comment_block.tags.itervalues():
883 self._clean_comment_block_part(tag)
885 for param in comment_block.params.itervalues():
886 self._clean_comment_block_part(param)
888 # Validate and store block.
889 comment_block.validate()
892 def _clean_comment_block_part(self, part):
894 part.comment = part.comment.strip()
899 part.value = part.value.strip()
903 def _validate_multiline_annotation_continuation(self, line, original_line,
904 column_offset, position):
906 Validate parameters and tags (except the first line) and generate
907 warnings about invalid annotations spanning multiple lines.
909 :param line: line to validate, stripped from ' * ' at start of the line.
910 :param original_line: original line to validate (used in warning messages)
911 :param column_offset: column width of ' * ' at the time it was stripped from `line`
912 :param position: position of `line` in the source file
915 result = MULTILINE_ANNOTATION_CONTINUATION_RE.search(line)
917 line = result.group('description')
918 column = result.start('annotations') + column_offset
919 marker = ' '*column + '^'
920 message.warn('ignoring invalid multiline annotation continuation:\n'
921 '%s\n%s' % (original_line, marker),
925 def parse_options(cls, tag, value):
927 # (annotation opt1 opt2 ...)
928 # (annotation opt1=value1 opt2=value2 ...)
930 options = DocOptions()
931 options.position = tag.position
933 for i, c in enumerate(value):
934 if c == '(' and opened == -1:
936 if c == ')' and opened != -1:
937 segment = value[opened:i]
938 parts = segment.split(' ', 1)
941 elif len(parts) == 1:
946 if option is not None:
947 option = DocOption(tag, option)
948 options.add(name, option)