Improve string formatting v5 (#33697)

* Improve string formatting v5

* Address review comments
This commit is contained in:
springstan 2020-04-05 17:48:55 +02:00 committed by GitHub
parent 39336d3ea3
commit fca90a8ddc
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
46 changed files with 221 additions and 252 deletions

View file

@ -8,19 +8,19 @@ Loosely based on https://github.com/astropy/astropy/pull/347
import os import os
import warnings import warnings
__licence__ = 'BSD (3 clause)' __licence__ = "BSD (3 clause)"
def get_github_url(app, view, path): def get_github_url(app, view, path):
github_fmt = 'https://github.com/{}/{}/{}/{}{}'
return ( return (
github_fmt.format(app.config.edit_on_github_project, view, f"https://github.com/{app.config.edit_on_github_project}/"
app.config.edit_on_github_branch, f"{view}/{app.config.edit_on_github_branch}/"
app.config.edit_on_github_src_path, path)) f"{app.config.edit_on_github_src_path}{path}"
)
def html_page_context(app, pagename, templatename, context, doctree): def html_page_context(app, pagename, templatename, context, doctree):
if templatename != 'page.html': if templatename != "page.html":
return return
if not app.config.edit_on_github_project: if not app.config.edit_on_github_project:
@ -29,16 +29,16 @@ def html_page_context(app, pagename, templatename, context, doctree):
if not doctree: if not doctree:
warnings.warn("doctree is None") warnings.warn("doctree is None")
return return
path = os.path.relpath(doctree.get('source'), app.builder.srcdir) path = os.path.relpath(doctree.get("source"), app.builder.srcdir)
show_url = get_github_url(app, 'blob', path) show_url = get_github_url(app, "blob", path)
edit_url = get_github_url(app, 'edit', path) edit_url = get_github_url(app, "edit", path)
context['show_on_github_url'] = show_url context["show_on_github_url"] = show_url
context['edit_on_github_url'] = edit_url context["edit_on_github_url"] = edit_url
def setup(app): def setup(app):
app.add_config_value('edit_on_github_project', '', True) app.add_config_value("edit_on_github_project", "", True)
app.add_config_value('edit_on_github_branch', 'master', True) app.add_config_value("edit_on_github_branch", "master", True)
app.add_config_value('edit_on_github_src_path', '', True) # 'eg' "docs/" app.add_config_value("edit_on_github_src_path", "", True) # 'eg' "docs/"
app.connect('html-page-context', html_page_context) app.connect("html-page-context", html_page_context)

View file

@ -22,25 +22,26 @@ import sys
from homeassistant.const import __short_version__, __version__ from homeassistant.const import __short_version__, __version__
PROJECT_NAME = 'Home Assistant' PROJECT_NAME = "Home Assistant"
PROJECT_PACKAGE_NAME = 'homeassistant' PROJECT_PACKAGE_NAME = "homeassistant"
PROJECT_AUTHOR = 'The Home Assistant Authors' PROJECT_AUTHOR = "The Home Assistant Authors"
PROJECT_COPYRIGHT = f' 2013-2020, {PROJECT_AUTHOR}' PROJECT_COPYRIGHT = f" 2013-2020, {PROJECT_AUTHOR}"
PROJECT_LONG_DESCRIPTION = ('Home Assistant is an open-source ' PROJECT_LONG_DESCRIPTION = (
'home automation platform running on Python 3. ' "Home Assistant is an open-source "
'Track and control all devices at home and ' "home automation platform running on Python 3. "
'automate control. ' "Track and control all devices at home and "
'Installation in less than a minute.') "automate control. "
PROJECT_GITHUB_USERNAME = 'home-assistant' "Installation in less than a minute."
PROJECT_GITHUB_REPOSITORY = 'home-assistant' )
PROJECT_GITHUB_USERNAME = "home-assistant"
PROJECT_GITHUB_REPOSITORY = "home-assistant"
GITHUB_PATH = '{}/{}'.format( GITHUB_PATH = f"{PROJECT_GITHUB_USERNAME}/{PROJECT_GITHUB_REPOSITORY}"
PROJECT_GITHUB_USERNAME, PROJECT_GITHUB_REPOSITORY) GITHUB_URL = f"https://github.com/{GITHUB_PATH}"
GITHUB_URL = f'https://github.com/{GITHUB_PATH}'
sys.path.insert(0, os.path.abspath('_ext')) sys.path.insert(0, os.path.abspath("_ext"))
sys.path.insert(0, os.path.abspath('../homeassistant')) sys.path.insert(0, os.path.abspath("../homeassistant"))
# -- General configuration ------------------------------------------------ # -- General configuration ------------------------------------------------
@ -52,27 +53,27 @@ sys.path.insert(0, os.path.abspath('../homeassistant'))
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones. # ones.
extensions = [ extensions = [
'sphinx.ext.autodoc', "sphinx.ext.autodoc",
'sphinx.ext.linkcode', "sphinx.ext.linkcode",
'sphinx_autodoc_annotation', "sphinx_autodoc_annotation",
'edit_on_github' "edit_on_github",
] ]
# 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 encoding of source files. # The encoding of source files.
# #
# source_encoding = 'utf-8-sig' # source_encoding = 'utf-8-sig'
# 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 = PROJECT_NAME project = PROJECT_NAME
@ -88,25 +89,25 @@ version = __short_version__
# The full version, including alpha/beta/rc tags. # The full version, including alpha/beta/rc tags.
release = __version__ release = __version__
code_branch = 'dev' if 'dev' in __version__ else 'master' code_branch = "dev" if "dev" in __version__ else "master"
# Edit on Github config # Edit on Github config
edit_on_github_project = GITHUB_PATH edit_on_github_project = GITHUB_PATH
edit_on_github_branch = code_branch edit_on_github_branch = code_branch
edit_on_github_src_path = 'docs/source/' edit_on_github_src_path = "docs/source/"
def linkcode_resolve(domain, info): def linkcode_resolve(domain, info):
"""Determine the URL corresponding to Python object.""" """Determine the URL corresponding to Python object."""
if domain != 'py': if domain != "py":
return None return None
modname = info['module'] modname = info["module"]
fullname = info['fullname'] fullname = info["fullname"]
submod = sys.modules.get(modname) submod = sys.modules.get(modname)
if submod is None: if submod is None:
return None return None
obj = submod obj = submod
for part in fullname.split('.'): for part in fullname.split("."):
try: try:
obj = getattr(obj, part) obj = getattr(obj, part)
except: except:
@ -131,7 +132,8 @@ def linkcode_resolve(domain, info):
fn = fn[index:] fn = fn[index:]
return f'{GITHUB_URL}/blob/{code_branch}/{fn}{linespec}' return f"{GITHUB_URL}/blob/{code_branch}/{fn}{linespec}"
# 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.
@ -174,7 +176,7 @@ exclude_patterns = []
# show_authors = False # show_authors = False
# The name of the Pygments (syntax highlighting) style to use. # The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx' pygments_style = "sphinx"
# A list of ignored prefixes for module index sorting. # A list of ignored prefixes for module index sorting.
# modindex_common_prefix = [] # modindex_common_prefix = []
@ -191,22 +193,22 @@ 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_theme_options = { html_theme_options = {
'logo': 'logo.png', "logo": "logo.png",
'logo_name': PROJECT_NAME, "logo_name": PROJECT_NAME,
'description': PROJECT_LONG_DESCRIPTION, "description": PROJECT_LONG_DESCRIPTION,
'github_user': PROJECT_GITHUB_USERNAME, "github_user": PROJECT_GITHUB_USERNAME,
'github_repo': PROJECT_GITHUB_REPOSITORY, "github_repo": PROJECT_GITHUB_REPOSITORY,
'github_type': 'star', "github_type": "star",
'github_banner': True, "github_banner": True,
'travis_button': True, "travis_button": True,
'touch_icon': 'logo-apple.png', "touch_icon": "logo-apple.png",
# 'fixed_sidebar': True, # Re-enable when we have more content # 'fixed_sidebar': True, # Re-enable when we have more content
} }
@ -232,12 +234,12 @@ html_theme_options = {
# This file should be a Windows icon file (.ico) being 16x16 or 32x32 # This file should be a Windows icon file (.ico) being 16x16 or 32x32
# pixels large. # pixels large.
# #
html_favicon = '_static/favicon.ico' html_favicon = "_static/favicon.ico"
# 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"]
# Add any extra paths that contain custom files (such as robots.txt or # Add any extra paths that contain custom files (such as robots.txt or
# .htaccess) here, relative to this directory. These files are copied # .htaccess) here, relative to this directory. These files are copied
@ -249,7 +251,7 @@ html_static_path = ['_static']
# bottom, using the given strftime format. # bottom, using the given strftime format.
# The empty string is equivalent to '%b %d, %Y'. # The empty string is equivalent to '%b %d, %Y'.
# #
html_last_updated_fmt = '%b %d, %Y' html_last_updated_fmt = "%b %d, %Y"
# If true, SmartyPants will be used to convert quotes and dashes to # If true, SmartyPants will be used to convert quotes and dashes to
# typographically correct entities. # typographically correct entities.
@ -259,13 +261,13 @@ html_use_smartypants = True
# Custom sidebar templates, maps document names to template names. # Custom sidebar templates, maps document names to template names.
# #
html_sidebars = { html_sidebars = {
'**': [ "**": [
'about.html', "about.html",
'links.html', "links.html",
'searchbox.html', "searchbox.html",
'sourcelink.html', "sourcelink.html",
'navigation.html', "navigation.html",
'relations.html' "relations.html",
] ]
} }
@ -326,7 +328,7 @@ html_sidebars = {
# html_search_scorer = 'scorer.js' # html_search_scorer = 'scorer.js'
# Output file base name for HTML help builder. # Output file base name for HTML help builder.
htmlhelp_basename = 'Home-Assistantdoc' htmlhelp_basename = "Home-Assistantdoc"
# -- Options for LaTeX output --------------------------------------------- # -- Options for LaTeX output ---------------------------------------------
@ -334,15 +336,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',
@ -352,8 +351,13 @@ 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, 'home-assistant.tex', 'Home Assistant Documentation', (
'Home Assistant Team', 'manual'), master_doc,
"home-assistant.tex",
"Home Assistant Documentation",
"Home Assistant Team",
"manual",
)
] ]
# The name of an image file (relative to this directory) to place at the top of # The name of an image file (relative to this directory) to place at the top of
@ -394,8 +398,7 @@ latex_documents = [
# 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, 'home-assistant', 'Home Assistant Documentation', (master_doc, "home-assistant", "Home Assistant Documentation", [author], 1)
[author], 1)
] ]
# If true, show URL addresses after external links. # If true, show URL addresses after external links.
@ -409,9 +412,15 @@ 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, 'Home-Assistant', 'Home Assistant Documentation', (
author, 'Home Assistant', 'Open-source home automation platform.', master_doc,
'Miscellaneous'), "Home-Assistant",
"Home Assistant Documentation",
author,
"Home Assistant",
"Open-source home automation platform.",
"Miscellaneous",
)
] ]
# Documents to append as an appendix to all manuals. # Documents to append as an appendix to all manuals.

View file

@ -36,9 +36,8 @@ def validate_python() -> None:
"""Validate that the right Python version is running.""" """Validate that the right Python version is running."""
if sys.version_info[:3] < REQUIRED_PYTHON_VER: if sys.version_info[:3] < REQUIRED_PYTHON_VER:
print( print(
"Home Assistant requires at least Python {}.{}.{}".format( "Home Assistant requires at least Python "
*REQUIRED_PYTHON_VER f"{REQUIRED_PYTHON_VER[0]}.{REQUIRED_PYTHON_VER[1]}.{REQUIRED_PYTHON_VER[2]}"
)
) )
sys.exit(1) sys.exit(1)

View file

@ -191,9 +191,7 @@ class AfterShipSensor(Entity):
"name": name, "name": name,
"tracking_number": track["tracking_number"], "tracking_number": track["tracking_number"],
"slug": track["slug"], "slug": track["slug"],
"link": "{}{}/{}".format( "link": f"{BASE}{track['slug']}/{track['tracking_number']}",
BASE, track["slug"], track["tracking_number"]
),
"last_update": track["updated_at"], "last_update": track["updated_at"],
"expected_delivery": track["expected_delivery"], "expected_delivery": track["expected_delivery"],
"status": track["tag"], "status": track["tag"],

View file

@ -90,8 +90,11 @@ async def async_request_validation(hass, config_entry, august_gateway):
hass.data[DOMAIN][entry_id][TWO_FA_REVALIDATE] = configurator.async_request_config( hass.data[DOMAIN][entry_id][TWO_FA_REVALIDATE] = configurator.async_request_config(
f"{DEFAULT_NAME} ({username})", f"{DEFAULT_NAME} ({username})",
async_august_configuration_validation_callback, async_august_configuration_validation_callback,
description="August must be re-verified. Please check your {} ({}) and enter the verification " description=(
"code below".format(login_method, username), "August must be re-verified. "
f"Please check your {login_method} ({username}) "
"and enter the verification code below"
),
submit_caption="Verify", submit_caption="Verify",
fields=[ fields=[
{"id": VERIFICATION_CODE_KEY, "name": "Verification code", "type": "string"} {"id": VERIFICATION_CODE_KEY, "name": "Verification code", "type": "string"}
@ -265,7 +268,7 @@ class AugustData(AugustSubscriberMixin):
self._api.async_get_doorbell_detail, self._api.async_get_doorbell_detail,
) )
_LOGGER.debug( _LOGGER.debug(
"async_signal_device_id_update (from detail updates): %s", device_id, "async_signal_device_id_update (from detail updates): %s", device_id
) )
self.async_signal_device_id_update(device_id) self.async_signal_device_id_update(device_id)

View file

@ -92,7 +92,7 @@ def calculate_offset(event, offset):
time = search.group(1) time = search.group(1)
if ":" not in time: if ":" not in time:
if time[0] == "+" or time[0] == "-": if time[0] == "+" or time[0] == "-":
time = "{}0:{}".format(time[0], time[1:]) time = f"{time[0]}0:{time[1:]}"
else: else:
time = f"0:{time}" time = f"0:{time}"

View file

@ -49,9 +49,7 @@ def setup(hass, config):
except (ConnectTimeout, HTTPError) as ex: except (ConnectTimeout, HTTPError) as ex:
_LOGGER.error("Unable to connect to Canary service: %s", str(ex)) _LOGGER.error("Unable to connect to Canary service: %s", str(ex))
hass.components.persistent_notification.create( hass.components.persistent_notification.create(
"Error: {}<br />" f"Error: {ex}<br />You will need to restart hass after fixing.",
"You will need to restart hass after fixing."
"".format(ex),
title=NOTIFICATION_TITLE, title=NOTIFICATION_TITLE,
notification_id=NOTIFICATION_ID, notification_id=NOTIFICATION_ID,
) )

View file

@ -76,7 +76,7 @@ class CanarySensor(Entity):
@property @property
def unique_id(self): def unique_id(self):
"""Return the unique ID of this sensor.""" """Return the unique ID of this sensor."""
return "{}_{}".format(self._device_id, self._sensor_type[0]) return f"{self._device_id}_{self._sensor_type[0]}"
@property @property
def unit_of_measurement(self): def unit_of_measurement(self):

View file

@ -48,7 +48,7 @@ class CiscoWebexTeamsNotificationService(BaseNotificationService):
title = "" title = ""
if kwargs.get(ATTR_TITLE) is not None: if kwargs.get(ATTR_TITLE) is not None:
title = "{}{}".format(kwargs.get(ATTR_TITLE), "<br>") title = f"{kwargs.get(ATTR_TITLE)}<br>"
try: try:
self.client.messages.create(roomId=self.room, html=f"{title}{message}") self.client.messages.create(roomId=self.room, html=f"{title}{message}")

View file

@ -119,9 +119,9 @@ class AlexaConfig(alexa_config.AbstractConfig):
if self.should_report_state: if self.should_report_state:
await self._prefs.async_update(alexa_report_state=False) await self._prefs.async_update(alexa_report_state=False)
self.hass.components.persistent_notification.async_create( self.hass.components.persistent_notification.async_create(
"There was an error reporting state to Alexa ({}). " f"There was an error reporting state to Alexa ({body['reason']}). "
"Please re-link your Alexa skill via the Alexa app to " "Please re-link your Alexa skill via the Alexa app to "
"continue using it.".format(body["reason"]), "continue using it.",
"Alexa state reporting disabled", "Alexa state reporting disabled",
"cloud_alexa_report", "cloud_alexa_report",
) )

View file

@ -82,7 +82,7 @@ class AccountSensor(Entity):
"""Get the latest state of the sensor.""" """Get the latest state of the sensor."""
self._coinbase_data.update() self._coinbase_data.update()
for account in self._coinbase_data.accounts["data"]: for account in self._coinbase_data.accounts["data"]:
if self._name == "Coinbase {}".format(account["name"]): if self._name == f"Coinbase {account['name']}":
self._state = account["balance"]["amount"] self._state = account["balance"]["amount"]
self._native_balance = account["native_balance"]["amount"] self._native_balance = account["native_balance"]["amount"]
self._native_currency = account["native_balance"]["currency"] self._native_currency = account["native_balance"]["currency"]

View file

@ -219,15 +219,16 @@ class HassEzvizCamera(Camera):
ffmpeg = ImageFrame(self._ffmpeg.binary, loop=self.hass.loop) ffmpeg = ImageFrame(self._ffmpeg.binary, loop=self.hass.loop)
image = await asyncio.shield( image = await asyncio.shield(
ffmpeg.get_image(self._rtsp_stream, output_format=IMAGE_JPEG,) ffmpeg.get_image(self._rtsp_stream, output_format=IMAGE_JPEG)
) )
return image return image
async def stream_source(self): async def stream_source(self):
"""Return the stream source.""" """Return the stream source."""
if self._local_rtsp_port: if self._local_rtsp_port:
rtsp_stream_source = "rtsp://{}:{}@{}:{}".format( rtsp_stream_source = (
self._username, self._password, self._local_ip, self._local_rtsp_port f"rtsp://{self._username}:{self._password}@"
f"{self._local_ip}:{self._local_rtsp_port}"
) )
_LOGGER.debug( _LOGGER.debug(
"Camera %s source stream: %s", self._serial, rtsp_stream_source "Camera %s source stream: %s", self._serial, rtsp_stream_source

View file

@ -67,9 +67,7 @@ class CecPlayerDevice(CecDevice, MediaPlayerDevice):
def __init__(self, device, logical) -> None: def __init__(self, device, logical) -> None:
"""Initialize the HDMI device.""" """Initialize the HDMI device."""
CecDevice.__init__(self, device, logical) CecDevice.__init__(self, device, logical)
self.entity_id = "{}.{}_{}".format( self.entity_id = f"{DOMAIN}.hdmi_{hex(self._logical_address)[2:]}"
DOMAIN, "hdmi", hex(self._logical_address)[2:]
)
def send_keypress(self, key): def send_keypress(self, key):
"""Send keypress to CEC adapter.""" """Send keypress to CEC adapter."""

View file

@ -28,9 +28,7 @@ class CecSwitchDevice(CecDevice, SwitchDevice):
def __init__(self, device, logical) -> None: def __init__(self, device, logical) -> None:
"""Initialize the HDMI device.""" """Initialize the HDMI device."""
CecDevice.__init__(self, device, logical) CecDevice.__init__(self, device, logical)
self.entity_id = "{}.{}_{}".format( self.entity_id = f"{DOMAIN}.hdmi_{hex(self._logical_address)[2:]}"
DOMAIN, "hdmi", hex(self._logical_address)[2:]
)
def turn_on(self, **kwargs) -> None: def turn_on(self, **kwargs) -> None:
"""Turn device on.""" """Turn device on."""

View file

@ -71,7 +71,7 @@ class N26Account(Entity):
@property @property
def name(self) -> str: def name(self) -> str:
"""Friendly name of the sensor.""" """Friendly name of the sensor."""
return "n26_{}".format(self._iban[-4:]) return f"n26_{self._iban[-4:]}"
@property @property
def state(self) -> float: def state(self) -> float:
@ -110,7 +110,7 @@ class N26Account(Entity):
} }
for limit in self._data.limits: for limit in self._data.limits:
limit_attr_name = "limit_{}".format(limit["limit"].lower()) limit_attr_name = f"limit_{limit['limit'].lower()}"
attributes[limit_attr_name] = limit["amount"] attributes[limit_attr_name] = limit["amount"]
return attributes return attributes
@ -143,7 +143,7 @@ class N26Card(Entity):
@property @property
def name(self) -> str: def name(self) -> str:
"""Friendly name of the sensor.""" """Friendly name of the sensor."""
return "{}_card_{}".format(self._account_name.lower(), self._card["id"]) return f"{self._account_name.lower()}_card_{self._card['id']}"
@property @property
def state(self) -> float: def state(self) -> float:
@ -206,9 +206,7 @@ class N26Space(Entity):
@property @property
def unique_id(self): def unique_id(self):
"""Return the unique ID of the entity.""" """Return the unique ID of the entity."""
return "space_{}_{}".format( return f"space_{self._data.balance['iban'][-4:]}_{self._space['name'].lower()}"
self._data.balance["iban"][-4:], self._space["name"].lower()
)
@property @property
def name(self) -> str: def name(self) -> str:

View file

@ -42,7 +42,7 @@ class N26CardSwitch(SwitchDevice):
@property @property
def name(self) -> str: def name(self) -> str:
"""Friendly name of the sensor.""" """Friendly name of the sensor."""
return "card_{}".format(self._card["id"]) return f"card_{self._card['id']}"
@property @property
def is_on(self): def is_on(self):

View file

@ -200,7 +200,7 @@ async def async_setup_entry(hass, entry):
now = datetime.utcnow() now = datetime.utcnow()
trip_id = service.data.get( trip_id = service.data.get(
ATTR_TRIP_ID, "trip_{}".format(int(now.timestamp())) ATTR_TRIP_ID, f"trip_{int(now.timestamp())}"
) )
eta_begin = now + service.data[ATTR_ETA] eta_begin = now + service.data[ATTR_ETA]
eta_window = service.data.get(ATTR_ETA_WINDOW, timedelta(minutes=1)) eta_window = service.data.get(ATTR_ETA_WINDOW, timedelta(minutes=1))
@ -368,15 +368,11 @@ class NestSensorDevice(Entity):
if device is not None: if device is not None:
# device specific # device specific
self.device = device self.device = device
self._name = "{} {}".format( self._name = f"{self.device.name_long} {self.variable.replace('_', ' ')}"
self.device.name_long, self.variable.replace("_", " ")
)
else: else:
# structure only # structure only
self.device = structure self.device = structure
self._name = "{} {}".format( self._name = f"{self.structure.name} {self.variable.replace('_', ' ')}"
self.structure.name, self.variable.replace("_", " ")
)
self._state = None self._state = None
self._unit = None self._unit = None

View file

@ -57,8 +57,8 @@ class AuthPhase:
try: try:
msg = AUTH_MESSAGE_SCHEMA(msg) msg = AUTH_MESSAGE_SCHEMA(msg)
except vol.Invalid as err: except vol.Invalid as err:
error_msg = "Auth message incorrectly formatted: {}".format( error_msg = (
humanize_error(msg, err) f"Auth message incorrectly formatted: {humanize_error(msg, err)}"
) )
self._logger.warning(error_msg) self._logger.warning(error_msg)
self._send_message(auth_invalid_message(error_msg)) self._send_message(auth_invalid_message(error_msg))

View file

@ -233,15 +233,13 @@ def _request_app_setup(hass, config):
start_url = f"{hass.config.api.base_url}{WINK_AUTH_CALLBACK_PATH}" start_url = f"{hass.config.api.base_url}{WINK_AUTH_CALLBACK_PATH}"
description = """Please create a Wink developer app at description = f"""Please create a Wink developer app at
https://developer.wink.com. https://developer.wink.com.
Add a Redirect URI of {}. Add a Redirect URI of {start_url}.
They will provide you a Client ID and secret They will provide you a Client ID and secret
after reviewing your request. after reviewing your request.
(This can take several days). (This can take several days).
""".format( """
start_url
)
hass.data[DOMAIN]["configuring"][DOMAIN] = configurator.request_config( hass.data[DOMAIN]["configuring"][DOMAIN] = configurator.request_config(
DOMAIN, DOMAIN,
@ -351,9 +349,7 @@ def setup(hass, config):
# Home . # Home .
else: else:
redirect_uri = "{}{}".format( redirect_uri = f"{hass.config.api.base_url}{WINK_AUTH_CALLBACK_PATH}"
hass.config.api.base_url, WINK_AUTH_CALLBACK_PATH
)
wink_auth_start_url = pywink.get_authorization_url( wink_auth_start_url = pywink.get_authorization_url(
config_file.get(ATTR_CLIENT_ID), redirect_uri config_file.get(ATTR_CLIENT_ID), redirect_uri

View file

@ -58,8 +58,8 @@ class WirelessTagSensor(WirelessTagBaseSensor):
# sensor.wirelesstag_bedroom_temperature # sensor.wirelesstag_bedroom_temperature
# and not as sensor.bedroom for temperature and # and not as sensor.bedroom for temperature and
# sensor.bedroom_2 for humidity # sensor.bedroom_2 for humidity
self._entity_id = "{}.{}_{}_{}".format( self._entity_id = (
"sensor", WIRELESSTAG_DOMAIN, self.underscored_name, self._sensor_type f"sensor.{WIRELESSTAG_DOMAIN}_{self.underscored_name}_{self._sensor_type}"
) )
async def async_added_to_hass(self): async def async_added_to_hass(self):

View file

@ -57,7 +57,7 @@ class WirelessTagSwitch(WirelessTagBaseSensor, SwitchDevice):
super().__init__(api, tag) super().__init__(api, tag)
self._switch_type = switch_type self._switch_type = switch_type
self.sensor_type = SWITCH_TYPES[self._switch_type][1] self.sensor_type = SWITCH_TYPES[self._switch_type][1]
self._name = "{} {}".format(self._tag.name, SWITCH_TYPES[self._switch_type][0]) self._name = f"{self._tag.name} {SWITCH_TYPES[self._switch_type][0]}"
def turn_on(self, **kwargs): def turn_on(self, **kwargs):
"""Turn on the switch.""" """Turn on the switch."""

View file

@ -320,8 +320,9 @@ class WithingsHealthSensor(Entity):
@property @property
def unique_id(self) -> str: def unique_id(self) -> str:
"""Return a unique, Home Assistant friendly identifier for this entity.""" """Return a unique, Home Assistant friendly identifier for this entity."""
return "withings_{}_{}_{}".format( return (
self._slug, self._user_id, slugify(self._attribute.measurement) f"withings_{self._slug}_{self._user_id}_"
f"{slugify(self._attribute.measurement)}"
) )
@property @property

View file

@ -217,9 +217,9 @@ class WUHourlyForecastSensorConfig(WUSensorConfig):
:param field: field name to use as value :param field: field name to use as value
""" """
super().__init__( super().__init__(
friendly_name=lambda wu: "{} {}".format( friendly_name=lambda wu: (
wu.data["hourly_forecast"][period]["FCTTIME"]["weekday_name_abbrev"], f"{wu.data['hourly_forecast'][period]['FCTTIME']['weekday_name_abbrev']} "
wu.data["hourly_forecast"][period]["FCTTIME"]["civil"], f"{wu.data['hourly_forecast'][period]['FCTTIME']['civil']}"
), ),
feature="hourly", feature="hourly",
value=lambda wu: wu.data["hourly_forecast"][period][field], value=lambda wu: wu.data["hourly_forecast"][period][field],
@ -477,8 +477,9 @@ SENSOR_TYPES = {
"Wind Summary", "wind_string", "mdi:weather-windy" "Wind Summary", "wind_string", "mdi:weather-windy"
), ),
"temp_high_record_c": WUAlmanacSensorConfig( "temp_high_record_c": WUAlmanacSensorConfig(
lambda wu: "High Temperature Record ({})".format( lambda wu: (
wu.data["almanac"]["temp_high"]["recordyear"] f"High Temperature Record "
f"({wu.data['almanac']['temp_high']['recordyear']})"
), ),
"temp_high", "temp_high",
"record", "record",
@ -487,8 +488,9 @@ SENSOR_TYPES = {
"mdi:thermometer", "mdi:thermometer",
), ),
"temp_high_record_f": WUAlmanacSensorConfig( "temp_high_record_f": WUAlmanacSensorConfig(
lambda wu: "High Temperature Record ({})".format( lambda wu: (
wu.data["almanac"]["temp_high"]["recordyear"] f"High Temperature Record "
f"({wu.data['almanac']['temp_high']['recordyear']})"
), ),
"temp_high", "temp_high",
"record", "record",
@ -497,8 +499,9 @@ SENSOR_TYPES = {
"mdi:thermometer", "mdi:thermometer",
), ),
"temp_low_record_c": WUAlmanacSensorConfig( "temp_low_record_c": WUAlmanacSensorConfig(
lambda wu: "Low Temperature Record ({})".format( lambda wu: (
wu.data["almanac"]["temp_low"]["recordyear"] f"Low Temperature Record "
f"({wu.data['almanac']['temp_low']['recordyear']})"
), ),
"temp_low", "temp_low",
"record", "record",
@ -507,8 +510,9 @@ SENSOR_TYPES = {
"mdi:thermometer", "mdi:thermometer",
), ),
"temp_low_record_f": WUAlmanacSensorConfig( "temp_low_record_f": WUAlmanacSensorConfig(
lambda wu: "Low Temperature Record ({})".format( lambda wu: (
wu.data["almanac"]["temp_low"]["recordyear"] f"Low Temperature Record "
f"({wu.data['almanac']['temp_low']['recordyear']})"
), ),
"temp_low", "temp_low",
"record", "record",

View file

@ -136,9 +136,7 @@ class XiaomiCamera(Camera):
else: else:
video = videos[-1] video = videos[-1]
return "ftp://{}:{}@{}:{}{}/{}".format( return f"ftp://{self.user}:{self.passwd}@{host}:{self.port}{ftp.pwd()}/{video}"
self.user, self.passwd, host, self.port, ftp.pwd(), video
)
async def async_camera_image(self): async def async_camera_image(self):
"""Return a still image response from the camera.""" """Return a still image response from the camera."""

View file

@ -242,8 +242,8 @@ class XiaomiDevice(Entity):
self.parse_voltage(device["data"]) self.parse_voltage(device["data"])
if hasattr(self, "_data_key") and self._data_key: # pylint: disable=no-member if hasattr(self, "_data_key") and self._data_key: # pylint: disable=no-member
self._unique_id = "{}{}".format( self._unique_id = (
self._data_key, self._sid # pylint: disable=no-member f"{self._data_key}{self._sid}" # pylint: disable=no-member
) )
else: else:
self._unique_id = f"{self._type}{self._sid}" self._unique_id = f"{self._type}{self._sid}"

View file

@ -718,7 +718,7 @@ class XiaomiPhilipsEyecareLampAmbientLight(XiaomiPhilipsAbstractLight):
"""Initialize the light device.""" """Initialize the light device."""
name = f"{name} Ambient Light" name = f"{name} Ambient Light"
if unique_id is not None: if unique_id is not None:
unique_id = "{}-{}".format(unique_id, "ambient") unique_id = f"{unique_id}-ambient"
super().__init__(name, light, model, unique_id) super().__init__(name, light, model, unique_id)
async def async_turn_on(self, **kwargs): async def async_turn_on(self, **kwargs):

View file

@ -429,7 +429,7 @@ class ChuangMiPlugSwitch(XiaomiPlugGenericSwitch):
name = f"{name} USB" if channel_usb else name name = f"{name} USB" if channel_usb else name
if unique_id is not None and channel_usb: if unique_id is not None and channel_usb:
unique_id = "{}-{}".format(unique_id, "usb") unique_id = f"{unique_id}-usb"
super().__init__(name, plug, model, unique_id) super().__init__(name, plug, model, unique_id)
self._channel_usb = channel_usb self._channel_usb = channel_usb

View file

@ -110,14 +110,9 @@ class YiCamera(Camera):
await ftp.quit() await ftp.quit()
self._is_on = True self._is_on = True
return "ftp://{}:{}@{}:{}{}/{}/{}".format( return (
self.user, f"ftp://{self.user}:{self.passwd}@{self.host}:"
self.passwd, f"{self.port}{self.path}/{latest_dir}/{videos[-1]}"
self.host,
self.port,
self.path,
latest_dir,
videos[-1],
) )
except (ConnectionRefusedError, StatusCodeError) as err: except (ConnectionRefusedError, StatusCodeError) as err:
_LOGGER.error("Error while fetching video: %s", err) _LOGGER.error("Error while fetching video: %s", err)

View file

@ -159,7 +159,7 @@ class ZamgData:
"""The class for handling the data retrieval.""" """The class for handling the data retrieval."""
API_URL = "http://www.zamg.ac.at/ogd/" API_URL = "http://www.zamg.ac.at/ogd/"
API_HEADERS = {USER_AGENT: "{} {}".format("home-assistant.zamg/", __version__)} API_HEADERS = {USER_AGENT: f"home-assistant.zamg/ {__version__}"}
def __init__(self, station_id): def __init__(self, station_id):
"""Initialize the probe.""" """Initialize the probe."""

View file

@ -78,8 +78,9 @@ class ZamgWeather(WeatherEntity):
@property @property
def name(self): def name(self):
"""Return the name of the sensor.""" """Return the name of the sensor."""
return self.stationname or "ZAMG {}".format( return (
self.zamg_data.data.get("Name") or "(unknown station)" self.stationname
or f"ZAMG {self.zamg_data.data.get('Name') or '(unknown station)'}"
) )
@property @property

View file

@ -93,9 +93,7 @@ class ZHADevice(LogMixin):
self._zigpy_device = zigpy_device self._zigpy_device = zigpy_device
self._zha_gateway = zha_gateway self._zha_gateway = zha_gateway
self._available = False self._available = False
self._available_signal = "{}_{}_{}".format( self._available_signal = f"{self.name}_{self.ieee}_{SIGNAL_AVAILABLE}"
self.name, self.ieee, SIGNAL_AVAILABLE
)
self._checkins_missed_count = 0 self._checkins_missed_count = 0
self.unsubs = [] self.unsubs = []
self.unsubs.append( self.unsubs.append(
@ -104,10 +102,11 @@ class ZHADevice(LogMixin):
) )
) )
self.quirk_applied = isinstance(self._zigpy_device, zigpy.quirks.CustomDevice) self.quirk_applied = isinstance(self._zigpy_device, zigpy.quirks.CustomDevice)
self.quirk_class = "{}.{}".format( self.quirk_class = (
self._zigpy_device.__class__.__module__, f"{self._zigpy_device.__class__.__module__}."
self._zigpy_device.__class__.__name__, f"{self._zigpy_device.__class__.__name__}"
) )
if self.is_mains_powered: if self.is_mains_powered:
self._consider_unavailable_time = _CONSIDER_UNAVAILABLE_MAINS self._consider_unavailable_time = _CONSIDER_UNAVAILABLE_MAINS
else: else:
@ -352,9 +351,7 @@ class ZHADevice(LogMixin):
if self._available != available and available: if self._available != available and available:
# Update the state the first time the device comes online # Update the state the first time the device comes online
async_dispatcher_send(self.hass, self._available_signal, False) async_dispatcher_send(self.hass, self._available_signal, False)
async_dispatcher_send( async_dispatcher_send(self.hass, f"{self._available_signal}_entity", available)
self.hass, "{}_{}".format(self._available_signal, "entity"), available
)
self._available = available self._available = available
@property @property

View file

@ -119,7 +119,7 @@ class BaseZhaEntity(RestoreEntity, LogMixin, entity.Entity):
self.remove_future = asyncio.Future() self.remove_future = asyncio.Future()
await self.async_accept_signal( await self.async_accept_signal(
None, None,
"{}_{}".format(SIGNAL_REMOVE, str(self.zha_device.ieee)), f"{SIGNAL_REMOVE}_{self.zha_device.ieee}",
self.async_remove, self.async_remove,
signal_override=True, signal_override=True,
) )
@ -182,7 +182,7 @@ class ZhaEntity(BaseZhaEntity):
await self.async_check_recently_seen() await self.async_check_recently_seen()
await self.async_accept_signal( await self.async_accept_signal(
None, None,
"{}_{}".format(self.zha_device.available_signal, "entity"), f"{self.zha_device.available_signal}_entity",
self.async_set_available, self.async_set_available,
signal_override=True, signal_override=True,
) )

View file

@ -261,7 +261,7 @@ def _obj_to_dict(obj):
def _value_name(value): def _value_name(value):
"""Return the name of the value.""" """Return the name of the value."""
return "{} {}".format(node_name(value.node), value.label).strip() return f"{node_name(value.node)} {value.label}".strip()
def nice_print_node(node): def nice_print_node(node):
@ -826,9 +826,7 @@ async def async_setup_entry(hass, config_entry):
) )
return return
_LOGGER.info( _LOGGER.info(
"Node %s on instance %s does not have resettable meters.", "Node %s on instance %s does not have resettable meters.", node_id, instance
node_id,
instance,
) )
def heal_node(service): def heal_node(service):

View file

@ -337,21 +337,20 @@ class ZwaveLock(ZWaveDeviceEntity, LockDevice):
) )
if alarm_type == 21: if alarm_type == 21:
self._lock_status = "{}{}".format( self._lock_status = (
LOCK_ALARM_TYPE.get(str(alarm_type)), f"{LOCK_ALARM_TYPE.get(str(alarm_type))}"
MANUAL_LOCK_ALARM_LEVEL.get(str(alarm_level)), f"{MANUAL_LOCK_ALARM_LEVEL.get(str(alarm_level))}"
) )
return return
if str(alarm_type) in ALARM_TYPE_STD: if str(alarm_type) in ALARM_TYPE_STD:
self._lock_status = "{}{}".format( self._lock_status = f"{LOCK_ALARM_TYPE.get(str(alarm_type))}{alarm_level}"
LOCK_ALARM_TYPE.get(str(alarm_type)), str(alarm_level)
)
return return
if alarm_type == 161: if alarm_type == 161:
self._lock_status = "{}{}".format( self._lock_status = (
LOCK_ALARM_TYPE.get(str(alarm_type)), f"{LOCK_ALARM_TYPE.get(str(alarm_type))}"
TAMPER_ALARM_LEVEL.get(str(alarm_level)), f"{TAMPER_ALARM_LEVEL.get(str(alarm_level))}"
) )
return return
if alarm_type != 0: if alarm_type != 0:
self._lock_status = LOCK_ALARM_TYPE.get(str(alarm_type)) self._lock_status = LOCK_ALARM_TYPE.get(str(alarm_type))

View file

@ -116,10 +116,9 @@ def _no_duplicate_auth_provider(
key = (config[CONF_TYPE], config.get(CONF_ID)) key = (config[CONF_TYPE], config.get(CONF_ID))
if key in config_keys: if key in config_keys:
raise vol.Invalid( raise vol.Invalid(
"Duplicate auth provider {} found. Please add unique IDs if " f"Duplicate auth provider {config[CONF_TYPE]} found. "
"you want to have the same auth provider twice".format( "Please add unique IDs "
config[CONF_TYPE] "if you want to have the same auth provider twice"
)
) )
config_keys.add(key) config_keys.add(key)
return configs return configs
@ -140,8 +139,9 @@ def _no_duplicate_auth_mfa_module(
key = config.get(CONF_ID, config[CONF_TYPE]) key = config.get(CONF_ID, config[CONF_TYPE])
if key in config_keys: if key in config_keys:
raise vol.Invalid( raise vol.Invalid(
"Duplicate mfa module {} found. Please add unique IDs if " f"Duplicate mfa module {config[CONF_TYPE]} found. "
"you want to have the same mfa module twice".format(config[CONF_TYPE]) "Please add unique IDs "
"if you want to have the same mfa module twice"
) )
config_keys.add(key) config_keys.add(key)
return configs return configs
@ -319,8 +319,9 @@ def load_yaml_config_file(config_path: str) -> Dict[Any, Any]:
conf_dict = load_yaml(config_path) conf_dict = load_yaml(config_path)
if not isinstance(conf_dict, dict): if not isinstance(conf_dict, dict):
msg = "The configuration file {} does not contain a dictionary".format( msg = (
os.path.basename(config_path) f"The configuration file {os.path.basename(config_path)} "
"does not contain a dictionary"
) )
_LOGGER.error(msg) _LOGGER.error(msg)
raise HomeAssistantError(msg) raise HomeAssistantError(msg)
@ -415,16 +416,13 @@ def _format_config_error(
message = f"Invalid config for [{domain}]: " message = f"Invalid config for [{domain}]: "
if isinstance(ex, vol.Invalid): if isinstance(ex, vol.Invalid):
if "extra keys not allowed" in ex.error_message: if "extra keys not allowed" in ex.error_message:
path = "->".join(str(m) for m in ex.path)
message += ( message += (
"[{option}] is an invalid option for [{domain}]. " f"[{ex.path[-1]}] is an invalid option for [{domain}]. "
"Check: {domain}->{path}.".format( f"Check: {domain}->{path}."
option=ex.path[-1],
domain=domain,
path="->".join(str(m) for m in ex.path),
)
) )
else: else:
message += "{}.".format(humanize_error(config, ex)) message += f"{humanize_error(config, ex)}."
else: else:
message += str(ex) message += str(ex)
@ -433,9 +431,9 @@ def _format_config_error(
except AttributeError: except AttributeError:
domain_config = config domain_config = config
message += " (See {}, line {}). ".format( message += (
getattr(domain_config, "__config_file__", "?"), f" (See {getattr(domain_config, '__config_file__', '?')}, "
getattr(domain_config, "__line__", "?"), f"line {getattr(domain_config, '__line__', '?')}). "
) )
if domain != CONF_CORE and link: if domain != CONF_CORE and link:
@ -551,9 +549,9 @@ def _log_pkg_error(package: str, component: str, config: Dict, message: str) ->
message = f"Package {package} setup failed. Integration {component} {message}" message = f"Package {package} setup failed. Integration {component} {message}"
pack_config = config[CONF_CORE][CONF_PACKAGES].get(package, config) pack_config = config[CONF_CORE][CONF_PACKAGES].get(package, config)
message += " (See {}:{}). ".format( message += (
getattr(pack_config, "__config_file__", "?"), f" (See {getattr(pack_config, '__config_file__', '?')}:"
getattr(pack_config, "__line__", "?"), f"{getattr(pack_config, '__line__', '?')}). "
) )
_LOGGER.error(message) _LOGGER.error(message)

View file

@ -179,9 +179,7 @@ class FlowManager(abc.ABC):
RESULT_TYPE_ABORT, RESULT_TYPE_ABORT,
RESULT_TYPE_EXTERNAL_STEP_DONE, RESULT_TYPE_EXTERNAL_STEP_DONE,
): ):
raise ValueError( raise ValueError(f"Handler returned incorrect type: {result['type']}")
"Handler returned incorrect type: {}".format(result["type"])
)
if result["type"] in ( if result["type"] in (
RESULT_TYPE_FORM, RESULT_TYPE_FORM,

View file

@ -146,7 +146,7 @@ class EntityRegistry:
Conflicts checked against registered and currently existing entities. Conflicts checked against registered and currently existing entities.
""" """
return ensure_unique_string( return ensure_unique_string(
"{}.{}".format(domain, slugify(suggested_object_id)), f"{domain}.{slugify(suggested_object_id)}",
chain( chain(
self.entities.keys(), self.entities.keys(),
self.hass.states.async_entity_ids(domain), self.hass.states.async_entity_ids(domain),

View file

@ -425,8 +425,7 @@ def _load_file(
if str(err) not in white_listed_errors: if str(err) not in white_listed_errors:
_LOGGER.exception( _LOGGER.exception(
("Error loading %s. Make sure all dependencies are installed"), ("Error loading %s. Make sure all dependencies are installed"), path
path,
) )
return None return None

View file

@ -233,9 +233,7 @@ def line_info(obj, **kwargs):
"""Display line config source.""" """Display line config source."""
if hasattr(obj, "__config_file__"): if hasattr(obj, "__config_file__"):
return color( return color(
"cyan", "cyan", f"[source {obj.__config_file__}:{obj.__line__ or '?'}]", **kwargs
"[source {}:{}]".format(obj.__config_file__, obj.__line__ or "?"),
**kwargs,
) )
return "?" return "?"

View file

@ -54,9 +54,8 @@ def _yaml_unsupported(
constructor: ExtSafeConstructor, node: ruamel.yaml.nodes.Node constructor: ExtSafeConstructor, node: ruamel.yaml.nodes.Node
) -> None: ) -> None:
raise UnsupportedYamlError( raise UnsupportedYamlError(
"Unsupported YAML, you can not use {} in {}".format( f"Unsupported YAML, you can not use {node.tag} in "
node.tag, os.path.basename(constructor.name or "(None)") f"{os.path.basename(constructor.name or '(None)')}"
)
) )

View file

@ -182,7 +182,7 @@ def gather_requirements_from_modules(errors, reqs):
try: try:
module = importlib.import_module(package) module = importlib.import_module(package)
except ImportError as err: except ImportError as err:
print("{}.py: {}".format(package.replace(".", "/"), err)) print(f"{package.replace('.', '/')}.py: {err}")
errors.append(package) errors.append(package)
continue continue

View file

@ -48,9 +48,7 @@ def generate_and_validate(integrations: Dict[str, Integration]):
"codeowners", "Code owners need to be valid GitHub handles." "codeowners", "Code owners need to be valid GitHub handles."
) )
parts.append( parts.append(f"homeassistant/components/{domain}/* {' '.join(codeowners)}")
"homeassistant/components/{}/* {}".format(domain, " ".join(codeowners))
)
parts.append(f"\n{INDIVIDUAL_FILES.strip()}") parts.append(f"\n{INDIVIDUAL_FILES.strip()}")

View file

@ -12,12 +12,7 @@ DOCUMENTATION_URL_HOST = "www.home-assistant.io"
DOCUMENTATION_URL_PATH_PREFIX = "/integrations/" DOCUMENTATION_URL_PATH_PREFIX = "/integrations/"
DOCUMENTATION_URL_EXCEPTIONS = ["https://www.home-assistant.io/hassio"] DOCUMENTATION_URL_EXCEPTIONS = ["https://www.home-assistant.io/hassio"]
SUPPORTED_QUALITY_SCALES = [ SUPPORTED_QUALITY_SCALES = ["gold", "internal", "platinum", "silver"]
"gold",
"internal",
"platinum",
"silver",
]
def documentation_url(value: str) -> str: def documentation_url(value: str) -> str:
@ -68,8 +63,7 @@ def validate_manifest(integration: Integration):
MANIFEST_SCHEMA(integration.manifest) MANIFEST_SCHEMA(integration.manifest)
except vol.Invalid as err: except vol.Invalid as err:
integration.add_error( integration.add_error(
"manifest", "manifest", f"Invalid manifest: {humanize_error(integration.manifest, err)}"
"Invalid manifest: {}".format(humanize_error(integration.manifest, err)),
) )
integration.manifest = None integration.manifest = None
return return

View file

@ -79,7 +79,7 @@ def validate_services(integration: Integration):
SERVICES_SCHEMA(data) SERVICES_SCHEMA(data)
except vol.Invalid as err: except vol.Invalid as err:
integration.add_error( integration.add_error(
"services", "Invalid services.yaml: {}".format(humanize_error(data, err)) "services", f"Invalid services.yaml: {humanize_error(data, err)}"
) )

View file

@ -79,8 +79,8 @@ def generate_and_validate(integrations: Dict[str, Integration]):
if model in homekit_dict: if model in homekit_dict:
integration.add_error( integration.add_error(
"zeroconf", "zeroconf",
"Integrations {} and {} have overlapping HomeKit " f"Integrations {domain} and {homekit_dict[model]} "
"models".format(domain, homekit_dict[model]), "have overlapping HomeKit models",
) )
break break
@ -100,8 +100,8 @@ def generate_and_validate(integrations: Dict[str, Integration]):
if key.startswith(key_2) or key_2.startswith(key): if key.startswith(key_2) or key_2.startswith(key):
integration.add_error( integration.add_error(
"zeroconf", "zeroconf",
"Integrations {} and {} have overlapping HomeKit " f"Integrations {homekit_dict[key]} and {homekit_dict[key_2]} "
"models".format(homekit_dict[key], homekit_dict[key_2]), "have overlapping HomeKit models",
) )
warned.add(key) warned.add(key)
warned.add(key_2) warned.add(key_2)

View file

@ -61,7 +61,7 @@ async def async_exec(*args, display=False):
argsp = [] argsp = []
for arg in args: for arg in args:
if os.path.isfile(arg): if os.path.isfile(arg):
argsp.append("\\\n {}".format(shlex.quote(arg))) argsp.append(f"\\\n {shlex.quote(arg)}")
else: else:
argsp.append(shlex.quote(arg)) argsp.append(shlex.quote(arg))
printc("cyan", *argsp) printc("cyan", *argsp)
@ -75,9 +75,7 @@ async def async_exec(*args, display=False):
kwargs["stderr"] = asyncio.subprocess.PIPE kwargs["stderr"] = asyncio.subprocess.PIPE
proc = await asyncio.create_subprocess_exec(*args, **kwargs) proc = await asyncio.create_subprocess_exec(*args, **kwargs)
except FileNotFoundError as err: except FileNotFoundError as err:
printc( printc(FAIL, f"Could not execute {args[0]}. Did you install test requirements?")
FAIL, f"Could not execute {args[0]}. Did you install test requirements?",
)
raise err raise err
if not display: if not display: