add-black (#1044)
* Setup pre-commit and black update pre-commit add setup * format with black format and flake
This commit is contained in:
parent
d8862928c5
commit
0915aba828
|
@ -0,0 +1,25 @@
|
||||||
|
# See https://pre-commit.com for more information
|
||||||
|
# See https://pre-commit.com/hooks.html for more hooks
|
||||||
|
repos:
|
||||||
|
- repo: https://github.com/ambv/black
|
||||||
|
rev: 20.8b1
|
||||||
|
hooks:
|
||||||
|
- id: black
|
||||||
|
args:
|
||||||
|
- --safe
|
||||||
|
- --quiet
|
||||||
|
- repo: https://gitlab.com/pycqa/flake8
|
||||||
|
rev: 3.8.4
|
||||||
|
hooks:
|
||||||
|
- id: flake8
|
||||||
|
additional_dependencies:
|
||||||
|
- flake8-docstrings==1.5.0
|
||||||
|
- pydocstyle==5.1.1
|
||||||
|
- repo: https://github.com/pre-commit/pre-commit-hooks
|
||||||
|
rev: v3.4.0
|
||||||
|
hooks:
|
||||||
|
- id: no-commit-to-branch
|
||||||
|
args:
|
||||||
|
- --branch=current
|
||||||
|
- --branch=next
|
||||||
|
- --branch=beta
|
95
conf.py
95
conf.py
|
@ -22,14 +22,10 @@
|
||||||
# sys.path.insert(0, os.path.abspath('.'))
|
# sys.path.insert(0, os.path.abspath('.'))
|
||||||
import hashlib
|
import hashlib
|
||||||
import os
|
import os
|
||||||
import subprocess
|
|
||||||
from sphinx import addnodes
|
|
||||||
from sphinx.util.docfields import Field, GroupedField
|
|
||||||
import re
|
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
|
||||||
sys.path.append(os.path.abspath('.'))
|
sys.path.append(os.path.abspath("."))
|
||||||
|
|
||||||
# -- General configuration ------------------------------------------------
|
# -- General configuration ------------------------------------------------
|
||||||
|
|
||||||
|
@ -41,58 +37,58 @@ sys.path.append(os.path.abspath('.'))
|
||||||
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
|
||||||
# ones.
|
# ones.
|
||||||
extensions = [
|
extensions = [
|
||||||
'github',
|
"github",
|
||||||
'seo',
|
"seo",
|
||||||
'sitemap',
|
"sitemap",
|
||||||
]
|
]
|
||||||
|
|
||||||
# Add any paths that contain templates here, relative to this directory.
|
# Add any paths that contain templates here, relative to this directory.
|
||||||
templates_path = ['_templates']
|
templates_path = ["_templates"]
|
||||||
|
|
||||||
# The suffix(es) of source filenames.
|
# The suffix(es) of source filenames.
|
||||||
# You can specify multiple suffix as a list of string:
|
# You can specify multiple suffix as a list of string:
|
||||||
#
|
#
|
||||||
# source_suffix = ['.rst', '.md']
|
# source_suffix = ['.rst', '.md']
|
||||||
source_suffix = '.rst'
|
source_suffix = ".rst"
|
||||||
|
|
||||||
# The master toctree document.
|
# The master toctree document.
|
||||||
master_doc = 'index'
|
master_doc = "index"
|
||||||
|
|
||||||
# General information about the project.
|
# General information about the project.
|
||||||
project = 'ESPHome'
|
project = "ESPHome"
|
||||||
copyright = '2019, Otto Winter'
|
copyright = "2019, Otto Winter"
|
||||||
html_show_copyright = False
|
html_show_copyright = False
|
||||||
html_show_sphinx = False
|
html_show_sphinx = False
|
||||||
author = 'Otto Winter'
|
author = "Otto Winter"
|
||||||
|
|
||||||
# The version info for the project you're documenting, acts as replacement for
|
# The version info for the project you're documenting, acts as replacement for
|
||||||
# |version| and |release|, also used in various other places throughout the
|
# |version| and |release|, also used in various other places throughout the
|
||||||
# built documents.
|
# built documents.
|
||||||
#
|
#
|
||||||
# The short X.Y version.
|
# The short X.Y version.
|
||||||
version = '1.17'
|
version = "1.17"
|
||||||
# The full version, including alpha/beta/rc tags.
|
# The full version, including alpha/beta/rc tags.
|
||||||
release = '1.17.0-dev'
|
release = "1.17.0-dev"
|
||||||
|
|
||||||
# The language for content autogenerated by Sphinx. Refer to documentation
|
# The language for content autogenerated by Sphinx. Refer to documentation
|
||||||
# for a list of supported languages.
|
# for a list of supported languages.
|
||||||
#
|
#
|
||||||
# This is also used if you do content translation via gettext catalogs.
|
# This is also used if you do content translation via gettext catalogs.
|
||||||
# Usually you set "language" from the command line for these cases.
|
# Usually you set "language" from the command line for these cases.
|
||||||
language = 'en'
|
language = "en"
|
||||||
|
|
||||||
# List of patterns, relative to source directory, that match files and
|
# List of patterns, relative to source directory, that match files and
|
||||||
# directories to ignore when looking for source files.
|
# directories to ignore when looking for source files.
|
||||||
# This patterns also effect to html_static_path and html_extra_path
|
# This patterns also effect to html_static_path and html_extra_path
|
||||||
exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
|
exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"]
|
||||||
|
|
||||||
# The reST default role (used for this markup: `text`) to use for all documents.
|
# The reST default role (used for this markup: `text`) to use for all documents.
|
||||||
# default_role = 'cpp:any'
|
# default_role = 'cpp:any'
|
||||||
|
|
||||||
# The name of the Pygments (syntax highlighting) style to use.
|
# The name of the Pygments (syntax highlighting) style to use.
|
||||||
pygments_style = 'xcode'
|
pygments_style = "xcode"
|
||||||
|
|
||||||
highlight_language = 'yaml'
|
highlight_language = "yaml"
|
||||||
|
|
||||||
primary_domain = None
|
primary_domain = None
|
||||||
|
|
||||||
|
@ -105,40 +101,40 @@ todo_include_todos = False
|
||||||
# The theme to use for HTML and HTML Help pages. See the documentation for
|
# The theme to use for HTML and HTML Help pages. See the documentation for
|
||||||
# a list of builtin themes.
|
# a list of builtin themes.
|
||||||
#
|
#
|
||||||
html_theme = 'alabaster'
|
html_theme = "alabaster"
|
||||||
|
|
||||||
# Theme options are theme-specific and customize the look and feel of a theme
|
# Theme options are theme-specific and customize the look and feel of a theme
|
||||||
# further. For a list of options available for each theme, see the
|
# further. For a list of options available for each theme, see the
|
||||||
# documentation.
|
# documentation.
|
||||||
#
|
#
|
||||||
html_baseurl = os.getenv('BASE_URL', 'https://esphome.io')
|
html_baseurl = os.getenv("BASE_URL", "https://esphome.io")
|
||||||
with open('_static/custom.css', 'rb') as f:
|
with open("_static/custom.css", "rb") as f:
|
||||||
custom_css_hash = hashlib.md5(f.read()).hexdigest()[:8]
|
custom_css_hash = hashlib.md5(f.read()).hexdigest()[:8]
|
||||||
|
|
||||||
html_theme_options = {
|
html_theme_options = {
|
||||||
# 'logo': 'logo-full.png',
|
# 'logo': 'logo-full.png',
|
||||||
'logo_name': False,
|
"logo_name": False,
|
||||||
'show_related': False,
|
"show_related": False,
|
||||||
'sidebar_collapse': True,
|
"sidebar_collapse": True,
|
||||||
'fixed_sidebar': True,
|
"fixed_sidebar": True,
|
||||||
'show_powered_by': False,
|
"show_powered_by": False,
|
||||||
}
|
}
|
||||||
|
|
||||||
html_context = {
|
html_context = {
|
||||||
'custom_css_hash': custom_css_hash,
|
"custom_css_hash": custom_css_hash,
|
||||||
}
|
}
|
||||||
|
|
||||||
html_logo = 'images/logo-text.svg'
|
html_logo = "images/logo-text.svg"
|
||||||
html_copy_source = True
|
html_copy_source = True
|
||||||
html_show_sourcelink = False
|
html_show_sourcelink = False
|
||||||
html_last_updated_fmt = None
|
html_last_updated_fmt = None
|
||||||
html_use_smartypants = False
|
html_use_smartypants = False
|
||||||
html_title = 'ESPHome'
|
html_title = "ESPHome"
|
||||||
|
|
||||||
# Add any paths that contain custom static files (such as style sheets) here,
|
# Add any paths that contain custom static files (such as style sheets) here,
|
||||||
# relative to this directory. They are copied after the builtin static files,
|
# relative to this directory. They are copied after the builtin static files,
|
||||||
# so a file named "default.css" will overwrite the builtin "default.css".
|
# so a file named "default.css" will overwrite the builtin "default.css".
|
||||||
html_static_path = ['_static']
|
html_static_path = ["_static"]
|
||||||
|
|
||||||
# Custom sidebar templates, must be a dictionary that maps document names
|
# Custom sidebar templates, must be a dictionary that maps document names
|
||||||
# to template names.
|
# to template names.
|
||||||
|
@ -146,10 +142,10 @@ html_static_path = ['_static']
|
||||||
# This is required for the alabaster theme
|
# This is required for the alabaster theme
|
||||||
# refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars
|
# refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars
|
||||||
html_sidebars = {
|
html_sidebars = {
|
||||||
'**': [
|
"**": [
|
||||||
# 'about.html',
|
# 'about.html',
|
||||||
'searchbox.html',
|
"searchbox.html",
|
||||||
'localtoc.html',
|
"localtoc.html",
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,7 +153,7 @@ html_sidebars = {
|
||||||
# -- Options for HTMLHelp output ------------------------------------------
|
# -- Options for HTMLHelp output ------------------------------------------
|
||||||
|
|
||||||
# Output file base name for HTML help builder.
|
# Output file base name for HTML help builder.
|
||||||
htmlhelp_basename = 'esphomedoc'
|
htmlhelp_basename = "esphomedoc"
|
||||||
|
|
||||||
|
|
||||||
# -- Options for LaTeX output ---------------------------------------------
|
# -- Options for LaTeX output ---------------------------------------------
|
||||||
|
@ -166,15 +162,12 @@ latex_elements = {
|
||||||
# The paper size ('letterpaper' or 'a4paper').
|
# The paper size ('letterpaper' or 'a4paper').
|
||||||
#
|
#
|
||||||
# 'papersize': 'letterpaper',
|
# 'papersize': 'letterpaper',
|
||||||
|
|
||||||
# The font size ('10pt', '11pt' or '12pt').
|
# The font size ('10pt', '11pt' or '12pt').
|
||||||
#
|
#
|
||||||
# 'pointsize': '10pt',
|
# 'pointsize': '10pt',
|
||||||
|
|
||||||
# Additional stuff for the LaTeX preamble.
|
# Additional stuff for the LaTeX preamble.
|
||||||
#
|
#
|
||||||
# 'preamble': '',
|
# 'preamble': '',
|
||||||
|
|
||||||
# Latex figure (float) alignment
|
# Latex figure (float) alignment
|
||||||
#
|
#
|
||||||
# 'figure_align': 'htbp',
|
# 'figure_align': 'htbp',
|
||||||
|
@ -184,21 +177,17 @@ latex_elements = {
|
||||||
# (source start file, target name, title,
|
# (source start file, target name, title,
|
||||||
# author, documentclass [howto, manual, or own class]).
|
# author, documentclass [howto, manual, or own class]).
|
||||||
latex_documents = [
|
latex_documents = [
|
||||||
(master_doc, 'esphome.tex', 'ESPHome Documentation',
|
(master_doc, "esphome.tex", "ESPHome Documentation", "Otto Winter", "manual"),
|
||||||
'Otto Winter', 'manual'),
|
|
||||||
]
|
]
|
||||||
|
|
||||||
latex_engine = 'xelatex'
|
latex_engine = "xelatex"
|
||||||
|
|
||||||
|
|
||||||
# -- Options for manual page output ---------------------------------------
|
# -- Options for manual page output ---------------------------------------
|
||||||
|
|
||||||
# One entry per manual page. List of tuples
|
# One entry per manual page. List of tuples
|
||||||
# (source start file, name, description, authors, manual section).
|
# (source start file, name, description, authors, manual section).
|
||||||
man_pages = [
|
man_pages = [(master_doc, "esphome", "ESPHome Documentation", [author], 1)]
|
||||||
(master_doc, 'esphome', 'ESPHome Documentation',
|
|
||||||
[author], 1)
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
# -- Options for Texinfo output -------------------------------------------
|
# -- Options for Texinfo output -------------------------------------------
|
||||||
|
@ -207,8 +196,14 @@ man_pages = [
|
||||||
# (source start file, target name, title, author,
|
# (source start file, target name, title, author,
|
||||||
# dir menu entry, description, category)
|
# dir menu entry, description, category)
|
||||||
texinfo_documents = [
|
texinfo_documents = [
|
||||||
(master_doc, 'esphome', 'ESPHome Documentation',
|
(
|
||||||
author, 'esphome', 'One line description of project.',
|
master_doc,
|
||||||
'Miscellaneous'),
|
"esphome",
|
||||||
|
"ESPHome Documentation",
|
||||||
|
author,
|
||||||
|
"esphome",
|
||||||
|
"One line description of project.",
|
||||||
|
"Miscellaneous",
|
||||||
|
),
|
||||||
]
|
]
|
||||||
linkcheck_ignore = [r'https://github.com/.*', r'https://discord.gg/.*']
|
linkcheck_ignore = [r"https://github.com/.*", r"https://discord.gg/.*"]
|
||||||
|
|
165
github.py
165
github.py
|
@ -9,39 +9,35 @@ from docutils.parsers.rst import directives
|
||||||
from docutils.parsers.rst.directives.tables import Table
|
from docutils.parsers.rst.directives.tables import Table
|
||||||
|
|
||||||
|
|
||||||
def libpr_role(name, rawtext, text, lineno, inliner, options=None,
|
def libpr_role(name, rawtext, text, lineno, inliner, options=None, content=None):
|
||||||
content=None):
|
ref = "https://github.com/esphome/esphome-core/pull/{}".format(text)
|
||||||
ref = 'https://github.com/esphome/esphome-core/pull/{}'.format(text)
|
return [make_link_node(rawtext, "core#{}".format(text), ref, options)], []
|
||||||
return [make_link_node(rawtext, 'core#{}'.format(text), ref, options)], []
|
|
||||||
|
|
||||||
|
|
||||||
def yamlpr_role(name, rawtext, text, lineno, inliner, options=None,
|
def yamlpr_role(name, rawtext, text, lineno, inliner, options=None, content=None):
|
||||||
content=None):
|
ref = "https://github.com/esphome/esphome/pull/{}".format(text)
|
||||||
ref = 'https://github.com/esphome/esphome/pull/{}'.format(text)
|
return [make_link_node(rawtext, "esphome#{}".format(text), ref, options)], []
|
||||||
return [make_link_node(rawtext, 'esphome#{}'.format(text), ref, options)], []
|
|
||||||
|
|
||||||
|
|
||||||
def docspr_role(name, rawtext, text, lineno, inliner, options=None,
|
def docspr_role(name, rawtext, text, lineno, inliner, options=None, content=None):
|
||||||
content=None):
|
ref = "https://github.com/esphome/esphome-docs/pull/{}".format(text)
|
||||||
ref = 'https://github.com/esphome/esphome-docs/pull/{}'.format(text)
|
return [make_link_node(rawtext, "docs#{}".format(text), ref, options)], []
|
||||||
return [make_link_node(rawtext, 'docs#{}'.format(text), ref, options)], []
|
|
||||||
|
|
||||||
|
|
||||||
def ghuser_role(name, rawtext, text, lineno, inliner, options=None,
|
def ghuser_role(name, rawtext, text, lineno, inliner, options=None, content=None):
|
||||||
content=None):
|
ref = "https://github.com/{}".format(text)
|
||||||
ref = 'https://github.com/{}'.format(text)
|
return [make_link_node(rawtext, "@{}".format(text), ref, options)], []
|
||||||
return [make_link_node(rawtext, '@{}'.format(text), ref, options)], []
|
|
||||||
|
|
||||||
|
|
||||||
value_re = re.compile(r'^(.*)\s*<(.*)>$')
|
value_re = re.compile(r"^(.*)\s*<(.*)>$")
|
||||||
DOXYGEN_LOOKUP = {}
|
DOXYGEN_LOOKUP = {}
|
||||||
for s in string.ascii_lowercase + string.digits:
|
for s in string.ascii_lowercase + string.digits:
|
||||||
DOXYGEN_LOOKUP[s] = s
|
DOXYGEN_LOOKUP[s] = s
|
||||||
for s in string.ascii_uppercase:
|
for s in string.ascii_uppercase:
|
||||||
DOXYGEN_LOOKUP[s] = '_{}'.format(s.lower())
|
DOXYGEN_LOOKUP[s] = "_{}".format(s.lower())
|
||||||
DOXYGEN_LOOKUP[':'] = '_1'
|
DOXYGEN_LOOKUP[":"] = "_1"
|
||||||
DOXYGEN_LOOKUP['_'] = '__'
|
DOXYGEN_LOOKUP["_"] = "__"
|
||||||
DOXYGEN_LOOKUP['.'] = '_8'
|
DOXYGEN_LOOKUP["."] = "_8"
|
||||||
|
|
||||||
|
|
||||||
def split_text_value(value):
|
def split_text_value(value):
|
||||||
|
@ -52,60 +48,57 @@ def split_text_value(value):
|
||||||
|
|
||||||
|
|
||||||
def encode_doxygen(value):
|
def encode_doxygen(value):
|
||||||
value = value.split('/')[-1]
|
value = value.split("/")[-1]
|
||||||
try:
|
try:
|
||||||
return ''.join(DOXYGEN_LOOKUP[s] for s in value)
|
return "".join(DOXYGEN_LOOKUP[s] for s in value)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise ValueError("Unknown character in doxygen string! '{}'".format(value))
|
raise ValueError("Unknown character in doxygen string! '{}'".format(value))
|
||||||
|
|
||||||
|
|
||||||
def apiref_role(name, rawtext, text, lineno, inliner, options=None,
|
def apiref_role(name, rawtext, text, lineno, inliner, options=None, content=None):
|
||||||
content=None):
|
|
||||||
text, value = split_text_value(text)
|
text, value = split_text_value(text)
|
||||||
if text is None:
|
if text is None:
|
||||||
text = 'API Reference'
|
text = "API Reference"
|
||||||
ref = '/api/{}.html'.format(encode_doxygen(value))
|
ref = "/api/{}.html".format(encode_doxygen(value))
|
||||||
return [make_link_node(rawtext, text, ref, options)], []
|
return [make_link_node(rawtext, text, ref, options)], []
|
||||||
|
|
||||||
|
|
||||||
def apiclass_role(name, rawtext, text, lineno, inliner, options=None,
|
def apiclass_role(name, rawtext, text, lineno, inliner, options=None, content=None):
|
||||||
content=None):
|
|
||||||
text, value = split_text_value(text)
|
text, value = split_text_value(text)
|
||||||
if text is None:
|
if text is None:
|
||||||
text = value
|
text = value
|
||||||
ref = '/api/classesphome_1_1{}.html'.format(encode_doxygen(value))
|
ref = "/api/classesphome_1_1{}.html".format(encode_doxygen(value))
|
||||||
return [make_link_node(rawtext, text, ref, options)], []
|
return [make_link_node(rawtext, text, ref, options)], []
|
||||||
|
|
||||||
|
|
||||||
def apistruct_role(name, rawtext, text, lineno, inliner, options=None,
|
def apistruct_role(name, rawtext, text, lineno, inliner, options=None, content=None):
|
||||||
content=None):
|
|
||||||
text, value = split_text_value(text)
|
text, value = split_text_value(text)
|
||||||
if text is None:
|
if text is None:
|
||||||
text = value
|
text = value
|
||||||
ref = '/api/structesphome_1_1{}.html'.format(encode_doxygen(value))
|
ref = "/api/structesphome_1_1{}.html".format(encode_doxygen(value))
|
||||||
return [make_link_node(rawtext, text, ref, options)], []
|
return [make_link_node(rawtext, text, ref, options)], []
|
||||||
|
|
||||||
|
|
||||||
def ghedit_role(name, rawtext, text, lineno, inliner, options=None,
|
def ghedit_role(name, rawtext, text, lineno, inliner, options=None, content=None):
|
||||||
content=None):
|
path = os.path.relpath(
|
||||||
path = os.path.relpath(inliner.document.current_source,
|
inliner.document.current_source, inliner.document.settings.env.app.srcdir
|
||||||
inliner.document.settings.env.app.srcdir)
|
)
|
||||||
ref = 'https://github.com/esphome/esphome-docs/blob/current/{}'.format(path)
|
ref = "https://github.com/esphome/esphome-docs/blob/current/{}".format(path)
|
||||||
return [make_link_node(rawtext, 'Edit this page on GitHub', ref, options)], []
|
return [make_link_node(rawtext, "Edit this page on GitHub", ref, options)], []
|
||||||
|
|
||||||
|
|
||||||
def make_link_node(rawtext, text, ref, options=None):
|
def make_link_node(rawtext, text, ref, options=None):
|
||||||
options = options or {}
|
options = options or {}
|
||||||
node = nodes.reference(rawtext,
|
node = nodes.reference(rawtext, utils.unescape(text), refuri=ref, **options)
|
||||||
utils.unescape(text),
|
|
||||||
refuri=ref,
|
|
||||||
**options)
|
|
||||||
return node
|
return node
|
||||||
|
|
||||||
|
|
||||||
# https://stackoverflow.com/a/3415150/8924614
|
# https://stackoverflow.com/a/3415150/8924614
|
||||||
def grouper(n, iterable, fillvalue=None):
|
def grouper(n, iterable, fillvalue=None):
|
||||||
"""grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx"""
|
"""Pythonic way to iterate over sequence, 4 items at a time.
|
||||||
|
|
||||||
|
grouper(3, 'ABCDEFG', 'x') --> ABC DEF Gxx
|
||||||
|
"""
|
||||||
args = [iter(iterable)] * n
|
args = [iter(iterable)] * n
|
||||||
return zip_longest(fillvalue=fillvalue, *args)
|
return zip_longest(fillvalue=fillvalue, *args)
|
||||||
|
|
||||||
|
@ -114,12 +107,11 @@ def grouper(n, iterable, fillvalue=None):
|
||||||
class ImageTableDirective(Table):
|
class ImageTableDirective(Table):
|
||||||
|
|
||||||
option_spec = {
|
option_spec = {
|
||||||
'columns': directives.positive_int,
|
"columns": directives.positive_int,
|
||||||
}
|
}
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
env = self.state.document.settings.env
|
cols = self.options.get("columns", 3)
|
||||||
cols = self.options.get('columns', 3)
|
|
||||||
|
|
||||||
items = []
|
items = []
|
||||||
|
|
||||||
|
@ -129,31 +121,30 @@ class ImageTableDirective(Table):
|
||||||
continue
|
continue
|
||||||
name, page, image = row
|
name, page, image = row
|
||||||
link = page.strip()
|
link = page.strip()
|
||||||
if link.startswith('http'):
|
if link.startswith("http"):
|
||||||
pass
|
pass
|
||||||
else:
|
else:
|
||||||
if not link.startswith('/'):
|
if not link.startswith("/"):
|
||||||
link = '/{}'.format(link)
|
link = "/{}".format(link)
|
||||||
if '.html' not in link:
|
if ".html" not in link:
|
||||||
link += '.html'
|
link += ".html"
|
||||||
items.append({
|
items.append(
|
||||||
'name': name.strip(),
|
{
|
||||||
'link': link,
|
"name": name.strip(),
|
||||||
'image': '/images/{}'.format(image.strip()),
|
"link": link,
|
||||||
})
|
"image": "/images/{}".format(image.strip()),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
col_widths = self.get_column_widths(cols)
|
col_widths = self.get_column_widths(cols)
|
||||||
title, messages = self.make_title()
|
title, messages = self.make_title()
|
||||||
table = nodes.table()
|
table = nodes.table()
|
||||||
table['classes'].append('table-center')
|
table["classes"].append("table-center")
|
||||||
|
|
||||||
# Set up column specifications based on widths
|
# Set up column specifications based on widths
|
||||||
tgroup = nodes.tgroup(cols=cols)
|
tgroup = nodes.tgroup(cols=cols)
|
||||||
table += tgroup
|
table += tgroup
|
||||||
tgroup.extend(
|
tgroup.extend(nodes.colspec(colwidth=col_width) for col_width in col_widths)
|
||||||
nodes.colspec(colwidth=col_width)
|
|
||||||
for col_width in col_widths
|
|
||||||
)
|
|
||||||
|
|
||||||
tbody = nodes.tbody()
|
tbody = nodes.tbody()
|
||||||
tgroup += tbody
|
tgroup += tbody
|
||||||
|
@ -166,12 +157,12 @@ class ImageTableDirective(Table):
|
||||||
entry += nodes.paragraph()
|
entry += nodes.paragraph()
|
||||||
trow += entry
|
trow += entry
|
||||||
continue
|
continue
|
||||||
name = cell['name']
|
name = cell["name"]
|
||||||
link = cell['link']
|
link = cell["link"]
|
||||||
image = cell['image']
|
image = cell["image"]
|
||||||
reference_node = nodes.reference(refuri=link)
|
reference_node = nodes.reference(refuri=link)
|
||||||
img = nodes.image(uri=directives.uri(image), alt=name)
|
img = nodes.image(uri=directives.uri(image), alt=name)
|
||||||
img['classes'].append('component-image')
|
img["classes"].append("component-image")
|
||||||
reference_node += img
|
reference_node += img
|
||||||
para = nodes.paragraph()
|
para = nodes.paragraph()
|
||||||
para += reference_node
|
para += reference_node
|
||||||
|
@ -186,8 +177,8 @@ class ImageTableDirective(Table):
|
||||||
entry += nodes.paragraph()
|
entry += nodes.paragraph()
|
||||||
trow += entry
|
trow += entry
|
||||||
continue
|
continue
|
||||||
name = cell['name']
|
name = cell["name"]
|
||||||
link = cell['link']
|
link = cell["link"]
|
||||||
ref = nodes.reference(name, name, refuri=link)
|
ref = nodes.reference(name, name, refuri=link)
|
||||||
para = nodes.paragraph()
|
para = nodes.paragraph()
|
||||||
para += ref
|
para += ref
|
||||||
|
@ -207,8 +198,6 @@ class PinTableDirective(Table):
|
||||||
option_spec = {}
|
option_spec = {}
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
env = self.state.document.settings.env
|
|
||||||
|
|
||||||
items = []
|
items = []
|
||||||
|
|
||||||
data = list(csv.reader(self.content))
|
data = list(csv.reader(self.content))
|
||||||
|
@ -227,18 +216,14 @@ class PinTableDirective(Table):
|
||||||
# Set up column specifications based on widths
|
# Set up column specifications based on widths
|
||||||
tgroup = nodes.tgroup(cols=2)
|
tgroup = nodes.tgroup(cols=2)
|
||||||
table += tgroup
|
table += tgroup
|
||||||
tgroup.extend(
|
tgroup.extend(nodes.colspec(colwidth=col_width) for col_width in col_widths)
|
||||||
nodes.colspec(colwidth=col_width)
|
|
||||||
for col_width in col_widths
|
|
||||||
)
|
|
||||||
|
|
||||||
thead = nodes.thead()
|
thead = nodes.thead()
|
||||||
tgroup += thead
|
tgroup += thead
|
||||||
trow = nodes.row()
|
trow = nodes.row()
|
||||||
thead += trow
|
thead += trow
|
||||||
trow.extend(
|
trow.extend(
|
||||||
nodes.entry(h, nodes.paragraph(text=h))
|
nodes.entry(h, nodes.paragraph(text=h)) for h in ("Pin", "Function")
|
||||||
for h in ('Pin', 'Function')
|
|
||||||
)
|
)
|
||||||
|
|
||||||
tbody = nodes.tbody()
|
tbody = nodes.tbody()
|
||||||
|
@ -269,18 +254,16 @@ class PinTableDirective(Table):
|
||||||
|
|
||||||
|
|
||||||
def setup(app):
|
def setup(app):
|
||||||
app.add_role('libpr', libpr_role)
|
app.add_role("libpr", libpr_role)
|
||||||
app.add_role('corepr', libpr_role)
|
app.add_role("corepr", libpr_role)
|
||||||
app.add_role('yamlpr', yamlpr_role)
|
app.add_role("yamlpr", yamlpr_role)
|
||||||
app.add_role('esphomepr', yamlpr_role)
|
app.add_role("esphomepr", yamlpr_role)
|
||||||
app.add_role('docspr', docspr_role)
|
app.add_role("docspr", docspr_role)
|
||||||
app.add_role('ghuser', ghuser_role)
|
app.add_role("ghuser", ghuser_role)
|
||||||
app.add_role('apiref', apiref_role)
|
app.add_role("apiref", apiref_role)
|
||||||
app.add_role('apiclass', apiclass_role)
|
app.add_role("apiclass", apiclass_role)
|
||||||
app.add_role('apistruct', apistruct_role)
|
app.add_role("apistruct", apistruct_role)
|
||||||
app.add_role('ghedit', ghedit_role)
|
app.add_role("ghedit", ghedit_role)
|
||||||
app.add_directive('imgtable', ImageTableDirective)
|
app.add_directive("imgtable", ImageTableDirective)
|
||||||
app.add_directive('pintable', PinTableDirective)
|
app.add_directive("pintable", PinTableDirective)
|
||||||
return {"version": "1.0.0",
|
return {"version": "1.0.0", "parallel_read_safe": True, "parallel_write_safe": True}
|
||||||
"parallel_read_safe": True,
|
|
||||||
"parallel_write_safe": True}
|
|
||||||
|
|
|
@ -2,7 +2,6 @@
|
||||||
|
|
||||||
import argparse
|
import argparse
|
||||||
import re
|
import re
|
||||||
import subprocess
|
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
@ -16,30 +15,27 @@ class Version:
|
||||||
dev: bool = False
|
dev: bool = False
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return f'{self.major}.{self.minor}.{self.full_patch}'
|
return f"{self.major}.{self.minor}.{self.full_patch}"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def full_patch(self):
|
def full_patch(self):
|
||||||
res = f'{self.patch}'
|
res = f"{self.patch}"
|
||||||
if self.beta > 0:
|
if self.beta > 0:
|
||||||
res += f'b{self.beta}'
|
res += f"b{self.beta}"
|
||||||
if self.dev:
|
if self.dev:
|
||||||
res += '-dev'
|
res += "-dev"
|
||||||
return res
|
return res
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def parse(cls, value):
|
def parse(cls, value):
|
||||||
match = re.match(r'(\d+).(\d+).(\d+)(b\d+)?(-dev)?', value)
|
match = re.match(r"(\d+).(\d+).(\d+)(b\d+)?(-dev)?", value)
|
||||||
assert match is not None
|
assert match is not None
|
||||||
major = int(match[1])
|
major = int(match[1])
|
||||||
minor = int(match[2])
|
minor = int(match[2])
|
||||||
patch = int(match[3])
|
patch = int(match[3])
|
||||||
beta = int(match[4][1:]) if match[4] else 0
|
beta = int(match[4][1:]) if match[4] else 0
|
||||||
dev = bool(match[5])
|
dev = bool(match[5])
|
||||||
return Version(
|
return Version(major=major, minor=minor, patch=patch, beta=beta, dev=dev)
|
||||||
major=major, minor=minor, patch=patch,
|
|
||||||
beta=beta, dev=dev
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
def sub(path, pattern, repl, expected_count=1):
|
def sub(path, pattern, repl, expected_count=1):
|
||||||
|
@ -55,35 +51,25 @@ def sub(path, pattern, repl, expected_count=1):
|
||||||
def write_version(version: Version):
|
def write_version(version: Version):
|
||||||
# ESPHOME_REF = v1.14.4
|
# ESPHOME_REF = v1.14.4
|
||||||
sub(
|
sub(
|
||||||
'Makefile',
|
"Makefile",
|
||||||
r'ESPHOME_REF = .*',
|
r"ESPHOME_REF = .*",
|
||||||
f'ESPHOME_REF = v{version}' if not version.dev else f'ESPHOME_REF = dev'
|
f"ESPHOME_REF = v{version}" if not version.dev else "ESPHOME_REF = dev",
|
||||||
)
|
)
|
||||||
# PROJECT_NUMBER = 1.14.4
|
# PROJECT_NUMBER = 1.14.4
|
||||||
sub(
|
sub(
|
||||||
'Doxygen',
|
"Doxygen", r"PROJECT_NUMBER = .*", f"PROJECT_NUMBER = {version}"
|
||||||
r'PROJECT_NUMBER = .*',
|
|
||||||
f'PROJECT_NUMBER = {version}'
|
|
||||||
)
|
)
|
||||||
# version = '1.14'
|
# version = '1.14'
|
||||||
sub(
|
sub("conf.py", r"version = '.*'", f"version = '{version.major}.{version.minor}'")
|
||||||
'conf.py',
|
|
||||||
r"version = '.*'",
|
|
||||||
f"version = '{version.major}.{version.minor}'"
|
|
||||||
)
|
|
||||||
# release = '1.14.4'
|
# release = '1.14.4'
|
||||||
sub(
|
sub("conf.py", r"release = '.*'", f"release = '{version}'")
|
||||||
'conf.py',
|
with open("_static/version", "wt") as fh:
|
||||||
r"release = '.*'",
|
|
||||||
f"release = '{version}'"
|
|
||||||
)
|
|
||||||
with open('_static/version', 'wt') as fh:
|
|
||||||
fh.write(str(version))
|
fh.write(str(version))
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
parser = argparse.ArgumentParser()
|
parser = argparse.ArgumentParser()
|
||||||
parser.add_argument('new_version', type=str)
|
parser.add_argument("new_version", type=str)
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
version = Version.parse(args.new_version)
|
version = Version.parse(args.new_version)
|
||||||
|
|
80
seo.py
80
seo.py
|
@ -6,11 +6,18 @@ from docutils.writers._html_base import HTMLTranslator
|
||||||
|
|
||||||
|
|
||||||
class SEONode(nodes.General, nodes.Element):
|
class SEONode(nodes.General, nodes.Element):
|
||||||
def __init__(self, title=None, description=None, image=None,
|
def __init__(
|
||||||
author=None, author_twitter=None, keywords=None):
|
self,
|
||||||
|
title=None,
|
||||||
|
description=None,
|
||||||
|
image=None,
|
||||||
|
author=None,
|
||||||
|
author_twitter=None,
|
||||||
|
keywords=None,
|
||||||
|
):
|
||||||
super(SEONode, self).__init__()
|
super(SEONode, self).__init__()
|
||||||
self.title = title
|
self.title = title
|
||||||
self.description = description.replace('\n', ' ')
|
self.description = description.replace("\n", " ")
|
||||||
self.image = image
|
self.image = image
|
||||||
self.author = author
|
self.author = author
|
||||||
self.author_twitter = author_twitter
|
self.author_twitter = author_twitter
|
||||||
|
@ -25,26 +32,34 @@ class RedirectNode(nodes.General, nodes.Element):
|
||||||
|
|
||||||
def seo_visit(self: HTMLTranslator, node: SEONode):
|
def seo_visit(self: HTMLTranslator, node: SEONode):
|
||||||
def encode_text(text):
|
def encode_text(text):
|
||||||
special_characters = {ord('&'): '&',
|
special_characters = {
|
||||||
ord('<'): '<',
|
ord("&"): "&",
|
||||||
ord('"'): '"',
|
ord("<"): "<",
|
||||||
ord('>'): '>'}
|
ord('"'): """,
|
||||||
|
ord(">"): ">",
|
||||||
|
}
|
||||||
return text.translate(special_characters)
|
return text.translate(special_characters)
|
||||||
|
|
||||||
def create_content_meta(name, content):
|
def create_content_meta(name, content):
|
||||||
if content is None:
|
if content is None:
|
||||||
return
|
return
|
||||||
self.meta.append('<meta name="{}" content="{}">\n'.format(name, encode_text(content)))
|
self.meta.append(
|
||||||
|
'<meta name="{}" content="{}">\n'.format(name, encode_text(content))
|
||||||
|
)
|
||||||
|
|
||||||
def create_itemprop_meta(name, content):
|
def create_itemprop_meta(name, content):
|
||||||
if content is None:
|
if content is None:
|
||||||
return
|
return
|
||||||
self.meta.append('<meta itemprop="{}" content="{}">\n'.format(name, encode_text(content)))
|
self.meta.append(
|
||||||
|
'<meta itemprop="{}" content="{}">\n'.format(name, encode_text(content))
|
||||||
|
)
|
||||||
|
|
||||||
def create_property_meta(name, content):
|
def create_property_meta(name, content):
|
||||||
if content is None:
|
if content is None:
|
||||||
return
|
return
|
||||||
self.meta.append('<meta property="{}" content="{}">\n'.format(name, encode_text(content)))
|
self.meta.append(
|
||||||
|
'<meta property="{}" content="{}">\n'.format(name, encode_text(content))
|
||||||
|
)
|
||||||
|
|
||||||
# Base
|
# Base
|
||||||
create_content_meta("description", node.description)
|
create_content_meta("description", node.description)
|
||||||
|
@ -76,8 +91,11 @@ def seo_visit(self: HTMLTranslator, node: SEONode):
|
||||||
def redirect_visit(self: HTMLTranslator, node: RedirectNode):
|
def redirect_visit(self: HTMLTranslator, node: RedirectNode):
|
||||||
self.meta.append('<meta http-equiv="refresh" content="0; url={}">'.format(node.url))
|
self.meta.append('<meta http-equiv="refresh" content="0; url={}">'.format(node.url))
|
||||||
|
|
||||||
self.body.append(self.starttag(node, 'p',
|
self.body.append(
|
||||||
'Redirecting to <a href="{0}">{0}</a>'.format(node.url)))
|
self.starttag(
|
||||||
|
node, "p", 'Redirecting to <a href="{0}">{0}</a>'.format(node.url)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
def seo_depart(self, _):
|
def seo_depart(self, _):
|
||||||
|
@ -85,36 +103,36 @@ def seo_depart(self, _):
|
||||||
|
|
||||||
|
|
||||||
def redirect_depart(self, _):
|
def redirect_depart(self, _):
|
||||||
self.body.append('</p>')
|
self.body.append("</p>")
|
||||||
|
|
||||||
|
|
||||||
class SEODirective(Directive):
|
class SEODirective(Directive):
|
||||||
option_spec = {
|
option_spec = {
|
||||||
'title': directives.unchanged,
|
"title": directives.unchanged,
|
||||||
'description': directives.unchanged,
|
"description": directives.unchanged,
|
||||||
'image': directives.path,
|
"image": directives.path,
|
||||||
'author': directives.unchanged,
|
"author": directives.unchanged,
|
||||||
'author_twitter': directives.unchanged,
|
"author_twitter": directives.unchanged,
|
||||||
'keywords': directives.unchanged,
|
"keywords": directives.unchanged,
|
||||||
}
|
}
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
env = self.state.document.settings.env
|
env = self.state.document.settings.env
|
||||||
title_match = re.match(r'.+<title>(.+)</title>.+', str(self.state.document))
|
title_match = re.match(r".+<title>(.+)</title>.+", str(self.state.document))
|
||||||
if title_match is not None and 'title' not in self.options:
|
if title_match is not None and "title" not in self.options:
|
||||||
self.options['title'] = title_match.group(1)
|
self.options["title"] = title_match.group(1)
|
||||||
|
|
||||||
image = self.options.get('image')
|
image = self.options.get("image")
|
||||||
if image is not None:
|
if image is not None:
|
||||||
if not image.startswith('/'):
|
if not image.startswith("/"):
|
||||||
image = '/_images/' + image
|
image = "/_images/" + image
|
||||||
self.options['image'] = env.config.html_baseurl + image
|
self.options["image"] = env.config.html_baseurl + image
|
||||||
return [SEONode(**self.options)]
|
return [SEONode(**self.options)]
|
||||||
|
|
||||||
|
|
||||||
class RedirectDirective(Directive):
|
class RedirectDirective(Directive):
|
||||||
option_spec = {
|
option_spec = {
|
||||||
'url': directives.unchanged,
|
"url": directives.unchanged,
|
||||||
}
|
}
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
|
@ -122,10 +140,8 @@ class RedirectDirective(Directive):
|
||||||
|
|
||||||
|
|
||||||
def setup(app):
|
def setup(app):
|
||||||
app.add_directive('seo', SEODirective)
|
app.add_directive("seo", SEODirective)
|
||||||
app.add_node(SEONode, html=(seo_visit, seo_depart))
|
app.add_node(SEONode, html=(seo_visit, seo_depart))
|
||||||
app.add_directive('redirect', RedirectDirective)
|
app.add_directive("redirect", RedirectDirective)
|
||||||
app.add_node(RedirectNode, html=(redirect_visit, redirect_depart))
|
app.add_node(RedirectNode, html=(redirect_visit, redirect_depart))
|
||||||
return {"version": "1.0.0",
|
return {"version": "1.0.0", "parallel_read_safe": True, "parallel_write_safe": True}
|
||||||
"parallel_read_safe": True,
|
|
||||||
"parallel_write_safe": True}
|
|
||||||
|
|
|
@ -0,0 +1,41 @@
|
||||||
|
[flake8]
|
||||||
|
max-line-length = 120
|
||||||
|
# Following 4 for black compatibility
|
||||||
|
# E501: line too long
|
||||||
|
# W503: Line break occurred before a binary operator
|
||||||
|
# E203: Whitespace before ':'
|
||||||
|
# D202 No blank lines allowed after function docstring
|
||||||
|
|
||||||
|
# TODO fix flake8
|
||||||
|
# D100 Missing docstring in public module
|
||||||
|
# D101 Missing docstring in public class
|
||||||
|
# D102 Missing docstring in public method
|
||||||
|
# D103 Missing docstring in public function
|
||||||
|
# D104 Missing docstring in public package
|
||||||
|
# D105 Missing docstring in magic method
|
||||||
|
# D107 Missing docstring in __init__
|
||||||
|
# D200 One-line docstring should fit on one line with quotes
|
||||||
|
# D205 1 blank line required between summary line and description
|
||||||
|
# D209 Multi-line docstring closing quotes should be on a separate line
|
||||||
|
# D400 First line should end with a period
|
||||||
|
# D401 First line should be in imperative mood
|
||||||
|
|
||||||
|
ignore =
|
||||||
|
E501,
|
||||||
|
W503,
|
||||||
|
E203,
|
||||||
|
D202,
|
||||||
|
|
||||||
|
D100,
|
||||||
|
D101,
|
||||||
|
D102,
|
||||||
|
D103,
|
||||||
|
D104,
|
||||||
|
D105,
|
||||||
|
D107,
|
||||||
|
D200,
|
||||||
|
D205,
|
||||||
|
D209,
|
||||||
|
D400,
|
||||||
|
D401,
|
||||||
|
|
45
sitemap.py
45
sitemap.py
|
@ -4,15 +4,17 @@ import xml.etree.ElementTree as ET
|
||||||
|
|
||||||
def setup(app):
|
def setup(app):
|
||||||
"""Setup connects events to the sitemap builder"""
|
"""Setup connects events to the sitemap builder"""
|
||||||
app.connect('html-page-context', add_html_link)
|
app.connect("html-page-context", add_html_link)
|
||||||
app.connect('build-finished', create_sitemap)
|
app.connect("build-finished", create_sitemap)
|
||||||
app.sitemap_links = []
|
app.sitemap_links = []
|
||||||
|
|
||||||
is_production = os.getenv('PRODUCTION') == 'YES'
|
is_production = os.getenv("PRODUCTION") == "YES"
|
||||||
|
|
||||||
return {"version": "1.0.0",
|
return {
|
||||||
|
"version": "1.0.0",
|
||||||
"parallel_read_safe": True,
|
"parallel_read_safe": True,
|
||||||
"parallel_write_safe": not is_production}
|
"parallel_write_safe": not is_production,
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
def add_html_link(app, pagename, templatename, context, doctree):
|
def add_html_link(app, pagename, templatename, context, doctree):
|
||||||
|
@ -29,26 +31,27 @@ def create_sitemap(app, exception):
|
||||||
for link in app.sitemap_links:
|
for link in app.sitemap_links:
|
||||||
url = ET.SubElement(root, "url")
|
url = ET.SubElement(root, "url")
|
||||||
priority = 0.5
|
priority = 0.5
|
||||||
if link == 'index.html':
|
if link == "index.html":
|
||||||
priority = 1.0
|
priority = 1.0
|
||||||
link = ''
|
link = ""
|
||||||
elif link.endswith('index.html'):
|
elif link.endswith("index.html"):
|
||||||
priority += 0.25
|
priority += 0.25
|
||||||
link = link[:-len('index.html')]
|
link = link[: -len("index.html")]
|
||||||
if link.endswith('.html'):
|
if link.endswith(".html"):
|
||||||
link = link[:-len('.html')]
|
link = link[: -len(".html")]
|
||||||
ET.SubElement(url, "loc").text = app.builder.config.html_baseurl + '/' + link
|
ET.SubElement(url, "loc").text = app.builder.config.html_baseurl + "/" + link
|
||||||
ET.SubElement(url, "priority").text = str(priority)
|
ET.SubElement(url, "priority").text = str(priority)
|
||||||
|
|
||||||
filename = os.path.join(app.outdir, "sitemap.xml")
|
filename = os.path.join(app.outdir, "sitemap.xml")
|
||||||
ET.ElementTree(root).write(filename,
|
ET.ElementTree(root).write(
|
||||||
xml_declaration=True,
|
filename, xml_declaration=True, encoding="utf-8", method="xml"
|
||||||
encoding='utf-8',
|
)
|
||||||
method="xml")
|
|
||||||
|
|
||||||
with open(os.path.join(app.builder.outdir, 'robots.txt'), 'wt') as f:
|
with open(os.path.join(app.builder.outdir, "robots.txt"), "wt") as f:
|
||||||
if os.getenv('PRODUCTION') != 'YES':
|
if os.getenv("PRODUCTION") != "YES":
|
||||||
f.write('User-agent: *\nDisallow: /\n')
|
f.write("User-agent: *\nDisallow: /\n")
|
||||||
else:
|
else:
|
||||||
f.write('User-agent: *\nDisallow: \n\n'
|
f.write(
|
||||||
'Sitemap: https://esphome.io/sitemap.xml\n')
|
"User-agent: *\nDisallow: \n\n"
|
||||||
|
"Sitemap: https://esphome.io/sitemap.xml\n"
|
||||||
|
)
|
||||||
|
|
31
svg2png.py
31
svg2png.py
|
@ -7,13 +7,16 @@ import queue
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
|
|
||||||
to_p = Path('svg2png')
|
to_p = Path("svg2png")
|
||||||
to_p.mkdir(exist_ok=True)
|
to_p.mkdir(exist_ok=True)
|
||||||
for f in to_p.glob('*.png'):
|
for f in to_p.glob("*.png"):
|
||||||
f.unlink()
|
f.unlink()
|
||||||
|
|
||||||
images = [f for f in Path('_build/html/_images/').glob('*.svg')
|
images = [
|
||||||
if not re.match(r'^seg[0-9A-F]{2}$', f.stem)]
|
f
|
||||||
|
for f in Path("_build/html/_images/").glob("*.svg")
|
||||||
|
if not re.match(r"^seg[0-9A-F]{2}$", f.stem)
|
||||||
|
]
|
||||||
q = queue.Queue()
|
q = queue.Queue()
|
||||||
|
|
||||||
|
|
||||||
|
@ -23,18 +26,28 @@ def worker():
|
||||||
if item is None:
|
if item is None:
|
||||||
break
|
break
|
||||||
|
|
||||||
to = to_p / item.with_suffix('.png').name
|
to = to_p / item.with_suffix(".png").name
|
||||||
args = ['inkscape', '-z', '-e', str(to.absolute()), '-w', '800',
|
args = [
|
||||||
'-background', 'white', str(item.absolute())]
|
"inkscape",
|
||||||
print("Running: {}".format(' '.join(shlex.quote(x) for x in args)))
|
"-z",
|
||||||
|
"-e",
|
||||||
|
str(to.absolute()),
|
||||||
|
"-w",
|
||||||
|
"800",
|
||||||
|
"-background",
|
||||||
|
"white",
|
||||||
|
str(item.absolute()),
|
||||||
|
]
|
||||||
|
print("Running: {}".format(" ".join(shlex.quote(x) for x in args)))
|
||||||
proc = subprocess.run(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
proc = subprocess.run(args, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
|
||||||
if b'Bitmap saved as' not in proc.stdout:
|
if b"Bitmap saved as" not in proc.stdout:
|
||||||
print("Error!")
|
print("Error!")
|
||||||
print(proc.stdout)
|
print(proc.stdout)
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
q.task_done()
|
q.task_done()
|
||||||
|
|
||||||
|
|
||||||
NUM_THREADS = 8
|
NUM_THREADS = 8
|
||||||
threads = []
|
threads = []
|
||||||
for i in range(NUM_THREADS):
|
for i in range(NUM_THREADS):
|
||||||
|
|
64
travis.py
64
travis.py
|
@ -16,32 +16,40 @@ def find_all(a_str, sub):
|
||||||
column += len(sub)
|
column += len(sub)
|
||||||
|
|
||||||
|
|
||||||
section_regex = re.compile(r'^(=+|-+|\*+|~+)$')
|
section_regex = re.compile(r"^(=+|-+|\*+|~+)$")
|
||||||
directive_regex = re.compile(r'^(\s*)\.\. (.*)::.*$')
|
directive_regex = re.compile(r"^(\s*)\.\. (.*)::.*$")
|
||||||
directive_arg_regex = re.compile(r'^(\s+):.*:\s*.*$')
|
directive_arg_regex = re.compile(r"^(\s+):.*:\s*.*$")
|
||||||
esphome_io_regex = re.compile(r'https://esphome.io/')
|
esphome_io_regex = re.compile(r"https://esphome.io/")
|
||||||
|
|
||||||
|
|
||||||
for f in sorted(Path('.').glob('**/*.rst')):
|
for f in sorted(Path(".").glob("**/*.rst")):
|
||||||
try:
|
try:
|
||||||
content = f.read_text('utf-8')
|
content = f.read_text("utf-8")
|
||||||
except UnicodeDecodeError:
|
except UnicodeDecodeError:
|
||||||
errors.append("File {} is not readable as UTF-8. Please set your editor to UTF-8 mode."
|
errors.append(
|
||||||
"".format(f))
|
"File {} is not readable as UTF-8. Please set your editor to UTF-8 mode."
|
||||||
|
"".format(f)
|
||||||
|
)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if not content.endswith('\n'):
|
if not content.endswith("\n"):
|
||||||
errors.append("Newline at end of file missing. Please insert an empty line at end "
|
errors.append(
|
||||||
"of file {}".format(f))
|
"Newline at end of file missing. Please insert an empty line at end "
|
||||||
|
"of file {}".format(f)
|
||||||
|
)
|
||||||
|
|
||||||
# Check tab character
|
# Check tab character
|
||||||
for line, col in find_all(content, '\t'):
|
for line, col in find_all(content, "\t"):
|
||||||
errors.append("File {} contains tab character on line {}:{}. "
|
errors.append(
|
||||||
"Please convert tabs to spaces.".format(f, line + 1, col))
|
"File {} contains tab character on line {}:{}. "
|
||||||
|
"Please convert tabs to spaces.".format(f, line + 1, col)
|
||||||
|
)
|
||||||
# Check windows newline
|
# Check windows newline
|
||||||
for line, col in find_all(content, '\r'):
|
for line, col in find_all(content, "\r"):
|
||||||
errors.append("File {} contains windows newline on line {}:{}. "
|
errors.append(
|
||||||
"Please set your editor to unix newline mode.".format(f, line + 1, col))
|
"File {} contains windows newline on line {}:{}. "
|
||||||
|
"Please set your editor to unix newline mode.".format(f, line + 1, col)
|
||||||
|
)
|
||||||
|
|
||||||
lines = content.splitlines(keepends=False)
|
lines = content.splitlines(keepends=False)
|
||||||
|
|
||||||
|
@ -65,7 +73,7 @@ for f in sorted(Path('.').glob('**/*.rst')):
|
||||||
continue
|
continue
|
||||||
base_indentation = len(m.group(1))
|
base_indentation = len(m.group(1))
|
||||||
directive_name = m.group(2)
|
directive_name = m.group(2)
|
||||||
if directive_name.startswith('|') or directive_name == 'seo':
|
if directive_name.startswith("|") or directive_name == "seo":
|
||||||
continue
|
continue
|
||||||
# Match directive args
|
# Match directive args
|
||||||
for j in range(i + 1, len(lines)):
|
for j in range(i + 1, len(lines)):
|
||||||
|
@ -77,8 +85,10 @@ for f in sorted(Path('.').glob('**/*.rst')):
|
||||||
|
|
||||||
# Empty line must follow
|
# Empty line must follow
|
||||||
if lines[j]:
|
if lines[j]:
|
||||||
errors.append("Directive '{}' is not followed by an empty line. Please insert an "
|
errors.append(
|
||||||
"empty line after {}:{}".format(directive_name, f, j))
|
"Directive '{}' is not followed by an empty line. Please insert an "
|
||||||
|
"empty line after {}:{}".format(directive_name, f, j)
|
||||||
|
)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
k = j + 1
|
k = j + 1
|
||||||
|
@ -93,16 +103,20 @@ for f in sorted(Path('.').glob('**/*.rst')):
|
||||||
break
|
break
|
||||||
num_indent = num_spaces - base_indentation
|
num_indent = num_spaces - base_indentation
|
||||||
if j == k and num_indent != 4:
|
if j == k and num_indent != 4:
|
||||||
errors.append("Directive '{}' must be indented with 4 spaces, not {}. See "
|
errors.append(
|
||||||
"{}:{}".format(directive_name, num_indent, f, j+1))
|
"Directive '{}' must be indented with 4 spaces, not {}. See "
|
||||||
|
"{}:{}".format(directive_name, num_indent, f, j + 1)
|
||||||
|
)
|
||||||
break
|
break
|
||||||
|
|
||||||
for i, line in enumerate(lines):
|
for i, line in enumerate(lines):
|
||||||
if esphome_io_regex.search(line):
|
if esphome_io_regex.search(line):
|
||||||
if 'privacy.rst' in str(f) or 'web_server.rst' in str(f):
|
if "privacy.rst" in str(f) or "web_server.rst" in str(f):
|
||||||
continue
|
continue
|
||||||
errors.append("All links to esphome.io should be relative, please remove esphome.io "
|
errors.append(
|
||||||
"from URL. See {}:{}".format(f, i+1))
|
"All links to esphome.io should be relative, please remove esphome.io "
|
||||||
|
"from URL. See {}:{}".format(f, i + 1)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
for error in errors:
|
for error in errors:
|
||||||
|
|
Loading…
Reference in New Issue