Coder Social home page Coder Social logo

akhilkumar001 / client-side-python-runner Goto Github PK

View Code? Open in Web Editor NEW

This project forked from niklasmh/client-side-python-runner

0.0 0.0 0.0 280 KB

A generalized client side Python runner

Home Page: https://www.npmjs.com/package/client-side-python-runner

JavaScript 100.00%

client-side-python-runner's Introduction

Client-side Python runner

Supported python runners so far: Pyodide, Skulpt, Brython

Try it out here.

npm i client-side-python-runner --save
# OR
yarn add client-side-python-runner

Usage

Basic example:

import { runCode, setEngine, setOptions } from 'client-side-python-runner';
// OR import { runCode, setEngine, setOptions } from 'https://cdn.jsdelivr.net/npm/client-side-python-runner@latest';

setOptions({ output: console.log });

await setEngine('pyodide'); // Specify "skulpt", "pyodide" or "brython"
await runCode(`print("printed from pyodide")`);
// You can now see the print output in the browser console

Setting options:

import { setOptions } from 'client-side-python-runner';

setOptions({
  output: console.log, // Output from print(...)-functions
  error: null, // Throws an exception unless this is set to a function
  input: prompt, // How to feed the input(...)-function
  pythonVersion: 3, // Preferred version
  loadVariablesBeforeRun: true,
  storeVariablesAfterRun: true,
  onLoading: (engine) => {},
  onLoaded: (engine) => {},
});

Handle variables:

import {
  runCode,
  getVariables,
  setVariable,
  clearVariables,
} from 'client-side-python-runner';

console.log(await getVariables()); // => {}
await setVariable('test', 123);
console.log(await getVariables()); // => { "test": 123 }

// This will now be used in the next execution:
await runCode(`print(test)`);
// Output in console.log:
// > 123

// Clear all variables:
await clearVariables();
console.log(await getVariables()); // => {}
await runCode(`print(test)`); // Will now result in NameError ("test" is not defined)
Advanced example

This will probably be more advanced in the future.

import {
  loadEngines,
  loadEngine,
  setEngine,
  setOptions,
  runCode,
} from 'client-side-python-runner';

// Load engines on beforehand
await loadEngines(['pyodide', 'skulpt']);
// OR
await loadEngine('pyodide');
await loadEngine('skulpt');

// Set current engine
await setEngine('skulpt');

// Load engine AND set current engine
await loadEngine('skulpt', { useEngine: true });

// Set options (this will merge with existing options)
setOptions({
  // This represents the values returned from the print
  // function in Python.
  output: (arg) => console.log(arg),

  // Some engines can stop and wait for input, others
  // cannot. To be safe, prompt is the default as it
  // stops JavaScript altogether and therefore works on
  // all cases.
  input: (question) => prompt(question),

  // Here you can opt into getting interpreted error
  // feedback with line numbers and error types or just
  // attempt to interpret these errors yourself.
  error: (err) => console.error(err.lineNumber, err.error),

  // Version 3 is default, unless it is not possible
  // using the current engine
  pythonVersion: 3,

  // Should load the previous stored variables into the
  // python environment. Does not load from other python
  // runners. They all have separate variables currently.
  loadVariablesBeforeRun: true,

  // Make sure the package stores the variables after
  // each run. This should only be done when necessary.
  // It may be set to false by default in the future.
  storeVariablesAfterRun: true,
});

// Run the code (and specifying engine)
await runCode('print("printed from skulpt")', { use: 'skulpt' });

// Switch engine
await setEngine('pyodide');

// Run the code again, but in pyodide (which also can
// return the result from the last execution)
const pyodideResult = await runCode(`
a = 1200
b = 137
print("printed from pyodide")
"this is the returned value " + str(a + b)
`);
// 'pyodideResult' now:
// > "this is the returned value 1337"

Using it as a loader only

If you want all the control - but not want to deal with loading of necessary scripts, it is possible to just use the loader:

import { loadEngine } from 'client-side-python-runner';

await loadEngine('pyodide');

// After this, the window.pyodide is ready
window.pyodide.runPython("print('I am using pyodide directly instead')");

Table of Contents

API

List of all exported functions:

async loadEngine

Signature:

async function loadEngine(
  engine: string,
  options = {
    useEngine = true as boolean,
  }
);

Description: Load an engine.

async loadEngines

Signature:

async function loadEngines(engines: string[]);

Load multiple engines. Waits until all of them are loaded.

async setEngine

Set the current engine.

Signature:

async function setEngine(engine: string);

async runCode

Run Python code using the current engine (or default if not set). This function will also return the result of the last line if possible (e.g. if 'pyodide' is the current engine).

Signature:

async function runCode(
  code: string,
  options = {
    variables = {} as { [name: string]: any },
    loadVariablesBeforeRun = true as boolean,
    storeVariablesAfterRun = true as boolean,
    use = currentEngine as string,
  }
);

getOptions

Get all options.

setOptions

Signature:

function setOptions({
  output = console.log as (...data: any[]) => void,
  error = null as (error?: Error | undefined) => void | null,
  input = window.prompt as (message: string, _default?: string) => string,
  pythonVersion = 3 as number, // Preferred version
  loadVariablesBeforeRun = true as boolean,
  storeVariablesAfterRun = true as boolean,
  onLoading = () => {} as (engine: string) => void,
  onLoaded = () => {} as (engine: string) => void,
});

Set options. This will go in effect immediately.

async getVariable

Signature:

async function getVariable(
  name: string,
  options = {
    use = currentEngine as string,
  }
);

async getVariables

Signature:

async function getVariables(
  options = {
    use = currentEngine as string,
    includeValues = true as boolean,
    filter = null as ((name: string) => boolean) | array | RegExp | null,
    onlyShowNewVariables = true as boolean,
  }
);

async setVariable

Signature:

async function setVariable(
  name: string,
  value: any,
  options = {
    use = currentEngine as string,
  }
);

async setVariables

Signature:

async function setVariables(
  variables: {
    [name: string]: any;
  },
  options = {
    use = currentEngine as string,
  }
);

async clearVariable

Signature:

async function clearVariable(
  name: string,
  options = {
    use = currentEngine as string,
  }
);

async clearVariables

Signature:

async function clearVariables({ use = currentEngine as string });

Why

There are a lot of client-side Python runners out there, and they all have benefits and disadvantages. Some can take a lot of loading time, and others are missing libraries you need (e.g. numpy) as well as core functionality (e.g. time.sleep). This package joins a few of the alternatives giving you the freedom to easily choose what you want in your project right now - without having to do major changes in your code. It also allows for using multiple interpreters at the same time!

Another benefit with using this library is that it can adapt to new Python runners that comes along. The world has just started with exploring the capabilities with Web Assembly (WASM) meaning there will potentially come more optimal solutions out there soon. However, so far, Pyodide has done a pretty thorough job with this, but other engines like Skulpt (not WASM) still loads much faster which - depending on your project - could be an essential factor.

Map of different Python runners

The choices below are highly inspired by this article and consists of currently working client-side Python runners. When denoting client-side Python runners, it means that the Python code is compiled and executed on the client side; not having to send the code to a server and return the result back to the client in form of compiled code or the actual result from the execution. This makes your project not dependent on servers (this is big!) and not prune to security issues as you only work in the browser sandbox.

DISCLAIMER: The numbers in the table are not scientifically calculated, thus they have been ran on the same machine multiple times and measured using the same tool (Chrome DevTools with cache disabled).

Python runner Python 3 Python 2 Computation* Loading time Memory Packages
Pyodide Fast (1.106s - WASM) ~1750ms ~7400kB Most scientific libraries
PyPy.js Very fast (0.034s - JS) ~1900ms ~15000kB Browser
Skulpt Fast (1.329s - JS) ~150ms ~227kB TurtleGraphics
Brython Slow (3.445s - JS) ~200ms ~184kB Browser
RustPython Very slow (11.567s - WASM) ~200ms ~184kB Browser

* Simple benchmark test:

from datetime import datetime

def f(a):
  t_start = float(str(datetime.now()).split(":")[-1]) # Start time
  for i in range(a*10):
    if i % a == 0: print(i)
  t_end = float(str(datetime.now()).split(":")[-1]) # End time
  print("Time used: {:.3f}s".format(t_end - t_start))

f(100000)

Versions

Check out the CHANGELOG.md.

Later

  • Add TypeScript support.
  • Support files.
  • Include more Python runners.
  • Make it possible to run them offline (by building them into the project somehow).
  • Add portals between JavaScript and Python. Useful for executing functions outside the Python scope and the other way around (if possible).

Contribute

If you find any bugs or have any requests, feel free to file an issue here. I am more than willing to take your requests into consideration such that this module can be used by more people.

You are also welcome to contribute with code as well, just fork the project, do your magic, then create a PR. It may be that I will do some changes on your changes, it will depend, but if the contribution is really useful - it will be included somehow!

To test out the project - such that you do not need to set up an environment for yourself - you can run:

npm run develop
npm run examples
npm run done # When done with contribution

Then go to localhost:5000/docs/ to test it out.

When done, we need to change the import URL in the demo (because it is hosted on GitHub :/):

npm run done

To generate types (from JSDoc comments in index.js):

npm run build

Check if tests still are working before creating PR:

npm test

client-side-python-runner's People

Contributors

niklasmh avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.