{"id":24838560,"url":"https://github.com/suren-atoyan/x-python","last_synced_at":"2025-10-14T12:30:22.591Z","repository":{"id":65138454,"uuid":"579625957","full_name":"suren-atoyan/x-python","owner":"suren-atoyan","description":"A complete solution for python-in-browser 🚀","archived":false,"fork":false,"pushed_at":"2023-12-12T20:06:03.000Z","size":481,"stargazers_count":27,"open_issues_count":0,"forks_count":0,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-01-22T14:05:18.681Z","etag":null,"topics":["javascript","pyodide","python","python-in-browser","webassembly"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/suren-atoyan.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":"CODE_OF_CONDUCT.md","threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2022-12-18T10:32:48.000Z","updated_at":"2025-01-03T07:28:01.000Z","dependencies_parsed_at":"2023-12-12T21:24:21.159Z","dependency_job_id":"e2ec8166-11ec-4790-9f41-d8348eaac671","html_url":"https://github.com/suren-atoyan/x-python","commit_stats":{"total_commits":65,"total_committers":1,"mean_commits":65.0,"dds":0.0,"last_synced_commit":"bac87f47fe90126460e7f5d6a64feb82eae6c54d"},"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/suren-atoyan%2Fx-python","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/suren-atoyan%2Fx-python/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/suren-atoyan%2Fx-python/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/suren-atoyan%2Fx-python/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/suren-atoyan","download_url":"https://codeload.github.com/suren-atoyan/x-python/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":236472046,"owners_count":19154163,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["javascript","pyodide","python","python-in-browser","webassembly"],"created_at":"2025-01-31T06:20:55.578Z","updated_at":"2025-10-14T12:30:17.179Z","avatar_url":"https://github.com/suren-atoyan.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# @x-python/core \u0026middot; [![monthly downloads](https://img.shields.io/npm/dm/@x-python/core)](https://www.npmjs.com/package/@x-python/core) [![gitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/suren-atoyan/x-python/blob/master/LICENSE) [![npm version](https://img.shields.io/npm/v/@x-python/core.svg?style=flat)](https://www.npmjs.com/package/@x-python/core) [![PRs welcome](https://img.shields.io/badge/PRs-welcome-brightgreen.svg)](https://github.com/suren-atoyan/x-python/pulls)\n\n\u003ca href=\"https://github.com/suren-atoyan/x-python/\" target=\"_blank\" rel=\"noreferrer\"\u003e\n  \u003cimg align=\"center\" width=\"30%\" height=\"auto\" src=\"./playground/logo.svg\" style=\"margin-bottom: 10px\"\u003e\n\u003c/a\u003e\n\n\u003cp /\u003e\n\nA complete solution for python-in-browser. (check the [Usage](#usage) section :point_down:)\n\n\u003chr /\u003e\n\n🔥 A REPL powered by `xPython` as `React` component is coming soon\n\u003cbr /\u003e\n⌛️ It's still in beta testing\n\n\u003chr /\u003e\n\n## Synopsis\n\nClean API to execute Python code, get code completions, format the code, install packages, and many more.\n\n## Motivation\n\nIn the past few years we used python in production browser-based applications in quite different scenarios. From just executing a python code to implementing autoformat and autocomplete. And while all of this is possible there are many questionable situations in the implementations. For example, it's been a while since we've had a full `Python` distribution for the browser based on webassembly - [pyodide](https://pyodide.org/en/stable/). `Pyodide` is great! You can just install and use it. But to use it correctly, instead of installing it in the main (UI) thread, it would be desirable to install/run it in a separate thread. Once you've created a separate thread you need to create a channel between main/UI and pyodide worker and come up with some protocol for communication. You also need to do something with error handling, handling standard output streams, non-sequential executions and other all possible corner cases. This was one of the things that `xPython` will handle for you :slightly_smiling_face: It also provides a clean API for code completion, installing packages, and formatting the code... and many more are coming soon. Long story short I tried to provide a clean and complete interface to interact with Python in browser-based applications.\n\n## Documentation\n\n#### Contents\n\n- [Installation](#installation)\n- [Usage](#usage)\n- [API](#api)\n  - [.init](#init)\n  - [.exec](#exec)\n  - [.complete](#complete)\n    - [.repl](#complete)\n  - [.format](#format)\n  - [.install](#install)\n- [Development](#development)\n\n### Installation\n\n```bash\nnpm install @x-python/core\n```\n\nor\n\n```bash\nyarn add @x-python/core\n```\n\n### Usage\n\n```javascript\nimport * as xPython from '@x-python/core';\n\n// initialize xPython\nawait xPython.init();\n\n// execute python code\nawait xPython.exec({ code: '1 + 1' });\nawait xPython.exec({ code: 'print(\"test\")' });\n\n// multiline example\nawait xPython.exec({\n  code: `\nimport sys\nsys.version\n`,\n});\n\n// you can use built-in packages without additionally installing them\nawait xPython.exec({\n  code: `\nimport numpy as np\nnp.random.rand()\n`,\n});\n\n// code completion\nawait xPython.complete.repl({ code: `import sys; sys.ver` });\n\n// specify the cursor position\nawait xPython.complete.repl({\n  code: `\nfrom math import factorial\n\ntest = 8\nprint(tes)\nfactorial(x)\n`,\n  line: 5,\n  column: 9,\n});\n\n// format the code\nconst { result } = await xPython.format({\n  code: `\ndef add(a,            b):\n  return a +        b\n\nprint(add(12,\n\n54))\n`,\n});\n\nconsole.log(result);\n\n// install packages\nawait xPython.install(['nicelog']);\n\n// and use the newly installed package :)\nconst { stderr } = await xPython.exec({\n  code: `\nimport logging\nimport sys\n\nfrom nicelog.formatters import Colorful\n\n# Setup a logger\nlogger = logging.getLogger('foo')\nlogger.setLevel(logging.DEBUG)\n\n# Setup a handler, writing colorful output\n# to the console\nhandler = logging.StreamHandler(sys.stderr)\nhandler.setFormatter(Colorful())\nhandler.setLevel(logging.DEBUG)\nlogger.addHandler(handler)\n\n# Now log some messages..\nlogger.debug('Debug message')\nlogger.info('Info message')\nlogger.warning('Warning message')\nlogger.error('Error message')\nlogger.critical('Critical message')\ntry:\n    raise ValueError('This is an exception')\nexcept:\n    logger.exception(\"An error occurred\")\n`,\n});\n\nconsole.log(stderr);\n```\n\n## API\n\n#### .init\n\nIt will initialize `xPython`. Most importantly it will create a separate thread (dedicated web worker), install `pyodide` inside that thread, create a channel, and setup all necessary packages and functions for further usage.\n\n```javascript\nimport * as xPython from '@x-python/core';\n\nawait xPython.init();\n```\n\nUsually, we do initialize `xPython` before using other methods (like `exec`, `complete`, etc), but it's not mandatory :slightly_smiling_face: So, you can go ahead and do `xPython.exec({ code: '...' })` without doing `xPython.init()` - it will do `xPython.init()` on first `xPython.exec` (or `.complete`, `.format` and any other supported method) call if it's not initialized. The aim of the existence of a separate initialize method is to provide full flexibility to developers. The initialization process takes time and it should be possible to handle that time in the way you want. So, you can do `await xPython.init();` at the beginning or do it after a certain user action or, if you are okay with your users waiting a little bit more after the first execution then you can skip the initialization process and it will be handled automatically :slightly_smiling_face:\n\n#### .exec\n\n`exec` is one of the most frequently used. Basically, it's for executing python code. A simple usage looks like this:\n\n```javascript\nimport * as xPython from '@x-python/core';\n\nawait xPython.exec({ code: '1 + 1' });\n```\n\nYou can also provide a `context` with global variables, like:\n\n```javascript\nimport * as xPython from '@x-python/core';\n\nawait xPython.exec({ code: 'x + 1', context: { x: 1 } });\n```\n\nBut let's take a closer look at what it returns. In both cases we will get something like this:\n\n```js\n{ result: 2, error: null, stdout: '', stderr: '' }\n```\n\n`result` is what is returned from the executed script. But we also have `stdout` (and `stderr`) for standard output streams. If we execute `print(\"test\")` nothing will be returned, but you will have a `stdout`.\n\n```javascript\nimport * as xPython from '@x-python/core';\n\nawait xPython.exec({ code: 'print(\"test\")' });\n\n// { result: undefined, error: null, stdout: 'test', stderr: '' }\n```\n\nOf course you can exec multiline code snippets:\n\n```javascript\nimport * as xPython from '@x-python/core';\n\nawait xPython.exec({\n  code: `\nimport sys\n\nsys.version\n`,\n});\n```\n\nYou can directly use `pyodide` built-in package list without installing them. The full list is [here](https://pyodide.org/en/stable/usage/packages-in-pyodide.html)\n\n```javascript\nimport * as xPython from '@x-python/core';\n\nawait xPython.exec({\n  code: `\nimport numpy as np\n\nnp.random.rand()\n`,\n});\n```\n\nIt will autodetect `numpy` and it will install it if you're using it for the first time.\n\n#### .complete\n\nTo get code completions you can use `xPython.complete.repl`. Simple usage looks like this:\n\n```javascript\nimport * as xPython from '@x-python/core';\n\nawait xPython.complete.repl({ code: 'import sys; sys.ver' });\n```\n\nThis example will return an array with two possible options: `version` and `version_info` :slightly_smiling_face:\n\nThe full signature of this method also includes `line` and `column` options to specify the cursor position. If `line` isn't provided `xPython` will assume it's the last line and, correspondingly, if `column` isn't provided it will assume that it's the last column. So, in the previous example, it assumed that cursor is at the end of `sys.var` and returned code completions based on that assumption. An example with cursor position specified:\n\n```javascript\nawait xPython.complete.repl({\n  code: `\nfrom math import factorial\n\ntest = 8\nprint(tes)\nfactorial(x)\n`,\n  line: 5,\n  column: 9,\n});\n```\n\nThis will return the only available option here: `test`.\n\nThe curious eye may notice that instead of `.complete` we called `.complete.repl`. When it comes to code completion at least two environments can be your target: `REPL` and `Script/File/Editor`. And based on the environment code completion can vary. In the current version, we do support only `REPL`, but very soon other options will also be available ⏳\n\n#### .format\n\nCode formatting is an essential part of interacting with your code. A simple usage looks like this:\n\n```javascript\nimport * as xPython from '@x-python/core';\n\nconst { result } = await xPython.format({\n  code: `\ndef add(a,            b):\n  return a +        b\n\nprint(add(12,\n\n54))\n`,\n});\n\nconsole.log(result);\n```\n\n**NOTE:** in upcoming versions a full configuration option will be provided.\n\n#### .install\n\nThe entire standard library is available out of the box, so you can import `sys`, `math`, or `os` without doing anything special. In addition to this `pyodide` also provides a list of built-in packages, like `numpy`, `pandas`, `scipy`, `matplotlib`, `scikit-learn`, etc. Check the full list [here](https://pyodide.org/en/stable/usage/packages-in-pyodide.html). You can use any package from the above-mentioned list and it will be installed automatically and on demand. And if that's not enough you can still install any pure `Python` packages with wheels available on `PyPI` :slightly_smiling_face: Let's install the package called `nicelog`.\n\n```javascript\nimport * as xPython from '@x-python/core';\n\nawait xPython.install(['nicelog']);\n```\n\nThat's it :slightly_smiling_face: Now you have `nicelog` installed and it's ready to be used. Not familiar with `nicelog`? Let's check what's inside:\n\n```javascript\nimport * as xPython from '@x-python/core';\n\nawait xPython.complete.repl({ code: 'from nicelog import ' });\n```\n\nAs it's already installed it should be available for code completion as well :white_check_mark:\n\nExample from the `nicelog` `PyPI` [page](https://pypi.org/project/nicelog/).\n\n```javascript\nconst { stderr } = await xPython.exec({\n  code: `\nimport logging\nimport sys\n\nfrom nicelog.formatters import Colorful\n\n# Setup a logger\nlogger = logging.getLogger('foo')\nlogger.setLevel(logging.DEBUG)\n\n# Setup a handler, writing colorful output\n# to the console\nhandler = logging.StreamHandler(sys.stderr)\nhandler.setFormatter(Colorful())\nhandler.setLevel(logging.DEBUG)\nlogger.addHandler(handler)\n\n# Now log some messages..\nlogger.debug('Debug message')\nlogger.info('Info message')\nlogger.warning('Warning message')\nlogger.error('Error message')\nlogger.critical('Critical message')\ntry:\n    raise ValueError('This is an exception')\nexcept:\n    logger.exception(\"An error occurred\")\n`,\n});\n\nconsole.log(stderr);\n```\n\n### Development\n\nTo play with the library locally do the following steps:\n\n1. clone this repo\n\n```bash\ngit clone git@github.com:suren-atoyan/x-python.git\n```\n\n2. install dependencies\n\n```bash\nnpm install # or yarn\n```\n\n3. run the dev server\n\n```bash\nnpm run dev\n```\n\nThat's it :slightly_smiling_face: Under `/playground` folder you can find the `index.html` file which contains a script with the demo code and under `/src` folder you can find the library source code. Enjoy it :tada:\n\n## License\n\n[MIT](./LICENSE)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsuren-atoyan%2Fx-python","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsuren-atoyan%2Fx-python","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsuren-atoyan%2Fx-python/lists"}