-
Notifications
You must be signed in to change notification settings - Fork 110
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
Merge tempdir api #33
Conversation
@Stebalien you'll probably want to take a thorough look through the docs I've added, I might've gotten some security notices wrong. |
Support for |
I will review this ASAP but that probably won't be till the weekend. |
Friendly ping @Stebalien to take a look at this when you've got bandwidth. No pressure though. We could move forward without that fix in |
A little question: when |
@opilar the idea is to keep the |
@KodrAus sorry, my family ended up visiting that weekend and now I'm traveling but I'll try to get to this in the next few days. |
I'm still alive, FYI (just busy). Sorry. |
@Stebalien that's no problem :) We're not working on a tight timeline here so there's no hurry, no pressure. I just remembered there was a new version of |
ping! I came across this when looking to write tests that use a temporary directory. Is this PR close to being merged, or shall I use |
@azriel91 sorry I just got a chance to loop back here. I would use |
118e964
to
7b15ad6
Compare
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.
Wow. A review? Yes! A review! Sorry, the line change count turned me off reviewing this but it really didn't take that long (lots of nice documentation). This looks really good.
Three main things:
- Misc doc fixes.
- Make
tempdir
functions consistent withtempfile
functions. - Reuse the builder and generalize it to directories (importantly, reusing the name logic).
Also, please rebase or allow me to push a rebased version. There are now a few merge conflicts.
At this point, I'd totally understand if you've moved on and can't work on this. If so, I can take this to completion.
src/lib.rs
Outdated
//! its destructor doesn't run. This is because `tempfile()` relies on the OS to cleanup the | ||
//! underlying file so the file while `NamedTempFile` relies on its destructor to do so. | ||
//! `tempfile` will (almost) never fail to cleanup temporary resources but `TempDir` and `NamedTempFile` will if | ||
//! their destructor doesn't run. This is because `tempfile` relies on the OS to cleanup the |
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.
distructors
src/lib.rs
Outdated
//! underlying file so the file while `NamedTempFile` relies on its destructor to do so. | ||
//! `tempfile` will (almost) never fail to cleanup temporary resources but `TempDir` and `NamedTempFile` will if | ||
//! their destructor doesn't run. This is because `tempfile` relies on the OS to cleanup the | ||
//! underlying file so the file while `TempDir` and `NamedTempFile` rely on their destructor to do so. |
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.
destructors
src/lib.rs
Outdated
|
||
#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", | ||
html_favicon_url = "https://www.rust-lang.org/favicon.ico", | ||
html_root_url = "https://docs.rs/tempdir/0.3.5")] |
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.
should probably remove this (or at least replace tempdir with tempfile).
src/dir.rs
Outdated
/// | ||
/// [`TempDir`]: struct.TempDir.html | ||
/// [resource-leaking]: struct.TempDir.html#resource-leaking | ||
pub fn tempdir(prefix: &str) -> io::Result<TempDir> { |
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.
I'd prefer it if this used the same temporary file builder system as NamedTempFile. As a matter of fact, we can even reuse the same builder (if we generalize it). I'd change the create
and create_in
methods on the builder to file
and file_in
and add directory
and directory_in
methods.
src/dir.rs
Outdated
/// # Ok(()) | ||
/// # } | ||
/// ``` | ||
pub fn new(prefix: &str) -> io::Result<TempDir> { |
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.
Same here (I wouldn't include the prefix option).
src/dir.rs
Outdated
/// # Ok(()) | ||
/// # } | ||
/// ``` | ||
pub fn new_in<P: AsRef<Path>>(tmpdir: P, prefix: &str) -> io::Result<TempDir> { |
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.
Same (no prefix option, use the builder). That should reduce code duplication.
src/dir.rs
Outdated
let cur_dir = env::current_dir()?; | ||
storage = cur_dir.join(tmpdir); | ||
tmpdir = &storage; | ||
// return TempDir::new_in(&cur_dir.join(tmpdir), prefix); |
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.
commented out code?
&mut self.inner_mut().file | ||
} | ||
|
||
/// Convert the temporary file into a `std::fs::File`. |
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.
This should note that the inner file will be deleted.
Thanks for the thorough review @Stebalien! I'll rebase and work through the changes over the next week 👍 |
I've pushed up some changes based on the feedback because having code only living on my local machine makes me nervous... I haven't updated the doc tests yet, and the behaviour of |
I'll try to take a look at it this weekend (and not just put this off for a few months this time...). Note: I'm not entirely against changing the default for tempfile if it's a problem for tempdir. IIRC, we never made any guarantees (unless the user explicitly specified the prefix). |
No problem! I'll try get the tests and docs updated before then. |
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.
I took it upon myself to review this. I'd like to get this merged because my PR is blocked on this one.
I noticed some of the new files have trailing white space. Would be nice to remove that. (Sorry, I'm a little pedantic about this because my editor shows it in bright red.)
src/dir.rs
Outdated
/// receiving a signal like `SIGINT`, then the temporary directory | ||
/// will not be deleted. | ||
/// | ||
// # Examples |
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.
Missing a triple slash (///
) here.
let result = remove_dir_all(self.path()); | ||
|
||
// Prevent the Drop impl from removing the dir a second time. | ||
self.path = None; |
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.
If the entire reason for wrapping the path in an Option
is to prevent Drop
from attempting to delete the directory a second time, then you should be able to eliminate the need for Option
with this:
pub fn close(mut self) -> io::Result<()> {
// Replace the path with an empty one to avoid a memory leak by mem::forget.
let path = std::mem::replace(self.path, PathBuf::new());
std::mem::forget(self); // Don't call drop
remove_dir_all(&path)
}
The same would have to be done to into_path()
of course.
This is cleaner than using an Option
in my opinion.
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.
Another problem to consider is what to do if close()
fails. The caller might wish to implement a retry if, for example, they've created a temporary directory on a network file system that might have intermittent failures. (I encounter this problem a lot with network shares under Windows.)
However, it looks like you want API compatibility with the tempdir
crate, so you probably can't solve this problem.
Edit: It looks like this problem has already been talked to death in rust-lang/api-guidelines#61. Unfortunately, it seems the conclusion is that the failure should only be reported and not handled by the program. Retries of file operations on spotty Windows network shares have solved a lot of problems for me in the past.
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.
The caller might wish to implement a retry if, for example, they've created a temporary directory on a network file system that might have intermittent failures.
...
It looks like this problem has already been talked to death in
Also, in posix, one should generally not retry a close. The correct way to handle such errors is to catch them with an fsync first.
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.
@Stebalien Are you talking about close(2)
or remove_dir_all
? I agree about close(2)
, but deleting files over a network can have intermittent failures where retrying is the right thing to do.
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.
Ah. I was thinking about close. You're right, this is a bit different. However, I kind of feel that this is, really, the best API. We could put the path into the error but that'll make this less ergonomic.
Cargo.toml
Outdated
version = "2.2.0" | ||
authors = ["Steven Allen <steven@stebalien.com>"] | ||
description = "Securely create temporary files." | ||
version = "2.1.6" |
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.
Looks like a package version number regression here.
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.
Ah well spotted!
src/lib.rs
Outdated
|
||
#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", | ||
html_favicon_url = "https://www.rust-lang.org/favicon.ico", | ||
html_root_url = "https://docs.rs/tempfile/2.1.6")] |
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.
This version number should match the one in Cargo.toml
(once the one in there is fixed).
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.
Thanks, nice job with the builder. However, I think I may have given you the wrong impression in a few of my comments regarding the helper functions.
src/lib.rs
Outdated
/// ``` | ||
/// | ||
/// [`NamedTempFile::new`]: struct.NamedTempFile.html#method.new | ||
pub fn named_tempfile(&self) -> io::Result<NamedTempFile> { |
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.
Kind of a nit... this is a really long name. I'd rather just tempfile
or, even better, file
and directory
(the fact that they're temporary is, IMO, unambiguous).
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.
That's a fair point 👍
src/lib.rs
Outdated
dir = &storage; | ||
} | ||
|
||
for _ in 0..::NUM_RETRIES { |
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.
It would be nice to refactor this out into a helper function if possible.
fn create_helper<T, F: Fn(PathBuf) -> io::Result<T>>(base: &Path, f: F) -> io::Result<T> { /* ... */ }
// ...
create_helper(path, dir::create)
src/dir.rs
Outdated
|
||
Err(Error::new(ErrorKind::AlreadyExists, | ||
"too many temporary directories already exist")) | ||
pub fn new() -> io::Result<TempDir> { |
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.
I think there may have been a miscommunication here. I still think providing a new_in
is worth it for the convenience, I just wanted it to use the builder internally (code duplication) and have neither new
nor new_in
take a prefix
argument (mirror NamedTempFile
).
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.
Ah yes, I did misunderstand, I'll restore those methods 👍
src/file/mod.rs
Outdated
/// ``` | ||
/// | ||
/// [`NamedTempFile::new`]: #method.new | ||
pub fn new_in<P: AsRef<Path>>(dir: P) -> io::Result<NamedTempFile> { |
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.
Same here, I'd keep the new_in
.
src/dir.rs
Outdated
@@ -355,3 +233,8 @@ impl Drop for TempDir { | |||
} | |||
} | |||
} | |||
|
|||
// pub(crate) | |||
pub fn create(path: PathBuf) -> io::Result<TempDir> { |
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.
What was wrong with tempdir
, tempdir_in
, named_tempfile
, and named_tempfile_in
? This may also have been a miscommunication on my part. I meant that one should use the builder when specifying custom options, not that we shouldn't have these convenience methods (they are quite convenient).
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.
Yep, that was another misunderstanding, I'll restore these too.
} | ||
} | ||
|
||
// pub(crate) |
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.
This isn't actually pub(crate)
. This module isn't exported (as far as I can tell).
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.
That's right, it's just a note that this method is accessible throughout the crate, but not public since our min Rust version doesn't support pub(restricted)
.
let result = remove_dir_all(self.path()); | ||
|
||
// Prevent the Drop impl from removing the dir a second time. | ||
self.path = None; |
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.
Ah. I was thinking about close. You're right, this is a bit different. However, I kind of feel that this is, really, the best API. We could put the path into the error but that'll make this less ergonomic.
Thanks @jasonwhite and @Stebalien! I'll work through this round of feedback over this week. |
Awesome! Thanks for putting this together. |
@Stebalien @jasonwhite this should be ready for another look now. |
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.
Looks good to me!
🎉 @KodrAus thanks so much for both implementing this and then bearing with me on this long and drawn out CR. @jasonwhite thanks for pitching in and waiting for this CR to complete. |
Thanks all! I'll get the ball rolling on deprecating the |
Combines the
tempdir
API, beefs out the docs, and refactorsNamedTempFile
a bit.If you'd rather I didn't refactor the internal structure then I can rejig this PR to minimise file movement.
Some previous discussion/iteration on combining the APIs: KodrAus#1
cc: @Stebalien @alexcrichton