-
Notifications
You must be signed in to change notification settings - Fork 29.8k
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
fs: add accessibleSync() which returns a boolean #4217
Conversation
In #4077 (comment) @domenic wrote that he'd consider a PR like this. In #4077 (comment) I argue that we shouldn't merge this PR; we should merge pull request #4077 instead. Everything wrong with #4077 is also wrong with #4217 as well. Race conditions? Check. Potential for misuse? Check. Easy to use? Check. But #4217 clutters the API with a function that's redundant with |
It seems like there are really three criticisms of One criticism was that The second criticism is that The third criticism is that both All of the arguments against If we insist on defending the user from these very rare and unimportant edge cases, as a script author, I suppose I just want a boolean, and I don't really care what the function is called. Must we clutter the API to have something easy to use? Challenge accepted. |
I'd be more sympathetic to that argument if history hasn't shown again and again that people get it wrong. It's 2015 and TOCTOU bugs are still some of the most frequent sources of CVEs. |
The user will not be protected by renaming the function. |
Yeah, we should probably just remove it entirely and keep existsSync deprecated. |
@domenic By "it" you mean removing Since For humor value, here's my most recent use case for
If node deletes This function will be used. Nothing we say here can stop it. We can only choose whether to let developers do what they want with a minimum of pain or slow developers down and waste their time. |
Wait, we've gone from "
|
I'm -1 on this because:
Having said all that, thanks for the contribution @dfabulich. I believe this would be your first core commit if it manages to get in, we're very grateful for the effort regardless of the outcome! |
@@ -207,6 +207,15 @@ fs.accessSync = function(path, mode) { | |||
binding.access(pathModule._makeLong(path), mode); | |||
}; | |||
|
|||
fs.accessibleSync = function(path, mode) { | |||
try { | |||
fs.accessSync(path, mode); |
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.
For the sake of being explicit I'd make sure that if mode === undefined
then you pass fs.F_OK
.
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.
Isn't that access's default?
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 lie to think there was a specific reason I suggested this, but can't think of it.
@dfabulich @rvagg Quick history of how we arrived here: I wanted to find a way to not do a stat call in Overall I don't see an issue having an API like this in core. There's a near zero additional burden on core developers to have it in (taken from the history of |
Probably a better idea than my PR, I don't object to this. |
I don't understand, portable node code is just using a wrapper that calls accessSync if it exists, existsSync if it does not. I agree with the analysis in #4217 (comment)... except for the conclusion!
My conclusion is "start using accessSync(), it works fine, wrap it a little if you want". As this PR shows, its a couple of lines to write a wrapper in your own code (or an npm module) if you want to coerce an exception to a boolean return. |
@sam-github some people believe in a big core with some sugar for their use cases, some in a small core. In this case the big-core people have won; in other cases we tell them to use a helper library. What can you do. |
@domenic Except the big-core people haven't won. The desired function was deprecated and is being removed and their complaints are being shot down across the board. They've lost. @sam-github "it works fine, wrap it a little if you want" - the complaint was never about whether it works. It was about how silly it is to have to write the following:
This isn't just about "waa, waa, I want a big core". It's about who Node serves. What the dev community is learning here is that the core contributors are more focused on serving a much smaller, API-server-focused use-case where these kinds of operations are rarely performed and thus considered unimportant. If that's true, that's fine - but you shouldn't pretend otherwise. This doesn't come down to "you can find this information via another call, just use it." It comes down to "if you want to code expressively for these kinds of operations, you should chose a language like Python or Ruby that allows you to do it directly." That's the real message you send when you tell developers "just wrap it." It's not enough to offer "some sort of solution" to a problem. You have to offer a GOOD solution for a platform to be usable and desirable by developers. |
@domenic Yeah, I am aware of the tension. But big-core hasn't won in the fs module [*], and making "access" a ternary API, with a third option that converts an exception to a boolean, isn't a pattern that exists elsewhere in fs. Or indeed, elsewhere in node, so it creates another fs API that is uniquely hard to document and justify, decreasing the sensibility and coherence of the API. IMHO, of course.
|
Yeah, but as you can see, if you advocate for a smaller core you will be greeted with a wall of text about how you are not serving developers, so at some point it's easier to just accede IMO. |
@domenic I'm not giving up quite yet - still pushing, gently! |
I actually also favor small core. That's why I just want But the problem is that If I have to choose between a small bad core and a big core, then I'll hold my nose and have a big core. |
Small and bad has endless price to be paid in terms of "WTF moments" for new users. So, never breaking compatibility is good for existing users, bad for everyone who comes after. More people are coming than are here, lets make it better. @dfabulich a 3 line wrapper is that painful to you? And a |
I agree that the goal is to minimize WTF moments for future users. Today, it looks like this: A1) ⌘F the If we undeprecate Now, let's suppose
But So it looks like this: That's not really an improvement over Relatedly, we could perhaps take some of the edge off of A3 by providing the wrapper as an example in the documentation for
Documentation like this is an embarrassment. It would put a piece of code in the doc that we reasonably expected every user of the API to copy and paste. Forever. How painful is that? I think you'll find that if (by a quirk of history) the core already had So, how often would people do that? I paged through a few pages of https://github.com/search?q=accessSync&type=Code&utf8=%E2%9C%93 and couldn't find anybody who interrogated the thrown exception; they all just re-implemented the
Add up all the hours we've spent arguing about this, and every second that will ever be spent in the future maintaining this trivial function; developers have already wasted that much time and more learning about the problem and re-implementing the wrapper themselves. That painful. |
I agree with @dfabulich. @sam-github: Yes,
Why not go all the way and drop I have yet to see any developer step up and say "My life/code will be made better by this deprecation. That old function was stupid, anyway. I prefer calling "access" when I mean "exists" - that's totally clearer. Thanks, folks!" If that's not a measure of whether value is being added or subtracted here, I don't know what is. |
I have another proposal that might be ever so slightly more likely to make progress.
Instead of the trivial implementation we have here, we (I?) could make a larger PR that allows the
WDYT? |
Please read my brief history above (#4217 (comment)) of why this isn't possible.
You say this as if no one would ever want to know if a file was readable or writable. Might want to reread the options that an be passed.
Access is actually more correct when taking a filesystem into consideration. Windows has shown us that existence and accessibility are not the same. This PR just restricts option usage to |
@crrobinson14 In other languages, facilities like https://www.npmjs.com/package/mkdirp are also in their "fs" libs... but not in node, because they can be implemented in pure js on top of the core API. Not everyone agrees on the "needs a compiler" test and a smaller more maintainable core, you don't! But the distinction is not arbitrary. To answer your specific questions:
Because it requires a thread pool and interaction with the system/event loop.
Requires a compiler vs can be implemented in pure js.
Because its widely requested, and can be used cross-platform if you are very careful. And requires a compiler and deep integration with the event loop. Many Node.js APIs, btw, are not 100% consistent across platforms. 100% consistency would mean "lowest commmon denominator", and Node.js is better than lowest, though it does mean coding carefully if you need portable fs or child_process code.
Is a trivial js wrapper around underling fs APIs.
Requires a compiler. Efforts are underway elsewhere (with websockets, for example), to integrate minimal amount of code into Node.js to allow user-land modules to be written in pure-js. |
That's not right; this PR #4217 allows you to run any of the accessibility checks, not just
No, I mean in a world where The only people who would say, " I take that to be evidence that As for your brief history, you didn't state exactly why I think changing |
Oop. Yup. Thanks for the correction. Responding via phone diminished my context.
That's a well put way for what I was getting at.
Sorry. Too lazy to dig through my IRC logs and find the specific discussion. It boiled down to lack of cross platform consistency. Issues like what @Fishrock123's PR addresses. |
re: "small core" vs "big core" This is in essence fixing something that already exists. |
I claim, at a minimum, that there is no "cross-platform" consistency issue with There is a separate concern which has nothing to do with cross-platform support, about checking for the existence of a file on an inaccessible volume (e.g. removable media). Even the Windows EPERM issue is just a special case of the "we can't be sure whether it exists or not" case. A directory you don't have the right to read is about the same as a directory that's no longer connected to the current machine. I've argued today that we have three options:
I claim that anyone who agrees that #1 is the wrong thing should not be motivated to do #3 for purely epistemological reasons. Even granting that Demanding the perfect name for every function paves the road to big core. Plenty of methods in core have questionable names; it would be chaos to deprecate them all. But I claim that the epistemological argument is, in turn, the only justification for #3 over #2. So if you agree that the epistemological argument alone shouldn't convince us to embiggen core, then you should agree that we shouldn't embiggen core with Either we should do nothing, or we should make The behavior of this deprecated function isn't set in stone, immutable for all time. We can make this better in a major version bump. Let's do it. |
@domenic In light of big/small core, remember you were the one that initially suggested @dfabulich I'd be alright with swapping the impl of |
@nodejs/ctc ... where are we at on this one? I know we discussed it last week on the call but we weren't able to settle on a decision as far as I recall. |
And comment: #1592 (comment) I believe it can be bundled as a single issue for now? Perhaps pulling it apart at some point might make it easier to find resolution(s)? |
7da4fd4
to
c7066fb
Compare
4aa70cb
to
0254fc6
Compare
As opposed to accessSync() which does nothing or throws
0254fc6
to
b0ae0f9
Compare
I implemented my proposal from Jan 4 to improve the performance of I hope that we merge this PR #7455, but if the team refuses to merge that PR, then I hope one of #7455 or #4217 can be merged. |
closing in favor of #8364 -- please let me know if this was in error. |
This has been dragged through various long discussions and has been elevated to the CTC multiple times. As noted in #7455 (comment), while this API is still generally considered an anti-pattern, there are still use-cases it is best suited for, such as checking if a git rebase is in progress by looking if ".git/rebase-apply/rebasing" exists. The general consensus is to undeprecate just the sync version, given that the async version still has the "arguments order inconsistency" problem. The consensus at the two last CTC meetings this came up at was also to undeprecate existsSync() but keep exists() deprecated. See: #8242 & #8330 (Description write-up by @Fishrock123) Fixes: #1592 Refs: #4217 Refs: #7455 PR-URL: #8364 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Ilkka Myller <ilkka.myller@nodefield.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Сковорода Никита Андреевич <chalkerx@gmail.com> Reviewed-By: Jeremiah Senkpiel <fishrock123@rocketmail.com>
This has been dragged through various long discussions and has been elevated to the CTC multiple times. As noted in #7455 (comment), while this API is still generally considered an anti-pattern, there are still use-cases it is best suited for, such as checking if a git rebase is in progress by looking if ".git/rebase-apply/rebasing" exists. The general consensus is to undeprecate just the sync version, given that the async version still has the "arguments order inconsistency" problem. The consensus at the two last CTC meetings this came up at was also to undeprecate existsSync() but keep exists() deprecated. See: #8242 & #8330 (Description write-up by @Fishrock123) Fixes: #1592 Refs: #4217 Refs: #7455 PR-URL: #8364 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Ilkka Myller <ilkka.myller@nodefield.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Сковорода Никита Андреевич <chalkerx@gmail.com> Reviewed-By: Jeremiah Senkpiel <fishrock123@rocketmail.com>
This has been dragged through various long discussions and has been elevated to the CTC multiple times. As noted in #7455 (comment), while this API is still generally considered an anti-pattern, there are still use-cases it is best suited for, such as checking if a git rebase is in progress by looking if ".git/rebase-apply/rebasing" exists. The general consensus is to undeprecate just the sync version, given that the async version still has the "arguments order inconsistency" problem. The consensus at the two last CTC meetings this came up at was also to undeprecate existsSync() but keep exists() deprecated. See: #8242 & #8330 (Description write-up by @Fishrock123) Fixes: #1592 Refs: #4217 Refs: #7455 PR-URL: #8364 Reviewed-By: James M Snell <jasnell@gmail.com> Reviewed-By: Ilkka Myller <ilkka.myller@nodefield.com> Reviewed-By: Matteo Collina <matteo.collina@gmail.com> Reviewed-By: Benjamin Gruenbaum <benjamingr@gmail.com> Reviewed-By: Сковорода Никита Андреевич <chalkerx@gmail.com> Reviewed-By: Jeremiah Senkpiel <fishrock123@rocketmail.com>
As opposed to accessSync() which does nothing or throws