pax_global_header00006660000000000000000000000064132226737340014523gustar00rootroot0000000000000052 comment=7fc6126e32854552843af7e26b218581db1f1b0f inspect.lua-3.1.1/000077500000000000000000000000001322267373400137525ustar00rootroot00000000000000inspect.lua-3.1.1/.travis.yml000066400000000000000000000013021322267373400160570ustar00rootroot00000000000000language: python sudo: false env: - LUA="lua=5.1" - LUA="lua=5.2" - LUA="lua=5.3" - LUA="luajit=2.0" - LUA="luajit=2.1" before_install: - pip install hererocks - hererocks lua_install -r^ --$LUA - export PATH=$PATH:$PWD/lua_install/bin # Add directory with all installed binaries to PATH install: - luarocks install luacheck - luarocks install busted - luarocks install luacov - luarocks install luacov-coveralls script: - luacheck --std max+busted *.lua spec - busted --verbose --coverage after_success: - luacov-coveralls --exclude $TRAVIS_BUILD_DIR/lua_install branches: except: - gh-pages notifications: email: on_success: change on_failure: always inspect.lua-3.1.1/CHANGELOG.md000066400000000000000000000026461322267373400155730ustar00rootroot00000000000000## v3.1.1 * Better handling of LuaJIT's `ctype` and `cdata` values (#34, thanks @akopytov) ## v3.1.0 * Fixes bug: all control codes are escaped correctly (instead of only the named ones such as \n). Example: \1 becomes \\1 (or \\001 when followed by a digit) * Fixes bug when using the `process` option in recursive tables * Overriding global `tostring` with inspect no longer results in an error. * Simplifies id generation, using less tables and metatables. ## v3.0.3 * Fixes a bug which sometimes displayed struct-like parts of tables as sequence-like due to the way rawlen/the # operator are implemented. ## v3.0.2 * Fixes a bug when a table was garbage-collected while inspect was trying to render it ## v3.0.1 * Fixes a bug when dealing with tables which have a __len metamethod in Lua >= 5.2 ## v3.0.0 The basic functionality remains as before, but there's one backwards-incompatible change if you used `options.filter`. * **Removed** `options.filter` * **Added** `options.process`, which can be used to do the same as `options.filter`, and more. * **Added** two new constants, `inspect.METATABLE` and `inspect.KEY` * **Added** `options.indent` & `options.newline`. ## v2.0.0 * Ability to deal with LuaJit's custom types * License change from BSD to MIT * Moved second parameter (depth) to options (options.depth) * Added a new parameter, options.filter. * Reimplemented some parts of the system without object orientation inspect.lua-3.1.1/MIT-LICENSE.txt000066400000000000000000000020501322267373400162210ustar00rootroot00000000000000Copyright (c) 2013 Enrique García Cota Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. inspect.lua-3.1.1/README.md000066400000000000000000000155071322267373400152410ustar00rootroot00000000000000inspect.lua =========== [![Build Status](https://travis-ci.org/kikito/inspect.lua.png?branch=master)](https://travis-ci.org/kikito/inspect.lua) [![Coverage Status](https://coveralls.io/repos/github/kikito/inspect.lua/badge.svg?branch=master)](https://coveralls.io/github/kikito/inspect.lua?branch=master) 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. Examples of use =============== `inspect` has the following declaration: `local str = inspect(value, )`. `value` can be any Lua value. `inspect` transforms simple types (like strings or numbers) into strings. ```lua 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: ```lua assert(inspect({1,2,3,4}) == "{ 1, 2, 3, 4 }") ``` "Dictionary-like" tables are rendered with one element per line: ```lua 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: ```lua assert(inspect({1,2,3,b=2,a=1}) == [[{ 1, 2, 3, a = 1, b = 2 }]]) ``` Subtables are indented with two spaces per level. ```lua assert(inspect({a={b=2}}) == [[{ a = { b = 2 } }]]) ``` Functions, userdata and any other custom types from Luajit are simply as ``, ``, etc.: ```lua assert(inspect({ f = print, ud = some_user_data, thread = a_thread} ) == [[{ f = , u = , thread = }]]) ``` If the table has a metatable, inspect will include it at the end, in a special field called ``: ```lua assert(inspect(setmetatable({a=1}, {b=2}) == [[{ a = 1 = { b = 2 } }]])) ``` `inspect` can handle tables with loops inside them. It will print `` right before the table is printed out the first time, and replace the whole table with `` from then on, preventing infinite loops. ```lua local a = {1, 2} local b = {3, 4, a} a[3] = b -- a references b, and b references a assert(inspect(a) == "<1>{ 1, 2, { 3, 4,
} }") ``` Notice that since both `a` appears more than once in the expression, it is prefixed by `<1>` and replaced by `
` every time it appears later on. ### options `inspect` has a second parameter, called `options`. It is not mandatory, but when it is provided, it must be a table. #### options.depth `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 `{...}`: ```lua local t5 = {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`). #### options.newline & options.indent These are the strings used by `inspect` to respectively add a newline and indent each level of a table. By default, `options.newline` is `"\n"` and `options.indent` is `" "` (two spaces). ``` lua local t = {a={b=1}} assert(inspect(t) == [[{ a = { b = 1 } }]]) assert(inspect(t, {newline='@', indent="++"}), "{@++a = {@++++b = 1@++}@}" ``` #### options.process `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: ``` lua local processed_item = function(item, path) ``` * `item` is either a key or a value on the table, or any of its subtables * `path` is an array-like table built with all the keys that have been used to reach `item`, from the root. * For values, it is just a regular list of keys. For example, to reach the 1 in `{a = {b = 1}}`, the `path` will be `{'a', 'b'}` * For keys, the special value `inspect.KEY` is inserted. For example, to reach the `c` 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}`". * `processed_item` is the value returned by `options.process`. If it is equal to `item`, then the inspected table will look unchanged. If it is different, then the table will look different; most notably, if it's `nil`, the item will dissapear on the inspected table. #### Examples Remove a particular metatable from the result: ``` lua local t = {1,2,3} local mt = {b = 2} setmetatable(t, mt) local remove_mt = function(item) if item ~= mt then return item end end -- mt does not appear assert(inspect(t, {process = remove_mt}) == "{ 1, 2, 3 }") ``` The previous exaple only works for a particular metatable. If you want to make *all* metatables, you can use the `path` parameter to check wether the last element is `inspect.METATABLE`, and return `nil` instead of the item: ``` lua local t, mt = ... -- (defined as before) local remove_all_metatables = function(item, path) if path[#path] ~= inspect.METATABLE then return item end end assert(inspect(t, {process = remove_all_metatables}) == "{ 1, 2, 3 }") ``` Filter a value: ```lua local anonymize_password = function(item, path) if path[#path] == 'password' then return "XXXX" end return item end local info = {user = 'peter', password = 'secret'} assert(inspect(info, {process = anonymize_password}) == [[{ password = "XXXX", user = "peter" }]]) ``` Gotchas / Warnings ================== This method is *not* appropriate for saving/restoring tables. It is meant to be used by the programmer mainly while debugging a program. Installation ============ 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. Specs ===== This project uses [busted](http://olivinelabs.com/busted/) for its specs. If you want to run the specs, you will have to install busted first. Then just execute the following from the root inspect folder: busted Change log ========== Read it on the CHANGELOG.md file inspect.lua-3.1.1/inspect.lua000066400000000000000000000231001322267373400161160ustar00rootroot00000000000000local inspect ={ _VERSION = 'inspect.lua 3.1.0', _URL = 'http://github.com/kikito/inspect.lua', _DESCRIPTION = 'human-readable representations of tables', _LICENSE = [[ MIT LICENSE Copyright (c) 2013 Enrique García Cota Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ]] } local tostring = tostring inspect.KEY = setmetatable({}, {__tostring = function() return 'inspect.KEY' end}) inspect.METATABLE = setmetatable({}, {__tostring = function() return 'inspect.METATABLE' end}) -- Apostrophizes the string if it has quotes, but not aphostrophes -- Otherwise, it returns a regular quoted string local function smartQuote(str) if str:match('"') and not str:match("'") then return "'" .. str .. "'" end return '"' .. str:gsub('"', '\\"') .. '"' end -- \a => '\\a', \0 => '\\0', 31 => '\31' local shortControlCharEscapes = { ["\a"] = "\\a", ["\b"] = "\\b", ["\f"] = "\\f", ["\n"] = "\\n", ["\r"] = "\\r", ["\t"] = "\\t", ["\v"] = "\\v" } local longControlCharEscapes = {} -- \a => nil, \0 => \000, 31 => \031 for i=0, 31 do local ch = string.char(i) if not shortControlCharEscapes[ch] then shortControlCharEscapes[ch] = "\\"..i longControlCharEscapes[ch] = string.format("\\%03d", i) end end local function escape(str) return (str:gsub("\\", "\\\\") :gsub("(%c)%f[0-9]", longControlCharEscapes) :gsub("%c", shortControlCharEscapes)) end local function isIdentifier(str) return type(str) == 'string' and str:match( "^[_%a][_%a%d]*$" ) end local function isSequenceKey(k, sequenceLength) return type(k) == 'number' and 1 <= k and k <= sequenceLength and math.floor(k) == k end local defaultTypeOrders = { ['number'] = 1, ['boolean'] = 2, ['string'] = 3, ['table'] = 4, ['function'] = 5, ['userdata'] = 6, ['thread'] = 7 } local function sortKeys(a, b) local ta, tb = type(a), type(b) -- strings and numbers are sorted numerically/alphabetically if ta == tb and (ta == 'string' or ta == 'number') then return a < b end local dta, dtb = defaultTypeOrders[ta], defaultTypeOrders[tb] -- Two default types are compared according to the defaultTypeOrders table if dta and dtb then return defaultTypeOrders[ta] < defaultTypeOrders[tb] elseif dta then return true -- default types before custom ones elseif dtb then return false -- custom types after default ones end -- custom types are sorted out alphabetically return ta < tb end -- For implementation reasons, the behavior of rawlen & # is "undefined" when -- tables aren't pure sequences. So we implement our own # operator. local function getSequenceLength(t) local len = 1 local v = rawget(t,len) while v ~= nil do len = len + 1 v = rawget(t,len) end return len - 1 end local function getNonSequentialKeys(t) local keys = {} local sequenceLength = getSequenceLength(t) for k,_ in pairs(t) do if not isSequenceKey(k, sequenceLength) then table.insert(keys, k) end end table.sort(keys, sortKeys) return keys, sequenceLength end local function getToStringResultSafely(t, mt) local __tostring = type(mt) == 'table' and rawget(mt, '__tostring') local str, ok if type(__tostring) == 'function' then ok, str = pcall(__tostring, t) str = ok and str or 'error: ' .. tostring(str) end if type(str) == 'string' and #str > 0 then return str end end local function countTableAppearances(t, tableAppearances) tableAppearances = tableAppearances or {} if type(t) == 'table' then if not tableAppearances[t] then tableAppearances[t] = 1 for k,v in pairs(t) do countTableAppearances(k, tableAppearances) countTableAppearances(v, tableAppearances) end countTableAppearances(getmetatable(t), tableAppearances) else tableAppearances[t] = tableAppearances[t] + 1 end end return tableAppearances end local copySequence = function(s) local copy, len = {}, #s for i=1, len do copy[i] = s[i] end return copy, len end local function makePath(path, ...) local keys = {...} local newPath, len = copySequence(path) for i=1, #keys do newPath[len + i] = keys[i] end return newPath end local function processRecursive(process, item, path, visited) if item == nil then return nil end if visited[item] then return visited[item] end local processed = process(item, path) if type(processed) == 'table' then local processedCopy = {} visited[item] = processedCopy local processedKey for k,v in pairs(processed) do processedKey = processRecursive(process, k, makePath(path, k, inspect.KEY), visited) if processedKey ~= nil then processedCopy[processedKey] = processRecursive(process, v, makePath(path, processedKey), visited) end end local mt = processRecursive(process, getmetatable(processed), makePath(path, inspect.METATABLE), visited) setmetatable(processedCopy, mt) processed = processedCopy end return processed end ------------------------------------------------------------------- local Inspector = {} local Inspector_mt = {__index = Inspector} function Inspector:puts(...) local args = {...} local buffer = self.buffer local len = #buffer for i=1, #args do len = len + 1 buffer[len] = args[i] end end function Inspector:down(f) self.level = self.level + 1 f() self.level = self.level - 1 end function Inspector:tabify() self:puts(self.newline, string.rep(self.indent, self.level)) end function Inspector:alreadyVisited(v) return self.ids[v] ~= nil end function Inspector:getId(v) local id = self.ids[v] if not id then local tv = type(v) id = (self.maxIds[tv] or 0) + 1 self.maxIds[tv] = id self.ids[v] = id end return tostring(id) end function Inspector:putKey(k) if isIdentifier(k) then return self:puts(k) end self:puts("[") self:putValue(k) self:puts("]") end function Inspector:putTable(t) if t == inspect.KEY or t == inspect.METATABLE then self:puts(tostring(t)) elseif self:alreadyVisited(t) then self:puts('
') elseif self.level >= self.depth then self:puts('{...}') else if self.tableAppearances[t] > 1 then self:puts('<', self:getId(t), '>') end local nonSequentialKeys, sequenceLength = getNonSequentialKeys(t) local mt = getmetatable(t) local toStringResult = getToStringResultSafely(t, mt) self:puts('{') self:down(function() if toStringResult then self:puts(' -- ', escape(toStringResult)) if sequenceLength >= 1 then self:tabify() end end local count = 0 for i=1, sequenceLength do if count > 0 then self:puts(',') end self:puts(' ') self:putValue(t[i]) count = count + 1 end for _,k in ipairs(nonSequentialKeys) do if count > 0 then self:puts(',') end self:tabify() self:putKey(k) self:puts(' = ') self:putValue(t[k]) count = count + 1 end if mt then if count > 0 then self:puts(',') end self:tabify() self:puts(' = ') self:putValue(mt) end end) if #nonSequentialKeys > 0 or mt then -- result is multi-lined. Justify closing } self:tabify() elseif sequenceLength > 0 then -- array tables have one extra space before closing } self:puts(' ') end self:puts('}') end end function Inspector:putValue(v) local tv = type(v) if tv == 'string' then self:puts(smartQuote(escape(v))) elseif tv == 'number' or tv == 'boolean' or tv == 'nil' or tv == 'cdata' or tv == 'ctype' then self:puts(tostring(v)) elseif tv == 'table' then self:putTable(v) else self:puts('<',tv,' ',self:getId(v),'>') end end ------------------------------------------------------------------- function inspect.inspect(root, options) options = options or {} local depth = options.depth or math.huge local newline = options.newline or '\n' local indent = options.indent or ' ' local process = options.process if process then root = processRecursive(process, root, {}, {}) end local inspector = setmetatable({ depth = depth, level = 0, buffer = {}, ids = {}, maxIds = {}, newline = newline, indent = indent, tableAppearances = countTableAppearances(root) }, Inspector_mt) inspector:putValue(root) return table.concat(inspector.buffer) end setmetatable(inspect, { __call = function(_, ...) return inspect.inspect(...) end }) return inspect inspect.lua-3.1.1/rockspecs/000077500000000000000000000000001322267373400157465ustar00rootroot00000000000000inspect.lua-3.1.1/rockspecs/inspect-1.2-2.rockspec000066400000000000000000000011451322267373400216040ustar00rootroot00000000000000package = "inspect" version = "1.2-2" source = { url = "https://github.com/kikito/inspect.lua/archive/v1.2.0.tar.gz", dir = "inspect.lua-1.2.0" } description = { summary = "Lua table visualizer, ideal for debugging", detailed = [[ inspect will print out your lua tables nicely so you can debug your programs quickly. It sorts keys by type and name, handles data recursion ]], homepage = "https://github.com/kikito/inspect.lua", license = "MIT " } dependencies = { "lua >= 5.1" } build = { type = "builtin", modules = { inspect = "inspect.lua" } } inspect.lua-3.1.1/rockspecs/inspect-2.0-1.rockspec000066400000000000000000000011451322267373400216020ustar00rootroot00000000000000package = "inspect" version = "2.0-1" source = { url = "https://github.com/kikito/inspect.lua/archive/v2.0.0.tar.gz", dir = "inspect.lua-2.0.0" } description = { summary = "Lua table visualizer, ideal for debugging", detailed = [[ inspect will print out your lua tables nicely so you can debug your programs quickly. It sorts keys by type and name, handles data recursion ]], homepage = "https://github.com/kikito/inspect.lua", license = "MIT " } dependencies = { "lua >= 5.1" } build = { type = "builtin", modules = { inspect = "inspect.lua" } } inspect.lua-3.1.1/rockspecs/inspect-3.0-1.rockspec000066400000000000000000000011641322267373400216040ustar00rootroot00000000000000package = "inspect" version = "3.0-1" source = { url = "https://github.com/kikito/inspect.lua/archive/v3.0.0.tar.gz", dir = "inspect.lua-3.0.0" } description = { summary = "Lua table visualizer, ideal for debugging", detailed = [[ inspect will print out your lua tables nicely so you can debug your programs quickly. It sorts keys by type and name and handles recursive tables properly. ]], homepage = "https://github.com/kikito/inspect.lua", license = "MIT " } dependencies = { "lua >= 5.1" } build = { type = "builtin", modules = { inspect = "inspect.lua" } } inspect.lua-3.1.1/rockspecs/inspect-3.0-2.rockspec000066400000000000000000000011641322267373400216050ustar00rootroot00000000000000package = "inspect" version = "3.0-2" source = { url = "https://github.com/kikito/inspect.lua/archive/v3.0.1.tar.gz", dir = "inspect.lua-3.0.1" } description = { summary = "Lua table visualizer, ideal for debugging", detailed = [[ inspect will print out your lua tables nicely so you can debug your programs quickly. It sorts keys by type and name and handles recursive tables properly. ]], homepage = "https://github.com/kikito/inspect.lua", license = "MIT " } dependencies = { "lua >= 5.1" } build = { type = "builtin", modules = { inspect = "inspect.lua" } } inspect.lua-3.1.1/rockspecs/inspect-3.0-3.rockspec000066400000000000000000000011641322267373400216060ustar00rootroot00000000000000package = "inspect" version = "3.0-3" source = { url = "https://github.com/kikito/inspect.lua/archive/v3.0.2.tar.gz", dir = "inspect.lua-3.0.2" } description = { summary = "Lua table visualizer, ideal for debugging", detailed = [[ inspect will print out your lua tables nicely so you can debug your programs quickly. It sorts keys by type and name and handles recursive tables properly. ]], homepage = "https://github.com/kikito/inspect.lua", license = "MIT " } dependencies = { "lua >= 5.1" } build = { type = "builtin", modules = { inspect = "inspect.lua" } } inspect.lua-3.1.1/rockspecs/inspect-3.0-4.rockspec000066400000000000000000000011641322267373400216070ustar00rootroot00000000000000package = "inspect" version = "3.0-4" source = { url = "https://github.com/kikito/inspect.lua/archive/v3.0.3.tar.gz", dir = "inspect.lua-3.0.3" } description = { summary = "Lua table visualizer, ideal for debugging", detailed = [[ inspect will print out your lua tables nicely so you can debug your programs quickly. It sorts keys by type and name and handles recursive tables properly. ]], homepage = "https://github.com/kikito/inspect.lua", license = "MIT " } dependencies = { "lua >= 5.1" } build = { type = "builtin", modules = { inspect = "inspect.lua" } } inspect.lua-3.1.1/spec/000077500000000000000000000000001322267373400147045ustar00rootroot00000000000000inspect.lua-3.1.1/spec/inspect_spec.lua000066400000000000000000000340671322267373400201000ustar00rootroot00000000000000local inspect = require 'inspect' local unindent = require 'spec.unindent' local is_luajit, ffi = pcall(require, 'ffi') local has_rawlen = type(_G.rawlen) == 'function' describe( 'inspect', function() describe('numbers', function() it('works', function() assert.equals("1", inspect(1)) assert.equals("1.5", inspect(1.5)) assert.equals("-3.14", inspect(-3.14)) end) end) describe('strings', function() it('puts quotes around regular strings', function() assert.equals('"hello"', inspect("hello")) end) it('puts apostrophes around strings with quotes', function() assert.equals("'I have \"quotes\"'", inspect('I have "quotes"')) end) it('uses regular quotes if the string has both quotes and apostrophes', function() assert.equals('"I have \\"quotes\\" and \'apostrophes\'"', inspect("I have \"quotes\" and 'apostrophes'")) end) it('escapes newlines properly', function() assert.equals('"I have \\n new \\n lines"', inspect('I have \n new \n lines')) end) it('escapes tabs properly', function() assert.equals('"I have \\t a tab character"', inspect('I have \t a tab character')) end) it('escapes backspaces properly', function() assert.equals('"I have \\b a back space"', inspect('I have \b a back space')) end) it('escapes unnamed control characters with 1 or 2 digits', function() assert.equals('"Here are some control characters: \\0 \\1 \\6 \\17 \\27 \\31"', inspect('Here are some control characters: \0 \1 \6 \17 \27 \31')) end) it('escapes unnamed control characters with 3 digits when they are followed by numbers', function() assert.equals('"Control chars followed by digits \\0001 \\0011 \\0061 \\0171 \\0271 \\0311"', inspect('Control chars followed by digits \0001 \0011 \0061 \0171 \0271 \0311')) end) it('backslashes its backslashes', function() assert.equals('"I have \\\\ a backslash"', inspect('I have \\ a backslash')) assert.equals('"I have \\\\\\\\ two backslashes"', inspect('I have \\\\ two backslashes')) assert.equals('"I have \\\\\\n a backslash followed by a newline"', inspect('I have \\\n a backslash followed by a newline')) end) end) it('works with nil', function() assert.equals('nil', inspect(nil)) end) it('works with functions', function() assert.equals('{ , , }', inspect({ print, type, print })) end) it('works with booleans', function() assert.equals('true', inspect(true)) assert.equals('false', inspect(false)) end) if is_luajit then it('works with luajit cdata', function() assert.equals('{ cdata: PTR, ctype, cdata: PTR }', inspect({ ffi.new("int", 1), ffi.typeof("int"), ffi.typeof("int")(1) }):gsub('(0x%x+)','PTR')) end) end describe('tables', function() it('works with simple array-like tables', function() assert.equals("{ 1, 2, 3 }", inspect({1,2,3})) end) it('works with nested arrays', function() assert.equals('{ "a", "b", "c", { "d", "e" }, "f" }', inspect({'a','b','c', {'d','e'}, 'f'})) end) if has_rawlen then it('handles arrays with a __len metatable correctly (ignoring the __len metatable and using rawlen)', function() local arr = setmetatable({1,2,3}, {__len = function() return nil end}) assert.equals("{ 1, 2, 3,\n = {\n __len = \n }\n}", inspect(arr)) end) end it('works with simple dictionary tables', function() assert.equals("{\n a = 1,\n b = 2\n}", inspect({a = 1, b = 2})) end) it('identifies tables with no number 1 as struct-like', function() assert.equals(unindent([[{ [2] = 1, [25] = 1, id = 1 } ]]), inspect({[2]=1,[25]=1,id=1})) end) it('identifies numeric non-array keys as dictionary keys', function() assert.equals("{ 1, 2,\n [-1] = true\n}", inspect({1, 2, [-1] = true})) assert.equals("{ 1, 2,\n [1.5] = true\n}", inspect({1, 2, [1.5] = true})) end) it('sorts keys in dictionary tables', function() local t = { 1,2,3, [print] = 1, ["buy more"] = 1, a = 1, [coroutine.create(function() end)] = 1, [14] = 1, [{c=2}] = 1, [true]= 1 } assert.equals(unindent([[ { 1, 2, 3, [14] = 1, [true] = 1, a = 1, ["buy more"] = 1, [{ c = 2 }] = 1, [] = 1, [] = 1 } ]]), inspect(t)) end) it('works with nested dictionary tables', function() assert.equals(unindent([[{ a = 1, b = { c = 2 }, d = 3 }]]), inspect( {d=3, b={c=2}, a=1} )) end) it('works with hybrid tables', function() assert.equals(unindent([[ { "a", { b = 1 }, 2, ["ahoy you"] = 4, c = 3 } ]]), inspect({ 'a', {b = 1}, 2, c = 3, ['ahoy you'] = 4 })) end) it('displays
instead of repeating an already existing table', function() local a = { 1, 2, 3 } local b = { 'a', 'b', 'c', a } a[4] = b a[5] = a a[6] = b assert.equals('<1>{ 1, 2, 3, <2>{ "a", "b", "c",
},
,
}', inspect(a)) end) describe('The depth parameter', function() local level5 = { 1,2,3, a = { b = { c = { d = { e = 5 } } } } } local keys = { [level5] = true } it('has infinite depth by default', function() assert.equals(unindent([[ { 1, 2, 3, a = { b = { c = { d = { e = 5 } } } } } ]]), inspect(level5)) end) it('is modifiable by the user', function() assert.equals(unindent([[ { 1, 2, 3, a = { b = {...} } } ]]), inspect(level5, {depth = 2})) assert.equals(unindent([[ { 1, 2, 3, a = {...} } ]]), inspect(level5, {depth = 1})) assert.equals(unindent([[ { 1, 2, 3, a = { b = { c = { d = {...} } } } } ]]), inspect(level5, {depth = 4})) assert.equals("{...}", inspect(level5, {depth = 0})) end) it('respects depth on keys', function() assert.equals(unindent([[ { [{ 1, 2, 3, a = { b = { c = {...} } } }] = true } ]]), inspect(keys, {depth = 4})) end) end) describe('the newline option', function() it('changes the substring used for newlines', function() local t = {a={b=1}} assert.equal("{@ a = {@ b = 1@ }@}", inspect(t, {newline='@'})) end) end) describe('the indent option', function() it('changes the substring used for indenting', function() local t = {a={b=1}} assert.equal("{\n>>>a = {\n>>>>>>b = 1\n>>>}\n}", inspect(t, {indent='>>>'})) end) end) describe('the process option', function() it('removes one element', function() local names = {'Andrew', 'Peter', 'Ann' } local removeAnn = function(item) if item ~= 'Ann' then return item end end assert.equals('{ "Andrew", "Peter" }', inspect(names, {process = removeAnn})) end) it('uses the path', function() local names = {'Andrew', 'Peter', 'Ann' } local removeThird = function(item, path) if path[1] ~= 3 then return item end end assert.equals('{ "Andrew", "Peter" }', inspect(names, {process = removeThird})) end) it('replaces items', function() local names = {'Andrew', 'Peter', 'Ann' } local filterAnn = function(item) return item == 'Ann' and '' or item end assert.equals('{ "Andrew", "Peter", "" }', inspect(names, {process = filterAnn})) end) it('nullifies metatables', function() local mt = {'world'} local t = setmetatable({'hello'}, mt) local removeMt = function(item) if item ~= mt then return item end end assert.equals('{ "hello" }', inspect(t, {process = removeMt})) end) it('nullifies metatables using their paths', function() local mt = {'world'} local t = setmetatable({'hello'}, mt) local removeMt = function(item, path) if path[#path] ~= inspect.METATABLE then return item end end assert.equals('{ "hello" }', inspect(t, {process = removeMt})) end) it('nullifies the root object', function() local names = {'Andrew', 'Peter', 'Ann' } local removeNames = function(item) if item ~= names then return item end end assert.equals('nil', inspect(names, {process = removeNames})) end) it('changes keys', function() local dict = {a = 1} local changeKey = function(item) return item == 'a' and 'x' or item end assert.equals('{\n x = 1\n}', inspect(dict, {process = changeKey})) end) it('nullifies keys', function() local dict = {a = 1, b = 2} local removeA = function(item) return item ~= 'a' and item or nil end assert.equals('{\n b = 2\n}', inspect(dict, {process = removeA})) end) it('prints inspect.KEY & inspect.METATABLE', function() local t = {inspect.KEY, inspect.METATABLE} assert.equals("{ inspect.KEY, inspect.METATABLE }", inspect(t)) end) it('marks key paths with inspect.KEY and metatables with inspect.METATABLE', function() local t = { [{a=1}] = setmetatable({b=2}, {c=3}) } local items = {} local addItem = function(item, path) items[#items + 1] = {item = item, path = path} return item end inspect(t, {process = addItem}) assert.same({ {item = t, path = {}}, {item = {a=1}, path = {{a=1}, inspect.KEY}}, {item = 'a', path = {{a=1}, inspect.KEY, 'a', inspect.KEY}}, {item = 1, path = {{a=1}, inspect.KEY, 'a'}}, {item = setmetatable({b=2}, {c=3}), path = {{a=1}}}, {item = 'b', path = {{a=1}, 'b', inspect.KEY}}, {item = 2, path = {{a=1}, 'b'}}, {item = {c=3}, path = {{a=1}, inspect.METATABLE}}, {item = 'c', path = {{a=1}, inspect.METATABLE, 'c', inspect.KEY}}, {item = 3, path = {{a=1}, inspect.METATABLE, 'c'}} }, items) end) it('handles recursive tables correctly', function() local tbl = { 1,2,3} tbl.loop = tbl inspect(tbl, { process=function(x) return x end}) end) end) describe('metatables', function() it('includes the metatable as an extra hash attribute', function() local foo = { foo = 1, __mode = 'v' } local bar = setmetatable({a = 1}, foo) assert.equals(unindent([[ { a = 1, = { __mode = "v", foo = 1 } } ]]), inspect(bar)) end) it('includes the __tostring metamethod if it exists', function() local foo = { foo = 1, __tostring = function() return 'hello\nworld' end } local bar = setmetatable({a = 1}, foo) assert.equals(unindent([[ { -- hello\nworld a = 1, = { __tostring = , foo = 1 } } ]]), inspect(bar)) end) it('includes an error string if __tostring metamethod throws an error', function() local foo = { foo = 1, __tostring = function() error('hello', 0) end } local bar = setmetatable({a = 1}, foo) assert.equals(unindent([[ { -- error: hello a = 1, = { __tostring = , foo = 1 } } ]]), inspect(bar)) end) it('does not allow collecting weak tables while they are being inspected', function() collectgarbage('stop') finally(function() collectgarbage('restart') end) local shimMetatable = { __mode = 'v', __index = function() return {} end, __tostring = function() collectgarbage() return 'shim' end } local function shim() return setmetatable({}, shimMetatable) end local t = shim() t.key = shim() assert.equals(unindent([[ { -- shim key = { -- shim = <1>{ __index = , __mode = "v", __tostring = } }, =
} ]]), inspect(t)) end) describe('When a table is its own metatable', function() it('accepts a table that is its own metatable without stack overflowing', function() local x = {} setmetatable(x,x) assert.equals(unindent([[ <1>{ =
} ]]), inspect(x)) end) it('can invoke the __tostring method without stack overflowing', function() local t = {} t.__index = t setmetatable(t,t) assert.equals(unindent([[ <1>{ __index =
, =
} ]]), inspect(t)) end) end) end) end) it('allows changing the global tostring', function() local save = _G.tostring _G.tostring = inspect local s = tostring({1, 2, 3}) _G.tostring = save assert.equals("{ 1, 2, 3 }", s) end) end) inspect.lua-3.1.1/spec/unindent.lua000066400000000000000000000014241322267373400172340ustar00rootroot00000000000000-- Unindenting transforms a string like this: -- [[ -- { -- foo = 1, -- bar = 2 -- } -- ]] -- -- Into the same one without indentation, nor start/end newlines -- -- [[{ -- foo = 1, -- bar = 2 -- }]] -- -- This makes the strings look and read better in the tests -- local getIndentPreffix = function(str) local level = math.huge local minPreffix = "" local len for preffix in str:gmatch("\n( +)") do len = #preffix if len < level then level = len minPreffix = preffix end end return minPreffix end local unindent = function(str) str = str:gsub(" +$", ""):gsub("^ +", "") -- remove spaces at start and end local preffix = getIndentPreffix(str) return (str:gsub("\n" .. preffix, "\n"):gsub("\n$", "")) end return unindent