예제 #1
0
    def test_source_relocated_twice(self):
        sitemap = (u'index.markdown\n'
                   '\ttest-index\n')
        index_path = self.__create_md_file(
            'index.markdown',
            (u'# My documentation\n'))

        a_source_path = 'source_a.test'
        b_source_path = 'source_b.test'
        c_source_path = 'source_c.test'

        symbols = [
            (
                [FunctionSymbol],
                {
                    'unique_name': 'symbol_a',
                    'filename': a_source_path,
                }
            ),
        ]

        comments = [
            Comment(name='page_1',
                    meta={'sources': ['source_a.test']},
                    filename=os.path.join(self.__src_dir, b_source_path),
                    toplevel=True),
            Comment(name='page_1',
                    meta={'sources': ['source_a.test']},
                    filename=os.path.join(self.__src_dir, c_source_path),
                    toplevel=True),
        ]

        with self.assertRaises(InvalidRelocatedSourceException):
            self.__make_project(sitemap, index_path, symbols=symbols, comments=comments,
                    output=self.__output_dir)
예제 #2
0
    def test_symbol_marked_private_twice(self):
        sitemap = (u'index.markdown\n'
                   '\ttest-index\n')
        index_path = self.__create_md_file(
            'index.markdown',
            (u'# My documentation\n'))

        a_source_path = 'source_a.test'
        b_source_path = 'source_b.test'
        c_source_path = 'source_c.test'

        symbols = [
            (
                [FunctionSymbol],
                {
                    'unique_name': 'symbol_a',
                    'filename': a_source_path,
                }
            ),
        ]

        comments = [
            Comment(name='page_1',
                    meta={'private-symbols': ['symbol_a']},
                    filename=b_source_path,
                    toplevel=True),
            Comment(name='page_2',
                    meta={'private-symbols': ['symbol_a']},
                    filename=c_source_path,
                    toplevel=True),
        ]

        with self.assertRaises(SymbolListedTwiceException):
            self.__make_project(sitemap, index_path, symbols=symbols, comments=comments,
                    output=self.__output_dir)
예제 #3
0
    def __extract_feature_comment(self, feature_type, feature):
        pagename = feature_type + '-' + feature['name']
        possible_comment_names = [pagename, feature['name']]
        if feature_type == 'element':
            possible_comment_names.append(feature['hierarchy'][0])

        comment = None
        for comment_name in possible_comment_names:
            comment = self.app.database.get_comment(comment_name)
            if comment:
                break

        description = feature.get('description')
        if not comment:
            comment = Comment(
                pagename,
                Comment(description=feature['name']),
                description=description,
                short_description=Comment(description=description))
            self.app.database.add_comment(comment)
        elif not comment.short_description:
            comment.short_description = Comment(description=description)
        comment.title = Comment(description=feature['name'])
        comment.name = feature.get('name', pagename)
        comment.meta['title'] = feature['name']
        self.__toplevel_comments.add(comment)

        return pagename, comment
예제 #4
0
    def test_multiple_toplevel_comments(self):
        sitemap = ('index.markdown\n'
                   '\ttest-index\n')

        symbols = [
            (
                [FunctionSymbol],
                {
                    'unique_name': 'symbol_a',
                    'filename': 'source1.test'
                }
            ),
            (
                [FunctionSymbol],
                {
                    'unique_name': 'symbol_b',
                    'filename': 'source1.test'
                }
            ),
            (
                [FunctionSymbol],
                {
                    'unique_name': 'symbol_c',
                    'filename': 'source1.test'
                }
            ),
        ]

        comments = [
            Comment(name='foo',
                    filename=os.path.join(self.__src_dir, 'source1.test'),
                    meta={'symbols': ['symbol_a']}, toplevel=True),
            Comment(name='bar',
                    filename=os.path.join(self.__src_dir, 'source1.test'),
                    meta={'symbols': ['symbol_b']}, toplevel=True),
        ]

        self.__create_test_layout(
            sitemap=sitemap, symbols=symbols, comments=comments,
            output=self.__output_dir)

        pages = self.app.project.tree.get_pages()

        self.assertEqual(len(pages), 4)
        self.assertIn('foo', pages)
        foo = pages['foo']
        self.assertEqual(foo.symbol_names, ['symbol_a', 'symbol_c'])

        self.assertIn('bar', pages)
        foo = pages['bar']
        self.assertEqual(foo.symbol_names, ['symbol_b'])
예제 #5
0
    def test_section_and_path_conflict(self):
        sitemap = (u'index.markdown\n'
                   '\ttest-index\n')
        index_path = self.__create_md_file(
            'index.markdown',
            (u'# My documentation\n'))

        a_source_path = 'source1.test'
        b_source_path = 'source2.test'

        symbols = [
            (
                [FunctionSymbol],
                {
                    'unique_name': 'symbol_a',
                    'filename': a_source_path,
                }
            ),
        ]

        comments = [
            Comment(name='source1',
                    filename=b_source_path,
                    meta={'auto-sort': True}, toplevel=True),
        ]

        with self.assertRaises(InvalidOutputException):
            self.__make_project(sitemap, index_path, symbols=symbols, comments=comments,
                    output=self.__output_dir)
예제 #6
0
    def __create_vfunc_symbol(self, node, parent_name):
        klass_node = node.getparent()
        ns = klass_node.getparent()
        gtype_struct = klass_node.attrib.get(glib_ns('type-struct'))

        klass_comment = self.app.database.get_comment(
            '%s%s' % (ns.attrib['name'], gtype_struct))

        unique_name, name, klass_name = get_symbol_names(node)

        # Virtual methods are documented in the class comment
        if klass_comment:
            param_comment = klass_comment.params.get(name)
            if (param_comment):
                self.app.database.add_comment(
                    Comment(name=unique_name,
                            meta={'description': param_comment.description},
                            annotations=param_comment.annotations))

        parameters, retval = self.__create_parameters_and_retval(node)
        symbol = self.get_or_create_symbol(
            VFunctionSymbol,
            node,
            parameters=parameters,
            return_value=retval,
            display_name=name,
            unique_name=unique_name,
            filename=self.__get_symbol_filename(klass_name),
            parent_name=parent_name,
            aliases=[unique_name.replace('::', '.')])

        self.__sort_parameters(symbol, retval, parameters)

        return symbol
예제 #7
0
    def __fetch_comment(self, sym, database):
        sym.comment = database.get_comment(
            sym.unique_name) or Comment(sym.unique_name)

        for sym in sym.get_children_symbols():
            if isinstance(sym, Symbol):
                self.__fetch_comment(sym, database)
예제 #8
0
 def __parse_parameter(self, name, desc):
     name = name.strip()[1:-1].strip()
     desc = desc.strip()
     desc, annotations = self.__extract_annotations(desc)
     annotations = {
         annotation.name: annotation
         for annotation in annotations
     }
     return Comment(name=name, annotations=annotations, description=desc)
예제 #9
0
 def __parse_parameter(self, name, desc):
     name = name.strip()[1:-1].strip()
     raw_comment = '%s:%s' % (name, desc)
     desc = desc.strip()
     desc, annotations = self.__extract_annotations(desc)
     annotations = {annotation.name: annotation for annotation in
                    annotations}
     return Comment(name=name, annotations=annotations,
                    meta={'description': desc}, raw_comment=raw_comment)
예제 #10
0
    def __fetch_comment(self, sym, database):
        old_comment = sym.comment
        new_comment = database.get_comment(sym.unique_name)
        sym.comment = Comment(sym.unique_name)

        if new_comment:
            sym.comment = new_comment
        elif old_comment:
            if old_comment.filename not in ChangeTracker.all_stale_files:
                sym.comment = old_comment
예제 #11
0
파일: tree.py 프로젝트: tintou/hotdoc
    def resolve_symbols(self, tree, database, link_resolver):
        """
        When this method is called, the page's symbol names are queried
        from `database`, and added to lists of actual symbols, sorted
        by symbol class.
        """
        self.typed_symbols = self.__get_empty_typed_symbols()
        all_syms = OrderedSet()
        for sym_name in self.symbol_names:
            sym = database.get_symbol(sym_name)
            self.__query_extra_symbols(sym, all_syms, tree, link_resolver,
                                       database)

        if tree.project.is_toplevel:
            page_path = self.link.ref
        else:
            page_path = self.project_name + '/' + self.link.ref

        if self.meta.get("auto-sort", True):
            all_syms = sorted(all_syms, key=lambda x: x.unique_name)
        for sym in all_syms:
            sym.update_children_comments()
            self.__resolve_symbol(sym, link_resolver, page_path)
            self.symbol_names.add(sym.unique_name)

        # Always put symbols with no parent at the end
        no_parent_syms = self.by_parent_symbols.pop(None, None)
        if no_parent_syms:
            self.by_parent_symbols[None] = no_parent_syms

        for sym_type in [
                ClassSymbol, AliasSymbol, InterfaceSymbol, StructSymbol
        ]:
            syms = self.typed_symbols[sym_type].symbols

            if not syms:
                continue

            if self.title is None:
                self.title = syms[0].display_name
            if self.comment is None:
                self.comment = Comment(name=self.name)
                self.comment.short_description = syms[
                    0].comment.short_description
                self.comment.title = syms[0].comment.title
            break
예제 #12
0
 def test_extension_auto_sorted_override(self):
     sitemap = (u'index.markdown\n'
                '\ttest-index\n'
                '\t\ttest-section.markdown\n'
                '\t\t\tsource_b.test\n'
                '\t\t\tsource_a.test\n'
                '\t\tpage_y.markdown\n'
                '\tcore_page.markdown\n')
     comments = [
         Comment(name='source_b.test',
                 filename=os.path.join(self.__src_dir, 'source_b.test'),
                 meta={'auto-sort': True}, toplevel=True),
     ]
     self.__create_test_layout(sitemap=sitemap, comments=comments)
     pages = self.app.project.tree.get_pages()
     self.assertTrue(pages['source_a.test'].pre_sorted)
     self.assertFalse(pages['source_b.test'].pre_sorted)
예제 #13
0
    def resolve_symbols(self, tree, database, link_resolver):
        """
        When this method is called, the page's symbol names are queried
        from `database`, and added to lists of actual symbols, sorted
        by symbol class.
        """
        typed_symbols_list = namedtuple('TypedSymbolsList',
                                        ['name', 'symbols'])

        for subclass in all_subclasses(Symbol):
            self.typed_symbols[subclass] = typed_symbols_list(
                subclass.get_plural_name(), [])

        all_syms = OrderedSet()
        for sym_name in self.symbol_names:
            sym = database.get_symbol(sym_name)
            self.__query_extra_symbols(sym, all_syms, tree, link_resolver,
                                       database)

        if tree.project.is_toplevel:
            page_path = self.link.ref
        else:
            page_path = self.project_name + '/' + self.link.ref

        for sym in all_syms:
            sym.update_children_comments()
            self.__resolve_symbol(sym, link_resolver, page_path)
            self.symbol_names.add(sym.unique_name)

        for sym_type in [
                ClassSymbol, AliasSymbol, InterfaceSymbol, StructSymbol
        ]:
            syms = self.typed_symbols[sym_type].symbols

            if not syms:
                continue

            if self.title is None:
                self.title = syms[0].display_name
            if self.comment is None:
                self.comment = Comment(name=self.source_file)
                self.comment.short_description = syms[
                    0].comment.short_description
                self.comment.title = syms[0].comment.title
            break
예제 #14
0
    def test_comment_relocation_basic(self):
        sitemap = ('index.markdown\n'
                   '\ttest-index\n')

        symbols = [
            (
                [FunctionSymbol],
                {
                    'unique_name': 'symbol_a',
                    'filename': 'source1.test'
                }
            ),
            (
                [FunctionSymbol],
                {
                    'unique_name': 'symbol_b',
                    'filename': 'source2.test'
                }
            ),
        ]

        # symbol_b should be documented in source1
        comments = [
            Comment(name='source1.test',
                    filename=os.path.join(self.__src_dir, 'source1.test'),
                    meta={'symbols': ['symbol_b']}, toplevel=True),
        ]

        self.__create_test_layout(
            sitemap=sitemap, symbols=symbols, comments=comments,
            output=self.__output_dir,
            source_roots=[os.path.join(self.__src_dir, 'a'),
                          os.path.join(self.__src_dir, 'b')])

        pages = self.app.project.tree.get_pages()
        self.assertEqual(len(pages), 3)
        self.assertIn('source1.test', pages)

        source1 = self.app.project.tree.get_pages()['source1.test']
        self.assertEqual(source1.symbol_names, ['symbol_a', 'symbol_b'])

        # source2 should not appear in the sitemap
        self.assertNotIn('source2.test', pages)
예제 #15
0
    def test_relocation_from_source(self):
        sitemap = ('index.markdown\n'
                   '\ttest-index\n')

        symbols = [
            (
                [FunctionSymbol],
                {
                    'unique_name': 'symbol_a',
                    'filename': os.path.join('a', 'source1.test')
                }
            ),
            (
                [FunctionSymbol],
                {
                    'unique_name': 'symbol_b',
                    'filename': os.path.join('b', 'source2.test')
                }
            ),
        ]

        # symbol_b should be documented in page_1
        comments = [
            Comment(name='page_1',
                    filename=os.path.join(self.__src_dir, 'a', 'source1.test'),
                    meta={'sources': [os.path.join(os.pardir, 'b', 'source2.test')]}, toplevel=True),
        ]

        self.__create_test_layout(
            sitemap=sitemap, symbols=symbols, comments=comments,
            output=self.__output_dir)

        pages = self.app.project.tree.get_pages()
        self.assertIn('page_1', pages)

        source1 = self.app.project.tree.get_pages()['page_1']
        self.assertEqual(source1.symbol_names, ['symbol_a', 'symbol_b'])

        # source2 should not appear in the with only its section
        self.assertNotIn(os.path.join('b', 'source2.test'), pages)
        self.assertEqual(len(pages), 3)
예제 #16
0
    def __create_property_symbols(self,
                                  obj,
                                  parent_uniquename,
                                  pagename,
                                  parent_name=None):
        properties = obj.get('properties', [])
        if not properties:
            return

        gi_extension = self.project.extensions.get('gi-extension')
        python_lang = gi_extension.get_language('python')
        for name, prop in properties.items():
            unique_name = '%s:%s' % (obj.get('name', parent_uniquename), name)
            flags = [ReadableFlag()]
            if prop['writable']:
                flags += [WritableFlag()]
            if prop['construct-only']:
                flags += [ConstructOnlyFlag()]
            elif prop['construct']:
                flags += [ConstructFlag()]

            type_name = prop['type-name']

            tokens = type_tokens_from_type_name(type_name, python_lang)
            type_ = QualifiedSymbol(type_tokens=tokens)

            default = prop.get('default')
            enum = prop.get('values')
            if enum:
                type_ = self.__create_enum_symbol(prop['type-name'],
                                                  enum,
                                                  obj.get(
                                                      'name',
                                                      parent_uniquename),
                                                  parent_name=parent_name)

            if obj['hierarchy'][0] != parent_uniquename:
                aliases = self._get_aliases(
                    ['%s:%s' % (obj['hierarchy'][0], name)])
            else:
                aliases = []

            res = self.app.database.get_symbol(unique_name)
            if res is None:
                res = self.create_symbol(
                    PropertySymbol,
                    prop_type=type_,
                    display_name=name,
                    unique_name=unique_name,
                    aliases=aliases,
                    parent_name=parent_name,
                    extra={'gst-element-name': pagename},
                )
            assert res

            if not self.app.database.get_comment(unique_name):
                comment = Comment(unique_name,
                                  Comment(name=name),
                                  description=prop['blurb'])
                self.app.database.add_comment(comment)

            # FIXME This is incorrect, it's not yet format time (from gi_extension)
            extra_content = self.formatter.format_flags(flags)
            res.extension_contents['Flags'] = extra_content
            if default:
                if prop['type-name'] in ['GstCaps', 'GstStructure']:
                    default = '<pre class="language-yaml">' + \
                        '<code class="language-yaml">%s</code></pre>' % default
                res.extension_contents['Default value'] = default
예제 #17
0
    def parse_comment(self, comment, filename, lineno, endlineno,
                      include_paths=None, stripped=False):
        """
        Returns a Comment given a string
        """
        if not stripped and not self.__validate_c_comment(comment.strip()):
            return None

        title_offset = 0

        column_offset = 0

        raw_comment = comment
        if not stripped:
            try:
                while comment[column_offset * -1 - 1] != '\n':
                    column_offset += 1
            except IndexError:
                column_offset = 0
            comment, title_offset = self.__strip_comment(comment)

        title_and_params, description = self.__extract_titles_params_and_description(comment)
        try:
            block_name, parameters, annotations, is_section = \
                self.__parse_title_and_parameters(filename, title_and_params)
        except HotdocSourceException as _:
            warn('gtk-doc-bad-syntax',
                 message=_.message,
                 filename=filename,
                 lineno=lineno + title_offset)
            return None

        params_offset = 0
        for param in parameters:
            param.filename = filename
            param.lineno = lineno
            param_offset = param.line_offset
            param.line_offset = title_offset + params_offset + 1
            params_offset += param_offset
            param.col_offset = column_offset

        if not block_name:
            return None

        description_offset = 0
        meta = {}
        tags = []
        if description is not None:
            n_lines = len(comment.split('\n'))
            description_offset = (title_offset + n_lines -
                                  len(description.split('\n')))
            meta['description'], tags = self.__parse_description_and_tags(description)

        actual_parameters = OrderedDict({})
        for param in parameters:
            if is_section:
                cleaned_up_name = param.name.lower().replace('_', '-')
                if cleaned_up_name in ['symbols', 'private-symbols', 'auto-sort', 'sources']:
                    meta.update(self.__parse_yaml_comment(param, filename))
                    if cleaned_up_name == 'sources':
                        sources_paths = [os.path.abspath(os.path.join(os.path.dirname(filename), path)) for path in meta[cleaned_up_name]]
                        meta[cleaned_up_name] = sources_paths
                else:
                    meta[param.name] = param.description
            else:
                actual_parameters[param.name] = param

        annotations = {annotation.name: annotation for annotation in
                       annotations}
        tags = {tag.name.lower(): tag for tag in tags}

        block = Comment(name=block_name, filename=filename, lineno=lineno,
                        endlineno=endlineno,
                        annotations=annotations, params=actual_parameters,
                        tags=tags, raw_comment=raw_comment,
                        meta=meta, toplevel=is_section)
        block.line_offset = description_offset
        block.col_offset = column_offset

        return block
예제 #18
0
    def parse_comment(self,
                      comment,
                      filename,
                      lineno,
                      endlineno,
                      include_paths=None,
                      stripped=False):
        """
        Returns a Comment given a string
        """
        if not stripped and not self.__validate_c_comment(comment.strip()):
            return None

        title_offset = 0

        column_offset = 0

        raw_comment = comment
        if not stripped:
            try:
                while comment[column_offset * -1 - 1] != '\n':
                    column_offset += 1
            except IndexError:
                column_offset = 0
            comment, title_offset = self.__strip_comment(comment)

        split = re.split(r'\n[\W]*\n', comment, maxsplit=1)

        try:
            block_name, parameters, annotations, is_section = \
                self.__parse_title_and_parameters(filename, split[0])
        except HotdocSourceException as _:
            warn('gtk-doc-bad-syntax',
                 message=_.message,
                 filename=filename,
                 lineno=lineno + title_offset)
            return None

        params_offset = 0
        for param in parameters:
            param.filename = filename
            param.lineno = lineno
            param_offset = param.line_offset
            param.line_offset = title_offset + params_offset + 1
            params_offset += param_offset
            param.col_offset = column_offset

        if not block_name:
            return None

        description_offset = 0
        meta = {}
        tags = []
        if len(split) > 1:
            n_lines = len(comment.split('\n'))
            description_offset = (title_offset + n_lines -
                                  len(split[1].split('\n')))
            meta['description'], tags = self.__parse_description_and_tags(
                split[1])

        actual_parameters = OrderedDict({})
        for param in parameters:
            if is_section:
                if param.name.lower().replace('_', '-') in [
                        'symbols', 'private-symbols', 'auto-sort'
                ]:
                    meta.update(self.__parse_yaml_comment(param, filename))
                else:
                    meta[param.name] = param.description
            else:
                actual_parameters[param.name] = param

        annotations = {
            annotation.name: annotation
            for annotation in annotations
        }
        tags = {tag.name.lower(): tag for tag in tags}

        block = Comment(name=block_name,
                        filename=filename,
                        lineno=lineno,
                        endlineno=endlineno,
                        annotations=annotations,
                        params=actual_parameters,
                        tags=tags,
                        raw_comment=raw_comment,
                        meta=meta)
        block.line_offset = description_offset
        block.col_offset = column_offset

        return block
예제 #19
0
    def parse_comment(self,
                      comment,
                      filename,
                      lineno,
                      endlineno,
                      include_paths=None,
                      stripped=False):
        """
        Returns a Comment given a string
        """
        if not stripped and not self.__validate_c_comment(comment.strip()):
            return None

        title_offset = 0

        column_offset = 0

        raw_comment = comment
        if not stripped:
            try:
                while comment[column_offset * -1 - 1] != '\n':
                    column_offset += 1
            except IndexError:
                column_offset = 0
            comment, title_offset = self.__strip_comment(comment)

        split = re.split(r'\n[\W]*\n', comment, maxsplit=1)

        try:
            block_name, parameters, annotations = \
                self.__parse_title_and_parameters(filename, split[0])
        except HotdocSourceException as _:
            warn('gtk-doc-bad-syntax',
                 message=_.message,
                 filename=filename,
                 lineno=lineno + title_offset)
            return None

        params_offset = 0
        for param in parameters:
            param.filename = filename
            param.lineno = lineno
            param_offset = param.line_offset
            param.line_offset = title_offset + params_offset + 1
            params_offset += param_offset
            param.col_offset = column_offset

        if not block_name:
            return None

        description_offset = 0
        description = ""
        tags = []
        if len(split) > 1:
            n_lines = len(comment.split('\n'))
            description_offset = (title_offset + n_lines -
                                  len(split[1].split('\n')))
            description, tags = self.__parse_description_and_tags(split[1])

        title = None
        short_description = None
        actual_parameters = OrderedDict({})
        for param in parameters:
            if param.name.lower() == 'short_description':
                short_description = param
            elif param.name.lower() == 'title':
                title = param
            else:
                actual_parameters[param.name] = param

        annotations = {
            annotation.name: annotation
            for annotation in annotations
        }
        tags = {tag.name.lower(): tag for tag in tags}

        block = Comment(name=block_name,
                        filename=filename,
                        lineno=lineno,
                        endlineno=endlineno,
                        annotations=annotations,
                        params=actual_parameters,
                        description=description,
                        short_description=short_description,
                        title=title,
                        tags=tags,
                        raw_comment=raw_comment)
        block.line_offset = description_offset
        block.col_offset = column_offset

        return block