9

I'm using xdg-menu-to-awesome-wm to generate a Lua file containing the GNOME menu, for inclusion in Awesome WM. Since the generator script may not be installed, I need some way for Lua to only require the menu module if it exists.

I don't want to go looking through file names, since it could be anywhere in package.path. One option would be to ignore the exception created when the module does not exist, but I'd rather not ignore any other exceptions - I do want to know if the module contains any syntax or other errors. The reference unfortunately doesn't specify which exceptions can be generated, so I'm not sure how to do it.

l0b0
  • 55,365
  • 30
  • 138
  • 223
  • Just had this issue today, kind of appalled at how many people in the answers section did not seem to realize that a module not being found is not the same thing as the module existing but having syntax errors, or (worse yet) requiring modules of their own which themselves do not exist or have syntax errors. finnw's answer is just what I needed – Thomas Nov 12 '15 at 08:52

4 Answers4

17

If you need to distinguish between a missing module and a syntax error, you can directly access the searcher functions in package.searchers.

These functions will:

  • Return a loader function if successful
  • Return a string if the module is not found
  • Throw an error if there is a syntax error

So what you can do is mimic the way require searches for a module, calling each searcher in turn until one of them returns a function. Unlike require, we need not throw an error if the module is not found, i.e. if every searcher function returns a string.

function isModuleAvailable(name)
  if package.loaded[name] then
    return true
  else
    for _, searcher in ipairs(package.searchers or package.loaders) do
      local loader = searcher(name)
      if type(loader) == 'function' then
        package.preload[name] = loader
        return true
      end
    end
    return false
  end
end
finnw
  • 47,861
  • 24
  • 143
  • 221
  • Test results with `if isModuleAvailable("menu") then require("menu") end`: Crashes if `menu` contains a syntax error, succeeds if it's missing or correct. Thank you! – l0b0 Mar 16 '13 at 11:55
9

Look, I had the same problem with 'luafilesystem' module, I worked it out like this,

local haslfs,lfs = pcall(require,"lfs")
if haslfs then
  configLines["PROJECT_HOME"] = lfs.currentdir()
else
  configLines["PROJECT_HOME"] = prompt("Project path > ")
end

'lfs' here is the module handle . And pcall is used to know if the module is really loaded without propagating the error.

KRoy
  • 1,290
  • 14
  • 10
6

What I do is wrap the require in a pcall so that the module is loaded and a fail to load can be caught. There is a fully worked function which I use to download and install the missing modules from our servers here: http://www.fhug.org.uk/wiki/doku.php?id=plugins:code_snippets:module_require_with_load

function loadrequire(module)
    local function requiref(module)
        require(module)
    end
    res = pcall(requiref,module)
    if not(res) then
        -- Do Stuff when no module
    end
end
loadrequire('menu')
finnw
  • 47,861
  • 24
  • 143
  • 221
Jane T
  • 2,081
  • 2
  • 17
  • 23
  • 1
    That looks nice; will try it soon. Nit: Is it just me, or is the indentation inconsistent? – l0b0 Mar 15 '13 at 11:54
  • 2
    why do you create a local inner function when you could just do `res = pcall(require, module)`? – Mike Corcoran Mar 15 '13 at 14:02
  • 10b0 - mainly because I typed some and pasted the rest. Mike To be honest I can't remember, I think I called it from a couple of places in some plugins. – Jane T Mar 15 '13 at 17:13
  • This doesn't propagate syntax errors - there's just a boolean result with no indication of what went wrong - so it's actually not what I need. – l0b0 Mar 16 '13 at 11:41
  • @DreamEater That's not what I asked. It's just a very common readability factor. – l0b0 Mar 16 '13 at 11:45
0

I created a simple want function for optional imports.

-- Optional require. Example:                                             
--     myMod, err = want'myMod'                                           
--     if not myMod then print(err) end                                   
local function want(name)                                                 
  local out; if xpcall(                         
      function()  out = require(name) end,           
      function(e) out = e end)
  then return out          -- success                                     
  else return nil, out end -- error                                       
end  

LICENSE: This code is in the public domain

vitiral
  • 8,446
  • 8
  • 29
  • 43