def test_template_lookup_add_template_allok() -> None:

    here = Path(__file__).parent

    with warnings.catch_warnings(record=True) as catch_warnings:
        warnings.simplefilter("always")
        lookup = TemplateLookup()
        lookup.add_templatedir(here / 'testcustomtemplates' / 'allok')
    assert len(catch_warnings) == 0, [str(w.message) for w in catch_warnings]
Esempio n. 2
0
 def __init__(self, system: model.System, template_lookup: TemplateLookup):
     # Override L{Page.loader} because here the page L{filename}
     # does not equal the template filename.
     super().__init__(
         system=system,
         template_lookup=template_lookup,
         loader=template_lookup.get_template('summary.html').loader)
Esempio n. 3
0
 def __init__(self,
              filebase: str,
              template_lookup: Optional[TemplateLookup] = None):
     """
     @arg filebase: Output directory.
     @arg template_lookup: Custom L{TemplateLookup} object.
     """
     self.base: Path = Path(filebase)
     self.written_pages: int = 0
     self.total_pages: int = 0
     self.dry_run: bool = False
     self.template_lookup: TemplateLookup = (
         template_lookup if template_lookup else TemplateLookup())
     """Writer's L{TemplateLookup} object"""
def test_template_lookup_add_template_raises() -> None:

    lookup = TemplateLookup()

    with pytest.raises(UnsupportedTemplateVersion):
        lookup.add_template(
            _HtmlTemplate(name="nav.html",
                          text="""
        <nav class="navbar navbar-default" xmlns:t="http://twistedmatrix.com/ns/twisted.web.template/0.1">
            <meta name="pydoctor-template-version" content="2050" />
            <div class="container"> </div>
        </nav>
        """))

    with pytest.raises(ValueError):
        lookup.add_template(
            _HtmlTemplate(name="nav.html",
                          text="""
        <nav class="navbar navbar-default" xmlns:t="http://twistedmatrix.com/ns/twisted.web.template/0.1">
            <meta name="pydoctor-template-version" content="1" />
            <div class="container"> </div>
        </nav>
        <span> Words </span>
        """))
def test_nonempty_table() -> None:
    mod = fromText('def f(): pass')
    t = ChildTable(pages.DocGetter(), mod, mod.contents.values(),
                   ChildTable.lookup_loader(TemplateLookup()))
    flattened = flatten(t)
    assert 'The renderer named' not in flattened
def test_empty_table() -> None:
    mod = fromText('')
    t = ChildTable(pages.DocGetter(), mod, [],
                   ChildTable.lookup_loader(TemplateLookup()))
    flattened = flatten(t)
    assert 'The renderer named' not in flattened
def test_template_lookup_add_template_warns() -> None:

    lookup = TemplateLookup()

    here = Path(__file__).parent

    with pytest.warns(UserWarning) as catch_warnings:
        with (here / 'testcustomtemplates' / 'faketemplate' / 'nav.html').open(
                'r', encoding='utf-8') as fobj:
            lookup.add_template(
                _HtmlTemplate(text=fobj.read(), name='nav.html'))
    assert len(catch_warnings) == 1, [str(w.message) for w in catch_warnings]
    assert "Your custom template 'nav.html' is out of date" in str(
        catch_warnings.pop().message)

    with pytest.warns(UserWarning) as catch_warnings:
        with (here / 'testcustomtemplates' / 'faketemplate' /
              'table.html').open('r', encoding='utf-8') as fobj:
            lookup.add_template(
                _HtmlTemplate(text=fobj.read(), name='table.html'))
    assert len(catch_warnings) == 1, [str(w.message) for w in catch_warnings]
    assert "Could not read 'table.html' template version" in str(
        catch_warnings.pop().message)

    with pytest.warns(UserWarning) as catch_warnings:
        with (here / 'testcustomtemplates' / 'faketemplate' /
              'summary.html').open('r', encoding='utf-8') as fobj:
            lookup.add_template(
                _HtmlTemplate(text=fobj.read(), name='summary.html'))
    assert len(catch_warnings) == 1, [str(w.message) for w in catch_warnings]
    assert "Could not read 'summary.html' template version" in str(
        catch_warnings.pop().message)

    with pytest.warns(UserWarning) as catch_warnings:
        with (here / 'testcustomtemplates' / 'faketemplate' /
              'random.html').open('r', encoding='utf-8') as fobj:
            lookup.add_template(
                _HtmlTemplate(text=fobj.read(), name='random.html'))
    assert len(catch_warnings) == 1, [str(w.message) for w in catch_warnings]
    assert "Invalid template filename 'random.html'" in str(
        catch_warnings.pop().message)

    with pytest.warns(UserWarning) as catch_warnings:
        lookup.add_templatedir(here / 'testcustomtemplates' / 'faketemplate')
    assert len(catch_warnings) == 4, [str(w.message) for w in catch_warnings]
def test_template_lookup_get_template() -> None:

    lookup = TemplateLookup()

    here = Path(__file__).parent

    assert lookup.get_template('index.html').text == filetext(
        here.parent / 'templates' / 'index.html')

    lookup.add_template(
        _HtmlTemplate(name='footer.html',
                      text=filetext(here / 'testcustomtemplates' /
                                    'faketemplate' / 'footer.html')))

    assert lookup.get_template('footer.html').text == filetext(
        here / 'testcustomtemplates' / 'faketemplate' / 'footer.html')

    assert lookup.get_template('index.html').text == filetext(
        here.parent / 'templates' / 'index.html')

    lookup = TemplateLookup()

    assert lookup.get_template('footer.html').text == filetext(
        here.parent / 'templates' / 'footer.html')

    assert lookup.get_template('subheader.html').version == -1

    assert lookup.get_template('table.html').version == 1
def test_html_template_version() -> None:
    lookup = TemplateLookup()
    for template in lookup._templates.values():
        if isinstance(template, _HtmlTemplate) and not template.is_empty():
            assert template.version >= 1
Esempio n. 10
0
def main(args: Sequence[str] = sys.argv[1:]) -> int:
    """
    This is the console_scripts entry point for pydoctor CLI.

    @param args: Command line arguments to run the CLI.
    """
    options, args = parse_args(args)

    exitcode = 0

    if options.configfile:
        readConfigFile(options)

    cache = prepareCache(clearCache=options.clear_intersphinx_cache,
                         enableCache=options.enable_intersphinx_cache,
                         cachePath=options.intersphinx_cache_path,
                         maxAge=options.intersphinx_cache_max_age)

    try:
        # step 1: make/find the system
        if options.systemclass:
            systemclass = findClassFromDottedName(options.systemclass,
                                                  '--system-class',
                                                  model.System)
        else:
            systemclass = zopeinterface.ZopeInterfaceSystem

        system = systemclass(options)
        system.fetchIntersphinxInventories(cache)

        if options.htmlsourcebase:
            if options.projectbasedirectory is None:
                error("you must specify --project-base-dir "
                      "when using --html-viewsource-base")
            system.sourcebase = options.htmlsourcebase

        # step 1.5: check that we're actually going to accomplish something here
        args = list(args) + options.modules + options.packages

        if options.makehtml == MAKE_HTML_DEFAULT:
            if not options.testing and not options.makeintersphinx:
                options.makehtml = True
            else:
                options.makehtml = False

        # Support source date epoch:
        # https://reproducible-builds.org/specs/source-date-epoch/
        try:
            system.buildtime = datetime.datetime.utcfromtimestamp(
                int(os.environ['SOURCE_DATE_EPOCH']))
        except ValueError as e:
            error(str(e))
        except KeyError:
            pass

        if options.buildtime:
            try:
                system.buildtime = datetime.datetime.strptime(
                    options.buildtime, BUILDTIME_FORMAT)
            except ValueError as e:
                error(str(e))

        # step 2: add any packages and modules

        if args:
            prependedpackage = None
            if options.prependedpackage:
                for m in options.prependedpackage.split('.'):
                    prependedpackage = system.Package(system, m,
                                                      prependedpackage)
                    system.addObject(prependedpackage)
                    initmodule = system.Module(system, '__init__',
                                               prependedpackage)
                    system.addObject(initmodule)
            added_paths = set()
            for arg in args:
                path = resolve_path(arg)
                if path in added_paths:
                    continue
                if options.projectbasedirectory is not None:
                    # Note: Path.is_relative_to() was only added in Python 3.9,
                    #       so we have to use this workaround for now.
                    try:
                        path.relative_to(options.projectbasedirectory)
                    except ValueError as ex:
                        error(f"Source path lies outside base directory: {ex}")
                if path.is_dir():
                    system.msg('addPackage', f"adding directory {path}")
                    if not (path / '__init__.py').is_file():
                        error(f"Source directory lacks __init__.py: {path}")
                    system.addPackage(path, prependedpackage)
                elif path.is_file():
                    system.msg('addModuleFromPath', f"adding module {path}")
                    system.addModuleFromPath(path, prependedpackage)
                elif path.exists():
                    error(f"Source path is neither file nor directory: {path}")
                else:
                    error(f"Source path does not exist: {path}")
                added_paths.add(path)
        else:
            error("No source paths given.")

        # step 3: move the system to the desired state

        if system.options.projectname is None:
            name = '/'.join(system.root_names)
            system.msg('warning',
                       f"Guessing '{name}' for project name.",
                       thresh=0)
            system.projectname = name
        else:
            system.projectname = system.options.projectname

        system.process()

        # step 4: make html, if desired

        if options.makehtml:
            options.makeintersphinx = True
            from pydoctor import templatewriter
            if options.htmlwriter:
                writerclass = findClassFromDottedName(options.htmlwriter,
                                                      '--html-writer', IWriter)
            else:
                writerclass = templatewriter.TemplateWriter

            system.msg(
                'html', 'writing html to %s using %s.%s' %
                (options.htmloutput, writerclass.__module__,
                 writerclass.__name__))

            writer: IWriter
            # Handle custom HTML templates
            if system.options.templatedir:
                custom_lookup = TemplateLookup()
                try:
                    custom_lookup.add_templatedir(
                        Path(system.options.templatedir))
                except UnsupportedTemplateVersion as e:
                    error(str(e))

                try:
                    # mypy error: Cannot instantiate abstract class 'IWriter'
                    writer = writerclass(
                        options.htmloutput,  # type: ignore[abstract]
                        template_lookup=custom_lookup)
                except TypeError:
                    # Custom class does not accept 'template_lookup' argument.
                    writer = writerclass(
                        options.htmloutput)  # type: ignore[abstract]
                    warnings.warn(
                        f"Writer '{writerclass.__name__}' does not support "
                        "HTML template customization with --template-dir.")
            else:
                writer = writerclass(
                    options.htmloutput)  # type: ignore[abstract]

            writer.prepOutputDirectory()

            subjects: Sequence[model.Documentable] = ()
            if options.htmlsubjects:
                subjects = [
                    system.allobjects[fn] for fn in options.htmlsubjects
                ]
            else:
                writer.writeSummaryPages(system)
                if not options.htmlsummarypages:
                    subjects = system.rootobjects
            writer.writeIndividualFiles(subjects)
            if system.docstring_syntax_errors:

                def p(msg: str) -> None:
                    system.msg('docstring-summary',
                               msg,
                               thresh=-1,
                               topthresh=1)

                p("these %s objects' docstrings contain syntax errors:" %
                  (len(system.docstring_syntax_errors), ))
                exitcode = 2
                for fn in sorted(system.docstring_syntax_errors):
                    p('    ' + fn)

        if system.violations and options.warnings_as_errors:
            # Update exit code if the run has produced warnings.
            exitcode = 3

        if options.makeintersphinx:
            if not options.makehtml:
                subjects = system.rootobjects
            # Generate Sphinx inventory.
            sphinx_inventory = SphinxInventoryWriter(
                logger=system.msg,
                project_name=system.projectname,
                project_version=system.options.projectversion,
            )
            if not os.path.exists(options.htmloutput):
                os.makedirs(options.htmloutput)
            sphinx_inventory.generate(
                subjects=subjects,
                basepath=options.htmloutput,
            )
    except:
        if options.pdb:
            import pdb
            pdb.post_mortem(sys.exc_info()[2])
        raise
    return exitcode