kikito / inspect.lua

Human-readable representation of Lua tables
https://github.com/kikito/inspect.lua
MIT License
1.38k stars 196 forks source link

Option to turn off metatable output #6

Closed dpashkevich closed 11 years ago

dpashkevich commented 11 years ago

I often find it unnecessary to include metatables in debug output. E.g. I want to inspect the own state of a class instance (defined via classlib or similar library) and don't care about all the "inherited" methods.

Having an option (perhaps as 3rd parameter to inspect() function) to exclude metatables would really unclutter debug output.

kikito commented 11 years ago

Hi, thanks for taking the time to open an issue.

I think your requirement is too specific to be included in inspect as an extra parameter. However, I can give you a couple workarounds:

First, take into account, inspect will only print each table (or metatable) only once. If it finds a table again, it will print <1>, <2> and so on, taking very little space.

local obj1 = MyClass:new(...)
local obj2 = MyClass:new(...)
print(inspect({obj1=obj1, obj2=obj2}))

In the printed out format, obj1 will have the "full metatable info", but obj2 will just have a single line.

Extending this a bit more, you can make sure that the metatable information is not printed in any of your tables, if you print it out in advance. In other words:

print(inspect({MyClass, obj1=obj1, obj2=obj2}))

It will print MyClass first, and then use <1> for the metatable data of obj1 and obj2, taking only 1 line of space. You will just have to scroll down a bit, but your objects will have (mostly) no metatable info.

There's a second way of doing this, which is temporarily modifying getmetatable to not return the metatables you don't want. I don't like this method very much, because it could leave getmetatable in a bad state if somehing goes wrong. Here's the idea:

local inspect = require 'inspect'
local ignoredMetatables = {MyClass1=1, MyClass2=1, MyClass3=1};

local oldGetmetatable = getmetatable
local selectiveGetmetatable = function(t)
  var mt = oldGetmetatable(t)
  if ignoredMetatables[mt] then return nil end
  return mt
end
var selectiveInspect = function(...)
  getmetatable = selectiveGetmetatable
  local result = inspect(...)
  getmetatable = oldGetmetatable
  return result
end

That will create a specialized inspect function that will ignore the tables you include in ignoredMetatables (the tables having them as metatables will appear to return nil).

I hope these two workarounds help you. For now, I will close down this issue.

Regards!

dpashkevich commented 11 years ago

Thanks for quick response.

The first workaround doesn't look practical in my cases. A common scenario would be to output something in a function that's called repetitively, e.g.

function Cache:get(id)
    local o = self.items[id]
    print("Object retrieved from cache: ", inspect(o))
    return o
end

So doing inspect({getmetatable(o), o}) doesn't help at all, I would have to "scroll down a bit" anyway and that's exactly what I'm trying to avoid. The output can be quite long since in OOP-styled code the metatables can be quite big and link one to another ("inheritance chain").

The second approach looks more like what I was thinking of, only I think I can just temporarily delete a metatable for the inspected object instead of overriding the global getmetatable() function:

function inspectOwn(t, d)
    mt = getmetatable(t)
    setmetatable(t, nil)
    inspect(t,d)
    setmetatable(t, mt)
end

I don't think my request is too specific but you're the boss :)

kikito commented 11 years ago

I'm repoening this in order to consider it again. I'm thinking about adding an options param with an options.ignore parameter to ignore certain tables.

kikito commented 11 years ago

On the new version of inspect (2.0.0) you can use the filter option to hide anything you want. For example:

local obj1 = MyClass:new(...)
local obj2 = MyClass:new(...)
print(inspect({obj1=obj1, obj2=obj2}, {filter = { MyClass} }))

The <metatable> field will still appear, but its value will be <filtered>.

{
  obj1 = {
    name = "Peter",
    <metatable> = <filtered>
  }
}

Sorry it took me so long to come around to fix this. Thanks again for reporting the issue, and regards!

dpashkevich commented 11 years ago

Thanks! I'll check out the new version tomorrow. Are there any other highlights of 2.0.0?

kikito commented 11 years ago

You are right, of course - the changes are not obvious.

I have included a changelog file: https://github.com/kikito/inspect.lua/blob/master/CHANGELOG.md

This version is 2.0.0 because it is interface-breacking (the second parameter was a number before, but now it is a table). But for usual cases, inspect should work just like before.

dpashkevich commented 11 years ago

(the second parameter was a number before, but now it is a table) I suggest supporting the older syntax because it's also shorter if one doesn't want to set other options. Created pull request #8, it's fairly trivial