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))
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)
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)
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]))
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)
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
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)
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)
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
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
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
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)
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
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
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
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
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)