Skip to content

Commit e000ce9

Browse files
authored
Merge pull request #4 from NodeX-AR/Beta-Version
Beta version
2 parents 3e43620 + 4752093 commit e000ce9

3 files changed

Lines changed: 59 additions & 67 deletions

File tree

README.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,8 @@ Option 2: External Python File (HTTP/HTTPS only)
4949
<script type="text/python" src="script.py"></script>
5050
</body>
5151
</html>
52-
py
52+
```
53+
```py
5354
name = input("Enter your name: ")
5455
age = input("Enter your age: ")
5556
print(f"Hello {name}, you are {age} years old!")

SECURITY.md

Lines changed: 13 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,20 +2,22 @@
22

33
## Supported Versions
44

5-
Use this section to tell people about which versions of your project are
6-
currently being supported with security updates.
5+
I recommend to use stable version.
6+
7+
If you want real challenges then use our beta verion and say us how is it!
8+
```html
9+
<script src="https://cdn.jsdelivr.net/gh/NodeX-AR/Pytml@Beta-Version/pytml.js"></script>
10+
```
711

812
| Version | Supported |
913
| ------- | ------------------ |
10-
| 5.1.x | :white_check_mark: |
11-
| 5.0.x | :x: |
12-
| 4.0.x | :white_check_mark: |
13-
| < 4.0 | :x: |
14+
| 2.3.x | :x: |
15+
| 2.2.x | :white_check_mark: |
16+
| 2.1.x | :white_check_mark: |
17+
| < 2.0 | :x: |
1418

1519
## Reporting a Vulnerability
1620

17-
Use this section to tell people how to report a vulnerability.
18-
19-
Tell them where to go, how often they can expect to get an update on a
20-
reported vulnerability, what to expect if the vulnerability is accepted or
21-
declined, etc.
21+
First off Thanks!
22+
You can open a [Issue](https://github.com/nodex-ar/pytml/issues) and report your bugs.
23+
If you are confident about how to fix it then fork this respo, fix the bug then open a pr. We will look after that !

pytml.js

Lines changed: 44 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,9 @@
1-
// At the very top of pytml.js
21
fetch('https://pytml.vercel.app/api/count')
32
.then(r => r.json())
4-
.then(data => console.log('Pytml loaded:', data.message, 'times'));
3+
.then(data => console.log('Pytml loaded:', data.message, 'times'))
4+
.catch(() => {});
55
(function(window) {
66
'use strict';
7-
8-
// ----------------------------------------------
9-
// PYTML – Main Class
10-
// ----------------------------------------------
117
class PYTML {
128
constructor() {
139
this.outputContainer = null;
@@ -16,33 +12,22 @@ fetch('https://pytml.vercel.app/api/count')
1612
this.pendingPromises = [];
1713
this.init();
1814
}
19-
20-
// --------------------------------------------------
21-
// Initialisation
22-
// --------------------------------------------------
2315
async init() {
2416
console.log('[PYTML] Initialising...');
2517
this.createOutputContainer();
2618
this.showStatus('Loading Python engine...');
2719

2820
try {
29-
// Load Pyodide script
3021
await this.loadPyodideScript();
31-
32-
// Initialise Pyodide
3322
const pyodide = await loadPyodide({
3423
indexURL: 'https://cdn.jsdelivr.net/pyodide/v0.26.4/full/'
3524
});
3625
window.pyodide = pyodide;
37-
38-
// Setup Python environment
3926
await this.setupPythonEnvironment();
4027

4128
this.isReady = true;
4229
this.hideStatus();
4330
console.log('[PYTML] Ready!');
44-
45-
// Execute all Python scripts (inline and external)
4631
await this.runAllPythonScripts();
4732

4833
} catch (error) {
@@ -51,10 +36,6 @@ fetch('https://pytml.vercel.app/api/count')
5136
console.error('[PYTML] Fatal error:', error);
5237
}
5338
}
54-
55-
// --------------------------------------------------
56-
// DOM Setup
57-
// --------------------------------------------------
5839
createOutputContainer() {
5940
let container = document.getElementById('pytml-output');
6041
if (!container) {
@@ -196,10 +177,12 @@ fetch('https://pytml.vercel.app/api/count')
196177
async setupPythonEnvironment() {
197178
const pyodide = window.pyodide;
198179

199-
// Override stdout and stderr
180+
181+
// Override stdout and stderr + DEFINE AST TRANSFORMER ONCE
200182
await pyodide.runPythonAsync(`
201183
import sys
202184
import js
185+
import ast
203186
204187
class OutputRedirect:
205188
def __init__(self, is_error=False):
@@ -215,11 +198,25 @@ sys.stderr = OutputRedirect(True)
215198
216199
# Override input – async function that calls JS
217200
async def input(prompt=""):
218-
# We do NOT print(prompt) here – the JS will show it in the DOM
219201
result = await js.pyodideInstance.getUserInput(str(prompt))
220202
return result
221-
`);
222203
204+
# ---------- AST TRANSFORMER (DEFINED ONCE) ----------
205+
class InputTransformer(ast.NodeTransformer):
206+
def visit_Call(self, node):
207+
if isinstance(node.func, ast.Name) and node.func.id == 'input':
208+
new_node = ast.Await(value=node)
209+
return ast.copy_location(new_node, node)
210+
self.generic_visit(node)
211+
return node
212+
213+
def transform_pytml_code(code):
214+
tree = ast.parse(code)
215+
transformer = InputTransformer()
216+
new_tree = transformer.visit(tree)
217+
ast.fix_missing_locations(new_tree)
218+
return ast.unparse(new_tree)
219+
`);
223220
// Also expose the instance to Python's js module
224221
window.pyodideInstance = this;
225222

@@ -286,38 +283,29 @@ async def input(prompt=""):
286283
// --------------------------------------------------
287284
// AST‑Based Code Transformation (safe input replacement)
288285
// --------------------------------------------------
289-
async transformPythonCode(code) {
290-
// We use Python's ast module to rewrite `input()` calls into `await input()`
291-
// This ensures we only replace actual function calls, not strings, comments, or variable names.
292-
const pyodide = window.pyodide;
293-
const transformScript = `
294-
import ast
295-
296-
class InputTransformer(ast.NodeTransformer):
297-
def visit_Call(self, node):
298-
# Check if it's a call to 'input'
299-
if isinstance(node.func, ast.Name) and node.func.id == 'input':
300-
# Wrap the call in an Await node
301-
new_node = ast.Await(value=node)
302-
return ast.copy_location(new_node, node)
303-
# Recursively transform child nodes
304-
self.generic_visit(node)
305-
return node
306-
307-
def transform(code):
308-
tree = ast.parse(code)
309-
transformer = InputTransformer()
310-
new_tree = transformer.visit(tree)
311-
ast.fix_missing_locations(new_tree)
312-
return ast.unparse(new_tree)
313-
`;
314-
await pyodide.runPythonAsync(transformScript);
286+
async transformPythonCode(code) {
287+
if (!this.isReady) {
288+
this.addError('Python engine not ready yet.');
289+
return code;
290+
}
315291

316-
// Call the transform function
317-
const result = await pyodide.runPythonAsync(`
318-
transform('''${code.replace(/'/g, "\\'")}''')
292+
try {
293+
// ✅ Escape triple quotes to safely pass code to Python
294+
const escapedCode = code.replace(/"""/g, '\\"\\"\\"');
295+
296+
// Pass code as a Python variable (safe, no interpolation crashes)
297+
await window.pyodide.runPythonAsync(`_pytml_code = """${escapedCode}"""`);
298+
299+
// Call the pre-defined transformer (defined once in setupPythonEnvironment)
300+
const result = await window.pyodide.runPythonAsync(`
301+
result = transform_pytml_code(_pytml_code)
302+
result
319303
`);
320-
return result;
304+
return result;
305+
} catch (error) {
306+
this.addError('Code transformation error: ' + error.message);
307+
return code; // Fallback to raw code if transformation fails
308+
}
321309
}
322310

323311
// --------------------------------------------------
@@ -412,7 +400,8 @@ transform('''${code.replace(/'/g, "\\'")}''')
412400

413401
hideStatus() {
414402
if (this.statusElement) {
415-
this.statusElement.style.display = 'none';
403+
this.statusElement.remove(); // Actually remove from DOM
404+
this.statusElement = null; // Free memory
416405
}
417406
}
418407
}

0 commit comments

Comments
 (0)