use ruff linter and formatter

This commit is contained in:
David Lord 2023-11-09 09:20:27 -08:00
parent 6edfd78c9f
commit 54ff9b2972
No known key found for this signature in database
GPG key ID: 7A1C87E3F5BC42A8
26 changed files with 99 additions and 128 deletions

25
.flake8
View file

@ -1,25 +0,0 @@
[flake8]
extend-select =
# bugbear
B
# bugbear opinions
B9
# implicit str concat
ISC
extend-ignore =
# slice notation whitespace, invalid
E203
# line length, handled by bugbear B950
E501
# bare except, handled by bugbear B001
E722
# zip with strict=, requires python >= 3.10
B905
# string formatting opinion, B028 renamed to B907
B028
B907
# up to 88 allowed by bugbear B950
max-line-length = 80
per-file-ignores =
# __init__ exports names
src/flask/__init__.py: F401

29
.gitignore vendored
View file

@ -1,25 +1,10 @@
.DS_Store
.env
.flaskenv
*.pyc
*.pyo
env/
venv/
.venv/
env*
dist/
build/
*.egg
*.egg-info/
.tox/
.cache/
.pytest_cache/
.idea/ .idea/
docs/_build/ .vscode/
.vscode __pycache__/
.tox/
# Coverage reports
htmlcov/
.coverage .coverage
.coverage.* .coverage.*
*,cover htmlcov/
docs/_build/
dist/
venv/

View file

@ -1,33 +1,16 @@
ci: ci:
autoupdate_branch: "2.3.x"
autoupdate_schedule: monthly autoupdate_schedule: monthly
repos: repos:
- repo: https://github.com/asottile/pyupgrade - repo: https://github.com/astral-sh/ruff-pre-commit
rev: v3.10.1 rev: v0.1.3
hooks: hooks:
- id: pyupgrade - id: ruff
args: ["--py38-plus"] - id: ruff-format
- repo: https://github.com/asottile/reorder-python-imports
rev: v3.10.0
hooks:
- id: reorder-python-imports
name: Reorder Python imports (src, tests)
files: "^(?!examples/)"
args: ["--application-directories", "src"]
- repo: https://github.com/psf/black
rev: 23.7.0
hooks:
- id: black
- repo: https://github.com/PyCQA/flake8
rev: 6.1.0
hooks:
- id: flake8
additional_dependencies:
- flake8-bugbear
- flake8-implicit-str-concat
- repo: https://github.com/pre-commit/pre-commit-hooks - repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0 rev: v4.4.0
hooks: hooks:
- id: check-merge-conflict
- id: debug-statements
- id: fix-byte-order-marker - id: fix-byte-order-marker
- id: trailing-whitespace - id: trailing-whitespace
- id: end-of-file-fixer - id: end-of-file-fixer

View file

@ -12,3 +12,6 @@ build-backend = "flit_core.buildapi"
[tool.flit.module] [tool.flit.module]
name = "task_app" name = "task_app"
[tool.ruff]
src = ["src"]

View file

@ -2,7 +2,7 @@ from flask import jsonify
from flask import render_template from flask import render_template
from flask import request from flask import request
from js_example import app from . import app
@app.route("/", defaults={"js": "fetch"}) @app.route("/", defaults={"js": "fetch"})

View file

@ -27,3 +27,6 @@ filterwarnings = ["error"]
[tool.coverage.run] [tool.coverage.run]
branch = true branch = true
source = ["js_example", "tests"] source = ["js_example", "tests"]
[tool.ruff]
src = ["src"]

View file

@ -31,12 +31,13 @@ def create_app(test_config=None):
return "Hello, World!" return "Hello, World!"
# register the database commands # register the database commands
from flaskr import db from . import db
db.init_app(app) db.init_app(app)
# apply the blueprints to the app # apply the blueprints to the app
from flaskr import auth, blog from . import auth
from . import blog
app.register_blueprint(auth.bp) app.register_blueprint(auth.bp)
app.register_blueprint(blog.bp) app.register_blueprint(blog.bp)

View file

@ -11,7 +11,7 @@ from flask import url_for
from werkzeug.security import check_password_hash from werkzeug.security import check_password_hash
from werkzeug.security import generate_password_hash from werkzeug.security import generate_password_hash
from flaskr.db import get_db from .db import get_db
bp = Blueprint("auth", __name__, url_prefix="/auth") bp = Blueprint("auth", __name__, url_prefix="/auth")

View file

@ -7,8 +7,8 @@ from flask import request
from flask import url_for from flask import url_for
from werkzeug.exceptions import abort from werkzeug.exceptions import abort
from flaskr.auth import login_required from .auth import login_required
from flaskr.db import get_db from .db import get_db
bp = Blueprint("blog", __name__) bp = Blueprint("blog", __name__)

View file

@ -34,3 +34,6 @@ filterwarnings = ["error"]
[tool.coverage.run] [tool.coverage.run]
branch = true branch = true
source = ["flaskr", "tests"] source = ["flaskr", "tests"]
[tool.ruff]
src = ["src"]

View file

@ -106,3 +106,24 @@ module = [
"importlib_metadata", "importlib_metadata",
] ]
ignore_missing_imports = true ignore_missing_imports = true
[tool.ruff]
src = ["src"]
fix = true
show-fixes = true
show-source = true
[tool.ruff.lint]
select = [
"B", # flake8-bugbear
"E", # pycodestyle error
"F", # pyflakes
"I", # isort
"UP", # pyupgrade
"W", # pycodestyle warning
]
ignore-init-module-imports = true
[tool.ruff.lint.isort]
force-single-line = true
order-by-type = false

View file

@ -1183,7 +1183,8 @@ class Flask(App):
# class to the correct type # class to the correct type
try: try:
rv = self.response_class.force_type( rv = self.response_class.force_type(
rv, request.environ # type: ignore[arg-type] rv, # type: ignore[arg-type]
request.environ,
) )
except TypeError as e: except TypeError as e:
raise TypeError( raise TypeError(
@ -1272,7 +1273,8 @@ class Flask(App):
return response return response
def do_teardown_request( def do_teardown_request(
self, exc: BaseException | None = _sentinel # type: ignore self,
exc: BaseException | None = _sentinel, # type: ignore[assignment]
) -> None: ) -> None:
"""Called after the request is dispatched and the response is """Called after the request is dispatched and the response is
returned, right before the request context is popped. returned, right before the request context is popped.
@ -1305,7 +1307,8 @@ class Flask(App):
request_tearing_down.send(self, _async_wrapper=self.ensure_sync, exc=exc) request_tearing_down.send(self, _async_wrapper=self.ensure_sync, exc=exc)
def do_teardown_appcontext( def do_teardown_appcontext(
self, exc: BaseException | None = _sentinel # type: ignore self,
exc: BaseException | None = _sentinel, # type: ignore[assignment]
) -> None: ) -> None:
"""Called right before the application context is popped. """Called right before the application context is popped.

View file

@ -21,6 +21,7 @@ from .signals import message_flashed
if t.TYPE_CHECKING: # pragma: no cover if t.TYPE_CHECKING: # pragma: no cover
from werkzeug.wrappers import Response as BaseResponse from werkzeug.wrappers import Response as BaseResponse
from .wrappers import Response from .wrappers import Response
@ -48,9 +49,7 @@ def get_load_dotenv(default: bool = True) -> bool:
def stream_with_context( def stream_with_context(
generator_or_function: ( generator_or_function: t.Iterator[t.AnyStr] | t.Callable[..., t.Iterator[t.AnyStr]]
t.Iterator[t.AnyStr] | t.Callable[..., t.Iterator[t.AnyStr]]
)
) -> t.Iterator[t.AnyStr]: ) -> t.Iterator[t.AnyStr]:
"""Request contexts disappear when the response is started on the server. """Request contexts disappear when the response is started on the server.
This is done for efficiency reasons and to make it less likely to encounter This is done for efficiency reasons and to make it less likely to encounter

View file

@ -134,9 +134,7 @@ class DefaultJSONProvider(JSONProvider):
method) will call the ``__html__`` method to get a string. method) will call the ``__html__`` method to get a string.
""" """
default: t.Callable[[t.Any], t.Any] = staticmethod( default: t.Callable[[t.Any], t.Any] = staticmethod(_default) # type: ignore[assignment]
_default
) # type: ignore[assignment]
"""Apply this function to any object that :meth:`json.dumps` does """Apply this function to any object that :meth:`json.dumps` does
not know how to serialize. It should return a valid JSON type or not know how to serialize. It should return a valid JSON type or
raise a ``TypeError``. raise a ``TypeError``.

View file

@ -35,9 +35,10 @@ from .scaffold import setupmethod
if t.TYPE_CHECKING: # pragma: no cover if t.TYPE_CHECKING: # pragma: no cover
from werkzeug.wrappers import Response as BaseResponse from werkzeug.wrappers import Response as BaseResponse
from .blueprints import Blueprint
from ..testing import FlaskClient from ..testing import FlaskClient
from ..testing import FlaskCliRunner from ..testing import FlaskCliRunner
from .blueprints import Blueprint
T_shell_context_processor = t.TypeVar( T_shell_context_processor = t.TypeVar(
"T_shell_context_processor", bound=ft.ShellContextProcessorCallable "T_shell_context_processor", bound=ft.ShellContextProcessorCallable
@ -905,7 +906,9 @@ class App(Scaffold):
Moved from ``flask.redirect``, which calls this method. Moved from ``flask.redirect``, which calls this method.
""" """
return _wz_redirect( return _wz_redirect(
location, code=code, Response=self.response_class # type: ignore[arg-type] location,
code=code,
Response=self.response_class, # type: ignore[arg-type]
) )
def inject_url_defaults(self, endpoint: str, values: dict) -> None: def inject_url_defaults(self, endpoint: str, values: dict) -> None:

View file

@ -14,7 +14,8 @@ from .json.tag import TaggedJSONSerializer
if t.TYPE_CHECKING: # pragma: no cover if t.TYPE_CHECKING: # pragma: no cover
from .app import Flask from .app import Flask
from .wrappers import Request, Response from .wrappers import Request
from .wrappers import Response
class SessionMixin(MutableMapping): class SessionMixin(MutableMapping):

View file

@ -6,7 +6,6 @@ from . import typing as ft
from .globals import current_app from .globals import current_app
from .globals import request from .globals import request
http_method_funcs = frozenset( http_method_funcs = frozenset(
["get", "post", "head", "options", "delete", "put", "trace", "patch"] ["get", "post", "head", "options", "delete", "put", "trace", "patch"]
) )

View file

@ -196,17 +196,14 @@ def test_clean_pop(app):
@app.teardown_request @app.teardown_request
def teardown_req(error=None): def teardown_req(error=None):
1 / 0 raise ZeroDivisionError
@app.teardown_appcontext @app.teardown_appcontext
def teardown_app(error=None): def teardown_app(error=None):
called.append("TEARDOWN") called.append("TEARDOWN")
try: with app.app_context():
with app.test_request_context(): called.append(flask.current_app.name)
called.append(flask.current_app.name)
except ZeroDivisionError:
pass
assert called == ["flask_test", "TEARDOWN"] assert called == ["flask_test", "TEARDOWN"]
assert not flask.current_app assert not flask.current_app

View file

@ -1,4 +1,3 @@
from flask import Module from flask import Module
mod = Module(__name__, "foo", subdomain="foo") mod = Module(__name__, "foo", subdomain="foo")

View file

@ -19,7 +19,6 @@ from werkzeug.routing import RequestRedirect
import flask import flask
require_cpython_gc = pytest.mark.skipif( require_cpython_gc = pytest.mark.skipif(
python_implementation() != "CPython", python_implementation() != "CPython",
reason="Requires CPython GC behavior", reason="Requires CPython GC behavior",
@ -190,7 +189,8 @@ def test_url_mapping(app, client):
def test_werkzeug_routing(app, client): def test_werkzeug_routing(app, client):
from werkzeug.routing import Submount, Rule from werkzeug.routing import Rule
from werkzeug.routing import Submount
app.url_map.add( app.url_map.add(
Submount("/foo", [Rule("/bar", endpoint="bar"), Rule("/", endpoint="index")]) Submount("/foo", [Rule("/bar", endpoint="bar"), Rule("/", endpoint="index")])
@ -210,7 +210,8 @@ def test_werkzeug_routing(app, client):
def test_endpoint_decorator(app, client): def test_endpoint_decorator(app, client):
from werkzeug.routing import Submount, Rule from werkzeug.routing import Rule
from werkzeug.routing import Submount
app.url_map.add( app.url_map.add(
Submount("/foo", [Rule("/bar", endpoint="bar"), Rule("/", endpoint="index")]) Submount("/foo", [Rule("/bar", endpoint="bar"), Rule("/", endpoint="index")])
@ -431,9 +432,9 @@ def test_session_special_types(app, client):
client.get("/") client.get("/")
s = flask.session s = flask.session
assert s["t"] == (1, 2, 3) assert s["t"] == (1, 2, 3)
assert type(s["b"]) is bytes assert type(s["b"]) is bytes # noqa: E721
assert s["b"] == b"\xff" assert s["b"] == b"\xff"
assert type(s["m"]) is Markup assert type(s["m"]) is Markup # noqa: E721
assert s["m"] == Markup("<html>") assert s["m"] == Markup("<html>")
assert s["u"] == the_uuid assert s["u"] == the_uuid
assert s["d"] == now assert s["d"] == now
@ -784,7 +785,7 @@ def test_teardown_request_handler_error(app, client):
@app.route("/") @app.route("/")
def fails(): def fails():
1 // 0 raise ZeroDivisionError
rv = client.get("/") rv = client.get("/")
assert rv.status_code == 500 assert rv.status_code == 500
@ -851,7 +852,7 @@ def test_error_handling(app, client):
@app.route("/error") @app.route("/error")
def error(): def error():
1 // 0 raise ZeroDivisionError
@app.route("/forbidden") @app.route("/forbidden")
def error2(): def error2():
@ -877,7 +878,7 @@ def test_error_handling_processing(app, client):
@app.route("/") @app.route("/")
def broken_func(): def broken_func():
1 // 0 raise ZeroDivisionError
@app.after_request @app.after_request
def after_request(resp): def after_request(resp):
@ -1047,12 +1048,13 @@ def test_error_handler_after_processor_error(app, client):
@app.before_request @app.before_request
def before_request(): def before_request():
if _trigger == "before": if _trigger == "before":
1 // 0 raise ZeroDivisionError
@app.after_request @app.after_request
def after_request(response): def after_request(response):
if _trigger == "after": if _trigger == "after":
1 // 0 raise ZeroDivisionError
return response return response
@app.route("/") @app.route("/")
@ -1507,7 +1509,7 @@ def test_exception_propagation(app, client, key):
@app.route("/") @app.route("/")
def index(): def index():
1 // 0 raise ZeroDivisionError
if key is not None: if key is not None:
app.config[key] = True app.config[key] = True

View file

@ -634,10 +634,11 @@ def test_add_template_test_with_name_and_template(app, client):
def test_context_processing(app, client): def test_context_processing(app, client):
answer_bp = flask.Blueprint("answer_bp", __name__) answer_bp = flask.Blueprint("answer_bp", __name__)
template_string = lambda: flask.render_template_string( # noqa: E731 def template_string():
"{% if notanswer %}{{ notanswer }} is not the answer. {% endif %}" return flask.render_template_string(
"{% if answer %}{{ answer }} is the answer.{% endif %}" "{% if notanswer %}{{ notanswer }} is not the answer. {% endif %}"
) "{% if answer %}{{ answer }} is the answer.{% endif %}"
)
# App global context processor # App global context processor
@answer_bp.app_context_processor @answer_bp.app_context_processor

View file

@ -98,7 +98,7 @@ def test_request_exception_signal():
@app.route("/") @app.route("/")
def index(): def index():
1 // 0 raise ZeroDivisionError
def record(sender, exception): def record(sender, exception):
recorded.append(exception) recorded.append(exception)
@ -169,7 +169,7 @@ def test_appcontext_tearing_down_signal(app, client):
@app.route("/") @app.route("/")
def index(): def index():
1 // 0 raise ZeroDivisionError
flask.appcontext_tearing_down.connect(record_teardown, app) flask.appcontext_tearing_down.connect(record_teardown, app)
try: try:

View file

@ -219,7 +219,7 @@ def test_test_client_context_binding(app, client):
@app.route("/other") @app.route("/other")
def other(): def other():
1 // 0 raise ZeroDivisionError
with client: with client:
resp = client.get("/") resp = client.get("/")
@ -227,18 +227,15 @@ def test_test_client_context_binding(app, client):
assert resp.data == b"Hello World!" assert resp.data == b"Hello World!"
assert resp.status_code == 200 assert resp.status_code == 200
with client:
resp = client.get("/other") resp = client.get("/other")
assert not hasattr(flask.g, "value") assert not hasattr(flask.g, "value")
assert b"Internal Server Error" in resp.data assert b"Internal Server Error" in resp.data
assert resp.status_code == 500 assert resp.status_code == 500
flask.g.value = 23 flask.g.value = 23
try: with pytest.raises(RuntimeError):
flask.g.value flask.g.value # noqa: B018
except (AttributeError, RuntimeError):
pass
else:
raise AssertionError("some kind of exception expected")
def test_reuse_client(client): def test_reuse_client(client):

View file

@ -41,10 +41,10 @@ def test_method_based_view(app):
def test_view_patching(app): def test_view_patching(app):
class Index(flask.views.MethodView): class Index(flask.views.MethodView):
def get(self): def get(self):
1 // 0 raise ZeroDivisionError
def post(self): def post(self):
1 // 0 raise ZeroDivisionError
class Other(Index): class Other(Index):
def get(self): def get(self):

View file

@ -1,7 +1,5 @@
from __future__ import annotations from __future__ import annotations
import typing as t
from flask import Flask from flask import Flask
from flask import Response from flask import Response
@ -29,10 +27,10 @@ async def before_async() -> None:
@app.teardown_appcontext @app.teardown_appcontext
def teardown_sync(exc: t.Optional[BaseException]) -> None: def teardown_sync(exc: BaseException | None) -> None:
... ...
@app.teardown_appcontext @app.teardown_appcontext
async def teardown_async(exc: t.Optional[BaseException]) -> None: async def teardown_async(exc: BaseException | None) -> None:
... ...

View file

@ -29,12 +29,12 @@ def hello_json() -> Response:
@app.route("/json/dict") @app.route("/json/dict")
def hello_json_dict() -> t.Dict[str, t.Any]: def hello_json_dict() -> dict[str, t.Any]:
return {"response": "Hello, World!"} return {"response": "Hello, World!"}
@app.route("/json/dict") @app.route("/json/dict")
def hello_json_list() -> t.List[t.Any]: def hello_json_list() -> list[t.Any]:
return [{"message": "Hello"}, {"message": "World"}] return [{"message": "Hello"}, {"message": "World"}]