question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

ModuleNotFoundError after writing multiple files with FS.writeFile

See original GitHub issue

🐛 Bug

This might be a duplicate of #737, but I’m opening a new issue because I’m seeing something very weird. I’m using Pyodide 0.21.3.

To Reproduce

Consider the following snippet (I run it in node, but I can reproduce the same behavior also in the browser):

const { loadPyodide } = require("pyodide");

function busyWait(ms) {
    let start = new Date().valueOf();
    while( (new Date()).valueOf() - start < ms)
        ;
}

async function main() {
    let pyodide = await loadPyodide({
        indexURL: "./node_modules/pyodide",
    });

    console.log("Pyodide version: ", pyodide.version);

    let success = 0;
    let errors = 0;
    for(let i=0; i<100; i++) {
        // uncommenting EITHER of the following lines, it works reliably
        //pyodide.runPython('import importlib; importlib.invalidate_caches()');
        //busyWait(1000);

        pyodide.FS.writeFile(`bar${i}.py`, `x = ${i}`);
        try {
            pyodide.runPython(`import bar${i} as bar; print('OK ', bar.x)`);
            success++;
        }
        catch(e) {
            //console.log(e);
            console.log('ERR', i);
            errors++;
        }
    }
    console.log("success:", success);
    console.log("errors :", errors);
    console.log("error rate: ", (errors/(success+errors))*100, "%");
}

main();

The expected result is obviously to see all OK and 0% error rate, but what happens is that very often I get ERR. I observed the following:

  • if I uncomment the call to invalidate_caches(), it passes reliably
  • if I uncomment busyWait(1000), (i.e., 1 second) it passes reliably

The interesting part is what happens with lower values of busyWait:

  • with busyWait(500), it fails ~50% of the times
  • with busyWait(100) it fails ~90% of the times
  • with busyWait(10) it fails ~99% of the times

The following is just a guess, but I think that considering the above it’s a reasonable explanation:

  • somewhere there is a timestamp which is stored with a resolution of 1s
  • if the “new” files are written “too early”, importlib think that it is of the same epoch as the “old files/directories” and thus it thinks the cache is still valid
  • if you wait long enough, the 1s timestamp gets a new value and importlib correctly notices that there is a “new” file

I am not sure whether this is a bug/“feature” of emscripten. I thought it’s a good idea to open it here so that we can maybe discuss possible pyodide-specific mitigations and/or workarounds

Issue Analytics

  • State:closed
  • Created 10 months ago
  • Reactions:2
  • Comments:25 (25 by maintainers)

github_iconTop GitHub Comments

1reaction
ryanking13commented, Dec 2, 2022

so… HAVE_STAT_TV_NSEC being 1 sounds promising…

Great! Thanks sound indeed promising.

BTW, even if we make st_mtime_ns available in Pyodide, I guess we have to persuade CPython devs to use st_mtime_ns instead of st_mtime here?

1reaction
ryanking13commented, Dec 2, 2022

I think the point is that this <1s behavior is not observed in native Python

I was able to observe the similar result in native Python (tested in Linux):

import os
import time
import importlib

success = 0
error = 0
for i in range(1000):
    os.system(f"echo x=1 >> bar{i}.py")
    # importlib.invalidate_caches()
    try:
        __import__(f"bar{i}")
        success += 1
    except:
        error += 1

print(f"Success: {success}")
print(f"Error: {error}")

os.system("rm -rf __pycache__")
os.system("rm -rf bar*.py")
Success: 254
Error: 746
Read more comments on GitHub >

github_iconTop Results From Across the Web

nodejs: write multiple files in for loop - javascript - Stack Overflow
I want to write a number of files and when it is all done continue on other task. Without for loop, I am...
Read more >
Module not found: Can't resolve 'fs' · Issue #7755 · vercel/next.js
I'm using the popular find-up npm package, which has locate-path as a dependency. locate-path requires fs within it's code. When I attempt to ......
Read more >
ModuleNotFoundError: no module named Python Error [Fixed]
When you try to import a module in a Python file, Python tries to resolve this module in several ways. · As the...
Read more >
How to write to files with Node.js - Emma Goto
In this post we'll be covering how you can use the fs module in Node.js to write to and modify files with writeFile()...
Read more >
Python – Import module from different directory - GeeksforGeeks
ModuleNotFoundError, because by default Python interpreter will check for the file in the current directory only, and we need to set the file...
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found