Skip to content

Commit

Permalink
Merge pull request #878 from pypa/ww/resource-warnings
Browse files Browse the repository at this point in the history
Remove two groups of resource leaks
  • Loading branch information
di authored Feb 3, 2025
2 parents 4cab7b1 + 1cda1b1 commit 96d0dd9
Show file tree
Hide file tree
Showing 3 changed files with 27 additions and 23 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ All versions prior to 0.0.9 are untracked.
* Auditing a fully-pinned requirements file with `--disable-pip` now allows for
duplicates, so long as the duplicates don't have conflicting specifier sets
([#749](https://github.com/pypa/pip-audit/pull/749))
* Fixed two sources of unnecessary resource leaks when doing file I/O
([#878](https://github.com/pypa/pip-audit/pull/878))

## [2.7.3]

Expand Down
9 changes: 6 additions & 3 deletions pip_audit/_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -209,7 +209,7 @@ def _parser() -> argparse.ArgumentParser: # pragma: no cover
dep_source_args.add_argument(
"-r",
"--requirement",
type=argparse.FileType("r"),
type=Path,
metavar="REQUIREMENT",
action="append",
dest="requirements",
Expand Down Expand Up @@ -465,9 +465,12 @@ def audit() -> None: # pragma: no cover

source: DependencySource
if args.requirements is not None:
req_files: list[Path] = [Path(req.name) for req in args.requirements]
for req in args.requirements:
if not req.exists():
_fatal(f"invalid requirements input: {req}")

source = RequirementSource(
req_files,
args.requirements,
require_hashes=args.require_hashes,
no_deps=args.no_deps,
disable_pip=args.disable_pip,
Expand Down
39 changes: 19 additions & 20 deletions pip_audit/_subprocess.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,10 +34,6 @@ def run(args: Sequence[str], *, log_stdout: bool = False, state: AuditState = Au
the process's `stdout` stream as a string.
"""

# Run the process with unbuffered I/O, to make the poll-and-read loop below
# more responsive.
process = Popen(args, bufsize=0, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

# NOTE(ww): We frequently run commands inside of ephemeral virtual environments,
# which have long absolute paths on some platforms. These make for confusing
# state updates, so we trim the first argument down to its basename.
Expand All @@ -47,22 +43,25 @@ def run(args: Sequence[str], *, log_stdout: bool = False, state: AuditState = Au
stdout = b""
stderr = b""

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

if process.returncode != 0:
raise CalledProcessError(
f"{pretty_args} exited with {process.returncode}",
stderr=stderr.decode(errors="replace"),
)
if process.returncode != 0:
raise CalledProcessError(
f"{pretty_args} exited with {process.returncode}",
stderr=stderr.decode(errors="replace"),
)

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

0 comments on commit 96d0dd9

Please sign in to comment.