Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions src/backend/workers/python/papyros/linting.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,21 @@
from pylint.lint import Run
from pylint.reporters.text import TextReporter

# Workaround for Pyodide + astroid: pylint hangs indefinitely when astroid
# tries to recursively parse imported modules' ASTs (e.g. re, pandas).
# Since we run in a single-threaded WebAssembly environment, this causes an
# infinite hang. We short-circuit ALL module resolution to return synthetic
# empty modules. This preserves core linting (syntax errors, undefined
# variables, unused imports, style checks, custom checkers) while only
# losing type-inference-based checks on imported symbols.
from astroid.manager import AstroidManager as _AstroidManager
from astroid.builder import AstroidBuilder as _AstroidBuilder

def _patched_ast_from_module_name(self, modname, context_file=None, use_cache=True):
return _AstroidBuilder(self).string_build("", modname=modname)

_AstroidManager.ast_from_module_name = _patched_ast_from_module_name

PYLINT_RC_FILE = os.path.abspath("/tmp/papyros/pylint_config.rc")
PYLINT_PLUGINS = "pylint_ast_checker"

Expand Down
2 changes: 1 addition & 1 deletion src/backend/workers/python/papyros/pylint_config.rc
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,5 @@ const-rgx=[_A-Za-z0-9]{1,30}$
# I0011 Warning locally suppressed using disable-msg
# I0012 Warning locally suppressed using disable-msg
# old version: disable=I0011,I0012,W0704,W0142,W0212,W0232,W0702,R0201,W0614,R0914,R0912,R0915,R0913,R0904,R0801,C0303,C0111,C0304,R0903,W0141,W0621,C0301,W0631,R0911,C1001
disable=W0311,W0621,W0622,R0902,R0903,C0111,C0301,C0303,C0304,C0413,I0011
disable=W0311,W0621,W0622,R0902,R0903,C0111,C0301,C0303,C0304,C0413,I0011,E0401,E0611,E1101
evaluation=max(10.0 - ((float(5 * error + warning + refactor + convention) / statement) * 10), 0)
31 changes: 31 additions & 0 deletions test/__tests__/state/Runner.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,37 @@ const c = a + b;`;
expect(papyros.runner.stateMessage).toMatch(/^Code interrupted after /);
});

it("should be able to import re", async () => {
const papyros = new Papyros();
await papyros.launch();
papyros.runner.programmingLanguage = ProgrammingLanguage.Python;
papyros.runner.code = "import re\nprint(re.findall(r'\\d+', 'a1 b2 c3'))";
await papyros.runner.start();
await waitForPapyrosReady(papyros);
await waitForOutput(papyros);
expect(papyros.runner.state).toBe(RunState.Ready);
expect(papyros.runner.stateMessage).toMatch(/^Code executed in/);
expect(papyros.io.output[0].content).toBe("['1', '2', '3']\n");
});

it("should lint bare import re", async () => {
const papyros = new Papyros();
await papyros.launch();
papyros.runner.programmingLanguage = ProgrammingLanguage.Python;
papyros.runner.code = "import re\n";
const diagnostics = await papyros.runner.lintSource();
expect(Array.isArray(diagnostics)).toBe(true);
}, 60000);
Comment thread
chvp marked this conversation as resolved.

it("should lint code that uses pandas without hanging", async () => {
const papyros = new Papyros();
await papyros.launch();
papyros.runner.programmingLanguage = ProgrammingLanguage.Python;
papyros.runner.code = "import pandas as pd\ndf = pd.DataFrame({'a': [1, 2, 3]})\n";
const diagnostics = await papyros.runner.lintSource();
expect(Array.isArray(diagnostics)).toBe(true);
}, 60000);

it("should be able to handle sleep", async () => {
const papyros = new Papyros();
await papyros.launch();
Expand Down
Loading