Bug #20175
closedBroken File.dirname(__FILE__) in eval blocks
Description
Since #19755 eval use caller location by default, however, it broke File.dirname(__FILE__)
in some cases.
# eval.rb
eval('puts file: __FILE__, dirname: File.dirname(__FILE__)')
Up to Ruby 3.2.2 it gave back the same results, even if it wasn't technically correct in some cases
## Ruby 3.2.2
ruby eval.rb
# {:file=>"(eval)", :dirname=>"."}
ruby ./eval.rb
# {:file=>"(eval)", :dirname=>"."}
cd folder && ruby ../eval.rb
# {:file=>"(eval)", :dirname=>"."}
# This one is not pointing to the right directory, but still returns a valid path
ruby /Codes/eval.rb
# {:file=>"(eval)", :dirname=>"."}
# This one is not pointing to the right directory, but still returns a valid path
In Ruby 3.3.0 (introduced in 43a5c19135), it gives back different paths, trying to point to the right directory, but it has the (eval at
prefix which makes it broken for codes expecting a valid path.
## Ruby 3.3.0
ruby eval.rb
# {:file=>"(eval at eval.rb:1)", :dirname=>"."}
ruby ./eval.rb
# {:file=>"(eval at ./eval.rb:1)", :dirname=>"(eval at ."}
# Broken path
cd folder && ruby ../eval.rb
# {:file=>"(eval at ../eval.rb:1)", :dirname=>"(eval at .."}
# This one is trying to point to the right directory, but it has a broken syntax
ruby /Codes/eval.rb
# {:file=>"(eval at /Codes/eval.rb:1)", :dirname=>"(eval at /Codes"}
# This one is trying to point to the right directory, but it has a broken syntax
I was able to reproduce it on current master as well.
Updated by mame (Yusuke Endoh) over 1 year ago
Changing __FILE__
is an intended change, so it is basically unavoidable. I think it is very accidental that File.dirname("(eval)")
returns "."
. Please do not depend on it.
You may want to replace File.dirname(__FILE__)
with __dir__
. If it is difficult for some reason, please elaborate on the context.
Updated by nobu (Nobuyoshi Nakada) over 1 year ago
mame (Yusuke Endoh) wrote in #note-1:
You may want to replace
File.dirname(__FILE__)
with__dir__
.
Note that __dir__
will return nil
not a string in that case.
Updated by kiskoza (Zsolt Kozaroczy) over 1 year ago
I see why __FILE__
was an intended change, I'm going to change all my codes inside eval that depends on its value, thanks for pointing it out.
However, as __dir__
returns nil instead of a string, it requires some changes to replace File.dirname(__FILE__)
with __dir__
.
Is it a viable option to return the already known directory inside evals? The change should be something like this:
diff --git a/vm_eval.c b/vm_eval.c
--- a/vm_eval.c
+++ b/vm_eval.c
@@ -2545,7 +2545,8 @@ rb_current_realfilepath(void)
const char *const ptr = RSTRING_PTR(path);
if (ptr[len - 1] == ')' &&
memcmp(ptr, "("EVAL_LOCATION_MARK, EVAL_LOCATION_MARK_LEN+1) == 0) {
- return Qnil;
+ VALUE implicit_path = rb_str_substr(path, 1 + EVAL_LOCATION_MARK_LEN, len - 1);
+ return implicit_path;
}
}
and then the output would be this:
# eval.rb
eval('puts file: __FILE__, dir: __dir__')
ruby /Codes/eval.rb
# {:file=>"(eval at /Codes/eval.rb:1)", :dir=>"/Codes"}
Updated by mame (Yusuke Endoh) over 1 year ago
__dir__ || "."
is good enough?
Updated by jeremyevans0 (Jeremy Evans) over 1 year ago
- Status changed from Open to Feedback