-
Notifications
You must be signed in to change notification settings - Fork 30k
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
child_process.fork: add string-shortcut for stdio #10866
child_process.fork: add string-shortcut for stdio #10866
Conversation
The subsystem name should just be |
@@ -50,7 +50,24 @@ exports.fork = function(modulePath /*, args, options*/) { | |||
|
|||
args = execArgv.concat([modulePath], args); | |||
|
|||
if (!Array.isArray(options.stdio)) { | |||
if (typeof options.stdio == 'string') { | |||
const stdioStringToArray = (option) => { |
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 think this should be moved outside of fork()
since it does not rely on any variables in the current scope, where it should use the function stdioStringToArray(option) { ... }
format.
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.
While it doesn't depend on any variables in the fork()
scope I did think it logically belonged inside of fork because it wasn't generic enough to be used by any of the other exported functions needing the stdioStringToArray
operation.
Case in point, fork is the only function that requires 'ipc'
be append to the returned array. This is just my opinion though so comments/feedback are welcomed.
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.
Well, the main idea is to avoid the possibility of V8 having to recompile/reoptimize the function. By pulling it out of the function, that can be achieved for sure.
@@ -50,7 +50,24 @@ exports.fork = function(modulePath /*, args, options*/) { | |||
|
|||
args = execArgv.concat([modulePath], args); | |||
|
|||
if (!Array.isArray(options.stdio)) { | |||
if (typeof options.stdio == 'string') { |
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.
Triple equals needed here.
break; | ||
case 'inherit': | ||
options.stdio = stdioStringToArray(options.stdio); | ||
break; |
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.
fwiw it looks like you can shorten these to
case 'ignore':
case 'pipe':
case 'inherit':
options.stdio = stdioStringToArray(options.stdio);
break;
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.
Wow, yep. Definitely missed that; thanks
90b64ff
to
aed4586
Compare
I guess I shouldn't squash until the entire review process is done right? |
options.stdio = stdioStringToArray(options.stdio); | ||
break; | ||
default: | ||
throw new TypeError(`Incorrect type of stdio option: ${options.stdio}`); |
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.
Nit: It would probably be more consistent with our other TypeError
messages if this were something like Unknown stdio option: ${options.stdio}
.
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.
Should the unknown option be embedded in the message? What happens if a complex JSON object is in there? One that is circular? Won't the attempt to stringify it fail? I don't think we generaly print the bad input in the message, we just say what is bad about the input.
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.
@sam-github grep -R 'new [A-Za-z]*Error' lib|grep '`'
does give about 10 positive results or so.
Fwiw I find it really useful when I can just take part of an error message and grep my sources for that bit with a reasonable expectation of instantly finding the offending lines. If you’re worried about failed stringifying, we can always wrap the input in util.inspect
; does that sound better?
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.
The fixed string part should be greppable and unique, as you say. Just tested, the stringification seems to just do [object Object]
for recursive and complex objects. Not useful, but not damaging.
git grep TypeError lib
is pretty clear: https://gist.github.com/f81b6d3e19ab6595244905575e55e082
There are many, many lines, and all the errors say WHAT is wrong with the input, but do not re-include the input itself in the message.
git grep TypeError.*\` lib
lib/dns.js: throw new TypeError(`"port" should be >= 0 and < 65536, got "${port}"`);
lib/internal/url.js: throw new TypeError('Value of `this` is not a URLSearchParams');
... (all examples of `this`, not of format strings)
So at this point, we have one example of a format string inputting an invalid argument into the type error message, and this would be the second, so now I'm convinced. The error should not echo the input back, it should include information that is not known to the caller - what is wrong with the input, not the input itself.
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.
git grep TypeError lib
Funny, why does git
(re-?)implement grep
?
So at this point, we have one example of a format string inputting an invalid argument into the type error message, and this would be the second, so now I'm convinced.
These are the positive results when not restricting to TypeError
s (or is there any particular reason you only looked for those?): https://gist.github.com/addaleax/cd3d3e5a6c7d752eaa1d15396e80608e (“only” 12 but still.)
But why does it really matter whether there are precedents for this? I can’t really see any situation in which this is harmful, and I can see cases in which echoing the input back might be helpful (very simple example: typos).
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 find randomness in APIs not helpful. When one API does it one way, and another does it another way, its confusing. If we believe that errors on invalid arguments to API calls should include the input, we should then start work on adding such information to every error, would make good code-and-learn contributions. If we don't believe that, and individual errors that say "X is wrong with argument Y" (maybe followed with) ": got/was/saw/whatever ${y}" are randomly formatted according to what looked good in a single PR or appeals to the taste of whoever happend to write and review that PR, we get a chaotic API. Btw, in your gist, lines 3 and 4 should be type errors to be consistent with line 2... the type of error chosen for those is another example of the kind of random API choice I would like us to make less of.
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.
@sam-github I opted to go with the TypeError: Unknown stdio option
although I do find it useful to have the corrupted input logged back to the developer. That being said, I don't think it's useful enough to go against consistency unless we do decide that it is beneficial to change all errors accordingly.
@addaleax I did have to use util.inspect
to prevent printing [Object Object]
. Not sure why I forgot to use that in the original PR
const malFormedOpts = {stdio: '33'}; | ||
const payload = {hello: 'world'}; | ||
|
||
assert.throws(() => fork(childScript, malFormedOpts), /Incorrect type/); |
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.
If possible, let's match the entire error message in the regular expression, so something like ^TypeError: Unknown stdio option: 33$/
or whatever the message ends up being.
Up to you, I think. In most cases, I prefer that we don't squash until the review process is done but others feel differently. |
options.stdio = stdioStringToArray(options.stdio); | ||
break; | ||
default: | ||
throw new TypeError(`Incorrect type of stdio option: ${options.stdio}`); |
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.
Should the unknown option be embedded in the message? What happens if a complex JSON object is in there? One that is circular? Won't the attempt to stringify it fail? I don't think we generaly print the bad input in the message, we just say what is bad about the input.
@@ -50,7 +50,20 @@ exports.fork = function(modulePath /*, args, options*/) { | |||
|
|||
args = execArgv.concat([modulePath], args); | |||
|
|||
if (!Array.isArray(options.stdio)) { | |||
if (typeof options.stdio === 'string') { | |||
const stdioStringToArray = (option) => { |
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.
You can make this a normal function, and pull it out of fork()
to the top level.
default: | ||
throw new TypeError(`Incorrect type of stdio option: ${options.stdio}`); | ||
} | ||
} else if (!Array.isArray(options.stdio)) { | ||
// Use a separate fd=3 for the IPC channel. Inherit stdin, stdout, | ||
// and stderr from the parent if silent isn't set. | ||
options.stdio = options.silent ? ['pipe', 'pipe', 'pipe', 'ipc'] : |
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.
You can use stdioStringToArray()
here too.
aed4586
to
79562f6
Compare
@cjihrig @sam-github changes commited. |
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.
Re-add the link to https://nodejs.org/api/child_process.html#child_process_options_stdio, which describes the meaning of this option. We need that info.
Otherwise, LGTM
79562f6
to
e00a6c6
Compare
@sam-github Done. |
ping @cjihrig @sam-github |
[`stdio`][] option. When this option is provided, it overrides `silent`. | ||
The array must contain exactly one item with value `'ipc'` or an error will | ||
be thrown. For instance `[0, 1, 2, 'ipc']`. | ||
* `stdio` {Array|String} Supports the Array and String versions of |
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 don't think we need to explicitly say "Supports the Array and String versions of..." since they are now the same. Maybe just a note to "see child_process.spawn()'s stdio"
@@ -455,6 +469,7 @@ function lookupSignal(signal) { | |||
if (typeof signal === 'number') | |||
return signal; | |||
|
|||
|
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.
Can you remove this blank line.
const errorRegexp = /^TypeError: Unknown stdio option$/; | ||
const malFormedOpts = {stdio: '33'}; | ||
const payload = {hello: 'world'}; | ||
const StringOpts = {stdio: 'pipe'}; |
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.
StringOpts -> stringOpts please
const child = fork(childScript, StringOpts); | ||
|
||
child.on('message', (message) => { | ||
assert.ok(message.foo); |
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.
Is there a strictEqual()
comparison that you can make here?
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.
Left a few comments. Looking good though.
e00a6c6
to
06bf0dd
Compare
Add string shortcut option for stdio parameter. PR-URL: nodejs#10866 Fixes: nodejs#10793
2513b29
to
710b468
Compare
@trendsetter37 Other than the error message text, it LGTM. |
Landed in 3268863, thanks. |
@sam-github My LGTM was conditional, I was wanting the error text to be changed first ... |
I am also wondering if this shouldn't be semver-major due to the change in throw conditions it introduces. |
Sorry, Brian, I misunderstood. And I can't figure out exactly what you want changed, I thought it was the fixes line. @mscdex Can you point me to your comment? I can fix and PR a follow up. @jasnell probably. That the conditions under which errors are thrown and the error message strings are part of the API should be mentioned in #7964 |
Its probably possible to create a backportable version of this that does not throw. Or even a version that doesn't throw, and then a tiny update PR following on, that makes it throw and will only go in 8.x? Should this be reverted and given more time? |
What about the error message text would you like changed? |
@sam-github In my first linked comment I suggested the same error message used for |
@sam-github saw this just now. Should I change this here or just let the change happen on #11044? |
@trendsetter37 I am fixing in #11044 |
@nodejs/lts This is a marginal case of If someone is passing a string to I guess this could be major... but its also a useful feature that would be nice to have on LTS. Is LTS just out of luck? |
Add string-shortcut option for stdio parameter.
closes #10793
Checklist
make -j4 test
(UNIX), orvcbuild test
(Windows) passesAffected core subsystem(s)
doc, child_process