Skip to content

Commit 96d0dd9

Browse files
authored
Merge pull request #878 from pypa/ww/resource-warnings
Remove two groups of resource leaks
2 parents 4cab7b1 + 1cda1b1 commit 96d0dd9

File tree

3 files changed

+27
-23
lines changed

3 files changed

+27
-23
lines changed

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,8 @@ All versions prior to 0.0.9 are untracked.
2727
* Auditing a fully-pinned requirements file with `--disable-pip` now allows for
2828
duplicates, so long as the duplicates don't have conflicting specifier sets
2929
([#749](https://github.com/pypa/pip-audit/pull/749))
30+
* Fixed two sources of unnecessary resource leaks when doing file I/O
31+
([#878](https://github.com/pypa/pip-audit/pull/878))
3032

3133
## [2.7.3]
3234

pip_audit/_cli.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -209,7 +209,7 @@ def _parser() -> argparse.ArgumentParser: # pragma: no cover
209209
dep_source_args.add_argument(
210210
"-r",
211211
"--requirement",
212-
type=argparse.FileType("r"),
212+
type=Path,
213213
metavar="REQUIREMENT",
214214
action="append",
215215
dest="requirements",
@@ -465,9 +465,12 @@ def audit() -> None: # pragma: no cover
465465

466466
source: DependencySource
467467
if args.requirements is not None:
468-
req_files: list[Path] = [Path(req.name) for req in args.requirements]
468+
for req in args.requirements:
469+
if not req.exists():
470+
_fatal(f"invalid requirements input: {req}")
471+
469472
source = RequirementSource(
470-
req_files,
473+
args.requirements,
471474
require_hashes=args.require_hashes,
472475
no_deps=args.no_deps,
473476
disable_pip=args.disable_pip,

pip_audit/_subprocess.py

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,6 @@ def run(args: Sequence[str], *, log_stdout: bool = False, state: AuditState = Au
3434
the process's `stdout` stream as a string.
3535
"""
3636

37-
# Run the process with unbuffered I/O, to make the poll-and-read loop below
38-
# more responsive.
39-
process = Popen(args, bufsize=0, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
40-
4137
# NOTE(ww): We frequently run commands inside of ephemeral virtual environments,
4238
# which have long absolute paths on some platforms. These make for confusing
4339
# state updates, so we trim the first argument down to its basename.
@@ -47,22 +43,25 @@ def run(args: Sequence[str], *, log_stdout: bool = False, state: AuditState = Au
4743
stdout = b""
4844
stderr = b""
4945

50-
# NOTE: We use `poll()` to control this loop instead of the `read()` call
51-
# to prevent deadlocks. Similarly, `read(size)` will return an empty bytes
52-
# once `stdout` hits EOF, so we don't have to worry about that blocking.
53-
while not terminated:
54-
terminated = process.poll() is not None
55-
stdout += process.stdout.read() # type: ignore
56-
stderr += process.stderr.read() # type: ignore
57-
state.update_state(
58-
f"Running {pretty_args}",
59-
stdout.decode(errors="replace") if log_stdout else None,
60-
)
46+
# Run the process with unbuffered I/O, to make the poll-and-read loop below
47+
# more responsive.
48+
with Popen(args, bufsize=0, stdout=subprocess.PIPE, stderr=subprocess.PIPE) as process:
49+
# NOTE: We use `poll()` to control this loop instead of the `read()` call
50+
# to prevent deadlocks. Similarly, `read(size)` will return an empty bytes
51+
# once `stdout` hits EOF, so we don't have to worry about that blocking.
52+
while not terminated:
53+
terminated = process.poll() is not None
54+
stdout += process.stdout.read() # type: ignore
55+
stderr += process.stderr.read() # type: ignore
56+
state.update_state(
57+
f"Running {pretty_args}",
58+
stdout.decode(errors="replace") if log_stdout else None,
59+
)
6160

62-
if process.returncode != 0:
63-
raise CalledProcessError(
64-
f"{pretty_args} exited with {process.returncode}",
65-
stderr=stderr.decode(errors="replace"),
66-
)
61+
if process.returncode != 0:
62+
raise CalledProcessError(
63+
f"{pretty_args} exited with {process.returncode}",
64+
stderr=stderr.decode(errors="replace"),
65+
)
6766

6867
return stdout.decode("utf-8", errors="replace")

0 commit comments

Comments
 (0)