This module contains a collection of various utilities and a "tracing" library.
local util = require('fb.util')
Supports the usual push_front
, pop_front
, push_back
, and pop_back
operations. May be indexed using the usual Lua 1-based indexing, or with a
stable index that doesn't change when elements are added or removed.
local deq = util.Deque()
deq:push_back(10)
deq:push_back(20)
deq:push_back(30)
print(deq[1]) -- prints 10
print(deq[3]) -- prints 30
print(deq:first) -- prints 1; the first element is at stable index 1
print(deq:last) -- prints 3; the last element is at stable index 3
deq:pop_front()
print(deq[1]) -- prints 20
print(deq[2]) -- prints 30
print(deq:get_stable(2)) -- prints 20
print(deq:first) -- prints 2
print(deq:last) -- prints 3
Modifies a table to always return a default value for nonexistent keys
(similar to Python's dict.get
method) and optionally store the default value
(similar to Python's dict.setdefault
method).
local tab1 = {foo = 10, bar = 20}
local tab2 = {foo = 10, bar = 20}
-- Note that the default value must be a callback that produces a
-- new object every time, just like for Python's collections.defaultdict
-- default value not stored in table
util.set_default(tab1, function() return 100 end)
print(tab1.baz) -- prints 100
for k,_ in pairs(tab1) print(k) end -- prints foo and bar
-- default value stored in table on first access
util.set_default(tab2, function() return 100 end, true)
print(tab1.baz) -- prints 100
for k,_ in pairs(tab2) print(k) end -- prints foo, bar, and baz
This is similar to os.time
, but it returns the time as a floating-point
number of seconds since Epoch (with microsecond precision).
Sleeps for a (floating point) number of seconds (with microsecond precision);
for example, util.sleep(0.1)
sleeps for 100 ms.
math.randomseed(util.random_seed())
util.create_temp_dir(prefix)
creates a temporary directory with a unique name
that starts with the given prefix; if prefix
is an absolute path, its
dirname indicates where the directory will be created; otherwise, it will
be created in a system-specific location (usually /tmp
).
-- Create a temp directory with default naming scheme; it will be named
-- starting with '/tmp/lua_temp.'
local name = util.create_temp_dir()
-- Create a temp directory in /usr/local/tmp with a name that starts with
-- 'foo'
local name = util.create_temp_dir('/usr/local/tmp/foo')
Richer error handling facilities: on_error
and finally
.
on_error
allows you to specify an error handler (just likexpcall
) that is called when a function raises an error. Unlikexpcall
, the error continues to propagate to any handlers higher on the stack. (And unlike reraising the error fromxpcall
yourself, the backtrace at the point of the initial error is preserved)finally
allows you to specify a handler that is always called, whether or not the function called raises an error. (If the handler is called due to an error, the error continues to propagate after the handler returns)
The syntax is the same as for (Lua 5.2) xpcall
local eh = require('fb.util.error')
-- Execute handler(err) if some_function(function_args...) throws
eh.on_error(some_function, handler, function_args...)
-- Execute handler(err) always, regardless of whether
-- function(function_args...) throws
eh.finally(some_function, handler, function_args...)
Note that this module redefines the standard pcall
and xpcall
functions
so they maintain some necessary internal state; this slows down error
processing (in our simple test, the cost of raising in a simple pcall
went from 120 to 600 nanoseconds, which is unlikely to be significant).
fb.util.trace
implements a hierarchical tracing facility.
On one side, the tracing facility allows you to fire trace events identified by a four-component key. On the other side, you can register "handlers" that are called whenever events matching specified patterns fire.
At the core, the function trace() allows you to fire a trace event
("probe"), which is a {key, args}
tuple.
The key is made up of 4 components in decreasing order of significance:
DOMAIN
, MODULE
, FUNCTION
, and EVENT
, separated by colon (:
). The
meaning of the four components is somewhat arbitrary:
DOMAIN
specifies the high-level activity that your program is undertaking (for example, if your program is training multiple neural nets in sequence, this could be the name of the net being trained)MODULE
identifies the library / module,FUNCTION
identifies the location in the code (usually the name of a function, appropriately qualified to distinguish it in the library)EVENT
indicates the event within a function (arbitrary name, although "entry" and "return" are used to signify entry and return to/from the function, respectively; see trace_function() below)
args
is an arbitrary tuple (although some probes have specific meanings for
args, see below).
The tracing facility sets args.time
to the current time (as a floating point
number of seconds since Epoch).
namespace()
will set the default first components of the key for the
duration of its callback; usually, this is used to set the DOMAIN
around
calls into a module, and the MODULE
within a module.
trace_function()
wraps a function execution and fires two events, with the
EVENT
components set to "entry" and "return", respectively. "entry" sets
args.args
to the list of arguments passed to the function; "return" sets
args.ret
to the list of values returned by the function, or args.error
to
the error string if the function threw an error. trace_function also tracks
function probe nesting level (which can be used for prettily indented output).
Callers may register "handlers" that will be called when certain probes
(matching specified patterns) fire. Handlers are registered using add_handler
and remove_handler
; handlers are called as handler(key, args, nesting_level)
.
Debugging code can be valuable, but verbose. The dbg module provides a granular, hierarchical facility for selecting which debug output to produce at runtime. The DBG environment variable consists of a comma-separated list of module=threshold pairs. The dbg lua facility provides a factory for module-specific print functions:
local dbg = reqire('fb.util.dbg') local dprint = dbg.new('myModule')
...
dprint(1, "sort of verbose") -- A dprint(10, "verbose") -- B
Ordinarily the dprint's are silent. When the DBG environment variable is set to 'myModule=1', the print statement labeled 'A' will start firing. When it is set to 'myModule=100', both A and B will fire.
This module implements a handler for the tracing module (see above) that logs the probe key, time, and nesting level for all probes matching given patterns. It can then print the log (with timing information) prettily to standard output. See the comments in the code.