Page MenuHomePhabricator

#invoke does not record the main module in package.loaded
Open, Needs TriagePublic

Description

Begining around the first week of december 2015, I have some difficulties to manage versions of modules. For this I use package.loaded.

Each require() records well the module in package.loaded, but #invoke do not that. Before, this was working fine.

To verify this, I just make 2 modules in french wikisource, used in their discussions: Module:TestRequire and Module:TestpackG

Event Timeline

Rical raised the priority of this task from to Needs Triage.
Rical updated the task description. (Show Details)
Rical subscribed.

Each require is well recorded in package.loaded

Where exactly can this be seen if I wanted to reproduce the problem?

The 2 test modules, TestRequire and TestpackG list the content of package.loaded (and of the global _G space for my own use). Exactly with the same code.
The only difference is the require() in TestRequire.
To increase the confidence in the test, I use twice the same instruction to list, one to detect "ext", the other to detect "Module".
The result lists 1 module when it is require(), and no module when it is #invoke, which denotes the bug.
Thanks for your attention, and sorry for this difference hiden between many others.

Rical renamed this task from #invoke seems do not record the module, for some weeks. to #invoke seems do not record the module.Feb 19 2016, 9:27 PM
Rical updated the task description. (Show Details)

To restore the main module in my case, I use:
local gettitle = mw.getCurrentFrame():getTitle() -- to get the mainmodule title
module = centre.pcallrequire(gettitle) -- require() must not fail in case of error.

For many other uses, this bug have no impact, but we need to repair that.
Find the origin or the bug is probably longer than repair it.

The bug appears probably around 2015-11-25 to 2015-11-28 if we look at my comments in history, when I begin to search the main module date and time.
To better understand what happens, I edit my file in local Jedit and try the module 20 to 100 times a day.
I record the module only when I get enought stable running. History of the module:

  • (actu | diff) 2015-12-01T14:18:22‎ Rical . . (find_main_module OK ?) .................. [ when I realy found the main module, using mw.getCurrentFrame():getTitle() ]
  • (actu | diff) 2015-11-28T09:55:37‎ Rical . . (find_main_module) .................. [ when I probably understand that I have to search the main module, but without mw.getCurrentFrame():getTitle() ]
  • (actu | diff) 2015-11-25T11:43:11‎ Rical . . (The versions management seems OK) (annuler)

Before, this was working fine.

Please indicate the specific version of Scribunto in which this was "working fine". I can't manage to reproduce it locally, even reverting much farther back than you've specified. As far as I can determine the module from the #invoke was never included in package.loaded.

Sorry for the ambiguity. I edit the module in local Jedit, and for each try I copy the code of the module in the standard edit window,
in the standard public page https://fr.wikisource.org/wiki/Module:Central or some other.
The version of Scribunto is the standard version in standard MediaWiki at each date, for fr.wikisource.
I never look at this version.

The version of Scribunto is the standard version in standard MediaWiki at each date, for fr.wikisource.

Unfortunately, as I said, I can't manage to reproduce your old behavior locally with the relevant versions.

Rical renamed this task from #invoke seems do not record the module to #invoke do not record the main module in package.loaded.Apr 26 2016, 6:58 AM
Pppery renamed this task from #invoke do not record the main module in package.loaded to #invoke does not record the main module in package.loaded.Jun 22 2023, 3:27 PM

@Rical: As far as I can tell this has never been supported so this is not a regression.

Incidentally, you can do something like:

local frame = mw.getCurrentFrame()
local pframe = frame:getParent()
if not pframe then
    if ... then --Yes this needs to be exactly three dots: contains the module name used in mw.loadData(), otherwise nil
        --mw.loadData() path: this will generate errors if the package does not adhere to its rules, i.e, no functions, etc.
        return {} --not interesting but data returned here can be cached across #invoke calls!
    else
        --debug console path: the package you return here will be associated with 'p' in the console
        return {} --not interesting but probably should return the same thing as the require() path to facilitate debug
    end
else
    if ... then --three dots: contains the module name used in require(), otherwise nil
        --require() path: the package you return here will get cached in package.loaded
        local p = {}
        function p.main(fk)
            return fk --not interesting: just returns the passed function key
        end
        return p
    else
        --#invoke path: the package returned here determines which functions can be called--usually from wikitext
        local api = require(frame:getTitle()) --require our own API package by passing the module name used in the #invoke
        local mt = {}
        function mt:__index(key)
            return function(frame)
                --catch *any* #invoke function: the called function can be accessed via key, i.e., {{#invoke:module|key}}
               return api.main(key)
            end
        end
        return setmetatable({}, mt)
    end
end

This will cause the #invoke to do a require() on the module with the same name it was invoked with. However, you should be very careful about re-entrancy issues. You can actually return different package tables in each path which makes weird naming like _main vs. main unnecessary.