- Notifications
You must be signed in to change notification settings - Fork0
Human-readable representation of Lua tables
License
Rosettea/inspect.lua
Folders and files
Name | Name | Last commit message | Last commit date | |
---|---|---|---|---|
Repository files navigation
This library transforms any Lua value into a human-readable representation. It is especially useful for debugging errors in tables.
The objective here is human understanding (i.e. for debugging), not serialization or compactness.
inspect
has the following declaration:local str = inspect(value, <options>)
.
value
can be any Lua value.
inspect
transforms simple types (like strings or numbers) into strings.
assert(inspect(1)=="1")assert(inspect("Hello")=='"Hello"')
Tables, on the other hand, are rendered in a way a human can read easily.
"Array-like" tables are rendered horizontally:
assert(inspect({1,2,3,4})=="{ 1, 2, 3, 4 }")
"Dictionary-like" tables are rendered with one element per line:
assert(inspect({a=1,b=2})==[[{ a = 1, b = 2}]])
The keys will be sorted alphanumerically when possible.
"Hybrid" tables will have the array part on the first line, and the dictionary part just below them:
assert(inspect({1,2,3,b=2,a=1})==[[{ 1, 2, 3, a = 1, b = 2}]])
Subtables are indented with two spaces per level.
assert(inspect({a={b=2}})==[[{ a = { b = 2 }}]])
Functions, userdata and any other custom types from Luajit are simply as<function x>
,<userdata x>
, etc.:
assert(inspect({f=print,ud=some_user_data,thread=a_thread} )==[[{ f = <function 1>, u = <userdata 1>, thread = <thread 1>}]])
If the table has a metatable, inspect will include it at the end, in a special field called<metatable>
:
assert(inspect(setmetatable({a=1}, {b=2})==[[{ a = 1 <metatable> = { b = 2 }}]]))
inspect
can handle tables with loops inside them. It will print<id>
right before the table is printed out the first time, and replace the whole table with<table id>
from then on, preventing infinite loops.
locala= {1,2}localb= {3,4,a}a[3]=b-- a references b, and b references aassert(inspect(a)=="<1>{ 1, 2, { 3, 4, <table 1> } }")
Notice that since botha
appears more than once in the expression, it is prefixed by<1>
and replaced by<table 1>
every time it appears later on.
inspect
has a second parameter, calledoptions
. It is not mandatory, but when it is provided, it must be a table.
options.depth
sets the maximum depth that will be printed out.When the max depth is reached,inspect
will stop parsing tables and just return{...}
:
localt5= {a= {b= {c= {d= {e=5}}}}}assert(inspect(t5, {depth=4})==[[{ a = { b = { c = { d = {...} } } }}]])assert(inspect(t5, {depth=2})==[[{ a = { b = {...} }}]])
options.depth
defaults to infinite (math.huge
).
These are the strings used byinspect
to respectively add a newline and indent each level of a table.
By default,options.newline
is"\n"
andoptions.indent
is" "
(two spaces).
localt= {a={b=1}}assert(inspect(t)==[[{ a = { b = 1 }}]])assert(inspect(t, {newline='@',indent="++"}),"{@++a = {@++++b = 1@++}@}"
options.process
is a function which allow altering the passed object before transforming it into a string.A typical way to use it would be to remove certain values so that they don't appear at all.
options.process
has the following signature:
localprocessed_item=function(item,path)
item
is either a key or a value on the table, or any of its subtablespath
is an array-like table built with all the keys that have been used to reachitem
, from the root.- For values, it is just a regular list of keys. For example, to reach the 1 in
{a = {b = 1}}
, thepath
will be{'a', 'b'}
- For keys, the special value
inspect.KEY
is inserted. For example, to reach thec
in{a = {b = {c = 1}}}
,the path will be{'a', 'b', 'c', inspect.KEY }
- For metatables, the special value
inspect.METATABLE
is inserted. For{a = {b = 1}}}
, the path{'a', {b = 1}, inspect.METATABLE}
means "the metatable of the table{b = 1}
".
- For values, it is just a regular list of keys. For example, to reach the 1 in
processed_item
is the value returned byoptions.process
. If it is equal toitem
, then the inspectedtable will look unchanged. If it is different, then the table will look different; most notably, if it'snil
,the item will dissapear on the inspected table.
Remove a particular metatable from the result:
localt= {1,2,3}localmt= {b=2}setmetatable(t,mt)localremove_mt=function(item)ifitem~=mtthenreturnitemendend-- mt does not appearassert(inspect(t, {process=remove_mt})=="{ 1, 2, 3 }")
The previous exaple only works for a particular metatable. If you want to makeall metatables, you can use thepath
parameter to checkwether the last element isinspect.METATABLE
, and returnnil
instead of the item:
localt,mt=...-- (defined as before)localremove_all_metatables=function(item,path)ifpath[#path]~=inspect.METATABLEthenreturnitemendendassert(inspect(t, {process=remove_all_metatables})=="{ 1, 2, 3 }")
Filter a value:
localanonymize_password=function(item,path)ifpath[#path]=='password'thenreturn"XXXX"endreturnitemendlocalinfo= {user='peter',password='secret'}assert(inspect(info, {process=anonymize_password})==[[{ password = "XXXX", user = "peter"}]])
This method isnot appropriate for saving/restoring tables. It is meant to be used by the programmer mainly while debugging a program.
If you are using luarocks, just run
luarocks install inspect
Otherwise, you can just copy the inspect.lua file somewhere in your projects (maybe inside a /lib/ folder) and require it accordingly.
Remember to store the value returned by require somewhere! (I suggest a local variable named inspect, although others might like table.inspect)
local inspect = require 'inspect' -- or --local inspect = require 'lib.inspect'
Also, make sure to read the license; the text of that license file must appear somewhere in your projects' files. For your convenience, it's included at the begining of inspect.lua.
This project usesTeal, a typed dialect of Lua (which generates plain lua files too)
If you want to send a pull request to this project, first of all, thank you! You will need to install the. You can install all of them by running:
make dev
When writing your PR, please make your modifications on theinspect.tl
file and then generate theinspect.lua
file from it. You will probably want to make sure that the tests are stillworking (github should run them from you, but they should run very fast). You can do both things in one go by just invoking
make
This will generateinspect.lua
, check it withluacheck and then launchbusted to run the specs.
If you are sending a pull request, you might want to add some specs in thespecs
folder.
Read it on the CHANGELOG.md file