Exemple #1
0
    def __init__(self, name, theme_path, factory):
        # type: (str, str, HTMLThemeFactory) -> None
        self.name = name
        self.base = None
        self.rootdir = None

        if path.isdir(theme_path):
            # already a directory, do nothing
            self.rootdir = None
            self.themedir = theme_path
        else:
            # extract the theme to a temp directory
            self.rootdir = tempfile.mkdtemp('sxt')
            self.themedir = path.join(self.rootdir, name)
            extract_zip(theme_path, self.themedir)

        self.config = configparser.RawConfigParser()
        self.config.read(path.join(self.themedir, THEMECONF))

        try:
            inherit = self.config.get('theme', 'inherit')
        except configparser.NoSectionError:
            raise ThemeError(
                __('theme %r doesn\'t have "theme" setting') % name)
        except configparser.NoOptionError:
            raise ThemeError(
                __('theme %r doesn\'t have "inherit" setting') % name)

        if inherit != 'none':
            try:
                self.base = factory.create(inherit)
            except ThemeError:
                raise ThemeError(
                    __('no theme named %r found, inherited by %r') %
                    (inherit, name))
Exemple #2
0
    def __init__(self, name):
        # type: (unicode) -> None
        if name not in self.themes:
            self.load_extra_theme(name)
            if name not in self.themes:
                if name == 'sphinx_rtd_theme':
                    raise ThemeError(
                        'sphinx_rtd_theme is no longer a hard dependency '
                        'since version 1.4.0. Please install it manually.'
                        '(pip install sphinx_rtd_theme)')
                else:
                    raise ThemeError('no theme named %r found '
                                     '(missing theme.conf?)' % name)
        self.name = name

        # Do not warn yet -- to be compatible with old Sphinxes, people *have*
        # to use "default".
        # if name == 'default' and warn:
        #     warn("'default' html theme has been renamed to 'classic'. "
        #          "Please change your html_theme setting either to "
        #          "the new 'alabaster' default theme, or to 'classic' "
        #          "to keep using the old default.")

        tdir, tinfo = self.themes[name]
        if tinfo is None:
            # already a directory, do nothing
            self.themedir = tdir
            self.themedir_created = False
        else:
            # extract the theme to a temp directory
            self.themedir = tempfile.mkdtemp('sxt')
            self.themedir_created = True
            for name in tinfo.namelist():
                if name.endswith('/'):
                    continue
                dirname = path.dirname(name)
                if not path.isdir(path.join(self.themedir, dirname)):
                    os.makedirs(path.join(self.themedir, dirname))
                with open(path.join(self.themedir, name), 'wb') as fp:
                    fp.write(tinfo.read(name))

        self.themeconf = configparser.RawConfigParser()
        self.themeconf.read(path.join(self.themedir,
                                      THEMECONF))  # type: ignore

        try:
            inherit = self.themeconf.get('theme', 'inherit')
        except configparser.NoOptionError:
            raise ThemeError('theme %r doesn\'t have "inherit" setting' % name)

        # load inherited theme automatically #1794, #1884, #1885
        self.load_extra_theme(inherit)

        if inherit == 'none':
            self.base = None
        elif inherit not in self.themes:
            raise ThemeError('no theme named %r found, inherited by %r' %
                             (inherit, name))
        else:
            self.base = Theme(inherit)
Exemple #3
0
    def __init__(self, name, warn=None):
        if name not in self.themes:
            self.load_extra_theme(name)
            if name not in self.themes:
                raise ThemeError('no theme named %r found '
                                 '(missing theme.conf?)' % name)
        self.name = name

        # Do not warn yet -- to be compatible with old Sphinxes, people *have*
        # to use "default".
        # if name == 'default' and warn:
        #     warn("'default' html theme has been renamed to 'classic'. "
        #          "Please change your html_theme setting either to "
        #          "the new 'alabaster' default theme, or to 'classic' "
        #          "to keep using the old default.")

        tdir, tinfo = self.themes[name]
        if tinfo is None:
            # already a directory, do nothing
            self.themedir = tdir
            self.themedir_created = False
        else:
            # extract the theme to a temp directory
            self.themedir = tempfile.mkdtemp('sxt')
            self.themedir_created = True
            for name in tinfo.namelist():
                if name.endswith('/'):
                    continue
                dirname = path.dirname(name)
                if not path.isdir(path.join(self.themedir, dirname)):
                    os.makedirs(path.join(self.themedir, dirname))
                fp = open(path.join(self.themedir, name), 'wb')
                fp.write(tinfo.read(name))
                fp.close()

        self.themeconf = configparser.RawConfigParser()
        self.themeconf.read(path.join(self.themedir, THEMECONF))

        try:
            inherit = self.themeconf.get('theme', 'inherit')
        except configparser.NoOptionError:
            raise ThemeError('theme %r doesn\'t have "inherit" setting' % name)

        if inherit in ['alabaster', 'sphinx_rtd_theme']:
            # include 'alabaster' or 'sphinx_themes' automatically #1794
            self.load_extra_theme(inherit)

        if inherit == 'none':
            self.base = None
        elif inherit not in self.themes:
            raise ThemeError('no theme named %r found, inherited by %r' %
                             (inherit, name))
        else:
            self.base = Theme(inherit, warn=warn)
Exemple #4
0
    def __init__(self, name: str, filename: str) -> None:
        self.name = name
        self.config = configparser.RawConfigParser()
        self.config.read(path.join(filename))

        try:
            self.docclass = self.config.get('theme', 'docclass')
            self.wrapperclass = self.config.get('theme', 'wrapperclass')
            self.toplevel_sectioning = self.config.get('theme', 'toplevel_sectioning')
        except configparser.NoSectionError:
            raise ThemeError(__('%r doesn\'t have "theme" setting') % filename)
        except configparser.NoOptionError as exc:
            raise ThemeError(__('%r doesn\'t have "%s" setting') % (filename, exc.args[0]))
Exemple #5
0
    def create(self, name: str) -> Theme:
        """Create an instance of theme."""
        if name not in self.themes:
            self.load_extra_theme(name)

        if name not in self.themes:
            if name == 'sphinx_rtd_theme':
                raise ThemeError(__('sphinx_rtd_theme is no longer a hard dependency '
                                    'since version 1.4.0. Please install it manually.'
                                    '(pip install sphinx_rtd_theme)'))
            else:
                raise ThemeError(__('no theme named %r found '
                                    '(missing theme.conf?)') % name)

        return Theme(name, self.themes[name], factory=self)
Exemple #6
0
def load_theme_plugins():
    # type: () -> List[unicode]
    """load plugins by using``sphinx_themes`` section in setuptools entry_points.
    This API will return list of directory that contain some theme directory.
    """

    if not pkg_resources:
        return []

    theme_paths = []  # type: List[unicode]

    for plugin in pkg_resources.iter_entry_points('sphinx_themes'):
        func_or_path = plugin.load()
        try:
            path = func_or_path()
        except Exception:
            path = func_or_path

        if isinstance(path, string_types):
            theme_paths.append(path)  # type: ignore
        else:
            raise ThemeError('Plugin %r does not response correctly.' %
                             plugin.module_name)

    return theme_paths
Exemple #7
0
    def create(self, name: str) -> Theme:
        """Create an instance of theme."""
        if name not in self.themes:
            self.load_extra_theme(name)

        if name not in self.themes:
            raise ThemeError(
                __('no theme named %r found (missing theme.conf?)') % name)

        return Theme(name, self.themes[name], factory=self)
Exemple #8
0
    def __init__(self, name):
        if name not in self.themes:
            self.load_extra_themes()
            if name not in self.themes:
                raise ThemeError('no theme named %r found '
                                 '(missing theme.conf?)' % name)
        self.name = name

        tdir, tinfo = self.themes[name]
        if tinfo is None:
            # already a directory, do nothing
            self.themedir = tdir
            self.themedir_created = False
        else:
            # extract the theme to a temp directory
            self.themedir = tempfile.mkdtemp('sxt')
            self.themedir_created = True
            for name in tinfo.namelist():
                if name.endswith('/'):
                    continue
                dirname = path.dirname(name)
                if not path.isdir(path.join(self.themedir, dirname)):
                    os.makedirs(path.join(self.themedir, dirname))
                fp = open(path.join(self.themedir, name), 'wb')
                fp.write(tinfo.read(name))
                fp.close()

        self.themeconf = configparser.RawConfigParser()
        self.themeconf.read(path.join(self.themedir, THEMECONF))

        try:
            inherit = self.themeconf.get('theme', 'inherit')
        except configparser.NoOptionError:
            raise ThemeError('theme %r doesn\'t have "inherit" setting' % name)
        if inherit == 'none':
            self.base = None
        elif inherit not in self.themes:
            raise ThemeError('no theme named %r found, inherited by %r' %
                             (inherit, name))
        else:
            self.base = Theme(inherit)
Exemple #9
0
    def __init__(self, name: str, filename: str) -> None:
        super().__init__(name)
        self.config = configparser.RawConfigParser()
        self.config.read(path.join(filename))

        for key in self.REQUIRED_CONFIG_KEYS:
            try:
                value = self.config.get('theme', key)
                setattr(self, key, value)
            except configparser.NoSectionError as exc:
                raise ThemeError(__('%r doesn\'t have "theme" setting') %
                                 filename) from exc
            except configparser.NoOptionError as exc:
                raise ThemeError(__('%r doesn\'t have "%s" setting') %
                                 (filename, exc.args[0])) from exc

        for key in self.OPTIONAL_CONFIG_KEYS:
            try:
                value = self.config.get('theme', key)
                setattr(self, key, value)
            except configparser.NoOptionError:
                pass
Exemple #10
0
 def get_confstr(self, section, name, default=NODEFAULT):
     """Return the value for a theme configuration setting, searching the
     base theme chain.
     """
     try:
         return self.themeconf.get(section, name)
     except (configparser.NoOptionError, configparser.NoSectionError):
         if self.base is not None:
             return self.base.get_confstr(section, name, default)
         if default is NODEFAULT:
             raise ThemeError('setting %s.%s occurs in none of the '
                              'searched theme configs' % (section, name))
         else:
             return default
Exemple #11
0
    def get_config(self, section: str, name: str, default: Any = NODEFAULT) -> Any:
        """Return the value for a theme configuration setting, searching the
        base theme chain.
        """
        try:
            return self.config.get(section, name)
        except (configparser.NoOptionError, configparser.NoSectionError) as exc:
            if self.base:
                return self.base.get_config(section, name, default)

            if default is NODEFAULT:
                raise ThemeError(__('setting %s.%s occurs in none of the '
                                    'searched theme configs') % (section, name)) from exc
            else:
                return default
Exemple #12
0
    def create(self, name: str) -> Theme:
        """Create an instance of theme."""
        if name not in self.themes:
            self.load_extra_theme(name)

        if name not in self.themes and name == 'sphinx_rtd_theme':
            # sphinx_rtd_theme (< 0.2.5)  # RemovedInSphinx60Warning
            logger.warning(__('sphinx_rtd_theme (< 0.3.0) found. '
                              'It will not be available since Sphinx-6.0'))
            self.load_sphinx_rtd_theme()

        if name not in self.themes:
            raise ThemeError(__('no theme named %r found (missing theme.conf?)') % name)

        return Theme(name, self.themes[name], factory=self)
Exemple #13
0
    def get_config(self, section, name, default=NODEFAULT):
        # type: (unicode, unicode, Any) -> Any
        """Return the value for a theme configuration setting, searching the
        base theme chain.
        """
        try:
            return self.config.get(section, name)  # type: ignore
        except (configparser.NoOptionError, configparser.NoSectionError):
            if self.base:
                return self.base.get_config(section, name, default)

            if default is NODEFAULT:
                raise ThemeError(__('setting %s.%s occurs in none of the '
                                    'searched theme configs') % (section, name))
            else:
                return default
Exemple #14
0
    def _extract_from_template(self) -> None:
        files = list(self._collect_templates())
        files.sort()
        logger.info(bold(__('building [%s]: ') % self.name), nonl=True)
        logger.info(__('targets for %d template files'), len(files))

        extract_translations = self.templates.environment.extract_translations

        for template in status_iterator(files, __('reading templates... '), "purple",
                                        len(files), self.app.verbosity):
            try:
                with open(template, encoding='utf-8') as f:
                    context = f.read()
                for line, meth, msg in extract_translations(context):
                    origin = MsgOrigin(template, line)
                    self.catalogs['sphinx'].add(msg, origin)
            except Exception as exc:
                raise ThemeError('%s: %r' % (template, exc)) from exc
Exemple #15
0
 def get_options(self, overrides):
     """Return a dictionary of theme options and their values."""
     chain = [self.themeconf]
     base = self.base
     while base is not None:
         chain.append(base.themeconf)
         base = base.base
     options = {}
     for conf in reversed(chain):
         try:
             options.update(conf.items('options'))
         except configparser.NoSectionError:
             pass
     for option, value in overrides.items():
         if option not in options:
             raise ThemeError('unsupported theme option %r given' % option)
         options[option] = value
     return options
Exemple #16
0
    def get_options(self, overrides={}):
        # type: (Dict[unicode, Any]) -> Dict[unicode, Any]
        """Return a dictionary of theme options and their values."""
        if self.base:
            options = self.base.get_options()
        else:
            options = {}

        try:
            options.update(self.config.items('options'))
        except configparser.NoSectionError:
            pass

        for option, value in iteritems(overrides):
            if option not in options:
                raise ThemeError('unsupported theme option %r given' % option)
            options[option] = value

        return options
Exemple #17
0
    def handle_page(self, pagename: str, addctx: Dict, templatename: str = 'page.html',
                    outfilename: str = None, event_arg: Any = None) -> None:
        ctx = self.globalcontext.copy()
        # current_page_name is backwards compatibility
        ctx['pagename'] = ctx['current_page_name'] = pagename
        ctx['encoding'] = self.config.html_output_encoding
        default_baseuri = self.get_target_uri(pagename)
        # in the singlehtml builder, default_baseuri still contains an #anchor
        # part, which relative_uri doesn't really like...
        default_baseuri = default_baseuri.rsplit('#', 1)[0]

        if self.config.html_baseurl:
            ctx['pageurl'] = posixpath.join(self.config.html_baseurl,
                                            pagename + self.out_suffix)
        else:
            ctx['pageurl'] = None

        def pathto(otheruri: str, resource: bool = False, baseuri: str = default_baseuri) -> str:  # NOQA
            if resource and '://' in otheruri:
                # allow non-local resources given by scheme
                return otheruri
            elif not resource:
                otheruri = self.get_target_uri(otheruri)
            uri = relative_uri(baseuri, otheruri) or '#'
            if uri == '#' and not self.allow_sharp_as_current_path:
                uri = baseuri
            return uri
        ctx['pathto'] = pathto

        def css_tag(css: Stylesheet) -> str:
            attrs = []
            for key in sorted(css.attributes):
                value = css.attributes[key]
                if value is not None:
                    attrs.append('%s="%s"' % (key, html.escape(value, True)))
            attrs.append('href="%s"' % pathto(css.filename, resource=True))
            return '<link %s />' % ' '.join(attrs)
        ctx['css_tag'] = css_tag

        def hasdoc(name: str) -> bool:
            if name in self.env.all_docs:
                return True
            elif name == 'search' and self.search:
                return True
            elif name == 'genindex' and self.get_builder_config('use_index', 'html'):
                return True
            return False
        ctx['hasdoc'] = hasdoc

        ctx['toctree'] = lambda **kw: self._get_local_toctree(pagename, **kw)
        self.add_sidebars(pagename, ctx)
        ctx.update(addctx)

        self.update_page_context(pagename, templatename, ctx, event_arg)
        newtmpl = self.app.emit_firstresult('html-page-context', pagename,
                                            templatename, ctx, event_arg)
        if newtmpl:
            templatename = newtmpl

        try:
            output = self.templates.render(templatename, ctx)
        except UnicodeError:
            logger.warning(__("a Unicode error occurred when rendering the page %s. "
                              "Please make sure all config values that contain "
                              "non-ASCII content are Unicode strings."), pagename)
            return
        except Exception as exc:
            raise ThemeError(__("An error happened in rendering the page %s.\nReason: %r") %
                             (pagename, exc))

        if not outfilename:
            outfilename = self.get_outfilename(pagename)
        # outfilename's path is in general different from self.outdir
        ensuredir(path.dirname(outfilename))
        try:
            with open(outfilename, 'w', encoding=ctx['encoding'],
                      errors='xmlcharrefreplace') as f:
                f.write(output)
        except OSError as err:
            logger.warning(__("error writing file %s: %s"), outfilename, err)
        if self.copysource and ctx.get('sourcename'):
            # copy the source file for the "show source" link
            source_name = path.join(self.outdir, '_sources',
                                    os_path(ctx['sourcename']))
            ensuredir(path.dirname(source_name))
            copyfile(self.env.doc2path(pagename), source_name)