From: "Eregon (Benoit Daloze) via ruby-core" Date: 2025-10-22T14:50:30+00:00 Subject: [ruby-core:123519] [Ruby Bug#21640] Core Pathname is missing 3 methods / is partially-defined Issue #21640 has been updated by Eregon (Benoit Daloze). Earlopain (Earlopain _) wrote in #note-12: > I want to adress some of these points: Thank you for your feedback. > * Problems 1 and 2 seem to be the same to me They are very related but not the same, here is what I meant: 1 is explaining that Pathname inherently depends on other gems/stdlib like find/fileutils/tmpdir, because it's a big part of what makes it useful, IOW we cannot remove this dependency or remove the corresponding methods. It would be a huge breaking change. 2 is about these methods being only available on `require "pathname"`. They are still available, but it's awkward and confusing to have a partially-defined class. > * Problem 3, is that even one? If pathname is core, I don't except development to continue in the gem repository. I think the gem should continue to exist and be a way to get new pathname features and fixes on any Ruby >= 3.0. `pathname` has been a default gem since Ruby 3.0, so I think it should remain upgradable. This functionality is basically lost in 3.5 with core pathname. It's also much easier to contribute or improve the gem, than changing pathname.rb in the ruby repository. Making it core will probably discourage most people to contribute to pathname. Unlike `Set`, the Pathname API is not "done and stable for years", there are plenty of useful things to add. > * Problem 4 you just adressed yourself, arguably a bug in bundler. If the pathname gem becomes a noop on modern rubies (see below on problem 6) then I believe this would just resolve itself. The example with bundler is a Bundler bug. But the `require "pathname"` is an issue (it uses to pick up the gem, and now it no longer does), unless indeed the gem becomes noop on 3.5+. It shows the incompatibility of having the gem non-noop but missing the default gem. > * Problem 6, I believe pathname is "doing it wrong". `set` for example got no-opped on versions of ruby where set got made core: https://github.com/ruby/set/blob/4aa1291c49a12eca8f8bb633a01afedab41800dd/lib/set.rb#L3-L8. Set was pure Ruby though, so it might need a slightly different approach. I hadn't considered the idea to make the pathname gem noop. It would mean Ruby 3.5 loses the ability to update pathname, be it for new functionality or bug fixes or security fixes, every change would need a Ruby release, which is very heavy for this. My gut feeling is we'd then basically freeze pathname as it is currently and it would probably not be changed much in years. As time goes by the community would find new convenient patterns for path-like objects and find pathname insufficient, and then probably create another gem for it, or possibly extend Pathname with another gem. It doesn't sound great/ideal to me. We can imagine there would be a security fix in the last release of the pathname gem, having `gem "pathname", "x.y.z"` in a Gemfile would ensure the fix is used on Ruby 3.0-3.4 but on 3.5 it would silently be ignored and vulnerable. Granted it is rather unlikely to have security issues with Pathname given it delegates most of the work, but it's still possible and in fact more likely if functionality like `mkpath` [is duplicated](https://github.com/ruby/ruby/commit/03800bff6999fd03076c03a3dec50fc4d1220824). Also I thought the goal is to gemify the stdlib, for that core pathname is clearly a step backward. I totally agree for trivial things like io/nonblock to have them core rather than a gem but pathname is far bigger and the API less established/finished. > For me, that only leaves the issue of the 3 methods that rely on default gems. Honestly, it doesn't seem such a big deal to me, I don't understand why they can't just be required on usage. But it seems decided, so I have nothing more to say on that. I think a NoMethodError as in the description is very confusing, how can Pathname be defined but not have the methods it always had? Also if people need to `require "pathname"` to get "the full Pathname" then there seems be no point to have this in core, people will then learn to always `require "pathname"` and it will become another gotcha to learn when programming Ruby (not unlike having to add the frozen string literal magic comment). --- I'm not sure if making the pathname gem noop is good or not, as you say it does address problems 3 to 6 but not 1 & 2, i.e. the dependency concern and the missing methods. My main goal with this issue is to address the missing methods, i.e. to never have a partially-defined Pathname. The dependency concern sounds like potential future problems, in fact it's not unlike #21645 where `fiddle` became a bundled gem but still being depending on by default gems/stdlib. I think that can't work and in such cases we should do it following the order of dependencies. I.e. for pathname we'd need to make `find`, `fileutils` and `tmpdir` core, or leave it as a default gem, since it depends on other default gems. ---------------------------------------- Bug #21640: Core Pathname is missing 3 methods / is partially-defined https://bugs.ruby-lang.org/issues/21640#change-114895 * Author: Eregon (Benoit Daloze) * Status: Open * ruby -v: ruby 3.5.0dev (2025-10-09T08:06:20Z master a29c90c3b0) +PRISM [x86_64-linux] * Backport: 3.2: UNKNOWN, 3.3: UNKNOWN, 3.4: UNKNOWN ---------------------------------------- ``` $ ruby -e 'puts Pathname.instance_methods(false).sort; puts Pathname.singleton_methods.sort' > core_pathname_methods.txt $ ruby -rpathname -e 'puts Pathname.instance_methods(false).sort; puts Pathname.singleton_methods.sort' > require_pathname_methods.txt $ diff core_pathname_methods.txt require_pathname_methods.txt 36a37 > find 72a74 > rmtree 98a101 > mktmpdir ``` So `#find`, `#rmtree` and `.mktmpdir` are missing from core Pathname. And indeed, they give NoMethodError, e.g. ``` $ ruby -ve 'Pathname.new("doesnotexist").rmtree' ruby 3.5.0dev (2025-10-09T08:06:20Z master a29c90c3b0) +PRISM [x86_64-linux] -e:1:in '
': undefined method 'rmtree' for an instance of Pathname (NoMethodError) ``` I think this is confusing and unexpected for most users. Either Pathname should be fully defined or not at all, having a partially-defined Pathname seems particularly confusing (a bit like a `require` that failed in the middle or so). AFAIK the only core class which used to have this was Fiber with `alive?` and `transfer`, that certainly caused its share of confusion, and Fiber has since been fixed, now even without `require "fiber"` Fiber has all methods. Furthermore these 3 methods are not documented as needing `require 'pathname'` in their documentation: https://github.com/ruby/ruby/blob/master/lib/pathname.rb The reason these 3 methods are not in core pathname seems to be (from [this comment](https://github.com/ruby/pathname/issues/64#issuecomment-3388907046)): > That's consensus with akr and me. We should avoid loading other libraries by simply calling methods from the embedded core classes. I understand that concern and I share it, but I think the situation of having a partially-defined Pathname in core is quite problematic. I think there are 2 solutions: * Define all Pathname methods in core. The `require` are done lazily inside the methods so AFAIK there is no technical issue blocking that, but it's not ideal design-wise that a core class can load default gems (i.e. give up that concern for this particular case). * Do not define Pathname in core and let it be a default gem as it was in Ruby 3.4. I discuss the second in more details, because I found more problems than I would have imagined with core Pathname: ## Should Pathname be in core? #17473 proposed to make Pathname core. That sounded great to me at first thought, but as I realized the practical problems with it I'm thinking it's actually better to keep Pathname non-core and as a default gem. Here are 6 problems/concerns with having Pathname in core: 1. Pathname inherently depends on other default gems/stdlib like `find`, `fileutils` and `tmpdir`. This is important to make Pathname useful. The docs even phrase it like this: `ri Pathname` > ... > All functionality from File, FileTest, and some from Dir and > FileUtils is included, in an unsurprising way. It is essentially a > facade for all of these, and more. 2. core Pathname is missing 3 methods compared to gem Pathname (shown above) 3. the pathname gem is likely to gain extra features, methods and bug fixes. For example I see [4 PRs](https://github.com/ruby/pathname/pulls) adding new methods. It means core Pathname will always be outdated and potentially missing some new methods. 4. One can still use the pathname gem even if it's in core, but surprisingly just `require 'pathname'` is not enough: ``` $ gem install pathname Building native extensions. This could take a while... Successfully installed pathname-0.4.0 1 gem installed $ ruby -e 'require "pathname"; puts $".grep(/pathname/)' pathname.so /home/eregon/prefix/ruby-master/lib/ruby/3.5.0+4/pathname.rb ^ wrong that's the stdlib pathname $ ruby -e 'gem "pathname"; require "pathname"; puts $".grep(/pathname/)' # some warnings, reported in https://github.com/ruby/pathname/issues/66 pathname.so /home/eregon/prefix/ruby-master/lib/ruby/gems/3.5.0+4/gems/pathname-0.4.0/lib/pathname.rb ^ correct .rb but the wrong .so! (could lead to pretty confusing issues) ``` This is another source of confusion caused by core pathname. It even happens with Bundler! ``` $ cat Gemfile source "https://rubygems.org" gem "pathname" $ bundle install $ ruby -e 'require "bundler/setup"; require "pathname"; puts $".grep(/pathname/); puts; puts $:' pathname.so /home/eregon/prefix/ruby-master/lib/ruby/3.5.0+4/pathname.rb /home/eregon/tmp/bundler-pathname/vendor/bundle/ruby/3.5.0+4/gems/pathname-0.4.0/lib /home/eregon/tmp/bundler-pathname/vendor/bundle/ruby/3.5.0+4/extensions/x86_64-linux/3.5.0+4-static/pathname-0.4.0 /home/eregon/prefix/ruby-master/lib/ruby/gems/3.5.0+4/gems/bundler-2.8.0.dev/lib /home/eregon/prefix/ruby-master/lib/ruby/site_ruby/3.5.0+4 /home/eregon/prefix/ruby-master/lib/ruby/site_ruby/3.5.0+4/x86_64-linux /home/eregon/prefix/ruby-master/lib/ruby/site_ruby /home/eregon/prefix/ruby-master/lib/ruby/vendor_ruby/3.5.0+4 /home/eregon/prefix/ruby-master/lib/ruby/vendor_ruby/3.5.0+4/x86_64-linux /home/eregon/prefix/ruby-master/lib/ruby/vendor_ruby /home/eregon/prefix/ruby-master/lib/ruby/3.5.0+4 /home/eregon/prefix/ruby-master/lib/ruby/3.5.0+4/x86_64-linux $ bundle exec ruby -e 'require "pathname"; puts $".grep(/pathname/); puts; puts $:' same as above ``` I'm not sure why, but that seems a serious bug that the `pathname` gem apparently can't be used at all with Bundler. 5. due to this difference in terms of methods for core & gem pathname it seems complicated to keep core Pathname and gem Pathname in sync, since e.g. everything is naturally in one `.rb` file in the gem, but in two `.rb` files in core 6. the pathname gem has to `remove_const :Pathname` to avoid conflicts and warnings in https://github.com/ruby/pathname/blob/4689b0b78d081ae855f325e086d95803fa5bd570/lib/pathname.rb#L16. This is bad for Ruby JITs, especially if core Pathname methods might have been used, as it will invalidate JITs compilations, invalidate some inline caches (also bad for the interpreter), caused the JIT to do more compilations which means slower warmup, makes it much harder to persist JITed code across process executions, etc. Based on all of these I think it would actually be better to not have Pathname in core, and let it be a default gem (as it was in 3.4). Having to `require "pathname"` as one always needed to do so far seems far better than a partially-defined core Pathname with rough edge cases (those concerns). -- https://bugs.ruby-lang.org/ ______________________________________________ ruby-core mailing list -- ruby-core@ml.ruby-lang.org To unsubscribe send an email to ruby-core-leave@ml.ruby-lang.org ruby-core info -- https://ml.ruby-lang.org/mailman3/lists/ruby-core.ml.ruby-lang.org/