Skip to content

Commit 8f4c8a7

Browse files
authored
Add 100% clean coverage (#2394)
* Add 100% clean coverage * Add 100% clean coverage * Add 100% clean coverage * Add 100% clean coverage
1 parent 9baded3 commit 8f4c8a7

19 files changed

+56
-57
lines changed

docs/contributing.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -144,10 +144,10 @@ If tests are failing you will see this message under the coverage report:
144144

145145
`=== 1 failed, 354 passed, 1 skipped, 1 xfailed in 37.08s ===`
146146

147-
If tests succeed but coverage doesn't reach our current threshold, you will see this
147+
If tests succeed but coverage doesn't reach 100%, you will see this
148148
message under the coverage report:
149149

150-
`Coverage failure: total of 88 is less than fail-under=95`
150+
`Coverage failure: total of 98 is less than fail-under=100`
151151

152152
## Releasing
153153

pyproject.toml

+3-4
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,7 @@ description = "The lightning-fast ASGI server."
99
readme = "README.md"
1010
license = "BSD-3-Clause"
1111
requires-python = ">=3.8"
12-
authors = [
13-
{ name = "Tom Christie", email = "[email protected]" },
14-
]
12+
authors = [{ name = "Tom Christie", email = "[email protected]" }]
1513
classifiers = [
1614
"Development Status :: 4 - Beta",
1715
"Environment :: Web Environment",
@@ -100,12 +98,13 @@ omit = ["uvicorn/workers.py", "uvicorn/__main__.py"]
10098

10199
[tool.coverage.report]
102100
precision = 2
103-
fail_under = 98.35
101+
fail_under = 100
104102
show_missing = true
105103
skip_covered = true
106104
exclude_lines = [
107105
"pragma: no cover",
108106
"pragma: nocover",
107+
"pragma: full coverage",
109108
"if TYPE_CHECKING:",
110109
"if typing.TYPE_CHECKING:",
111110
"raise NotImplementedError",

tests/supervisors/test_multiprocess.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ async def app(scope: Scope, receive: ASGIReceiveCallable, send: ASGISendCallable
4545

4646

4747
def run(sockets: list[socket.socket] | None) -> None:
48-
while True:
48+
while True: # pragma: no cover
4949
time.sleep(1)
5050

5151

tests/supervisors/test_signal.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ async def forever_app(scope, receive, send):
6060
await send({"type": "http.response.body", "body": b"start", "more_body": True})
6161
# we never continue this one, so this request will time out
6262
await server_event.wait()
63-
await send({"type": "http.response.body", "body": b"end", "more_body": False})
63+
await send({"type": "http.response.body", "body": b"end", "more_body": False}) # pragma: full coverage
6464

6565
config = Config(app=forever_app, reload=False, port=unused_tcp_port, timeout_graceful_shutdown=1)
6666
server: Server
@@ -90,7 +90,7 @@ async def test_sigint_deny_request_after_triggered(unused_tcp_port: int, caplog)
9090

9191
async def app(scope, receive, send):
9292
await send({"type": "http.response.start", "status": 200, "headers": []})
93-
await asyncio.sleep(1)
93+
await asyncio.sleep(1) # pragma: full coverage
9494

9595
config = Config(app=app, reload=False, port=unused_tcp_port, timeout_graceful_shutdown=1)
9696
server: Server

tests/test_server.py

+2-2
Original file line numberDiff line numberDiff line change
@@ -37,10 +37,10 @@ async def dummy_app(scope, receive, send): # pragma: py-win32
3737
pass
3838

3939

40-
if sys.platform == "win32":
40+
if sys.platform == "win32": # pragma: py-not-win32
4141
signals = [signal.SIGBREAK]
4242
signal_captures = [capture_signal_sync]
43-
else:
43+
else: # pragma: py-win32
4444
signals = [signal.SIGTERM, signal.SIGINT]
4545
signal_captures = [capture_signal_sync, capture_signal_async]
4646

uvicorn/_subprocess.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -70,7 +70,7 @@ def subprocess_started(
7070
"""
7171
# Re-open stdin.
7272
if stdin_fileno is not None:
73-
sys.stdin = os.fdopen(stdin_fileno)
73+
sys.stdin = os.fdopen(stdin_fileno) # pragma: full coverage
7474

7575
# Logging needs to be setup again for each child.
7676
config.configure_logging()

uvicorn/config.py

+9-9
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ def is_dir(path: Path) -> bool:
124124
if not path.is_absolute():
125125
path = path.resolve()
126126
return path.is_dir()
127-
except OSError:
127+
except OSError: # pragma: full coverage
128128
return False
129129

130130

@@ -153,9 +153,9 @@ def resolve_reload_patterns(patterns_list: list[str], directories_list: list[str
153153

154154
children = []
155155
for j in range(len(directories)):
156-
for k in range(j + 1, len(directories)):
156+
for k in range(j + 1, len(directories)): # pragma: full coverage
157157
if directories[j] in directories[k].parents:
158-
children.append(directories[k]) # pragma: py-darwin
158+
children.append(directories[k])
159159
elif directories[k] in directories[j].parents:
160160
children.append(directories[j])
161161

@@ -298,12 +298,12 @@ def __init__(
298298
if directory == reload_directory or directory in reload_directory.parents:
299299
try:
300300
self.reload_dirs.remove(reload_directory)
301-
except ValueError:
301+
except ValueError: # pragma: full coverage
302302
pass
303303

304304
for pattern in self.reload_excludes:
305305
if pattern in self.reload_includes:
306-
self.reload_includes.remove(pattern)
306+
self.reload_includes.remove(pattern) # pragma: full coverage
307307

308308
if not self.reload_dirs:
309309
if reload_dirs:
@@ -332,7 +332,7 @@ def __init__(
332332
if forwarded_allow_ips is None:
333333
self.forwarded_allow_ips = os.environ.get("FORWARDED_ALLOW_IPS", "127.0.0.1")
334334
else:
335-
self.forwarded_allow_ips = forwarded_allow_ips
335+
self.forwarded_allow_ips = forwarded_allow_ips # pragma: full coverage
336336

337337
if self.reload and self.workers > 1:
338338
logger.warning('"workers" flag is ignored when reloading is enabled.')
@@ -485,7 +485,7 @@ def bind_socket(self) -> socket.socket:
485485
sock.bind(path)
486486
uds_perms = 0o666
487487
os.chmod(self.uds, uds_perms)
488-
except OSError as exc:
488+
except OSError as exc: # pragma: full coverage
489489
logger.error(exc)
490490
sys.exit(1)
491491

@@ -503,7 +503,7 @@ def bind_socket(self) -> socket.socket:
503503
family = socket.AF_INET
504504
addr_format = "%s://%s:%d"
505505

506-
if self.host and ":" in self.host: # pragma: py-win32
506+
if self.host and ":" in self.host: # pragma: full coverage
507507
# It's an IPv6 address.
508508
family = socket.AF_INET6
509509
addr_format = "%s://[%s]:%d"
@@ -512,7 +512,7 @@ def bind_socket(self) -> socket.socket:
512512
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
513513
try:
514514
sock.bind((self.host, self.port))
515-
except OSError as exc:
515+
except OSError as exc: # pragma: full coverage
516516
logger.error(exc)
517517
sys.exit(1)
518518

uvicorn/loops/asyncio.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,4 @@
77

88
def asyncio_setup(use_subprocess: bool = False) -> None:
99
if sys.platform == "win32" and use_subprocess:
10-
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy())
10+
asyncio.set_event_loop_policy(asyncio.WindowsSelectorEventLoopPolicy()) # pragma: full coverage

uvicorn/main.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -576,7 +576,7 @@ def run(
576576
else:
577577
server.run()
578578
except KeyboardInterrupt:
579-
pass
579+
pass # pragma: full coverage
580580
finally:
581581
if config.uds and os.path.exists(config.uds):
582582
os.remove(config.uds) # pragma: py-win32

uvicorn/middleware/proxy_headers.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ def get_trusted_client_host(self, x_forwarded_for_hosts: list[str]) -> str | Non
3737
if host not in self.trusted_hosts:
3838
return host
3939

40-
return None
40+
return None # pragma: full coverage
4141

4242
async def __call__(self, scope: Scope, receive: ASGIReceiveCallable, send: ASGISendCallable) -> None:
4343
if scope["type"] in ("http", "websocket"):

uvicorn/protocols/http/flow_control.py

+3-3
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ def __init__(self, transport: asyncio.Transport) -> None:
1616
self._is_writable_event.set()
1717

1818
async def drain(self) -> None:
19-
await self._is_writable_event.wait()
19+
await self._is_writable_event.wait() # pragma: full coverage
2020

2121
def pause_reading(self) -> None:
2222
if not self.read_paused:
@@ -29,12 +29,12 @@ def resume_reading(self) -> None:
2929
self._transport.resume_reading()
3030

3131
def pause_writing(self) -> None:
32-
if not self.write_paused:
32+
if not self.write_paused: # pragma: full coverage
3333
self.write_paused = True
3434
self._is_writable_event.clear()
3535

3636
def resume_writing(self) -> None:
37-
if self.write_paused:
37+
if self.write_paused: # pragma: full coverage
3838
self.write_paused = False
3939
self._is_writable_event.set()
4040

uvicorn/protocols/http/h11_impl.py

+5-5
Original file line numberDiff line numberDiff line change
@@ -263,7 +263,7 @@ def handle_events(self) -> None:
263263
self.cycle.message_event.set()
264264

265265
def handle_websocket_upgrade(self, event: h11.Request) -> None:
266-
if self.logger.level <= TRACE_LOG_LEVEL:
266+
if self.logger.level <= TRACE_LOG_LEVEL: # pragma: full coverage
267267
prefix = "%s:%d - " % self.client if self.client else ""
268268
self.logger.log(TRACE_LOG_LEVEL, "%sUpgrading to WebSocket", prefix)
269269

@@ -333,13 +333,13 @@ def pause_writing(self) -> None:
333333
"""
334334
Called by the transport when the write buffer exceeds the high water mark.
335335
"""
336-
self.flow.pause_writing()
336+
self.flow.pause_writing() # pragma: full coverage
337337

338338
def resume_writing(self) -> None:
339339
"""
340340
Called by the transport when the write buffer drops below the low water mark.
341341
"""
342-
self.flow.resume_writing()
342+
self.flow.resume_writing() # pragma: full coverage
343343

344344
def timeout_keep_alive_handler(self) -> None:
345345
"""
@@ -441,10 +441,10 @@ async def send(self, message: ASGISendEvent) -> None:
441441
message_type = message["type"]
442442

443443
if self.flow.write_paused and not self.disconnected:
444-
await self.flow.drain()
444+
await self.flow.drain() # pragma: full coverage
445445

446446
if self.disconnected:
447-
return
447+
return # pragma: full coverage
448448

449449
if not self.response_started:
450450
# Sending response status line and headers

uvicorn/protocols/http/httptools_impl.py

+7-7
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,7 @@ def _get_upgrade(self) -> bytes | None:
139139
upgrade = value.lower()
140140
if b"upgrade" in connection:
141141
return upgrade
142-
return None
142+
return None # pragma: full coverage
143143

144144
def _should_upgrade_to_ws(self, upgrade: bytes | None) -> bool:
145145
if upgrade == b"websocket" and self.ws_protocol_class is not None:
@@ -193,7 +193,7 @@ def handle_websocket_upgrade(self) -> None:
193193
def send_400_response(self, msg: str) -> None:
194194
content = [STATUS_LINE[400]]
195195
for name, value in self.server_state.default_headers:
196-
content.extend([name, b": ", value, b"\r\n"])
196+
content.extend([name, b": ", value, b"\r\n"]) # pragma: full coverage
197197
content.extend(
198198
[
199199
b"content-type: text/plain; charset=utf-8\r\n",
@@ -336,13 +336,13 @@ def pause_writing(self) -> None:
336336
"""
337337
Called by the transport when the write buffer exceeds the high water mark.
338338
"""
339-
self.flow.pause_writing()
339+
self.flow.pause_writing() # pragma: full coverage
340340

341341
def resume_writing(self) -> None:
342342
"""
343343
Called by the transport when the write buffer drops below the low water mark.
344344
"""
345-
self.flow.resume_writing()
345+
self.flow.resume_writing() # pragma: full coverage
346346

347347
def timeout_keep_alive_handler(self) -> None:
348348
"""
@@ -441,10 +441,10 @@ async def send(self, message: ASGISendEvent) -> None:
441441
message_type = message["type"]
442442

443443
if self.flow.write_paused and not self.disconnected:
444-
await self.flow.drain()
444+
await self.flow.drain() # pragma: full coverage
445445

446446
if self.disconnected:
447-
return
447+
return # pragma: full coverage
448448

449449
if not self.response_started:
450450
# Sending response status line and headers
@@ -477,7 +477,7 @@ async def send(self, message: ASGISendEvent) -> None:
477477

478478
for name, value in headers:
479479
if HEADER_RE.search(name):
480-
raise RuntimeError("Invalid HTTP header name.")
480+
raise RuntimeError("Invalid HTTP header name.") # pragma: full coverage
481481
if HEADER_VALUE_RE.search(value):
482482
raise RuntimeError("Invalid HTTP header value.")
483483

uvicorn/protocols/websockets/websockets_impl.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -242,7 +242,7 @@ async def run_asgi(self) -> None:
242242
"""
243243
try:
244244
result = await self.app(self.scope, self.asgi_receive, self.asgi_send) # type: ignore[func-returns-value]
245-
except ClientDisconnected:
245+
except ClientDisconnected: # pragma: full coverage
246246
self.closed_event.set()
247247
self.transport.close()
248248
except BaseException:

uvicorn/protocols/websockets/wsproto_impl.py

+4-4
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ def __init__(
4444
_loop: asyncio.AbstractEventLoop | None = None,
4545
) -> None:
4646
if not config.loaded:
47-
config.load()
47+
config.load() # pragma: full coverage
4848

4949
self.config = config
5050
self.app = cast(ASGI3Application, config.loaded_app)
@@ -140,13 +140,13 @@ def pause_writing(self) -> None:
140140
"""
141141
Called by the transport when the write buffer exceeds the high water mark.
142142
"""
143-
self.writable.clear()
143+
self.writable.clear() # pragma: full coverage
144144

145145
def resume_writing(self) -> None:
146146
"""
147147
Called by the transport when the write buffer drops below the low water mark.
148148
"""
149-
self.writable.set()
149+
self.writable.set() # pragma: full coverage
150150

151151
def shutdown(self) -> None:
152152
if self.handshake_complete:
@@ -233,7 +233,7 @@ async def run_asgi(self) -> None:
233233
try:
234234
result = await self.app(self.scope, self.receive, self.send) # type: ignore[func-returns-value]
235235
except ClientDisconnected:
236-
self.transport.close()
236+
self.transport.close() # pragma: full coverage
237237
except BaseException:
238238
self.logger.exception("Exception in ASGI application\n")
239239
self.send_500_response()

uvicorn/server.py

+6-6
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,7 @@ def create_protocol(
112112
loop = asyncio.get_running_loop()
113113

114114
listeners: Sequence[socket.SocketType]
115-
if sockets is not None:
115+
if sockets is not None: # pragma: full coverage
116116
# Explicitly passed a list of open sockets.
117117
# We use this when the server is run from a Gunicorn worker.
118118

@@ -147,7 +147,7 @@ def _share_socket(
147147
# Create a socket using UNIX domain socket.
148148
uds_perms = 0o666
149149
if os.path.exists(config.uds):
150-
uds_perms = os.stat(config.uds).st_mode
150+
uds_perms = os.stat(config.uds).st_mode # pragma: full coverage
151151
server = await loop.create_unix_server(
152152
create_protocol, path=config.uds, ssl=config.ssl, backlog=config.backlog
153153
)
@@ -180,7 +180,7 @@ def _share_socket(
180180
else:
181181
# We're most likely running multiple workers, so a message has already been
182182
# logged by `config.bind_socket()`.
183-
pass
183+
pass # pragma: full coverage
184184

185185
self.started = True
186186

@@ -243,7 +243,7 @@ async def on_tick(self, counter: int) -> bool:
243243

244244
# Callback to `callback_notify` once every `timeout_notify` seconds.
245245
if self.config.callback_notify is not None:
246-
if current_time - self.last_notified > self.config.timeout_notify:
246+
if current_time - self.last_notified > self.config.timeout_notify: # pragma: full coverage
247247
self.last_notified = current_time
248248
await self.config.callback_notify()
249249

@@ -261,7 +261,7 @@ async def shutdown(self, sockets: list[socket.socket] | None = None) -> None:
261261
for server in self.servers:
262262
server.close()
263263
for sock in sockets or []:
264-
sock.close()
264+
sock.close() # pragma: full coverage
265265

266266
# Request shutdown on all existing connections.
267267
for connection in list(self.server_state.connections):
@@ -330,6 +330,6 @@ def capture_signals(self) -> Generator[None, None, None]:
330330
def handle_exit(self, sig: int, frame: FrameType | None) -> None:
331331
self._captured_signals.append(sig)
332332
if self.should_exit and sig == signal.SIGINT:
333-
self.force_exit = True
333+
self.force_exit = True # pragma: full coverage
334334
else:
335335
self.should_exit = True

0 commit comments

Comments
 (0)