Repository navigation
How do I implement MyObject.inspect(depth, inspectOpts)? #8442
Description
Activity
- addedquestionIssues asking questions about Node.js.Issues asking questions about Node.js.utilIssues and PRs related to the built-in util module.Issues and PRs related to the built-in util module.
on Sep 8, 2016 cc @addaleax since you've been doing some work w.r.t. util.inspect recently.
Hi @Havvy – I agree, the examples don’t really go into that aspect. I probably have time to look into that later, but it would be really cool if you could start out with basically showing a full example with the output you’d like to see (and leave out the things you’re unsure about)?
Here's an initial attempt. Feel free to warp completely. I'm not all that good at documentation. I'm assuming that the inspect will never be called such that you need to handle not returning anything. The main goal here is to tell me the main conventions and logic the I should be thinking about when implementing things, especially recursive things. I went with an example of a Box because it's the simplest thing I could think of that embodied all of the major issues you should run into when implementing this function.
When implementing
inspecton your object, to properly respect the recursion limit, you must use the depth value properly. When you want to include the inspected value of another object, callutil.inspectwith your inspect options but change thedepthto ___ unlessdepthisnull.If you want to support custom colors (and no colors), you can style your text by doing
....To support the
breakLengthvalue, if the size of your returned string is greater than thebreakLengthvalue, then you should break the return value into multiple lines. This is best done by...1. You don't need to modifybreakLengthwhen passing it to your object.If your object is an arraylike, remember to check the
maxArrayLengthvalue.And to combine all of these, we look at implementing
inspecton aBoxobject. We want the box object to display its "value" property surrounded by "Box<>"2. For example, a box of an empty object would display as "Box<{}>" while a box of a longer object would show up like the following:Box< { foo: "A long string." } >The implementation of this would look like:
function Box(value) { this.value = value; } Box.prototype[util.inspect.custom] = function (depth, inspectArgs) { // impl goes here with comments explaining nuances. }
Footnotes:
- I can't actually think of a good general solution here.
- This should be colored cyan (a.k.a. special) in the docs.
- addedgood first issueIssues that are suitable for first-time contributors.Issues that are suitable for first-time contributors.
on Sep 9, 2016 Hmm. How close would this example get to what you want to show?
const util = require('util'); class Box { constructor(value) { this.value = value; } [util.inspect.custom](depth, options) { const newOptions = Object.assign({}, options, { depth: options.depth - depth }); return options.stylize('Box', 'special') + '<' + util.inspect(this.value, newOptions) + '>'; } } util.inspect.defaultOptions.colors = true; console.log(new Box({})) console.log(new Box({ aaaaaaaaaaaaaaaaa: 1, aaaaaaaaaaaaaaaab: 2, aaaaaaaaaaaaaaaae: 3 })) console.log(new Box({a:{b:{c:{d:{}}}}}));
Output:
Box<{}> Box<{ aaaaaaaaaaaaaaaaa: 1, aaaaaaaaaaaaaaaab: 2, aaaaaaaaaaaaaaaae: 3 }> Box<{ a: [Object] }>
I agree, there are some parts, like checking
maxArrayLengthand friends, that are not that easy to implement, but your suggestions sounds pretty solid to me.Reacted by Ryan ScheelIt's close.
It doesn't seem to be respecting depth properly.
const box = new Box({x: 5}); console.log(util.inspect(box)); console.log(util.inspect({wrap: box})); console.log(util.inspect({wrap: {wrap: box} }); console.log(util.inspect({wrap: {wrap: {wrap: box} } })); console.log(util.inspect({wrap: {wrap: {wrap: {wrap: box} } } })); // Expected Outputs // Box<{ x: 5 }> // { wrap: Box<{ x: 5 }> } // { wrap: { wrap: { Box<[Object]> } } // { wrap: { wrap: { wrap: [Box] } } } // { wrap: { wrap: { wrap: [Object] } } } // Actual Outputs // Box<{ x: 5 }> // { wrap: Box<{ x: 5 }> } // { wrap: { wrap: Box<{ x: 5 }> } } // { wrap: { wrap: { wrap: Box<{ x: 5 }> } } } // { wrap: { wrap: { wrap: [Object] } } }
Experimenting with util.inspect, it shows the short form (e.g.
"[Object]") if the depth is less than zero.This line
depth: options.depth - depthactually sets the depth to 0, and of course, you're not checkingdepthto see if you should not recurse downwards or not. As such,let box = new Box(5); for (var ix = 0; ix < 10; ix += 1) { box = new Box(box); } util.inspect(box)will show all 10 or so levels of boxing instead of stopping earlier. Also,null - 1evaluates to-1, so we need to specifically check for that. (Why was null chosen as the value instead of Infinity?)So, taking those into consideration, here's a better version:
class Box { constructor(value) { this.value = value; } inspect(depth, options) { if (depth < 0) { return options.stylize('[Box]', 'special'); } const newOptions = Object.assign({}, options, { depth: options.depth === null ? null : options.depth - 1 }); return options.stylize('Box', 'special') + '<' + util.inspect(this.value, newOptions) + '>'; } }
And here's code dealing with whitespace. (I haven't actually tested this code yet)
class Box { constructor(value) { this.value = value; } inspect(depth, options) { if (depth < 0) { return options.stylize('[Box]', 'special'); } const newOptions = Object.assign({}, options, { depth: options.depth === null ? null : options.depth - 1 }); // Four space padding because that's the size of "Box<". const padding = " "; const inner = util.inspect(this.value, newOptions).replace(/\n/g, "\n" + padding); return options.stylize('Box', 'special') + '<' + inner + '>'; } }
@Havvy I guess that looks pretty okay, and trying it out seems to work just fine – do you want to open a PR against
doc/api/util.mdwith this example?- added a commit that references this issue
on Oct 26, 2016 - added a commit that references this issue
on Nov 3, 2016 - added 2 commits that reference this issue
on Nov 17, 2016 - addeddocIssues and PRs related to Node.js documentation.Issues and PRs related to Node.js documentation.and removeddocIssues and PRs related to Node.js documentation.Issues and PRs related to Node.js documentation.
on Dec 1, 2016 I’m going to close this as #8875 has landed and there hasn’t been any new discussion since then. If you feel like there’s more to this issue, feel free to re-open, or open a new issue or pull request. :)
I want to implement inspect for my own object. I want to mostly follow the semantics of
inspectfor non-custom implementations, but just by going by the documentation alone, I have no possible way of doing so. What conventions are there? How do I recursively callinspectwhile respecting depth? Do I need to handle the case of depth being null?The example ignores the parameters passed to it completely currently. Maybe if we had an example of a
Boxobject that inspects as"Box<InnerInspectValue>"so e.g.inspect({value: "a", inspect: ... })returns"Box<'a'>"but the value can be anything (including over objects with custom inspect impls).