-
Notifications
You must be signed in to change notification settings - Fork 29.6k
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
Deprecate object.inspect for custom inspection #16393
Conversation
'Custom inspection function on Objects via .inspect() is deprecated', | ||
'DEP0079' | ||
); | ||
} |
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’d want to generate a deprecated function every time value.inspect
is present. I’d prefer at least checking value[customInspectSymbol]
first, before going through this trouble. (And if you want to go further, maybe a WeakMap to keep the generated closures cached for performance – but that might not be that important for deprecated features…)
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.
It should also only trigger in case it is a function. Right now it would always trigger.
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.
@addaleax OK, took care of the "it will generate a deprecated function every time" issue.
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 simplicity, I'd fire the warning here, and not delegate it to the actual call, or just set a flag and fire next to the call. That saves the closure creation.
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.
@BridgeAR Yeah, definitely should have a typeof
check there. Will add that in. Thanks!
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.
typeof
check added along with test that exercises the additional code branch.
c028092
to
f5ced4f
Compare
That was my thought in #16266. |
Kind of true for nearly any runtime deprecation we implement, isn't it? (And a good reason not to accept them lightly.) |
doc/api/util.md
Outdated
* `msg` {string} A warning message to display when the deprecated function is | ||
invoked. | ||
* `code` {string} A deprecation code. See the [list of deprecated APIs][] for a | ||
list of codes. deprecations.html#deprecations_list_of_deprecated_apis |
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.
Seems like a typo)
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.
@vsemozhetbyt Whoops, yes. will fix. Thanks!
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.
@vsemozhetbyt typo fixed!
if (!codesWarned[code]) { | ||
process.emitWarning(msg, 'DeprecationWarning', code, deprecated); | ||
codesWarned[code] = true; | ||
} |
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.
This should be obsolete due to the warned
variable. It should actually do exactly this.
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.
@BridgeAR No, warned
doesn't take care of this situation. warned
is scoped to the function so this will emit twice currently:
{
const fn1 = util.deprecate(() => { ... }, 'foo', 'DEP0001');
const fn2 = util.deprecate(() => { ... }, 'foo', 'DEP0001');
fn1();
fn2();
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.
That is correct but normally the deprecation function is only called once with a message. We could remove the warned
variable though as it got obsolete with codesWarned
.
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.
@BridgeAR I thought about removing warned
but it's still used if code
is not provided.
'Custom inspection function on Objects via .inspect() is deprecated', | ||
'DEP0079' | ||
); | ||
} |
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.
It should also only trigger in case it is a function. Right now it would always trigger.
f5ced4f
to
74f9c7c
Compare
This is a little more complex since there's nothing wrong with the call to |
doc/api/util.md
Outdated
time it is called. After the warning is emitted, the wrapped `function` | ||
is called. | ||
`DeprecationWarning` using the `process.on('warning')` event. The warning will | ||
be emitted and printed to `stderr` the first time it is called. After the |
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 the 'it' here a bit confusing?
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 the 'it' here a bit confusing?
@vsemozhetbyt Yes. I'll reword it.
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.
OK, clarified now, I hope.
CI comments:
|
77e02ee
to
61e875a
Compare
Squashedi into four logical commits.
|
AIX build failure: |
@gireeshpunathil Already investigated and fixed. A bunch of stalled citgm processes. See nodejs/build#900 (comment). |
@gireeshpunathil Oh whoops, sorry, clearly not the same thing! Yeah, thanks! (EDIT: Well, the |
Retrying on AIX: https://ci.nodejs.org/job/node-test-commit-aix/9585/ |
@gireeshpunathil OK, I see the source of my confusion. The Removing tools/gyp/pylib/gyp/input.pyc
Removing tools/gyp/pylib/gyp/simple_copy.pyc
Removing tools/gyp/pylib/gyp/xcode_emulation.pyc
Removing tools/gyp_node.pyc
stderr:
at org.jenkinsci.plugins.gitclient.CliGitAPIImpl.launchCommandIn(CliGitAPIImpl.java:1924)
at org.jenkinsci.plugins.gitclient.CliGitAPIImpl.launchCommandIn(CliGitAPIImpl.java:1892)
at org.jenkinsci.plugins.gitclient.CliGitAPIImpl.launchCommandIn(CliGitAPIImpl.java:1888)
at org.jenkinsci.plugins.gitclient.CliGitAPIImpl.launchCommand(CliGitAPIImpl.java:1533)
at org.jenkinsci.plugins.gitclient.CliGitAPIImpl.launchCommand(CliGitAPIImpl.java:1545)
at org.jenkinsci.plugins.gitclient.CliGitAPIImpl.clean(CliGitAPIImpl.java:732)
at hudson.plugins.git.GitAPI.clean(GitAPI.java:311)
at sun.reflect.GeneratedMethodAccessor61.invoke(Unknown Source)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:55)
at java.lang.reflect.Method.invoke(Method.java:508)
at hudson.remoting.RemoteInvocationHandler$RPCRequest.perform(RemoteInvocationHandler.java:884)
at hudson.remoting.RemoteInvocationHandler$RPCRequest.call(RemoteInvocationHandler.java:859)
at hudson.remoting.RemoteInvocationHandler$RPCRequest.call(RemoteInvocationHandler.java:818)
at hudson.remoting.UserRequest.perform(UserRequest.java:152)
at hudson.remoting.UserRequest.perform(UserRequest.java:50)
at hudson.remoting.Request$2.run(Request.java:332)
at hudson.remoting.InterceptingExecutorService$1.call(InterceptingExecutorService.java:68)
at java.util.concurrent.FutureTask.run(FutureTask.java:277)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1160)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
at hudson.remoting.Engine$1$1.run(Engine.java:85)
at java.lang.Thread.run(Thread.java:785)
at ......remote call to Channel to /140.211.9.100(Native Method)
at hudson.remoting.Channel.attachCallSiteStackTrace(Channel.java:1654)
at hudson.remoting.UserResponse.retrieve(UserRequest.java:311)
at hudson.remoting.Channel.call(Channel.java:905)
at hudson.remoting.RemoteInvocationHandler.invoke(RemoteInvocationHandler.java:257)
at com.sun.proxy.$Proxy77.clean(Unknown Source)
at org.jenkinsci.plugins.gitclient.RemoteGitImpl.clean(RemoteGitImpl.java:450)
at hudson.plugins.git.extensions.impl.CleanCheckout.onCheckoutCompleted(CleanCheckout.java:30)
at hudson.plugins.git.GitSCM.checkout(GitSCM.java:1193)
at hudson.scm.SCM.checkout(SCM.java:495)
at hudson.model.AbstractProject.checkout(AbstractProject.java:1212)
at hudson.model.AbstractBuild$AbstractBuildExecution.defaultCheckout(AbstractBuild.java:566)
at jenkins.scm.SCMCheckoutStrategy.checkout(SCMCheckoutStrategy.java:86)
at hudson.model.AbstractBuild$AbstractBuildExecution.run(AbstractBuild.java:491)
at hudson.model.Run.execute(Run.java:1737)
at hudson.matrix.MatrixRun.run(MatrixRun.java:146)
at hudson.model.ResourceController.execute(ResourceController.java:97)
at hudson.model.Executor.run(Executor.java:421)
Run condition [Always] enabling perform for step [[]] |
@Trott - thanks. Probably all are root caused by one issue (stale processes in the system from previous runs), and manifest differently based on which code part suffers (g++/java). |
@@ -107,30 +107,50 @@ environment variable set, then it will not print anything. | |||
Multiple comma-separated `section` names may be specified in the `NODE_DEBUG` | |||
environment variable. For example: `NODE_DEBUG=fs,net,tls`. | |||
|
|||
## util.deprecate(function, string) | |||
## util.deprecate(fn, msg[, code]) |
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: s/fn/function
, s/msg/message
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.
Not looking for a debate here, but do want to make sure we have broad agreement and perhaps something that needs an entry in the style guide or something:
If I'm not mistaken, we try to use the same argument name in the docs as we use for the variables in the source code.
I believe there are two reasons, but correction welcome. First, it makes the reading of source code along with docs easier. Second, it allows the mapping of documentation variable names to variable names in stack traces possible.
If I'm right about that, then we can not use function
as it is a keyword and not a valid variable name. We use fn
and msg
elsewhere in the docs. I'm inclined to leave them. They are pretty self-explanatory, and get an explanation immediately below anyway.
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.
(More here: #20489.)
@@ -18,6 +18,10 @@ function objectToString(o) { | |||
return Object.prototype.toString.call(o); | |||
} | |||
|
|||
// Keep a list of deprecation codes that have been warned on so we only warn on | |||
// each one once. | |||
const codesWarned = {}; |
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.
ooo nice, this crosses a long standing todo off my list, thank you!
This is |
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.
LGTM
lib/util.js
Outdated
const maybeCustomInspect = value[customInspectSymbol] || value.inspect; | ||
let maybeCustom = value[customInspectSymbol]; | ||
|
||
if (!maybeCustom && value.inspect && value.inspect !== exports.inspect && |
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 the && value.inspect
part unnecessary? The two remaining checks would cover that case, and I'd think would be faster.
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.
@cjihrig Indeed, that appears to be the case. I've removed it. Thanks.
In test-util-inspect, apply ESLint exception for accessor-pairs rule narrowly. It had been applied to nearly the whole file, but is only needed for two lines.
Improve documentation for `util.deprecate()`. In particular, provide complete function signature, document arguments, and document return value.
If another function has already emitted the deprecation warning with the same code as the warning that is about to be emitted, do not emit the warning.
2907af3
to
3916830
Compare
Change documentation-only deprecation for custom inspection using `object.inspect` property to a runtime deprecation. Refs: nodejs#15549
3916830
to
90cccae
Compare
Latest changes CI: https://ci.nodejs.org/job/node-test-pull-request/11364/ |
Still LGTM |
In test-util-inspect, apply ESLint exception for accessor-pairs rule narrowly. It had been applied to nearly the whole file, but is only needed for two lines. PR-URL: nodejs#16393 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Colin Ihrig <[email protected]>
Improve documentation for `util.deprecate()`. In particular, provide complete function signature, document arguments, and document return value. PR-URL: nodejs#16393 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Colin Ihrig <[email protected]>
If another function has already emitted the deprecation warning with the same code as the warning that is about to be emitted, do not emit the warning. This is a breaking change. Previously, different functions could emit the same deprecation warning multiple times. This was a known bug rather than a feature, but this change is being treated as a breaking change out of caution. Identical deprecation warnings should not be emitted. PR-URL: nodejs#16393 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Colin Ihrig <[email protected]>
Change documentation-only deprecation for custom inspection using `object.inspect` property to a runtime deprecation. This is a breaking change. Custom inspection via `object.inspect` is deprecated because there is a more robust Symbol-based alternative to `.inspect` and the custom inspection via `object.inspect` feature means that people can accidentally break `console.log()` simply by attaching a `.inspect` property to their objects. Note that since this is a deprecation, the custom inspection will still work. The breaking change is simply the printing of a warning which could alarm users, break tests or other things that might be dependent on specific output, etc. PR-URL: nodejs#16393 Ref: nodejs#15549 Reviewed-By: James M Snell <[email protected]> Reviewed-By: Matteo Collina <[email protected]> Reviewed-By: Colin Ihrig <[email protected]>
Argh, I probably should have run CITGM. Sorry about that. Here we go: https://ci.nodejs.org/view/Node.js-citgm/job/citgm-smoker/1078/ |
CITGM looks OK to me, but I sure wouldn't mind a more CITGM-experienced pair of eyes looking at it. Basically, I looked at what packages failed, and compared that to the failures in the previous several runs. If I didn't see mine adding anything to the list, I shrugged it off. Where I did see mine adding a package or two, I checked the errors and it was usually something unrelated like an install failure. |
@nodejs/citgm ^^^^^ |
There are two semver-major changes here:
deprecate object.inspect for custom inspection. See Deprecate object.inspect for custom inspection #15549.
change
util.deprecate()
so that if the same deprecation code is used on multiple calls toutil.deprecate()
, it only prints the deprecation warning once. This makes it so that every call toutil.inspect()
doesn't print a new deprecation message (since it will re-create the custom inspect function each time). This may not technically be semver-major, but since the deprecation is semver-major and it kinda needs this, might as well be safe and bundle them.Refs: #15549
Checklist
make -j4 test
(UNIX), orvcbuild test
(Windows) passesAffected core subsystem(s)
util