Skip to content

Commit 2b3b2d3

Browse files
Update ruff configuration: remove defaults and extend rules by ALL. (#24)
* Update `ruff` configuration: remove defaults and extend rules by `ALL`. * Apply new rules.
1 parent cd7cd90 commit 2b3b2d3

File tree

12 files changed

+78
-60
lines changed

12 files changed

+78
-60
lines changed

pyproject.toml

Lines changed: 15 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -91,58 +91,25 @@ python = ["3.8", "3.9", "3.10", "3.11", "3.12"]
9191

9292

9393
[tool.ruff]
94-
target-version = "py38"
9594
show-fixes = true
9695
fix = true
96+
9797
[tool.ruff.lint]
98-
select = [
99-
"A",
100-
"ARG",
101-
"B", # flake8-bugbear
102-
"B9",
103-
"C", # flake8-comprehensions
104-
"C4", # flake8-comprehensions
105-
"C90", # mccabe
106-
"DTZ",
107-
"E", # pycodestyle errors
108-
"E4",
109-
"E7",
110-
"E9",
111-
"EM",
112-
"ERA", # eradicate
113-
"F", # pyflakes
114-
"I", # isort
115-
"INT", # flake8-gettext
116-
"ICN",
117-
"N", # pep8-naming
118-
"PIE", # flake8-pie,
119-
# "PLC", # pylint - convention
120-
"PLE", # pylint - error
121-
"PLR",
122-
"PLW", # pylint - warning
123-
"Q", # flake8-quotes
124-
"RET", # flake8-return,
125-
"RUF", # Ruff-specific rules
126-
"SIM", # flake8-simplify
127-
# "S",
128-
"T",
129-
"TID",
130-
"T20", # flake8-print
131-
# "UP", # pyupgrade
132-
"W", # pycodestyle warnings
133-
"YTT",
98+
extend-select = ["ALL"]
99+
ignore = [
100+
"ANN401",
101+
"COM",
102+
"S",
103+
"UP",
104+
"CPY",
105+
"PLC0414",
106+
"PLR6201",
107+
"RUF029",
108+
"D203",
109+
"D213"
134110
]
135-
# Allow fix for all enabled rules (when `--fix`) is provided.
136-
fixable = ["ALL"]
137-
[tool.ruff.format]
138-
# Like Black, use double quotes for strings.
139-
quote-style = "double"
140-
# Like Black, indent with spaces, rather than tabs.
141-
indent-style = "space"
142-
# Like Black, respect magic trailing commas.
143-
skip-magic-trailing-comma = false
144-
# Like Black, automatically detect the appropriate line ending.
145-
line-ending = "auto"
111+
preview = true
112+
146113
[tool.ruff.lint.isort]
147114
known-first-party = ["asgi_user_agents"]
148115

src/asgi_user_agents/__about__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,3 @@
1+
"""Package metadata."""
2+
13
__version__ = "0.2.0"

src/asgi_user_agents/__init__.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
"""ASGI User Agents package initialization."""
2+
13
from contextlib import suppress
24

35
from .datastructures import UADetails as UADetails

src/asgi_user_agents/datastructures.py

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,73 +1,93 @@
1+
"""Data structures for user-agent details."""
2+
13
from __future__ import annotations
24

3-
from typing import Optional
5+
from typing import TYPE_CHECKING, Optional
46
from urllib.parse import unquote
57

68
from user_agents.parsers import Browser, Device, OperatingSystem, UserAgent
79

8-
from asgi_user_agents.types import Scope
10+
if TYPE_CHECKING:
11+
from asgi_user_agents.types import Scope
912

1013

1114
class UADetails:
15+
"""User-Agent details object."""
16+
1217
def __init__(self, scope: Scope) -> None:
18+
"""Initialize the user-agent details object."""
1319
self._scope = scope
1420
self._ua: UserAgent
1521

1622
def _get_header(self, name: bytes) -> Optional[str]:
23+
"""Get header value."""
1724
return _get_header(self._scope, name)
1825

1926
@property
2027
def _ua_string(self) -> Optional[str]:
28+
"""Extract user-agent string from the headers of the request and return it."""
2129
return self._get_header(b"User-Agent") or ""
2230

2331
@property
2432
def ua(self) -> UserAgent:
33+
"""Return the user-agent object."""
2534
if not hasattr(self, "_ua"):
2635
self._ua = UserAgent(self._ua_string)
2736
return self._ua
2837

2938
@property
3039
def ua_string(self) -> str:
40+
"""Return the user-agent string."""
3141
return self.ua.ua_string
3242

3343
@property
3444
def is_provided(self) -> bool:
45+
"""Check if the user-agent string is provided."""
3546
return bool(self.ua_string)
3647

3748
@property
3849
def os(self) -> OperatingSystem:
50+
"""Return the operating system."""
3951
return self.ua.os
4052

4153
@property
4254
def browser(self) -> Browser:
55+
"""Return the browser."""
4356
return self.ua.browser
4457

4558
@property
4659
def device(self) -> Device:
60+
"""Return the device."""
4761
return self.ua.device
4862

4963
@property
5064
def is_tablet(self) -> bool:
65+
"""Check if the device is a tablet."""
5166
return self.ua.is_tablet
5267

5368
@property
5469
def is_mobile(self) -> bool:
70+
"""Check if the device is a mobile."""
5571
return self.ua.is_mobile
5672

5773
@property
5874
def is_touch_capable(self) -> bool:
75+
"""Check if the device is touch capable."""
5976
return self.ua.is_touch_capable
6077

6178
@property
6279
def is_pc(self) -> bool:
80+
"""Check if the device is a PC."""
6381
return self.ua.is_pc
6482

6583
@property
6684
def is_bot(self) -> bool:
85+
"""Check if the device is a bot."""
6786
return self.ua.is_bot
6887

6988
@property
7089
def is_email_client(self) -> bool:
90+
"""Check if the device is an email client."""
7191
return self.ua.is_email_client
7292

7393

src/asgi_user_agents/middleware.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,18 @@
1+
"""Middleware for ASGI applications."""
2+
13
from asgi_user_agents.datastructures import UADetails
24
from asgi_user_agents.types import ASGIApp, Receive, Scope, Send
35

46

57
class UAMiddleware:
8+
"""User Agent Middleware for ASGI applications."""
9+
610
def __init__(self, app: ASGIApp) -> None:
11+
"""Initialize the middleware."""
712
self._app = app
813

914
async def __call__(self, scope: Scope, receive: Receive, send: Send) -> None:
15+
"""Call the middleware."""
1016
if scope["type"] in ("http", "websocket"):
1117
scope["ua"] = UADetails(scope)
1218

src/asgi_user_agents/requests.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
"""Request classes with `scope` attribute of type `UAScope` for type-checking."""
2+
13
from typing import Any, Literal, Protocol, overload
24

35
from asgi_user_agents.datastructures import UADetails
@@ -10,18 +12,19 @@
1012
else:
1113

1214
class UAScopeProtocol(Protocol):
15+
"""Protocol for `UAScope` class."""
16+
1317
@overload
1418
def __getitem__(self, key: Literal["ua"]) -> UADetails: ... # pragma: no cover
1519

1620
@overload
1721
def __getitem__(self, key: str) -> Any: ... # pragma: no cover
1822

1923
class UAScope(UAScopeProtocol, Scope):
20-
pass
24+
"""User-Agent scope object with `ua` attribute of type `UADetails`."""
2125

2226
class UARequest(Request):
23-
"""
24-
User-Agent request object with `scope` attribute of type `UAScope`.
27+
"""User-Agent request object with `scope` attribute of type `UAScope`.
2528
2629
!!! note
2730
Use this class to make code editors type-check `request.scope["ua"]`.

src/asgi_user_agents/types.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
"""Types for ASGI apps."""
2+
13
# Ripped from `starlette.types`.
24
import typing
35

tests/__init__.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1 @@
1-
# SPDX-FileCopyrightText: 2024-present Hasan Sezer Taşan <[email protected]>
2-
#
3-
# SPDX-License-Identifier: MIT
1+
"""Entry point for tests."""

tests/test_fastapi.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
"""Test usage of the middleware in FastAPI."""
2+
13
import httpx
24
import parametrize_from_file as pff
35
import pytest
@@ -13,6 +15,7 @@
1315

1416
@app.get("/")
1517
async def index(request: Request) -> Response:
18+
"""Return user-agent data."""
1619
ua = request.scope["ua"]
1720
assert isinstance(ua, UADetails)
1821
data = {
@@ -40,9 +43,10 @@ async def index(request: Request) -> Response:
4043
return JSONResponse(data)
4144

4245

43-
@pytest.mark.asyncio
46+
@pytest.mark.asyncio()
4447
@pff.parametrize(path="assets/test_middleware.json")
4548
async def test_user_agent_data(ua_string: str, response_data: dict) -> None:
49+
"""Test user-agent data."""
4650
async with httpx.AsyncClient(app=app) as client:
4751
response = await client.get(
4852
"http://testserver/", headers={"User-Agent": ua_string}

tests/test_litestar.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
"""Test usage of the middleware in Litestar."""
2+
3+
from __future__ import annotations
4+
15
from typing import Any, Dict
26

37
import httpx
@@ -11,6 +15,7 @@
1115

1216
@get("/")
1317
async def index(request: Request) -> Dict[str, Any]:
18+
"""Return user-agent data."""
1419
ua = request.scope["ua"]
1520
assert isinstance(ua, UADetails)
1621
return {
@@ -40,9 +45,10 @@ async def index(request: Request) -> Dict[str, Any]:
4045
app = Litestar(route_handlers=[index], middleware=[UAMiddleware])
4146

4247

43-
@pytest.mark.asyncio
48+
@pytest.mark.asyncio()
4449
@pff.parametrize(path="assets/test_middleware.json")
4550
async def test_user_agent_data(ua_string: str, response_data: dict) -> None:
51+
"""Test user-agent data."""
4652
async with httpx.AsyncClient(app=app) as client:
4753
response = await client.get(
4854
"http://testserver/", headers={"User-Agent": ua_string}

tests/test_quart.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
"""Test usage of the middleware in Quart."""
2+
13
from typing import cast
24

35
import httpx
@@ -12,6 +14,7 @@
1214

1315
@app.route("/")
1416
async def home() -> str:
17+
"""Return user-agent data."""
1518
ua = UADetails(cast(dict, request.scope))
1619
data = {
1720
"ua_string": ua.ua_string,
@@ -38,9 +41,10 @@ async def home() -> str:
3841
return jsonify(data)
3942

4043

41-
@pytest.mark.asyncio
44+
@pytest.mark.asyncio()
4245
@pff.parametrize(path="assets/test_middleware.json")
4346
async def test_user_agent_data(ua_string: str, response_data: dict) -> None:
47+
"""Test user-agent data."""
4448
async with httpx.AsyncClient(app=app) as client:
4549
response = await client.get(
4650
"http://testserver/", headers={"User-Agent": ua_string}

tests/test_starlette.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
"""Test usage of the middleware in Starlette."""
2+
13
import httpx
24
import parametrize_from_file as pff
35
import pytest
@@ -11,6 +13,7 @@
1113

1214

1315
async def index(request: Request) -> Response:
16+
"""Return user-agent data."""
1417
ua = request.scope["ua"]
1518
assert isinstance(ua, UADetails)
1619
data = {
@@ -41,9 +44,10 @@ async def index(request: Request) -> Response:
4144
app = Starlette(routes=[Route("/", index)], middleware=[Middleware(UAMiddleware)])
4245

4346

44-
@pytest.mark.asyncio
47+
@pytest.mark.asyncio()
4548
@pff.parametrize(path="assets/test_middleware.json")
4649
async def test_user_agent_data(ua_string: str, response_data: dict) -> None:
50+
"""Test user-agent data."""
4751
async with httpx.AsyncClient(app=app) as client:
4852
response = await client.get(
4953
"http://testserver/", headers={"User-Agent": ua_string}

0 commit comments

Comments
 (0)