CC: Tweaked

CC: Tweaked

42M Downloads

require() not working correctly in interactive shell

Zirunis opened this issue · 5 comments

commented

Minecraft Version

1.16.x

Version

1.101.3

Details

require(filePath) doesn't work correctly with following setup:

  1. There is a folder in the home directory (in my case called "datatypes")
  2. It contains two lua files (in my case "Bidict.lua" and "Set.lua") with one importing the other (in my case the former importing the latter via Set = require("Set")
  3. Starting the interactive lua shell from the home directory
  4. Importing the lua file which imports the other lua file (in my case: Bidict = require("Bidict")

While it can find 'Bidict.lua', during its import it tries importing 'Set' inside of 'Bidict.lua' which fails. It can't find the module throwing a stack-trace with all the locations it looked for it. Unfortunately it is cut off so I can't paste it here. The logs also didn't have anything of interest.
Interestingly, starting the interactive shell from the folder containing the two files works fine. My programs in the home directory which import 'Bidict' just like I'm trying to in the shell also work fine.

I hope you can reproduce the bug. Please let me know if I can be of any further assistance!

commented

Thanks for the report!

I'm afraid I'm getting a little stuck on following your reproduction steps (testing on CC 1.110.3, but I don't think this code has change much since).

Importing the lua file which imports the other lua file (in my case: Bidict = require("Bidict")

I'm slightly surprised/confused by this step. I wouldn't expect this to work — if you're running from the root directory, require("Bidict") will look for files at /Bidict.lua, and so shouldn't find /datatypes/Bidict.lua.

I'd also expect you to hit this problem with the programs in the root directory too. Just to check, you're not changing package.path anywhere are you?

This is my current setup, with the following files:

  • datatypes/Set.lua, just containing print("Set").
  • datatypes/Bidict.lua, containing print("Bidict"); require("Set")

Then opening the Lua REPL and running the following:

A CC computer. I have run ls /andls /datatypesto show there are exactly two files on the disk, and then opened the Lua REPL and runrequire("Bidict")`. There's an error saying the module cannot be found

Am I missing/misunderstanding a step here?

commented

Thanks for the fast reply!

I'd also expect you to hit this problem with the programs in the root directory too. Just to check, you're not changing package.path anywhere are you?

Nope

I'm slightly surprised/confused by this step. I wouldn't expect this to work — if you're running from the root directory, require("Bidict") will look for files at /Bidict.lua, and so shouldn't find /datatypes/Bidict.lua.

Yeah, I'm sorry, I messed up the description at that step. I'm actually calling require("datatypes/Bidict") which should work, right?

This is what I'm actually running:
requireNotWorking1
requireNotWorking2

This works even though the files are the same:
requireWorking

I have now noticed that my programs in the home directory calling require("datatypes/Bidict") in the same exact way actually don't work either. So it does not have to do with the interactive shell but require() in general.

commented

Files are required from the currently running program, so if /startup.lua requires datatypesDemo/Bidict, then Bidict.lua would need to require datatypesDemo/Set.

Alternatively...

Updating package.path

In your root file, you will want to have some approximation of the following code:

package.path = package.path .. ";/datatypesDemo/?.lua;/datatypesDemo/?/init.lua"

What this does is tells require that it should search in the datatypesDemo folder for * or */init.lua, where * is replaced by your require string.

Then, you can leave Bidict.lua as is, to require("Set"). As well, in your root file, you can just require("Bidict") now, instead of needing to include the folder name.

commented

Thank you for your answer but that makes no sense to me. How are dependencies then handled in the general case? I'd need to go through all dependencies and replace their require(relativePath) which would work fine on its own with a require(absolutePath)? That can't be right. Especially since there might even be naming conflicts. Who is to say that dependency A and dependency B don't both have a separate dependency like 'utils' that is contained in each of their directories? Now when my program imports these two dependencies it 1. Doesn't know where to look and 2. Might choose the wrong one if I use the package.path solution?

commented

Nevermind. I looked it up and you're right. require() seems to just suck.
However for people stumbling across this in the future: I found out that there is a better way. There is an argument passed to an api when it is require()'d which is the argument given to require() itself, which is either its absolute path or the relative path as seen from the importing program. So one can do something like this at the top of each file:

local pathToThisFile = (...):match("(.-)[^%.]+$")  --turns "datatypes.Bidict" into "datatypes."
local Set = require(pathToThisFile .."Set")

In this case you'd always have to import files using dot-notation, but changing the pattern to also transform something like "datatypes/Bidict.lua" into "datatypes." shouldn't be too hard.