Skip to content

Commit 2bace51

Browse files
authored
Merge pull request #136 from Distributive-Network/Xmader/docs/update-readme
update readme
2 parents 36ceee2 + 0b94ff3 commit 2bace51

File tree

6 files changed

+87
-25
lines changed

6 files changed

+87
-25
lines changed

README.md

Lines changed: 60 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ js_eval("console.log")('hello, world')
2828

2929
### Data Interchange
3030
- Strings share immutable backing stores whenever possible (when allocating engine choses UCS-2 or Latin-1 internal string representation) to keep memory consumption under control, and to make it possible to move very large strings between JS and Python library code without memory-copy overhead.
31-
- TypedArrays to share mutable backing stores; if this is not possible we will implement a copy-on-write (CoW) solution.
31+
- TypedArrays share mutable backing stores.
3232
- JS objects are represented by Python dicts
3333
- JS Date objects are represented by Python datetime.datetime objects
3434
- Intrinsics (boolean, number, null, undefined) are passed by value
@@ -86,9 +86,9 @@ For VSCode users, similar to the Build Task, we have a Test Task ready to use.
8686

8787
## Using the library
8888

89-
### Install from [PyPI](https://pypi.org/project/pythonmonkey/)
89+
> npm (Node.js) is required **during installation only** to populate the JS dependencies.
9090
91-
> PythonMonkey is not release-ready yet. Our first public release is scheduled for mid-June 2023.
91+
### Install from [PyPI](https://pypi.org/project/pythonmonkey/)
9292

9393
```bash
9494
$ pip install pythonmonkey
@@ -116,7 +116,12 @@ Type "help", "copyright", "credits" or "license" for more information.
116116
'Hello from Spidermonkey!'
117117
```
118118

119-
Alternatively, you can build a `wheel` package by running `poetry build --format=wheel`, and install it by `pip install dist/*.whl`.
119+
Alternatively, you can build installable packages by running
120+
```bash
121+
$ cd python/pminit && poetry build --format=sdist && cd - && mv -v python/pminit/dist/* ./dist/
122+
$ poetry build --format=wheel
123+
```
124+
and install them by `pip install ./dist/*`.
120125

121126
## Debugging Steps
122127

@@ -135,6 +140,16 @@ If you are using VSCode, it's more convenient to debug in [VSCode's built-in deb
135140
## API
136141
These methods are exported from the pythonmonkey module.
137142

143+
### eval(code, evalOpts)
144+
### isCompilableUnit(code)
145+
### collect()
146+
### bigint(int)
147+
### `SpiderMonkeyError`
148+
### `JSObjectProxy`
149+
### `null`
150+
151+
See definitions in [python/pythonmonkey/pythonmonkey.pyi](python/pythonmonkey/pythonmonkey.pyi).
152+
138153
### require(moduleIdentifier)
139154
Return the exports of a CommonJS module identified by `moduleIdentifier`, using standard CommonJS
140155
semantics
@@ -146,9 +161,9 @@ semantics
146161
- Modules are evaluated immediately after loading
147162
- Modules are not loaded until they are required
148163
- The following extensions are supported:
149-
** `.js` - JavaScript module; source code decorates `exports` object
150-
** `.py` - Python module; source code decorates `exports` dict
151-
** `.json` -- JSON module; exports are the result of parsing the JSON text in the file
164+
* `.js` - JavaScript module; source code decorates `exports` object
165+
* `.py` - Python module; source code decorates `exports` dict
166+
* `.json` - JSON module; exports are the result of parsing the JSON text in the file
152167

153168
### globalThis
154169
A Python Dict which is equivalent to the globalThis object in JavaScript.
@@ -168,19 +183,23 @@ necessary unless the main entry point of your program is written in JavaScript.
168183

169184
Care should be taken to ensure that only one program module is run per JS context.
170185

171-
## Built-In Functions
186+
## [Built-In Functions](python/pythonmonkey/global.d.ts)
187+
172188
- `console`
189+
- `atob`
190+
- `btoa`
173191
- `setTimeout`
174-
- `setInterval`
175192
- `clearTimeout`
176-
- `clearInterval`
177193

178194
### CommonJS Subsystem Additions
179195
The CommonJS subsystem is activated by invoking the `require` or `createRequire` exports of the (Python)
180196
pythonmonkey module.
197+
181198
- `require`
182199
- `exports`
183200
- `module`
201+
- `__filename`
202+
- `__dirname`
184203
- `python.print` - the Python print function
185204
- `python.getenv` - the Python getenv function
186205
- `python.stdout` - an object with `read` and `write` methods, which read and write to stdout
@@ -264,6 +283,26 @@ globalThis.python.exit = pm.eval("""'use strict';
264283
""")(sys.exit);
265284
```
266285

286+
### Run Python event-loop
287+
288+
You need an event-loop running to use `setTimeout` and `Promise`<=>`awaitable` coercion.
289+
290+
```python
291+
import asyncio
292+
293+
async def async_fn():
294+
await pm.eval("""
295+
new Promise((resolve) => setTimeout((...args) => {
296+
console.log(args);
297+
resolve();
298+
}, 1000, 42, "abc")
299+
)
300+
""")
301+
await pm.eval("async (x) => await x")(asyncio.sleep(0.5))
302+
303+
asyncio.run(async_fn())
304+
```
305+
267306
# pmjs
268307
A basic JavaScript shell, `pmjs`, ships with PythonMonkey. This shell can act as a REPL or run
269308
JavaScript programs; it is conceptually similar to the `node` shell which ships with Node.js.
@@ -286,15 +325,15 @@ The program module, or main module, is a special module in CommonJS. In a progra
286325
(command-line arguments)
287326

288327
```console
289-
# echo "console.log('hello world')" > my-program.js
290-
# pmjs my-program.js
328+
$ echo "console.log('hello world')" > my-program.js
329+
$ pmjs my-program.js
291330
hello world
292-
#
331+
$
293332
```
294333

295334
### CommonJS Module: JavaScript language
296-
```python
297-
# date-lib.js - require("./date-lib")
335+
```js
336+
// date-lib.js - require("./date-lib")
298337
const d = new Date();
299338
exports.today = `${d.getFullYear()}-${String(d.getMonth()).padStart(2,'0')}-${String(d.getDay()).padStart(2,'0')}`
300339
```
@@ -309,15 +348,18 @@ exports['today'] = date.today()
309348
# Troubleshooting Tips
310349

311350
## CommonJS (require)
312-
If you are having trouble with the CommonJS require function, set environment variable DEBUG='ctx-module*' and you can see the filenames it tries to laod.
351+
If you are having trouble with the CommonJS require function, set environment variable `DEBUG='ctx-module*'` and you can see the filenames it tries to laod.
313352

314353
## pmjs
315354
- there is a `.help` menu in the REPL
316355
- there is a `--help` command-line option
317356
- the `-r` option can be used to load a module before your program or the REPL runs
318357
- the `-e` option can be used evaluate code -- e.g. define global variables -- before your program or the REPL runs
319-
- The REPL can evaluate Python expressions, storing them in variables named `$1`, `$2`, etc. ```
320-
# pmjs
358+
- The REPL can evaluate Python expressions, storing them in variables named `$1`, `$2`, etc.
359+
360+
```console
361+
$ pmjs
362+
321363
Welcome to PythonMonkey v0.2.0.
322364
Type ".help" for more information.
323365
> .python import sys

python/pythonmonkey/__init__.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
# Expose the package version
66
import importlib.metadata
77
__version__= importlib.metadata.version(__name__)
8+
del importlib
89

910
# Load the module by default to make `console`/`atob`/`btoa` globally available
1011
require("console")
@@ -13,7 +14,7 @@
1314
# Add the `.keys()` method on `Object.prototype` to get JSObjectProxy dict() conversion working
1415
# Conversion from a dict-subclass to a strict dict by `dict(subclass)` internally calls the .keys() method to read the dictionary keys,
1516
# but .keys on a JSObjectProxy can only come from the JS side
16-
pm.eval("""
17+
pythonmonkey.eval("""
1718
(makeList) => {
1819
const keysMethod = {
1920
get() {

python/pythonmonkey/cli/pmjs.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
import sys, os, readline, signal, getopt
77
import pythonmonkey as pm
88
globalThis = pm.eval("globalThis")
9-
evalOpts = { 'filename': __file__, 'fromPythonFrame': True, 'strict': False }
9+
evalOpts = { 'filename': __file__, 'fromPythonFrame': True, 'strict': False } # type: pm.EvalOptions
1010

1111
if (os.getenv('PMJS_PATH')):
1212
requirePath = list(map(os.path.abspath, os.getenv('PMJS_PATH').split(':')))

python/pythonmonkey/global.d.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,9 @@ declare const python: {
3737
paths: string[];
3838
};
3939

40+
declare var __filename: string;
41+
declare var __dirname: string;
42+
4043
/** see `pm.eval` */
4144
declare function pmEval(code: string): any;
4245

python/pythonmonkey/pythonmonkey.pyi

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,18 @@ def eval(code: str, evalOpts: EvalOptions = {}, /) -> _typing.Any:
2222
JavaScript evaluator in Python
2323
"""
2424

25+
def isCompilableUnit(code: str) -> bool:
26+
"""
27+
Hint if a string might be compilable Javascript without actual evaluation
28+
"""
29+
30+
def internalBinding(namespace: str) -> JSObjectProxy:
31+
"""
32+
INTERNAL USE ONLY
33+
34+
See function declarations in ./builtin_modules/internal-binding.d.ts
35+
"""
36+
2537
def collect() -> None:
2638
"""
2739
Calls the spidermonkey garbage collector

python/pythonmonkey/require.py

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -32,15 +32,16 @@
3232
import functools
3333

3434
from . import pythonmonkey as pm
35+
3536
node_modules = os.path.abspath(
3637
os.path.join(
37-
importlib.util.find_spec("pminit").submodule_search_locations[0],
38+
importlib.util.find_spec("pminit").submodule_search_locations[0], # type: ignore
3839
"..",
3940
"pythonmonkey",
4041
"node_modules"
4142
)
4243
)
43-
evalOpts = { 'filename': __file__, 'fromPythonFrame': True }
44+
evalOpts = { 'filename': __file__, 'fromPythonFrame': True } # type: pm.EvalOptions
4445

4546
# Add some python functions to the global python object for code in this file to use.
4647
globalThis = pm.eval("globalThis;", evalOpts)
@@ -240,11 +241,11 @@ def load(filename: str) -> Dict:
240241
name = os.path.basename(filename)
241242
if name not in sys.modules:
242243
sourceFileLoader = machinery.SourceFileLoader(name, filename)
243-
spec = importlib.util.spec_from_loader(sourceFileLoader.name, sourceFileLoader)
244+
spec: machinery.ModuleSpec = importlib.util.spec_from_loader(sourceFileLoader.name, sourceFileLoader) # type: ignore
244245
module = importlib.util.module_from_spec(spec)
245246
sys.modules[name] = module
246-
module.exports = {}
247-
spec.loader.exec_module(module)
247+
module.exports = {} # type: ignore
248+
spec.loader.exec_module(module) # type: ignore
248249
else:
249250
module = sys.modules[name]
250251
return module.exports
@@ -344,3 +345,6 @@ def require(moduleIdentifier: str):
344345
if not os.path.exists(filename):
345346
filename = os.path.join(os.getcwd(), "__main__") # use the CWD instead
346347
return createRequire(filename)(moduleIdentifier)
348+
349+
# Restrict what are exposed to the pythonmonkey module.
350+
__all__ = ["globalThis", "require", "createRequire", "runProgramModule"]

0 commit comments

Comments
 (0)