-
-
Notifications
You must be signed in to change notification settings - Fork 2.9k
pytester: slightly clean up LsofFdLeakChecker #7436
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -94,21 +94,16 @@ def pytest_configure(config: Config) -> None: | |
|
||
|
||
class LsofFdLeakChecker: | ||
def get_open_files(self): | ||
out = self._exec_lsof() | ||
open_files = self._parse_lsof_output(out) | ||
return open_files | ||
|
||
def _exec_lsof(self): | ||
pid = os.getpid() | ||
# py3: use subprocess.DEVNULL directly. | ||
with open(os.devnull, "wb") as devnull: | ||
return subprocess.check_output( | ||
("lsof", "-Ffn0", "-p", str(pid)), stderr=devnull | ||
).decode() | ||
|
||
def _parse_lsof_output(self, out): | ||
def isopen(line): | ||
def get_open_files(self) -> List[Tuple[str, str]]: | ||
out = subprocess.run( | ||
("lsof", "-Ffn0", "-p", str(os.getpid())), | ||
stdout=subprocess.PIPE, | ||
stderr=subprocess.DEVNULL, | ||
check=True, | ||
universal_newlines=True, | ||
).stdout | ||
|
||
def isopen(line: str) -> bool: | ||
return line.startswith("f") and ( | ||
"deleted" not in line | ||
and "mem" not in line | ||
|
@@ -130,9 +125,9 @@ def isopen(line): | |
|
||
return open_files | ||
|
||
def matching_platform(self): | ||
def matching_platform(self) -> bool: | ||
try: | ||
subprocess.check_output(("lsof", "-v")) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. just out of curiosity, why do you prefer There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There's no good reason, only that the subprocess docs start with describing only the
And that "Older high-level API" includes There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. thanks for explaining @bluetech |
||
subprocess.run(("lsof", "-v"), check=True) | ||
except (OSError, subprocess.CalledProcessError): | ||
return False | ||
else: | ||
|
@@ -149,16 +144,17 @@ def pytest_runtest_protocol(self, item: Item) -> Generator[None, None, None]: | |
new_fds = {t[0] for t in lines2} - {t[0] for t in lines1} | ||
leaked_files = [t for t in lines2 if t[0] in new_fds] | ||
if leaked_files: | ||
error = [] | ||
error.append("***** %s FD leakage detected" % len(leaked_files)) | ||
error.extend([str(f) for f in leaked_files]) | ||
error.append("*** Before:") | ||
error.extend([str(f) for f in lines1]) | ||
error.append("*** After:") | ||
error.extend([str(f) for f in lines2]) | ||
error.append(error[0]) | ||
error.append("*** function %s:%s: %s " % item.location) | ||
error.append("See issue #2366") | ||
error = [ | ||
"***** %s FD leakage detected" % len(leaked_files), | ||
*(str(f) for f in leaked_files), | ||
"*** Before:", | ||
*(str(f) for f in lines1), | ||
"*** After:", | ||
*(str(f) for f in lines2), | ||
"***** %s FD leakage detected" % len(leaked_files), | ||
"*** function %s:%s: %s " % item.location, | ||
"See issue #2366", | ||
] | ||
item.warn(pytest.PytestWarning("\n".join(error))) | ||
|
||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
why do we not handle any errors? Just curious since the original method doesn't seem to have done it either. But, it looks like it is possible for
subprocess.run
to throw at leastCalledProcessError
andTimeoutExpired
.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Good point! I think it would be good to handle errors from the
lsof
call here.If the
lsof
command doesn't exist, theLsofFdLeakChecker
plugin isn't registered at all, so the issue can't be thatlsof
isn't found at all, but it can still return an error itself.I don't think
TimeoutExpired
can happen because we don't passtimeout
tosubprocess.run
, butCalledProcessError
can happen.Then there's a question what to do if an error occurs. I guess in this case the most reasonable thing is to just skip the check for the particular test, and issue a warning.