From 0915aba828b0750440fe6bb0b9a76c25b1b7274f Mon Sep 17 00:00:00 2001
From: Guillermo Ruffino
Date: Sun, 7 Mar 2021 15:29:02 -0300
Subject: [PATCH] add-black (#1044)
* Setup pre-commit and black
update pre-commit
add setup
* format with black
format and flake
---
.pre-commit-config.yaml | 25 ++++++
conf.py | 95 +++++++++++------------
github.py | 165 ++++++++++++++++++----------------------
script/bump-version.py | 42 ++++------
seo.py | 80 +++++++++++--------
setup.cfg | 41 ++++++++++
sitemap.py | 47 ++++++------
svg2png.py | 31 +++++---
travis.py | 64 ++++++++++------
9 files changed, 333 insertions(+), 257 deletions(-)
create mode 100644 .pre-commit-config.yaml
create mode 100644 setup.cfg
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 000000000..c56ce094a
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -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
diff --git a/conf.py b/conf.py
index 247a657cc..4b02a1e8e 100644
--- a/conf.py
+++ b/conf.py
@@ -22,14 +22,10 @@
# sys.path.insert(0, os.path.abspath('.'))
import hashlib
import os
-import subprocess
-from sphinx import addnodes
-from sphinx.util.docfields import Field, GroupedField
-import re
import sys
-sys.path.append(os.path.abspath('.'))
+sys.path.append(os.path.abspath("."))
# -- General configuration ------------------------------------------------
@@ -41,58 +37,58 @@ sys.path.append(os.path.abspath('.'))
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
- 'github',
- 'seo',
- 'sitemap',
+ "github",
+ "seo",
+ "sitemap",
]
# Add any paths that contain templates here, relative to this directory.
-templates_path = ['_templates']
+templates_path = ["_templates"]
# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
#
# source_suffix = ['.rst', '.md']
-source_suffix = '.rst'
+source_suffix = ".rst"
# The master toctree document.
-master_doc = 'index'
+master_doc = "index"
# General information about the project.
-project = 'ESPHome'
-copyright = '2019, Otto Winter'
+project = "ESPHome"
+copyright = "2019, Otto Winter"
html_show_copyright = False
html_show_sphinx = False
-author = 'Otto Winter'
+author = "Otto Winter"
# The version info for the project you're documenting, acts as replacement for
# |version| and |release|, also used in various other places throughout the
# built documents.
#
# The short X.Y version.
-version = '1.17'
+version = "1.17"
# 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
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# 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
# directories to ignore when looking for source files.
# 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.
# default_role = 'cpp:any'
# 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
@@ -105,40 +101,40 @@ todo_include_todos = False
# The theme to use for HTML and HTML Help pages. See the documentation for
# 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
# further. For a list of options available for each theme, see the
# documentation.
#
-html_baseurl = os.getenv('BASE_URL', 'https://esphome.io')
-with open('_static/custom.css', 'rb') as f:
+html_baseurl = os.getenv("BASE_URL", "https://esphome.io")
+with open("_static/custom.css", "rb") as f:
custom_css_hash = hashlib.md5(f.read()).hexdigest()[:8]
html_theme_options = {
# 'logo': 'logo-full.png',
- 'logo_name': False,
- 'show_related': False,
- 'sidebar_collapse': True,
- 'fixed_sidebar': True,
- 'show_powered_by': False,
+ "logo_name": False,
+ "show_related": False,
+ "sidebar_collapse": True,
+ "fixed_sidebar": True,
+ "show_powered_by": False,
}
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_show_sourcelink = False
html_last_updated_fmt = None
html_use_smartypants = False
-html_title = 'ESPHome'
+html_title = "ESPHome"
# 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,
# 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
# to template names.
@@ -146,10 +142,10 @@ html_static_path = ['_static']
# This is required for the alabaster theme
# refs: http://alabaster.readthedocs.io/en/latest/installation.html#sidebars
html_sidebars = {
- '**': [
+ "**": [
# 'about.html',
- 'searchbox.html',
- 'localtoc.html',
+ "searchbox.html",
+ "localtoc.html",
]
}
@@ -157,7 +153,7 @@ html_sidebars = {
# -- Options for HTMLHelp output ------------------------------------------
# Output file base name for HTML help builder.
-htmlhelp_basename = 'esphomedoc'
+htmlhelp_basename = "esphomedoc"
# -- Options for LaTeX output ---------------------------------------------
@@ -166,15 +162,12 @@ latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#
# 'papersize': 'letterpaper',
-
# The font size ('10pt', '11pt' or '12pt').
#
# 'pointsize': '10pt',
-
# Additional stuff for the LaTeX preamble.
#
# 'preamble': '',
-
# Latex figure (float) alignment
#
# 'figure_align': 'htbp',
@@ -184,21 +177,17 @@ latex_elements = {
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
- (master_doc, 'esphome.tex', 'ESPHome Documentation',
- 'Otto Winter', 'manual'),
+ (master_doc, "esphome.tex", "ESPHome Documentation", "Otto Winter", "manual"),
]
-latex_engine = 'xelatex'
+latex_engine = "xelatex"
# -- Options for manual page output ---------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
-man_pages = [
- (master_doc, 'esphome', 'ESPHome Documentation',
- [author], 1)
-]
+man_pages = [(master_doc, "esphome", "ESPHome Documentation", [author], 1)]
# -- Options for Texinfo output -------------------------------------------
@@ -207,8 +196,14 @@ man_pages = [
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
- (master_doc, 'esphome', 'ESPHome Documentation',
- author, 'esphome', 'One line description of project.',
- 'Miscellaneous'),
+ (
+ master_doc,
+ "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/.*"]
diff --git a/github.py b/github.py
index b0e986710..a9e3f14d0 100644
--- a/github.py
+++ b/github.py
@@ -9,39 +9,35 @@ from docutils.parsers.rst import directives
from docutils.parsers.rst.directives.tables import Table
-def libpr_role(name, rawtext, text, lineno, inliner, options=None,
- content=None):
- ref = 'https://github.com/esphome/esphome-core/pull/{}'.format(text)
- return [make_link_node(rawtext, 'core#{}'.format(text), ref, options)], []
+def libpr_role(name, rawtext, text, lineno, inliner, options=None, content=None):
+ ref = "https://github.com/esphome/esphome-core/pull/{}".format(text)
+ return [make_link_node(rawtext, "core#{}".format(text), ref, options)], []
-def yamlpr_role(name, rawtext, text, lineno, inliner, options=None,
- content=None):
- ref = 'https://github.com/esphome/esphome/pull/{}'.format(text)
- return [make_link_node(rawtext, 'esphome#{}'.format(text), ref, options)], []
+def yamlpr_role(name, rawtext, text, lineno, inliner, options=None, content=None):
+ ref = "https://github.com/esphome/esphome/pull/{}".format(text)
+ return [make_link_node(rawtext, "esphome#{}".format(text), ref, options)], []
-def docspr_role(name, rawtext, text, lineno, inliner, options=None,
- content=None):
- ref = 'https://github.com/esphome/esphome-docs/pull/{}'.format(text)
- return [make_link_node(rawtext, 'docs#{}'.format(text), ref, options)], []
+def docspr_role(name, rawtext, text, lineno, inliner, options=None, content=None):
+ ref = "https://github.com/esphome/esphome-docs/pull/{}".format(text)
+ return [make_link_node(rawtext, "docs#{}".format(text), ref, options)], []
-def ghuser_role(name, rawtext, text, lineno, inliner, options=None,
- content=None):
- ref = 'https://github.com/{}'.format(text)
- return [make_link_node(rawtext, '@{}'.format(text), ref, options)], []
+def ghuser_role(name, rawtext, text, lineno, inliner, options=None, content=None):
+ ref = "https://github.com/{}".format(text)
+ return [make_link_node(rawtext, "@{}".format(text), ref, options)], []
-value_re = re.compile(r'^(.*)\s*<(.*)>$')
+value_re = re.compile(r"^(.*)\s*<(.*)>$")
DOXYGEN_LOOKUP = {}
for s in string.ascii_lowercase + string.digits:
DOXYGEN_LOOKUP[s] = s
for s in string.ascii_uppercase:
- DOXYGEN_LOOKUP[s] = '_{}'.format(s.lower())
-DOXYGEN_LOOKUP[':'] = '_1'
-DOXYGEN_LOOKUP['_'] = '__'
-DOXYGEN_LOOKUP['.'] = '_8'
+ DOXYGEN_LOOKUP[s] = "_{}".format(s.lower())
+DOXYGEN_LOOKUP[":"] = "_1"
+DOXYGEN_LOOKUP["_"] = "__"
+DOXYGEN_LOOKUP["."] = "_8"
def split_text_value(value):
@@ -52,60 +48,57 @@ def split_text_value(value):
def encode_doxygen(value):
- value = value.split('/')[-1]
+ value = value.split("/")[-1]
try:
- return ''.join(DOXYGEN_LOOKUP[s] for s in value)
+ return "".join(DOXYGEN_LOOKUP[s] for s in value)
except KeyError:
raise ValueError("Unknown character in doxygen string! '{}'".format(value))
-def apiref_role(name, rawtext, text, lineno, inliner, options=None,
- content=None):
+def apiref_role(name, rawtext, text, lineno, inliner, options=None, content=None):
text, value = split_text_value(text)
if text is None:
- text = 'API Reference'
- ref = '/api/{}.html'.format(encode_doxygen(value))
+ text = "API Reference"
+ ref = "/api/{}.html".format(encode_doxygen(value))
return [make_link_node(rawtext, text, ref, options)], []
-def apiclass_role(name, rawtext, text, lineno, inliner, options=None,
- content=None):
+def apiclass_role(name, rawtext, text, lineno, inliner, options=None, content=None):
text, value = split_text_value(text)
if text is None:
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)], []
-def apistruct_role(name, rawtext, text, lineno, inliner, options=None,
- content=None):
+def apistruct_role(name, rawtext, text, lineno, inliner, options=None, content=None):
text, value = split_text_value(text)
if text is None:
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)], []
-def ghedit_role(name, rawtext, text, lineno, inliner, options=None,
- content=None):
- path = os.path.relpath(inliner.document.current_source,
- inliner.document.settings.env.app.srcdir)
- ref = 'https://github.com/esphome/esphome-docs/blob/current/{}'.format(path)
- return [make_link_node(rawtext, 'Edit this page on GitHub', ref, options)], []
+def ghedit_role(name, rawtext, text, lineno, inliner, options=None, content=None):
+ path = os.path.relpath(
+ inliner.document.current_source, inliner.document.settings.env.app.srcdir
+ )
+ ref = "https://github.com/esphome/esphome-docs/blob/current/{}".format(path)
+ return [make_link_node(rawtext, "Edit this page on GitHub", ref, options)], []
def make_link_node(rawtext, text, ref, options=None):
options = options or {}
- node = nodes.reference(rawtext,
- utils.unescape(text),
- refuri=ref,
- **options)
+ node = nodes.reference(rawtext, utils.unescape(text), refuri=ref, **options)
return node
# https://stackoverflow.com/a/3415150/8924614
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
return zip_longest(fillvalue=fillvalue, *args)
@@ -114,12 +107,11 @@ def grouper(n, iterable, fillvalue=None):
class ImageTableDirective(Table):
option_spec = {
- 'columns': directives.positive_int,
+ "columns": directives.positive_int,
}
def run(self):
- env = self.state.document.settings.env
- cols = self.options.get('columns', 3)
+ cols = self.options.get("columns", 3)
items = []
@@ -129,31 +121,30 @@ class ImageTableDirective(Table):
continue
name, page, image = row
link = page.strip()
- if link.startswith('http'):
+ if link.startswith("http"):
pass
else:
- if not link.startswith('/'):
- link = '/{}'.format(link)
- if '.html' not in link:
- link += '.html'
- items.append({
- 'name': name.strip(),
- 'link': link,
- 'image': '/images/{}'.format(image.strip()),
- })
+ if not link.startswith("/"):
+ link = "/{}".format(link)
+ if ".html" not in link:
+ link += ".html"
+ items.append(
+ {
+ "name": name.strip(),
+ "link": link,
+ "image": "/images/{}".format(image.strip()),
+ }
+ )
col_widths = self.get_column_widths(cols)
title, messages = self.make_title()
table = nodes.table()
- table['classes'].append('table-center')
+ table["classes"].append("table-center")
# Set up column specifications based on widths
tgroup = nodes.tgroup(cols=cols)
table += tgroup
- tgroup.extend(
- nodes.colspec(colwidth=col_width)
- for col_width in col_widths
- )
+ tgroup.extend(nodes.colspec(colwidth=col_width) for col_width in col_widths)
tbody = nodes.tbody()
tgroup += tbody
@@ -166,12 +157,12 @@ class ImageTableDirective(Table):
entry += nodes.paragraph()
trow += entry
continue
- name = cell['name']
- link = cell['link']
- image = cell['image']
+ name = cell["name"]
+ link = cell["link"]
+ image = cell["image"]
reference_node = nodes.reference(refuri=link)
img = nodes.image(uri=directives.uri(image), alt=name)
- img['classes'].append('component-image')
+ img["classes"].append("component-image")
reference_node += img
para = nodes.paragraph()
para += reference_node
@@ -186,8 +177,8 @@ class ImageTableDirective(Table):
entry += nodes.paragraph()
trow += entry
continue
- name = cell['name']
- link = cell['link']
+ name = cell["name"]
+ link = cell["link"]
ref = nodes.reference(name, name, refuri=link)
para = nodes.paragraph()
para += ref
@@ -207,8 +198,6 @@ class PinTableDirective(Table):
option_spec = {}
def run(self):
- env = self.state.document.settings.env
-
items = []
data = list(csv.reader(self.content))
@@ -227,18 +216,14 @@ class PinTableDirective(Table):
# Set up column specifications based on widths
tgroup = nodes.tgroup(cols=2)
table += tgroup
- tgroup.extend(
- nodes.colspec(colwidth=col_width)
- for col_width in col_widths
- )
+ tgroup.extend(nodes.colspec(colwidth=col_width) for col_width in col_widths)
thead = nodes.thead()
tgroup += thead
trow = nodes.row()
thead += trow
trow.extend(
- nodes.entry(h, nodes.paragraph(text=h))
- for h in ('Pin', 'Function')
+ nodes.entry(h, nodes.paragraph(text=h)) for h in ("Pin", "Function")
)
tbody = nodes.tbody()
@@ -269,18 +254,16 @@ class PinTableDirective(Table):
def setup(app):
- app.add_role('libpr', libpr_role)
- app.add_role('corepr', libpr_role)
- app.add_role('yamlpr', yamlpr_role)
- app.add_role('esphomepr', yamlpr_role)
- app.add_role('docspr', docspr_role)
- app.add_role('ghuser', ghuser_role)
- app.add_role('apiref', apiref_role)
- app.add_role('apiclass', apiclass_role)
- app.add_role('apistruct', apistruct_role)
- app.add_role('ghedit', ghedit_role)
- app.add_directive('imgtable', ImageTableDirective)
- app.add_directive('pintable', PinTableDirective)
- return {"version": "1.0.0",
- "parallel_read_safe": True,
- "parallel_write_safe": True}
+ app.add_role("libpr", libpr_role)
+ app.add_role("corepr", libpr_role)
+ app.add_role("yamlpr", yamlpr_role)
+ app.add_role("esphomepr", yamlpr_role)
+ app.add_role("docspr", docspr_role)
+ app.add_role("ghuser", ghuser_role)
+ app.add_role("apiref", apiref_role)
+ app.add_role("apiclass", apiclass_role)
+ app.add_role("apistruct", apistruct_role)
+ app.add_role("ghedit", ghedit_role)
+ app.add_directive("imgtable", ImageTableDirective)
+ app.add_directive("pintable", PinTableDirective)
+ return {"version": "1.0.0", "parallel_read_safe": True, "parallel_write_safe": True}
diff --git a/script/bump-version.py b/script/bump-version.py
index 3517d110b..83ffa4b4d 100755
--- a/script/bump-version.py
+++ b/script/bump-version.py
@@ -2,7 +2,6 @@
import argparse
import re
-import subprocess
from dataclasses import dataclass
import sys
@@ -16,30 +15,27 @@ class Version:
dev: bool = False
def __str__(self):
- return f'{self.major}.{self.minor}.{self.full_patch}'
+ return f"{self.major}.{self.minor}.{self.full_patch}"
@property
def full_patch(self):
- res = f'{self.patch}'
+ res = f"{self.patch}"
if self.beta > 0:
- res += f'b{self.beta}'
+ res += f"b{self.beta}"
if self.dev:
- res += '-dev'
+ res += "-dev"
return res
@classmethod
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
major = int(match[1])
minor = int(match[2])
patch = int(match[3])
beta = int(match[4][1:]) if match[4] else 0
dev = bool(match[5])
- return Version(
- major=major, minor=minor, patch=patch,
- beta=beta, dev=dev
- )
+ return Version(major=major, minor=minor, patch=patch, beta=beta, dev=dev)
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):
# ESPHOME_REF = v1.14.4
sub(
- 'Makefile',
- r'ESPHOME_REF = .*',
- f'ESPHOME_REF = v{version}' if not version.dev else f'ESPHOME_REF = dev'
+ "Makefile",
+ r"ESPHOME_REF = .*",
+ f"ESPHOME_REF = v{version}" if not version.dev else "ESPHOME_REF = dev",
)
# PROJECT_NUMBER = 1.14.4
sub(
- 'Doxygen',
- r'PROJECT_NUMBER = .*',
- f'PROJECT_NUMBER = {version}'
+ "Doxygen", r"PROJECT_NUMBER = .*", f"PROJECT_NUMBER = {version}"
)
# version = '1.14'
- sub(
- 'conf.py',
- r"version = '.*'",
- f"version = '{version.major}.{version.minor}'"
- )
+ sub("conf.py", r"version = '.*'", f"version = '{version.major}.{version.minor}'")
# release = '1.14.4'
- sub(
- 'conf.py',
- r"release = '.*'",
- f"release = '{version}'"
- )
- with open('_static/version', 'wt') as fh:
+ sub("conf.py", r"release = '.*'", f"release = '{version}'")
+ with open("_static/version", "wt") as fh:
fh.write(str(version))
def main():
parser = argparse.ArgumentParser()
- parser.add_argument('new_version', type=str)
+ parser.add_argument("new_version", type=str)
args = parser.parse_args()
version = Version.parse(args.new_version)
diff --git a/seo.py b/seo.py
index 819b0206b..1255a66cf 100644
--- a/seo.py
+++ b/seo.py
@@ -6,11 +6,18 @@ from docutils.writers._html_base import HTMLTranslator
class SEONode(nodes.General, nodes.Element):
- def __init__(self, title=None, description=None, image=None,
- author=None, author_twitter=None, keywords=None):
+ def __init__(
+ self,
+ title=None,
+ description=None,
+ image=None,
+ author=None,
+ author_twitter=None,
+ keywords=None,
+ ):
super(SEONode, self).__init__()
self.title = title
- self.description = description.replace('\n', ' ')
+ self.description = description.replace("\n", " ")
self.image = image
self.author = author
self.author_twitter = author_twitter
@@ -25,26 +32,34 @@ class RedirectNode(nodes.General, nodes.Element):
def seo_visit(self: HTMLTranslator, node: SEONode):
def encode_text(text):
- special_characters = {ord('&'): '&',
- ord('<'): '<',
- ord('"'): '"',
- ord('>'): '>'}
+ special_characters = {
+ ord("&"): "&",
+ ord("<"): "<",
+ ord('"'): """,
+ ord(">"): ">",
+ }
return text.translate(special_characters)
def create_content_meta(name, content):
if content is None:
return
- self.meta.append('\n'.format(name, encode_text(content)))
+ self.meta.append(
+ '\n'.format(name, encode_text(content))
+ )
def create_itemprop_meta(name, content):
if content is None:
return
- self.meta.append('\n'.format(name, encode_text(content)))
+ self.meta.append(
+ '\n'.format(name, encode_text(content))
+ )
def create_property_meta(name, content):
if content is None:
return
- self.meta.append('\n'.format(name, encode_text(content)))
+ self.meta.append(
+ '\n'.format(name, encode_text(content))
+ )
# Base
create_content_meta("description", node.description)
@@ -76,8 +91,11 @@ def seo_visit(self: HTMLTranslator, node: SEONode):
def redirect_visit(self: HTMLTranslator, node: RedirectNode):
self.meta.append(''.format(node.url))
- self.body.append(self.starttag(node, 'p',
- 'Redirecting to {0}'.format(node.url)))
+ self.body.append(
+ self.starttag(
+ node, "p", 'Redirecting to {0}'.format(node.url)
+ )
+ )
def seo_depart(self, _):
@@ -85,36 +103,36 @@ def seo_depart(self, _):
def redirect_depart(self, _):
- self.body.append('
')
+ self.body.append("")
class SEODirective(Directive):
option_spec = {
- 'title': directives.unchanged,
- 'description': directives.unchanged,
- 'image': directives.path,
- 'author': directives.unchanged,
- 'author_twitter': directives.unchanged,
- 'keywords': directives.unchanged,
+ "title": directives.unchanged,
+ "description": directives.unchanged,
+ "image": directives.path,
+ "author": directives.unchanged,
+ "author_twitter": directives.unchanged,
+ "keywords": directives.unchanged,
}
def run(self):
env = self.state.document.settings.env
- title_match = re.match(r'.+(.+).+', str(self.state.document))
- if title_match is not None and 'title' not in self.options:
- self.options['title'] = title_match.group(1)
+ title_match = re.match(r".+(.+).+", str(self.state.document))
+ if title_match is not None and "title" not in self.options:
+ self.options["title"] = title_match.group(1)
- image = self.options.get('image')
+ image = self.options.get("image")
if image is not None:
- if not image.startswith('/'):
- image = '/_images/' + image
- self.options['image'] = env.config.html_baseurl + image
+ if not image.startswith("/"):
+ image = "/_images/" + image
+ self.options["image"] = env.config.html_baseurl + image
return [SEONode(**self.options)]
class RedirectDirective(Directive):
option_spec = {
- 'url': directives.unchanged,
+ "url": directives.unchanged,
}
def run(self):
@@ -122,10 +140,8 @@ class RedirectDirective(Directive):
def setup(app):
- app.add_directive('seo', SEODirective)
+ app.add_directive("seo", SEODirective)
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))
- return {"version": "1.0.0",
- "parallel_read_safe": True,
- "parallel_write_safe": True}
+ return {"version": "1.0.0", "parallel_read_safe": True, "parallel_write_safe": True}
diff --git a/setup.cfg b/setup.cfg
new file mode 100644
index 000000000..d8e6856d8
--- /dev/null
+++ b/setup.cfg
@@ -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,
+
diff --git a/sitemap.py b/sitemap.py
index 6878b12b5..3f50ff322 100644
--- a/sitemap.py
+++ b/sitemap.py
@@ -4,15 +4,17 @@ import xml.etree.ElementTree as ET
def setup(app):
"""Setup connects events to the sitemap builder"""
- app.connect('html-page-context', add_html_link)
- app.connect('build-finished', create_sitemap)
+ app.connect("html-page-context", add_html_link)
+ app.connect("build-finished", create_sitemap)
app.sitemap_links = []
- is_production = os.getenv('PRODUCTION') == 'YES'
+ is_production = os.getenv("PRODUCTION") == "YES"
- return {"version": "1.0.0",
- "parallel_read_safe": True,
- "parallel_write_safe": not is_production}
+ return {
+ "version": "1.0.0",
+ "parallel_read_safe": True,
+ "parallel_write_safe": not is_production,
+ }
def add_html_link(app, pagename, templatename, context, doctree):
@@ -29,26 +31,27 @@ def create_sitemap(app, exception):
for link in app.sitemap_links:
url = ET.SubElement(root, "url")
priority = 0.5
- if link == 'index.html':
+ if link == "index.html":
priority = 1.0
- link = ''
- elif link.endswith('index.html'):
+ link = ""
+ elif link.endswith("index.html"):
priority += 0.25
- link = link[:-len('index.html')]
- if link.endswith('.html'):
- link = link[:-len('.html')]
- ET.SubElement(url, "loc").text = app.builder.config.html_baseurl + '/' + link
+ link = link[: -len("index.html")]
+ if link.endswith(".html"):
+ link = link[: -len(".html")]
+ ET.SubElement(url, "loc").text = app.builder.config.html_baseurl + "/" + link
ET.SubElement(url, "priority").text = str(priority)
filename = os.path.join(app.outdir, "sitemap.xml")
- ET.ElementTree(root).write(filename,
- xml_declaration=True,
- encoding='utf-8',
- method="xml")
+ ET.ElementTree(root).write(
+ filename, xml_declaration=True, encoding="utf-8", method="xml"
+ )
- with open(os.path.join(app.builder.outdir, 'robots.txt'), 'wt') as f:
- if os.getenv('PRODUCTION') != 'YES':
- f.write('User-agent: *\nDisallow: /\n')
+ with open(os.path.join(app.builder.outdir, "robots.txt"), "wt") as f:
+ if os.getenv("PRODUCTION") != "YES":
+ f.write("User-agent: *\nDisallow: /\n")
else:
- f.write('User-agent: *\nDisallow: \n\n'
- 'Sitemap: https://esphome.io/sitemap.xml\n')
+ f.write(
+ "User-agent: *\nDisallow: \n\n"
+ "Sitemap: https://esphome.io/sitemap.xml\n"
+ )
diff --git a/svg2png.py b/svg2png.py
index 02b3dc4c1..fe5165aeb 100644
--- a/svg2png.py
+++ b/svg2png.py
@@ -7,13 +7,16 @@ import queue
import sys
-to_p = Path('svg2png')
+to_p = Path("svg2png")
to_p.mkdir(exist_ok=True)
-for f in to_p.glob('*.png'):
+for f in to_p.glob("*.png"):
f.unlink()
-images = [f for f in Path('_build/html/_images/').glob('*.svg')
- if not re.match(r'^seg[0-9A-F]{2}$', f.stem)]
+images = [
+ 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()
@@ -23,18 +26,28 @@ def worker():
if item is None:
break
- to = to_p / item.with_suffix('.png').name
- args = ['inkscape', '-z', '-e', str(to.absolute()), '-w', '800',
- '-background', 'white', str(item.absolute())]
- print("Running: {}".format(' '.join(shlex.quote(x) for x in args)))
+ to = to_p / item.with_suffix(".png").name
+ args = [
+ "inkscape",
+ "-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)
- if b'Bitmap saved as' not in proc.stdout:
+ if b"Bitmap saved as" not in proc.stdout:
print("Error!")
print(proc.stdout)
sys.exit(1)
q.task_done()
+
NUM_THREADS = 8
threads = []
for i in range(NUM_THREADS):
diff --git a/travis.py b/travis.py
index c3ca38e31..fd5b96c15 100644
--- a/travis.py
+++ b/travis.py
@@ -16,32 +16,40 @@ def find_all(a_str, sub):
column += len(sub)
-section_regex = re.compile(r'^(=+|-+|\*+|~+)$')
-directive_regex = re.compile(r'^(\s*)\.\. (.*)::.*$')
-directive_arg_regex = re.compile(r'^(\s+):.*:\s*.*$')
-esphome_io_regex = re.compile(r'https://esphome.io/')
+section_regex = re.compile(r"^(=+|-+|\*+|~+)$")
+directive_regex = re.compile(r"^(\s*)\.\. (.*)::.*$")
+directive_arg_regex = re.compile(r"^(\s+):.*:\s*.*$")
+esphome_io_regex = re.compile(r"https://esphome.io/")
-for f in sorted(Path('.').glob('**/*.rst')):
+for f in sorted(Path(".").glob("**/*.rst")):
try:
- content = f.read_text('utf-8')
+ content = f.read_text("utf-8")
except UnicodeDecodeError:
- errors.append("File {} is not readable as UTF-8. Please set your editor to UTF-8 mode."
- "".format(f))
+ errors.append(
+ "File {} is not readable as UTF-8. Please set your editor to UTF-8 mode."
+ "".format(f)
+ )
continue
- if not content.endswith('\n'):
- errors.append("Newline at end of file missing. Please insert an empty line at end "
- "of file {}".format(f))
+ if not content.endswith("\n"):
+ errors.append(
+ "Newline at end of file missing. Please insert an empty line at end "
+ "of file {}".format(f)
+ )
# Check tab character
- for line, col in find_all(content, '\t'):
- errors.append("File {} contains tab character on line {}:{}. "
- "Please convert tabs to spaces.".format(f, line + 1, col))
+ for line, col in find_all(content, "\t"):
+ errors.append(
+ "File {} contains tab character on line {}:{}. "
+ "Please convert tabs to spaces.".format(f, line + 1, col)
+ )
# Check windows newline
- for line, col in find_all(content, '\r'):
- errors.append("File {} contains windows newline on line {}:{}. "
- "Please set your editor to unix newline mode.".format(f, line + 1, col))
+ for line, col in find_all(content, "\r"):
+ errors.append(
+ "File {} contains windows newline on line {}:{}. "
+ "Please set your editor to unix newline mode.".format(f, line + 1, col)
+ )
lines = content.splitlines(keepends=False)
@@ -65,7 +73,7 @@ for f in sorted(Path('.').glob('**/*.rst')):
continue
base_indentation = len(m.group(1))
directive_name = m.group(2)
- if directive_name.startswith('|') or directive_name == 'seo':
+ if directive_name.startswith("|") or directive_name == "seo":
continue
# Match directive args
for j in range(i + 1, len(lines)):
@@ -77,8 +85,10 @@ for f in sorted(Path('.').glob('**/*.rst')):
# Empty line must follow
if lines[j]:
- errors.append("Directive '{}' is not followed by an empty line. Please insert an "
- "empty line after {}:{}".format(directive_name, f, j))
+ errors.append(
+ "Directive '{}' is not followed by an empty line. Please insert an "
+ "empty line after {}:{}".format(directive_name, f, j)
+ )
continue
k = j + 1
@@ -93,16 +103,20 @@ for f in sorted(Path('.').glob('**/*.rst')):
break
num_indent = num_spaces - base_indentation
if j == k and num_indent != 4:
- errors.append("Directive '{}' must be indented with 4 spaces, not {}. See "
- "{}:{}".format(directive_name, num_indent, f, j+1))
+ errors.append(
+ "Directive '{}' must be indented with 4 spaces, not {}. See "
+ "{}:{}".format(directive_name, num_indent, f, j + 1)
+ )
break
for i, line in enumerate(lines):
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
- errors.append("All links to esphome.io should be relative, please remove esphome.io "
- "from URL. See {}:{}".format(f, i+1))
+ errors.append(
+ "All links to esphome.io should be relative, please remove esphome.io "
+ "from URL. See {}:{}".format(f, i + 1)
+ )
for error in errors: