Skip to content

Commit

Permalink
fix things and support more stuff
Browse files Browse the repository at this point in the history
  • Loading branch information
diceroll123 committed Aug 21, 2024
1 parent ed49437 commit 5e54c64
Show file tree
Hide file tree
Showing 3 changed files with 524 additions and 49 deletions.
191 changes: 182 additions & 9 deletions crates/ruff_linter/resources/test/fixtures/flake8_simplify/SIM115.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,13 @@
import pathlib as pl
from pathlib import Path
from pathlib import Path as P
import tempfile

# SIM115
f = open("foo.txt")
f = Path("foo.txt").open()
f = pathlib.Path("foo.txt").open()
f = pl.Path("foo.txt").open()
f = P("foo.txt").open()
f = tempfile.NamedTemporaryFile()
f = tempfile.TemporaryFile()
f = tempfile.SpooledTemporaryFile()
data = f.read()
f.close()

Expand Down Expand Up @@ -47,14 +43,9 @@
exit_stack_ = exit_stack
f = exit_stack_.enter_context(open("filename"))

# OK
with tempfile.TemporaryFile() as f:
data = f.read()

# OK (quick one-liner to clear file contents)
open("filename", "w").close()
pathlib.Path("filename").open("w").close()
tempfile.NamedTemporaryFile().close()

# OK (custom context manager)
class MyFile:
Expand All @@ -66,3 +57,185 @@ def __enter__(self):

def __exit__(self, exc_type, exc_val, exc_tb):
self.file.close()


import tempfile
import tarfile
from tarfile import TarFile
import zipfile
import io
import codecs
import bz2
import gzip
import dbm
import dbm.gnu
import dbm.ndbm
import lzma
import shelve
import tokenize
import wave
import fileinput

f = tempfile.NamedTemporaryFile()
f = tempfile.TemporaryFile()
f = tempfile.SpooledTemporaryFile()
f = tarfile.open("foo.tar")
f = TarFile("foo.tar").open()
f = tarfile.TarFile("foo.tar").open()
f = tarfile.TarFile().open()
f = zipfile.ZipFile("foo.zip").open("foo.txt")
f = io.open("foo.txt")
f = io.open_code("foo.txt")
f = io.BytesIO(b"data")
f = io.StringIO("data")
f = codecs.open("foo.txt")
f = bz2.open("foo.txt")
f = gzip.open("foo.txt")
f = dbm.open("foo.db")
f = dbm.gnu.open("foo.db")
f = dbm.ndbm.open("foo.db")
f = lzma.open("foo.xz")
f = lzma.LZMAFile("foo.xz")
f = shelve.open("foo.db")
f = tokenize.open("foo.py")
f = wave.open("foo.wav")
f = tarfile.TarFile.taropen("foo.tar")
f = fileinput.input("foo.txt")
f = fileinput.FileInput("foo.txt")

with contextlib.suppress(Exception):
# The following line is for example's sake.
# For some f's above, this would raise an error (since it'd be f.readline() etc.)
data = f.read()

f.close()

# OK
with tempfile.TemporaryFile() as f:
data = f.read()

# OK
with tarfile.open("foo.tar") as f:
data = f.add("foo.txt")

# OK
with tarfile.TarFile("foo.tar") as f:
data = f.add("foo.txt")

# OK
with tarfile.TarFile("foo.tar").open() as f:
data = f.add("foo.txt")

# OK
with zipfile.ZipFile("foo.zip") as f:
data = f.read("foo.txt")

# OK
with zipfile.ZipFile("foo.zip").open("foo.txt") as f:
data = f.read()

# OK
with zipfile.ZipFile("foo.zip") as zf:
with zf.open("foo.txt") as f:
data = f.read()

# OK
with io.open("foo.txt") as f:
data = f.read()

# OK
with io.open_code("foo.txt") as f:
data = f.read()

# OK
with io.BytesIO(b"data") as f:
data = f.read()

# OK
with io.StringIO("data") as f:
data = f.read()

# OK
with codecs.open("foo.txt") as f:
data = f.read()

# OK
with bz2.open("foo.txt") as f:
data = f.read()

# OK
with gzip.open("foo.txt") as f:
data = f.read()

# OK
with dbm.open("foo.db") as f:
data = f.get("foo")

# OK
with dbm.gnu.open("foo.db") as f:
data = f.get("foo")

# OK
with dbm.ndbm.open("foo.db") as f:
data = f.get("foo")

# OK
with lzma.open("foo.xz") as f:
data = f.read()

# OK
with lzma.LZMAFile("foo.xz") as f:
data = f.read()

# OK
with shelve.open("foo.db") as f:
data = f["foo"]

# OK
with tokenize.open("foo.py") as f:
data = f.read()

# OK
with wave.open("foo.wav") as f:
data = f.readframes(1024)

# OK
with tarfile.TarFile.taropen("foo.tar") as f:
data = f.add("foo.txt")

# OK
with fileinput.input("foo.txt") as f:
data = f.readline()

# OK
with fileinput.FileInput("foo.txt") as f:
data = f.readline()

# OK (quick one-liner to clear file contents)
tempfile.NamedTemporaryFile().close()
tempfile.TemporaryFile().close()
tempfile.SpooledTemporaryFile().close()
tarfile.open("foo.tar").close()
tarfile.TarFile("foo.tar").close()
tarfile.TarFile("foo.tar").open().close()
tarfile.TarFile.open("foo.tar").close()
zipfile.ZipFile("foo.zip").close()
zipfile.ZipFile("foo.zip").open("foo.txt").close()
io.open("foo.txt").close()
io.open_code("foo.txt").close()
codecs.open("foo.txt").close()
bz2.open("foo.txt").close()
gzip.open("foo.txt").close()
dbm.open("foo.db").close()
dbm.gnu.open("foo.db").close()
dbm.ndbm.open("foo.db").close()
lzma.open("foo.xz").close()
lzma.LZMAFile("foo.xz").close()
shelve.open("foo.db").close()
tokenize.open("foo.py").close()
wave.open("foo.wav").close()
tarfile.TarFile.taropen("foo.tar").close()
fileinput.input("foo.txt").close()
fileinput.FileInput("foo.txt").close()
io.BytesIO(b"data").close()
io.StringIO("data").close()
Original file line number Diff line number Diff line change
Expand Up @@ -114,19 +114,73 @@ fn match_exit_stack(semantic: &SemanticModel) -> bool {

/// Return `true` if the expression is an `open` call or temporary file constructor.
fn is_open(semantic: &SemanticModel, func: &Expr) -> bool {
let Some(qualified_name) = semantic.resolve_qualified_name(func) else {
// Ex) `open(...)`
if semantic.match_builtin_expr(func, "open") {
return true;
}

// Ex) `pathlib.Path(...).open()`
let Expr::Attribute(ast::ExprAttribute { attr, value, .. }) = func else {
return false;
};

matches!(
qualified_name.segments(),
["" | "builtins", "open"]
| ["pathlib", "Path", "open"]
| [
"tempfile",
"TemporaryFile" | "NamedTemporaryFile" | "SpooledTemporaryFile"
]
)
let segments: Option<Vec<&str>> = match value.as_ref() {
Expr::Call(ast::ExprCall {
func: value_func, ..
}) => {
// Ex) `pathlib.Path(...).open()` -> ["pathlib", "Path", "open"]
semantic
.resolve_qualified_name(value_func)
.map(|qualified_name| qualified_name.append_member(attr).segments().to_vec())
}
Expr::Name(ast::ExprName { id, .. }) => {
// Ex) `tarfile.open(...)` -> ["tarfile", "open"]
Some(vec![&**id, attr])
}
Expr::Attribute(ast::ExprAttribute {
attr: value_attr,
value,
..
}) => {
// Ex) `dbm.gnu.open(...)` -> ["dbm", "gnu", "open"]

semantic
.resolve_qualified_name(value)
.map(|qualified_name| {
qualified_name
.append_member(value_attr)
.append_member(attr)
.segments()
.to_vec()
})
}
_ => None,
};

let Some(segments) = segments else {
return false;
};

match segments[..] {
// Ex) `pathlib.Path(...).open()`
["pathlib", "Path", "open"] => true,
["tarfile", "TarFile", "open" | "taropen" | "gzopen" | "bz2open" | "xzopen"] => true,
["zipfile", "ZipFile", "open"] => true,
["lzma", "LZMAFile", "open"] => true,
["dbm", "gnu" | "ndbm", "open"] => true,

// Ex) `foo.open(...)`
["codecs" | "dbm" | "tarfile" | "bz2" | "gzip" | "io" | "lzma" | "shelve" | "tokenize"
| "wave", "open"] => true,
["fileinput", "FileInput" | "input"] => true,

// fringe cases
["tempfile", "TemporaryFile" | "NamedTemporaryFile" | "SpooledTemporaryFile"] => true,
["io", "open_code" | "BytesIO" | "StringIO"] => true,
["lzma", "LZMAFile"] => true,

_ => false,
}
}

/// Return `true` if the current expression is followed by a `close` call.
Expand Down
Loading

0 comments on commit 5e54c64

Please sign in to comment.