Project

General

Profile

Actions

Bug #17144

closed

Updated by Eregon (Benoit Daloze) almost 5 years ago

Out of these 2 usages that relied on the file to still exist, 1 was incorrect:
https://.com/ruby/ruby/blob/e8c3872555fc85640505974e6b1c39d315572689/lib/reline/line_editor.rb#L2085-L2087
The GC can trigger anywhere between these 3 lines and it would break.

The other usage seemed correct, but was IMHO quite ugly (a resource block + an ensure, needed to workaround this issue):
https://.com/ruby/ruby/blob/e8c3872555fc85640505974e6b1c39d315572689/test/openssl/test_x509store.rb#L36-L49

Updated by akr (Akira Tanaka) almost 5 years ago

I sympathize with this issue.
I wish Tempfile.open had worked like Tempfile.create from the beginning.

But changing it is incompatible.
It is not our (current) practice that introducing an incompatible change without prior notice.

It would be possible to change it with some migration period (if matz approves).
But I doubt that the benefits are not greater than the pain of its incompatibility because desired functionality, "block exit removes the temporary file", is already provided in Tempfile.create.

Anyway, it would be good to improve the document of Tempfile.open to recommend Tempfile.create.

Updated by akr (Akira Tanaka) almost 5 years ago

One idea: creating tmpfile library and
Tmpfile.open calls Tempfile.create.

This also resolves an inconsistency between tempfile.rb and tmpdir.rb.
For this consistency, File.tmpfile may be better than Tmpfile.open
because it is similar to Dir.tmpdir.

Updated by Eregon (Benoit Daloze) almost 5 years ago

It seems hard to deprecate here without changing behavior. Any idea?
OTOH, when an usage relied on the file to still exist it should be quite clear what happens (the file will not be there, so an exception).
It took me seconds to find out when looking at the failing tests.

The change is already part of NEWS, I could mark it as experimental if desired.

I think very few cases will break, so it seems reasonable to change to me.
I'd think most cases which want the file to still be on disk either have all the logic inside the block, or don't use a block (so those are unaffected).
Ruby 3.0 has other incompatible changes of course, IMHO we need to evolve the old APIs, not be stuck forever with strange and non-intuitive APIs.

Many gems test against ruby-head, maybe let's simply wait a bit and see if they report some breakage?
From experience, that seems a practical way to estimate incompatibility.

Updated by Glass_saga (Masaki Matsushita) almost 5 years ago

  • Related to : Improved Tempfile w/o DelegateClass added

Updated by Dan0042 (Daniel DeLorme) almost 5 years ago

-1 for breaking compatibility with no deprecation, just for the sake of perceived consistency.

But then again it's important to note that Tempfile.open{ } returns nil, so the only way to cause an incompatibility is if the blocks "s" the Tempfile reference, like Tempfile.open{ @f = _1 } or Tempfile.open{ return _1 }

Updated by Glass_saga (Masaki Matsushita) almost 5 years ago

If we could allow incompatible changes of Tempfile in 3.0 or later, it is a good chance to reimplement it as a subclass of File without delegate.rb.

Updated by Dan0042 (Daniel DeLorme) almost 5 years ago

Tempfile.open{ } returns nil

I apologize for this brain fart. Tempfile.open{ } returns the result of the block. So it's entirely likely that someone would use
tmp = Tempfile.open{ |f| f.write(data); f }
instead of
tmp = Tempfile.open; tmp.write(data); tmp.close

Updated by Eregon (Benoit Daloze) almost 5 years ago

Dan0042 (Daniel DeLorme) wrote in #note-6:

-1 for breaking compatibility with no deprecation, just for the sake of perceived consistency.

Not "the sake of perceived consistency".
It's ing a resource (a file on the disk) outside of a resource block.
Seems a severe issue to me.

And people must be annoyed all the time by this bug/surprising behavior.
I think we annoy less people by fixing this behavior (very few places need to adapt) than keeping it (all future usages have to use Tempfile.open { ... }; ensure or discover the non-standard-named method Tempfile.create { ... }).

Regarding the pattern, Tempfile.open requires this complete anti-pattern to correctly release all resources:
(from https://.com/ruby/ruby/commit/e8c3872555fc85640505974e6b1c39d315572689)

  begin
    tf = Tempfile.open 'test' do |io| # yet another variable name for the same thing
      io.write "..."
      io # ing the argument of the resource block, that feels very hacky
    end
    # use tf.path
  ensure
    tf.close! if tf # Am I using Ruby or Go? The block should deal with this.
  end

Do we want to perpetuate this anti-pattern?

With this change, it's:

  Tempfile.open 'test' do |io|
    io.write "..."
    io.close # or io.flush
    # use tf.path
  end

If we want to deprecate then we'd deprecate Tempfile.open with a block (and use Tempfile.create instead).
That's for sure causing more pain than the change I did.

Updated by akr (Akira Tanaka) almost 5 years ago

Eregon (Benoit Daloze) wrote in #note-9:

Dan0042 (Daniel DeLorme) wrote in #note-6:

-1 for breaking compatibility with no deprecation, just for the sake of perceived consistency.

Not "the sake of perceived consistency".
It's ing a resource (a file on the disk) outside of a resource block.
Seems a severe issue to me.

It is compared to wrong use of Tempfile.open

I understand "perceived consistency" is compared to Tempfile.create.

And people must be annoyed all the time by this bug/surprising behavior.
I think we annoy less people by fixing this behavior (very few places need to adapt) than keeping it (all future usages have to use Tempfile.open { ... }; ensure or discover the non-standard-named method Tempfile.create { ... }).

I feel that "create" is the second standard choice of factory method.

Regarding the pattern, Tempfile.open requires this complete anti-pattern to correctly release all resources:
(from https://.com/ruby/ruby/commit/e8c3872555fc85640505974e6b1c39d315572689)

  begin
    tf = Tempfile.open 'test' do |io| # yet another variable name for the same thing
      io.write "..."
      io # ing the argument of the resource block, that feels very hacky
    end
    # use tf.path
  ensure
    tf.close! if tf # Am I using Ruby or Go? The block should deal with this.
  end

Do we want to perpetuate this anti-pattern?

With this change, it's:

  Tempfile.open 'test' do |io|
    io.write "..."
    io.close # or io.flush
    # use tf.path
  end

If we want to deprecate then we'd deprecate Tempfile.open with a block (and use Tempfile.create instead).
That's for sure causing more pain than the change I did.

We can use Tempfile.create.
There is no incompatibility pain if we don't change Tempfile.open behavior.

Also, Tempfile.create has advantages to the proposed Tempfile.open change:

  • It is available for all maintained Ruby versions. (It is available since Ruby 2.1. Zero-argument call is permitted since Ruby 2.4.) Tempfile.create is usable without worrying Ruby version dependencies.
  • The proposed change doesn't eliminate all curiousness of Tempfile.open. It still use Tempfile class for delegation which is eliminated in Tempfile.create.

As far as I understand the advantage of the proposed Tempfile.open change over Tempfile.create is just a method name which is consistent with other classes.

Updated by Dan0042 (Daniel DeLorme) almost 5 years ago

Austin Ziegler wrote on mailing list:

If we don’t change the behaviour, could we at least modify the documentation for Tempfile.open to recommend most people use Tempfile.create, since I don’t think that I’ve ever used it and reach for Tempfile.open most of the time, because of its similarity to File.open (and I’ve been using Ruby since 2002).

That would be good, and in fact I believe improving the documentation is the best way to address this issue.

Updated by matz (Yukihiro Matsumoto) over 4 years ago

  • Status changed from Open to Closed

To keep compatibility, we are not going to change the behavior of Tempfile.open. But to reduce the confusion, documentation was updated.
In the future, maybe something like RuboCop warning will be convienient.

Matz.

Updated by Eregon (Benoit Daloze) over 4 years ago

OK, https://.com/ruby/tempfile/pull/4 is the PR that reverts it and already applied to ruby master.

I will try to improve the documentation further, something like
"Tempfile.open is basically deprecated but kept for compatibility, please use Tempfile.create instead whenever possible".

Updated by akr (Akira Tanaka) over 4 years ago

Tempfile.open would be useful when an user want to remove the file by GC.
Tempfile.create doesn't provide the feature, intentionally.

So, as far as an user needs temporary file removal by GC, we should not deprecate Tempfile.open
(unless we provide an alternative).

Actions

Also available in: Atom PDF

Like0
Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0Like0