diff --git a/starlette/authentication.py b/starlette/authentication.py index 9aa4ca5e..95a65473 100644 --- a/starlette/authentication.py +++ b/starlette/authentication.py @@ -28,7 +28,7 @@ def requires( if parameter.name == "request": break else: - raise Exception('No "request" argument on function "%s"' % func) + raise Exception(f'No "request" argument on function "{func}"') if asyncio.iscoroutinefunction(func): diff --git a/starlette/config.py b/starlette/config.py index 03571bdf..295b3efe 100644 --- a/starlette/config.py +++ b/starlette/config.py @@ -23,16 +23,14 @@ class Environ(MutableMapping): def __setitem__(self, key: typing.Any, value: typing.Any) -> None: if key in self._has_been_read: raise EnvironError( - "Attempting to set environ['%s'], but the value has already be read." - % key + f"Attempting to set environ['{key}'], but the value has already be read." ) self._environ.__setitem__(key, value) def __delitem__(self, key: typing.Any) -> None: if key in self._has_been_read: raise EnvironError( - "Attempting to delete environ['%s'], but the value has already be read." - % key + f"Attempting to delete environ['{key}'], but the value has already be read." ) self._environ.__delitem__(key) @@ -71,7 +69,7 @@ class Config: return self._perform_cast(key, value, cast) if default is not undefined: return self._perform_cast(key, default, cast) - raise KeyError("Config '%s' is missing, and has no default." % key) + raise KeyError(f"Config '{key}' is missing, and has no default.") def _read_file(self, file_name: str) -> typing.Dict[str, str]: file_values = {} # type: typing.Dict[str, str] @@ -95,13 +93,12 @@ class Config: value = value.lower() if value not in mapping: raise ValueError( - "Config '%s' has value '%s'. Not a valid bool." % (key, value) + f"Config '{key}' has value '{value}'. Not a valid bool." ) return mapping[value] try: return cast(value) except (TypeError, ValueError) as exc: raise ValueError( - "Config '%s' has value '%s'. Not a valid %s." - % (key, value, cast.__name__) + f"Config '{key}' has value '{value}'. Not a valid {cast.__name__}." ) diff --git a/starlette/datastructures.py b/starlette/datastructures.py index 70f5614b..510dbcd1 100644 --- a/starlette/datastructures.py +++ b/starlette/datastructures.py @@ -28,16 +28,16 @@ class URL: break if host_header is not None: - url = "%s://%s%s" % (scheme, host_header, path) + url = f"{scheme}://{host_header}{path}" elif server is None: url = path else: host, port = server default_port = {"http": 80, "https": 443, "ws": 80, "wss": 443}[scheme] if port == default_port: - url = "%s://%s%s" % (scheme, host, path) + url = f"{scheme}://{host}{path}" else: - url = "%s://%s:%s%s" % (scheme, host, port, path) + url = f"{scheme}://{host}:{port}{path}" if query_string: url += "?" + query_string.decode() @@ -111,12 +111,12 @@ class URL: netloc = hostname if port is not None: - netloc += ":%d" % port + netloc += f":{port}" if username is not None: userpass = username if password is not None: - userpass += ":%s" % password - netloc = "%s@%s" % (userpass, netloc) + userpass += f":{password}" + netloc = f"{userpass}@{netloc}" kwargs["netloc"] = netloc @@ -133,7 +133,7 @@ class URL: url = str(self) if self.password: url = str(self.replace(password="********")) - return "%s(%s)" % (self.__class__.__name__, repr(url)) + return f"{self.__class__.__name__}({repr(url)})" class DatabaseURL(URL): @@ -191,7 +191,7 @@ class Secret: self._value = value def __repr__(self) -> str: - return "%s('**********')" % self.__class__.__name__ + return f"{self.__class__.__name__}('**********')" def __str__(self) -> str: return self._value @@ -218,7 +218,7 @@ class CommaSeparatedStrings(Sequence): def __repr__(self) -> str: list_repr = repr([item for item in self]) - return "%s(%s)" % (self.__class__.__name__, list_repr) + return f"{self.__class__.__name__}({list_repr})" def __str__(self) -> str: return ", ".join([repr(item) for item in self]) @@ -294,7 +294,7 @@ class QueryParams(typing.Mapping[str, str]): return urlencode(self._list) def __repr__(self) -> str: - return "%s(query_string=%s)" % (self.__class__.__name__, repr(str(self))) + return f"{self.__class__.__name__}(query_string={repr(str(self))})" class Headers(typing.Mapping[str, str]): @@ -383,8 +383,8 @@ class Headers(typing.Mapping[str, str]): def __repr__(self) -> str: as_dict = dict(self.items()) if len(as_dict) == len(self): - return "%s(%s)" % (self.__class__.__name__, repr(as_dict)) - return "%s(raw=%s)" % (self.__class__.__name__, repr(self.raw)) + return f"{self.__class__.__name__}({repr(as_dict)})" + return f"{self.__class__.__name__}(raw={repr(self.raw)})" class MutableHeaders(Headers): diff --git a/starlette/middleware/wsgi.py b/starlette/middleware/wsgi.py index eedb144b..182ce3b8 100644 --- a/starlette/middleware/wsgi.py +++ b/starlette/middleware/wsgi.py @@ -16,7 +16,7 @@ def build_environ(scope: Scope, body: bytes) -> dict: "SCRIPT_NAME": "", "PATH_INFO": scope["path"], "QUERY_STRING": scope["query_string"].decode("ascii"), - "SERVER_PROTOCOL": "HTTP/%s" % scope["http_version"], + "SERVER_PROTOCOL": f"HTTP/{scope['http_version']}", "wsgi.version": (1, 0), "wsgi.url_scheme": scope.get("scheme", "http"), "wsgi.input": io.BytesIO(body), @@ -43,7 +43,7 @@ def build_environ(scope: Scope, body: bytes) -> dict: elif name == "content-type": corrected_name = "CONTENT_TYPE" else: - corrected_name = "HTTP_%s" % name.upper().replace("-", "_") + corrected_name = f"HTTP_{name}".upper().replace("-", "_") # HTTPbis say only ASCII chars are allowed in headers, but we latin1 just in case value = value.decode("latin1") if corrected_name in environ: diff --git a/starlette/routing.py b/starlette/routing.py index 10598e58..893bbf19 100644 --- a/starlette/routing.py +++ b/starlette/routing.py @@ -103,13 +103,13 @@ def compile_path( for match in PARAM_REGEX.finditer(path): param_name, convertor_type = match.groups("str") convertor_type = convertor_type.lstrip(":") - assert convertor_type in CONVERTOR_TYPES, ( - "Unknown path convertor '%s'" % convertor_type - ) + assert ( + convertor_type in CONVERTOR_TYPES + ), f"Unknown path convertor '{convertor_type}'" convertor = CONVERTOR_TYPES[convertor_type] path_regex += path[idx : match.start()] - path_regex += "(?P<%s>%s)" % (param_name, convertor.regex) + path_regex += f"(?P<{param_name}>{convertor.regex})" path_format += path[idx : match.start()] path_format += "{%s}" % param_name @@ -146,7 +146,7 @@ class Route(BaseRoute): *, methods: typing.List[str] = None, name: str = None, - include_in_schema: bool = True + include_in_schema: bool = True, ) -> None: assert path.startswith("/"), "Routed paths must start with '/'" self.path = path diff --git a/starlette/staticfiles.py b/starlette/staticfiles.py index 8e5e3102..d712b551 100644 --- a/starlette/staticfiles.py +++ b/starlette/staticfiles.py @@ -22,7 +22,7 @@ NOT_MODIFIED_HEADERS = ( class StaticFiles: def __init__(self, *, directory: str, check_dir: bool = True) -> None: if check_dir and not os.path.isdir(directory): - raise RuntimeError("Directory '%s' does not exist" % directory) + raise RuntimeError(f"Directory '{directory}' does not exist") self.directory = directory self.config_checked = False @@ -58,9 +58,9 @@ class _StaticFilesResponder: try: stat_result = await aio_stat(directory) except FileNotFoundError: - raise RuntimeError("StaticFiles directory '%s' does not exist." % directory) + raise RuntimeError(f"StaticFiles directory '{directory}' does not exist.") if not (stat.S_ISDIR(stat_result.st_mode) or stat.S_ISLNK(stat_result.st_mode)): - raise RuntimeError("StaticFiles path '%s' is not a directory." % directory) + raise RuntimeError(f"StaticFiles path '{directory}' is not a directory.") def is_not_modified(self, stat_headers: typing.Dict[str, str]) -> bool: etag = stat_headers["etag"] diff --git a/starlette/testclient.py b/starlette/testclient.py index dd3f2976..daac0700 100644 --- a/starlette/testclient.py +++ b/starlette/testclient.py @@ -86,7 +86,7 @@ class _ASGIAdapter(requests.adapters.HTTPAdapter): elif port == default_port: headers = [(b"host", host.encode())] else: - headers = [(b"host", ("%s:%d" % (host, port)).encode())] + headers = [(b"host", (f"{host}:{port}").encode())] # Include other request headers. headers += [ diff --git a/tests/test_applications.py b/tests/test_applications.py index 925bbd4f..0a0345d1 100644 --- a/tests/test_applications.py +++ b/tests/test_applications.py @@ -70,7 +70,7 @@ def all_users_page(request): @users.route("/{username}") def user_page(request): username = request.path_params["username"] - return PlainTextResponse("Hello, %s!" % username) + return PlainTextResponse(f"Hello, {username}!") app.mount("/users", users) diff --git a/tests/test_responses.py b/tests/test_responses.py index 2c056ed5..1c0e5a99 100644 --- a/tests/test_responses.py +++ b/tests/test_responses.py @@ -255,7 +255,7 @@ def test_template_response(): self.name = name def render(self, context): - return "username: %s" % context["username"] + return f"username: {context['username']}" async def asgi(receive, send): template = Template("index.html") diff --git a/tests/test_routing.py b/tests/test_routing.py index 712b796e..aebb7297 100644 --- a/tests/test_routing.py +++ b/tests/test_routing.py @@ -90,7 +90,7 @@ async def websocket_endpoint(session): @app.websocket_route("/ws/{room}") async def websocket_params(session): await session.accept() - await session.send_text("Hello, %s!" % session.path_params["room"]) + await session.send_text(f"Hello, {session.path_params['room']}!") await session.close() @@ -208,7 +208,7 @@ def test_router_add_websocket_route(): def http_endpoint(request): url = request.url_for("http_endpoint") - return Response("URL: %s" % url, media_type="text/plain") + return Response(f"URL: {url}", media_type="text/plain") class WebsocketEndpoint: