ruby-core@ruby-lang.org archive (unofficial mirror)
 help / color / mirror / Atom feed
* [ruby-core:109924] [Ruby master Bug#19006] Inconsistent behaviour of autoload in wrapped script
@ 2022-09-17  1:49 shioyama (Chris Salzberg)
  2022-09-17  2:35 ` [ruby-core:109928] " shioyama (Chris Salzberg)
                   ` (3 more replies)
  0 siblings, 4 replies; 5+ messages in thread
From: shioyama (Chris Salzberg) @ 2022-09-17  1:49 UTC (permalink / raw)
  To: ruby-core

Issue #19006 has been reported by shioyama (Chris Salzberg).

----------------------------------------
Bug #19006: Inconsistent behaviour of autoload in wrapped script
https://bugs.ruby-lang.org/issues/19006

* Author: shioyama (Chris Salzberg)
* Status: Open
* Priority: Normal
* Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN
----------------------------------------
Suppose I have two files, `foo.rb` and `bar.rb`:

```ruby
# foo.rb

puts "loading Foo..."

module Foo
  autoload :Bar, "foo/bar"
end
```

and

```ruby
# foo/bar.rb

puts "loading Foo::Bar..."

module Foo
  module Bar
  end
end
```

I can `require "foo"` and access both `Foo` and `Foo::Bar`:

```ruby
require "foo"
# loading Foo...
#=> true
Foo::Bar
# loading Foo::Bar...
#=> Foo::Bar
```

However, if I _load_ `foo` under a wrap module with `load`:

```ruby
MyModule = Module.new
load "./foo.rb", MyModule
# loading Foo...
#=> true
```

... I'm now unable to access `Foo::Bar` anywhere, because whereas the constant is autoloaded from `MyModule::Foo::Bar`, it is required from the top-level as `Foo::Bar`:

```ruby
MyModule::Foo::Bar
# loading Foo::Bar
#=> uninitialized constant MyModule::Foo::Bar (NameError)
```

This means that `autoload` is basically useless inside anything loaded with the `wrap` argument to `load`, because the file being autoloaded can't know in advance what the base namespace will be.

I would argue that it makes much more sense to apply the wrap module (`top_wrapper`) to any autoloaded file loaded when `top_wrapper` is set. In the example above, this would mean that accessing `MyModule::Foo::Bar` would work, since `MyModule` would apply when the autoload triggers to load `foo/bar`.



-- 
https://bugs.ruby-lang.org/

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [ruby-core:109928] [Ruby master Bug#19006] Inconsistent behaviour of autoload in wrapped script
  2022-09-17  1:49 [ruby-core:109924] [Ruby master Bug#19006] Inconsistent behaviour of autoload in wrapped script shioyama (Chris Salzberg)
@ 2022-09-17  2:35 ` shioyama (Chris Salzberg)
  2022-09-17  3:55 ` [ruby-core:109929] " jeremyevans0 (Jeremy Evans)
                   ` (2 subsequent siblings)
  3 siblings, 0 replies; 5+ messages in thread
From: shioyama (Chris Salzberg) @ 2022-09-17  2:35 UTC (permalink / raw)
  To: ruby-core

Issue #19006 has been updated by shioyama (Chris Salzberg).


This is related to: https://bugs.ruby-lang.org/issues/10320#note-13

----------------------------------------
Bug #19006: Inconsistent behaviour of autoload in wrapped script
https://bugs.ruby-lang.org/issues/19006#change-99175

* Author: shioyama (Chris Salzberg)
* Status: Open
* Priority: Normal
* Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN
----------------------------------------
Suppose I have two files, `foo.rb` and `bar.rb`:

```ruby
# foo.rb

puts "loading Foo..."

module Foo
  autoload :Bar, "foo/bar"
end
```

and

```ruby
# foo/bar.rb

puts "loading Foo::Bar..."

module Foo
  module Bar
  end
end
```

I can `require "foo"` and access both `Foo` and `Foo::Bar`:

```ruby
require "foo"
# loading Foo...
#=> true
Foo::Bar
# loading Foo::Bar...
#=> Foo::Bar
```

However, if I _load_ `foo` under a wrap module with `load`:

```ruby
MyModule = Module.new
load "./foo.rb", MyModule
# loading Foo...
#=> true
```

... I'm now unable to access `Foo::Bar` anywhere, because whereas the constant is autoloaded from `MyModule::Foo::Bar`, it is required from the top-level as `Foo::Bar`:

```ruby
MyModule::Foo::Bar
# loading Foo::Bar
#=> uninitialized constant MyModule::Foo::Bar (NameError)
```

This means that `autoload` is basically useless inside anything loaded with the `wrap` argument to `load`, because the file being autoloaded can't know in advance what the base namespace will be.

I would argue that it makes much more sense to apply the wrap module (`top_wrapper`) to any autoloaded file loaded when `top_wrapper` is set. In the example above, this would mean that accessing `MyModule::Foo::Bar` would work, since `MyModule` would apply when the autoload triggers to load `foo/bar`.



-- 
https://bugs.ruby-lang.org/

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [ruby-core:109929] [Ruby master Bug#19006] Inconsistent behaviour of autoload in wrapped script
  2022-09-17  1:49 [ruby-core:109924] [Ruby master Bug#19006] Inconsistent behaviour of autoload in wrapped script shioyama (Chris Salzberg)
  2022-09-17  2:35 ` [ruby-core:109928] " shioyama (Chris Salzberg)
@ 2022-09-17  3:55 ` jeremyevans0 (Jeremy Evans)
  2022-09-17  4:42 ` [ruby-core:109932] [Ruby master Feature#19006] " shioyama (Chris Salzberg)
  2022-09-17  5:13 ` [ruby-core:109935] " shioyama (Chris Salzberg)
  3 siblings, 0 replies; 5+ messages in thread
From: jeremyevans0 (Jeremy Evans) @ 2022-09-17  3:55 UTC (permalink / raw)
  To: ruby-core

Issue #19006 has been updated by jeremyevans0 (Jeremy Evans).


When loading `foo.rb` with the `MyModule` wrapper, you are loading the equivalent of:

```ruby
module MyModule
  module Foo
    autoload :Bar, "foo/bar"
  end
end
```

`foo/bar.rb` defines `::Foo::Bar`, not `::MyModule::Foo::Bar`, so I think the error you receive when you attempt to load `MyModule::Foo::Bar` is expected.

`load`'s wrapped module is not transitive to `load`/`require`/`autoload` calls inside the loaded file.  It was never designed to be transitive, nor documented as being transitive, so I don't think the current behavior is a bug.  Making the behavior transitive would be a feature request, IMO.

FWIW, I agree that the lack of transitivity makes `load`'s wrapped module not very useful in practice.  However, I don't see that as a problem.

----------------------------------------
Bug #19006: Inconsistent behaviour of autoload in wrapped script
https://bugs.ruby-lang.org/issues/19006#change-99177

* Author: shioyama (Chris Salzberg)
* Status: Open
* Priority: Normal
* Backport: 2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN
----------------------------------------
Suppose I have two files, `foo.rb` and `bar.rb`:

```ruby
# foo.rb

puts "loading Foo..."

module Foo
  autoload :Bar, "foo/bar"
end
```

and

```ruby
# foo/bar.rb

puts "loading Foo::Bar..."

module Foo
  module Bar
  end
end
```

I can `require "foo"` and access both `Foo` and `Foo::Bar`:

```ruby
require "foo"
# loading Foo...
#=> true
Foo::Bar
# loading Foo::Bar...
#=> Foo::Bar
```

However, if I _load_ `foo` under a wrap module with `load`:

```ruby
MyModule = Module.new
load "./foo.rb", MyModule
# loading Foo...
#=> true
```

... I'm now unable to access `Foo::Bar` anywhere, because whereas the constant is autoloaded from `MyModule::Foo::Bar`, it is required from the top-level as `Foo::Bar`:

```ruby
MyModule::Foo::Bar
# loading Foo::Bar
#=> uninitialized constant MyModule::Foo::Bar (NameError)
```

This means that `autoload` is basically useless inside anything loaded with the `wrap` argument to `load`, because the file being autoloaded can't know in advance what the base namespace will be.

I would argue that it makes much more sense to apply the wrap module (`top_wrapper`) to any autoloaded file loaded when `top_wrapper` is set. In the example above, this would mean that accessing `MyModule::Foo::Bar` would work, since `MyModule` would apply when the autoload triggers to load `foo/bar`.



-- 
https://bugs.ruby-lang.org/

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [ruby-core:109932] [Ruby master Feature#19006] Inconsistent behaviour of autoload in wrapped script
  2022-09-17  1:49 [ruby-core:109924] [Ruby master Bug#19006] Inconsistent behaviour of autoload in wrapped script shioyama (Chris Salzberg)
  2022-09-17  2:35 ` [ruby-core:109928] " shioyama (Chris Salzberg)
  2022-09-17  3:55 ` [ruby-core:109929] " jeremyevans0 (Jeremy Evans)
@ 2022-09-17  4:42 ` shioyama (Chris Salzberg)
  2022-09-17  5:13 ` [ruby-core:109935] " shioyama (Chris Salzberg)
  3 siblings, 0 replies; 5+ messages in thread
From: shioyama (Chris Salzberg) @ 2022-09-17  4:42 UTC (permalink / raw)
  To: ruby-core

Issue #19006 has been updated by shioyama (Chris Salzberg).

Tracker changed from Bug to Feature
Backport deleted (2.7: UNKNOWN, 3.0: UNKNOWN, 3.1: UNKNOWN)

> It was never designed to be transitive, nor documented as being transitive, so I don't think the current behavior is a bug. Making the behavior transitive would be a feature request, IMO.

Agree, I've changed the tracker to "feature" to reflect this.

> FWIW, I agree that the lack of transitivity makes load's wrapped module not very useful in practice. However, I don't see that as a problem.

Agree with the first part, not sure though if I agree with the last part. This is really a larger discussion, but making `load`'s wrapped module transitive to `require`, `autoload`, etc would open the door to a lot of interesting things (https://bugs.ruby-lang.org/issues/10320#note-13 etc.)

My goal is to leave Ruby itself as much unchanged as possible, but without transitivity at the language level it's virtually impossible to implement true namespace isolation at the gem level, which is my original goal. I also think there is a strong argument to be made that transitivity is at least as "natural" as the current implementation. e.g. `require` currently has to _reset_ `top_wrapper` before doing its requiring; making `require` transitive actually entails _removing_ code, not adding it.

Not suggesting we just suddenly change `require` to remove that line (obviously backwards compatibility is a thing), but providing something that would allow that to happen — maybe a flag? &mdash would unlock a lot of latent potential in Ruby.

----------------------------------------
Feature #19006: Inconsistent behaviour of autoload in wrapped script
https://bugs.ruby-lang.org/issues/19006#change-99179

* Author: shioyama (Chris Salzberg)
* Status: Open
* Priority: Normal
----------------------------------------
Suppose I have two files, `foo.rb` and `bar.rb`:

```ruby
# foo.rb

puts "loading Foo..."

module Foo
  autoload :Bar, "foo/bar"
end
```

and

```ruby
# foo/bar.rb

puts "loading Foo::Bar..."

module Foo
  module Bar
  end
end
```

I can `require "foo"` and access both `Foo` and `Foo::Bar`:

```ruby
require "foo"
# loading Foo...
#=> true
Foo::Bar
# loading Foo::Bar...
#=> Foo::Bar
```

However, if I _load_ `foo` under a wrap module with `load`:

```ruby
MyModule = Module.new
load "./foo.rb", MyModule
# loading Foo...
#=> true
```

... I'm now unable to access `Foo::Bar` anywhere, because whereas the constant is autoloaded from `MyModule::Foo::Bar`, it is required from the top-level as `Foo::Bar`:

```ruby
MyModule::Foo::Bar
# loading Foo::Bar
#=> uninitialized constant MyModule::Foo::Bar (NameError)
```

This means that `autoload` is basically useless inside anything loaded with the `wrap` argument to `load`, because the file being autoloaded can't know in advance what the base namespace will be.

I would argue that it makes much more sense to apply the wrap module (`top_wrapper`) to any autoloaded file loaded when `top_wrapper` is set. In the example above, this would mean that accessing `MyModule::Foo::Bar` would work, since `MyModule` would apply when the autoload triggers to load `foo/bar`.



-- 
https://bugs.ruby-lang.org/

^ permalink raw reply	[flat|nested] 5+ messages in thread

* [ruby-core:109935] [Ruby master Feature#19006] Inconsistent behaviour of autoload in wrapped script
  2022-09-17  1:49 [ruby-core:109924] [Ruby master Bug#19006] Inconsistent behaviour of autoload in wrapped script shioyama (Chris Salzberg)
                   ` (2 preceding siblings ...)
  2022-09-17  4:42 ` [ruby-core:109932] [Ruby master Feature#19006] " shioyama (Chris Salzberg)
@ 2022-09-17  5:13 ` shioyama (Chris Salzberg)
  3 siblings, 0 replies; 5+ messages in thread
From: shioyama (Chris Salzberg) @ 2022-09-17  5:13 UTC (permalink / raw)
  To: ruby-core

Issue #19006 has been updated by shioyama (Chris Salzberg).


Rethinking this, `autoload` can fairly easily be patched to make this work without changes at the language level, so this isn't strictly necessary. In terms of transitivity, `require` is the more important one.

I'm happy to close this, thanks for the feedback!

----------------------------------------
Feature #19006: Inconsistent behaviour of autoload in wrapped script
https://bugs.ruby-lang.org/issues/19006#change-99182

* Author: shioyama (Chris Salzberg)
* Status: Open
* Priority: Normal
----------------------------------------
Suppose I have two files, `foo.rb` and `bar.rb`:

```ruby
# foo.rb

puts "loading Foo..."

module Foo
  autoload :Bar, "foo/bar"
end
```

and

```ruby
# foo/bar.rb

puts "loading Foo::Bar..."

module Foo
  module Bar
  end
end
```

I can `require "foo"` and access both `Foo` and `Foo::Bar`:

```ruby
require "foo"
# loading Foo...
#=> true
Foo::Bar
# loading Foo::Bar...
#=> Foo::Bar
```

However, if I _load_ `foo` under a wrap module with `load`:

```ruby
MyModule = Module.new
load "./foo.rb", MyModule
# loading Foo...
#=> true
```

... I'm now unable to access `Foo::Bar` anywhere, because whereas the constant is autoloaded from `MyModule::Foo::Bar`, it is required from the top-level as `Foo::Bar`:

```ruby
MyModule::Foo::Bar
# loading Foo::Bar
#=> uninitialized constant MyModule::Foo::Bar (NameError)
```

This means that `autoload` is basically useless inside anything loaded with the `wrap` argument to `load`, because the file being autoloaded can't know in advance what the base namespace will be.

I would argue that it makes much more sense to apply the wrap module (`top_wrapper`) to any autoloaded file loaded when `top_wrapper` is set. In the example above, this would mean that accessing `MyModule::Foo::Bar` would work, since `MyModule` would apply when the autoload triggers to load `foo/bar`.



-- 
https://bugs.ruby-lang.org/

^ permalink raw reply	[flat|nested] 5+ messages in thread

end of thread, other threads:[~2022-09-17  5:13 UTC | newest]

Thread overview: 5+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2022-09-17  1:49 [ruby-core:109924] [Ruby master Bug#19006] Inconsistent behaviour of autoload in wrapped script shioyama (Chris Salzberg)
2022-09-17  2:35 ` [ruby-core:109928] " shioyama (Chris Salzberg)
2022-09-17  3:55 ` [ruby-core:109929] " jeremyevans0 (Jeremy Evans)
2022-09-17  4:42 ` [ruby-core:109932] [Ruby master Feature#19006] " shioyama (Chris Salzberg)
2022-09-17  5:13 ` [ruby-core:109935] " shioyama (Chris Salzberg)

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).