Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Project] Produce standalone ES6 modules. #2292

Open
14 tasks
Tracked by #2250
denis-migdal opened this issue Oct 26, 2023 · 31 comments
Open
14 tasks
Tracked by #2250

[Project] Produce standalone ES6 modules. #2292

denis-migdal opened this issue Oct 26, 2023 · 31 comments

Comments

@denis-migdal
Copy link
Contributor

denis-migdal commented Oct 26, 2023

Goals

  • Perfect integration with existing WebDev tools/pipelines.

  • Better JS <=> Brython communications through ES6 JS modules.

  • Enable AOT for Brython.

  • async imports to anticipate removal of synchronous AJAX due to depreciation.

Step 1: Produce semi-standalone JS

  • Objective: Make the generated JS less dependant on the Brython module format.

  • Motivation: A first step that'd enable easier benchmarking as well as easier Brython usage. Would enable to easily create JS files from Brython code.

  • Validation: Generate code in the Editor and execute it inside JSPerf.

  • Substeps:

    • Put the generated JS code inside a <script type="module"> appened to the <head> , instead of using eval(). Would avoid exposing the current scope, and would authorize top level await and import if we wishes to use it.

    • When generating JS code, add a build_context parameter, contaning a build_context.async: [true or false] option. By default, all python code would be generated with a build_context.async = true, but when imported by $B.import() or $B.import_from(), the imported module would be built with a build_context.async = false.

    • Enable to copy the generated JS into a file we can then import with a <script src='...'> tag. For that we need to solve some timing issues. To ensure scripts are executed one at the time, even if some uses top level await, I suggest using a $B.waitMyTurn() function returning a resolver. Add this to the generated JS code only when build_context.async == true :

// at the start of the generated JS
const $B = __BRYTHON__
const declareModule = await $B.waitMyTurn();

// do stuff here

// at the end of the file.
declareModule(some_args);
// at the start of brython.js
const $B = {}
globalThis.__BRYTHON__ = $B; // put __BRYTHON__ into the global scope

const waitingList = [];
$B.waitMyTurn = function() { // no need for async as it returns a Promise

  let start;
  let resolver;

  let waitMyTurn = new Promise( (r,err) => { start    = r });
  let declareMod = new Promise( (r,err) => { resolver = r });

  waitingList.push({start, resolver, declareMod});

  return waitMyTurn;   
}

// when Brython is ready :
for(let i = 0; i < waitingList.length; ++i) {
    let module = waitingList[i];
  module.start(module.resolver); // waitMyTurn is resolved.
  let some_args = await module.declareMod;
  // add the module to the list of Brython intialized modules.
}
  • To be able to test it, allow users to use the current github dev version inside the Editor. Ideally, select (with a <select>) the Brython version he wishes to use. But could be a simple copy/paste of the Editor page, with some modification in order to use the current github dev version. Would enable users to easily test if a bug is still in the dev version (cf recent issues).

  • Optionnally: make a "stable" (3.11), "latest" (3.12), and "testing" (3.12-dev - the Github version) Brython versions in order to avoid unstabilities in production when changing versions (cf recent issues) ?

Step 2 : Async imports

  • Objective: Enables async import for top level imports in Brython.

  • Motivation: Synchronous AJAX queries are depreciated.

  • Note: The strategy is to make the top level import asynchronous. Print a warning for non-top level imports, offering alternatives to users.

  • Substeps :

    • When build_context.async == true, make the top level dependancies async, by new import functions await $B.importAsync() and await $B.import_from(). Then the dependancies (if not already known ofc) would be built with build_context.async == true, and their waitMyTurn() would be immediately resolved (and set to null in the waitingList - if in it ofc).

    • When still having to use sync AJAX in $B.import(), and $B.import_from(), print a warning "Non top level sync import may not be supported in the future due to the evolution of web standards. Please import this module using a top level import if you want to use it."

Step 3: Beta feature: ES6 module format

  • Objective: Enable easy and "safe" Brython <=> Interactions

  • Motivation: Enable usage of WebDev tools (bundler, tree shaking, etc.).

  • Validation: Import a Brython module in ES6 format from a JS code, and import a JS ES6 module from a Brython module.

  • Note: Would be noted as a "Beta feature", meaning it might not support all of Brython features.

  • Substeps:

    • Add support to ES6 JS Module import, replace top level imports by something like:

      const $B = __BRYTHON__
      
      // + version sync...
      async function importAsync(module_name) {
          // Python : from .a.b.c import foo
          let x = $B.getModule(module_name);
          if( x === null )
              try {
                  $B.moduleName2Path(module_name) {
                      module_path = module_name.replaceAll('.', '/');
                      if(module_path[0] === '/')
                          module_path = '.' + module_path + ".mjs";
                  }
                  x = await import(module_path); // .mjs are ES6 JS module files
              } catch(e) { // ES6 JS module not found, try with Brython
                  x = $B.downloadModuleAsync(module_name);
                  x = $B.downloadModuleSync(module_name);
              }
          // delegate verification and assignation here:
          return $B.loadModule(x, ["foo"], {}, 0, locals);
      }
    • Add support to ES6 JS Module export. At the end of the generated JS, add several export .... to export all top-level symbols (functions, variables). This is equivalant to __all__ in Python. Alternatively, can use a @export python decorator to indicate the symbols to export/expose in the generated JS module.

Step 4: Compatibility with Web Dev tool with 0 adaptation.

  • Contexte: Let a project with 4 directories :

    • /libs/Brython (contains current Brython code)

    • /build/Brython (contains produced ES6 module files)

    • /src/ (e.g. contains users created python files)

    • /dist/ (contains the Website that can be distributed)

  • Objective: Being able to easily build ES6 .mjs files from /src/ to /build/Brython/ which can then be used by existing Web Dev tools like bundlers, tree shaking, etc. For performances, see also HTTP/2 Frequently Asked Questions.

  • Motivation: Such tools provides a large array of feature we can't expect Brython to implement. It also enables to integrate Web Dev pipeline that would be able to listen to changes into /build/Brython.

  • Validation: Create a Website with e.g. Webpack and generate a JS bundle.

  • Note: Would be noted as a "Beta feature", meaning it might not support all of Brython features.

  • Substeps:

    • brython-cli build $SRC [$DST] command to convert a single python file to an ES6 module. In the webpage, could then include a .mjs file instead of a .py file if the user wants to.
    • brython-cli build $DIR [$DST], idem, but with a directory. If in a processed file, an imported modules will also have his ES6 JS file, can use normal JS import import {} from ... instead of the Brython functions s.a. $B.import_async() (?). Would facilitate works of JS bundlers tools.
    • brython-cli watch $DIR [$DST] watch the directory $DIR and rebuid the .mjs files when its python source file is modified (using ionotify_wait ?).

Step 5: Fully standalone ES6 modules

  • Objective: Integrate current modules and Brython files into the brython-cli build/watch process in order to use Web Dev tools/feature on them too (e.g. tree shaking). If we use ES6 modules, would not require to use brython.js in the HTML file anymore.

  • Motivation: Would make produce .mjs files fully standalone, and to easily create personnalized JS bundles.

  • Validation: Create a Website with e.g. Webpack and generate a JS bundle.

  • Substeps:

    • import a brython_runtime.mjs (subpart of brython.js) at the start of files produced by brython-cli build/watch :
      import __BRYTHON__ from '$BRYTHON_PATH/brython_runtime.mjs'
      Could be an alias of brython.js in a first time - it doesn't need support to transpile Python code.

    • provides .mjs files for current Brython modules. Thus allowing to reduce the generated bundle to a bare minimum when using AOT Brython.

    • provides a brython_eval.mjs (subpart of brython.js) to support (or not) Python interpretation on the browser (could be an alias of brython.js in a first time). This may bring more security to the Web Page :

    import BRYTHON_EVAL from '$BRYTHON_PATH/brython_eval.mjs'
    
    BRYTHON_EVAL.eval("PYTHON_CODE") // execute the python code.
    BRYTHON_EVAL.watchCurrentPage(); // seach script type=text/python in the webpages   (the current behavior of brython.js)
    
    • Add an option build_context.aot = [true|false] . When false, do like the current behavior of make_dist, by keeping the Python source (so not transpiled) but make it standalone and expose a ES6 export interface at the end. Relying on existing bundlers to build the final .js file (e.g. Parcel ? or provide a default Webpack config ?) would be great and would reduce the number of tools to maintain.

@PierreQuentel What do you think ?

@PierreQuentel
Copy link
Contributor

I don't have yet an opinion on all these topics, but marking non-module-level imports as incorrect is not possible, because the Python language supports them and by design Brython is as compliant as possible with Python.

In the Brython standard library only, these scripts have such imports:

['_codecs.py', '_dummy_thread.py', '_frozen_importlib.py', '_pydatetime.py', '_pydecimal.py', '_socket.py', '_sre.py', '_thread.py', '_typing.py', 'argparse.py', 'ast.py', 'base64.py', 'bdb.py', 'calendar.py', 'cmd.py', 'code.py', 'codecs.py', 'collections\init.py', 'concurrent\futures\process.py', 'copy.py', 'copyreg.py', 'difflib.py', 'doctest.py', 'email\utils.py', 'encodings\init.py', 'encodings\rot_13.py', 'enum.py', 'functools.py', 'getopt.py', 'getpass.py', 'gettext.py', 'gzip.py', 'heapq.py', 'hmac.py', 'http\client.py', 'imp.py', 'importlib\init.py', 'importlib\_bootstrap.py', 'importlib\_bootstrap_external.py', 'importlib\abc.py', 'inspect.py', 'ipaddress.py', 'json\init.py', 'locale.py', 'logging\init.py', 'logging\config.py', 'logging\handlers.py', 'mimetypes.py', 'multiprocessing\util.py', 'nntplib.py', 'ntpath.py', 'os.py', 'pathlib.py', 'pdb.py', 'pickle.py', 'pkgutil.py', 'posixpath.py', 'profile.py', 'py_compile.py', 'pydoc.py', 'quopri.py', 're1.py', 'select.py', 'shutil.py', 'site-packages\simpleaio\helpers.py', 'site.py', 'socket.py', 'subprocess.py', 'symtable.py', 'sysconfig.py', 'tabnanny.py', 'tarfile.py', 'threading.py', 'time.py', 'timeit.py', 'tokenize.py', 'traceback.py', 'types.py', 'typing.py', 'unittest\init.py', 'unittest\main.py', 'unittest\loader.py', 'unittest\mock.py', 'urllib\parse.py', 'uu.py', 'uuid.py', 'warnings.py', 'weakref.py', 'zipfile.py']

@denis-migdal
Copy link
Contributor Author

I don't have yet an opinion on all these topics,

I can isolate some "sub-steps", that can still be interesting to do regardless of the whole global scheme.

but marking non-module-level imports as incorrect is not possible, because the Python language supports them and by design Brython is as compliant as possible with Python.

It is not exactly marking them as "incorrect", but notifying users that it might not be supported in the future due to the evolution of Web standards, independently of Python/Brython.

But yeah I understand your point.

@denis-migdal
Copy link
Contributor Author

denis-migdal commented Oct 26, 2023

Okay, some stuff that can be interesting regardless :

  1. Being able to test the current dev version in the Editor (either by duplicating the page, or through a <select> to choose a Brython version).
  2. Enabling to import ES6 JS files through normal Python import instead of having to use javascript.import_modules() (?).
  3. Having a brython-cli build $SRC [$DST] command generating a js file from a python source file (1 input file = 1 output file).
    a. With this command enabling to select an output format through an option :
    -> JS files, like the result of __BRYTHON__.pythonToJS() or javascript.py2js() (enables easy AOT in Brython).
    -> Python source code inside JS files, like the format of what brython-cli make_dist is producing (aka the Brython module format), but without dependencies resolutions.
    b. Adding a --watch option to this brython-cli build command.
    c. Enabling this command to take a directory as a source (then all elements in the directory would recursively be processed).

This would enable some level of integration with current WebDev tools... Well, won't be as performant as an ES6 output for tree shaking, but still, can be an easy step to be able to use some nice features (e.g. advanced Bundling).

Modules would then be loaded in Brython thanks to javascript.load() or javascript.import_js() I think.
I also noticed that in JS we can import Brython module thanks to getPythonModule().

Still, could be nice to be able to just include them as <script type="text/javascript>. Then it'd require some work on brython.js so that it'd be immediately "ready" once the file is synchronously executed (would also solves some timing issues when working with JS). I'd suggest to have at least a __BRYTHON__.whenReady() returning a promise that'd be resolved once Brython is ready. This function could then be added at the start of all generated JS (the top-level function could be marked as async to allow top level await ?).

Could be the firsts steps in this project, then the only remaining tasks would be :

  • making the generated JS ES6 compatible (could be nice) [could be a third option for output format as a beta feature]
  • making the top level Python import using awaited asynchronous operation internally (maybe not a priority ?)

@PierreQuentel What do you think now ?

EDIT:
To enable better bundling and module dependencies resolution during build time :

  1. Replace the (function(){})() by :
let singleton = null;

function X(){ // the original (function(){}) here:
      // ...
}

export default function FOO() {
     if( singleton !== null)
         return singleton
     return singleton = X();
}

if( __BRYTHON__.__IN_IMPORT__ !== true ) // if included in a <script>, execute it immediately.
     FOO();
  1. In generated JS files (using javascript.py2js() or brython-cli make_dist formats ) :
// at the top of the files:
const orig = __BRYTHON__.__IN_IMPORT__
__BRYTHON__.__IN_IMPORT__ = true
// only if resolved during build time:
import MODULE_A from './path_to_module_A'; // pre-import without executing it.
__BRYTHON__.__IN_IMPORT__ = orig

// the generated JS stuff
// ...
// replace top level imports resolved during build time by:
MODULE_A(); // execute it now (only when we need it).
__BRYTHON__.getPythonModule(module_name); // do stuff the Brython way.
// ...
// end of the generated JS stuff

With that we can now use tools like Parcell, Webpack, etc.
For normal generation would require to put the generated JS inside <script> tags instead of using eval().
Or can be a behavior used only when building files with brython-cli build.

If the format mode is ES6, then it could simply replace Python top level import resolved during build time, by JS top level import.
Then it'd be able to do way better tree shaking.

EDIT2: Instead of a await __BRYTHON__.whenReady(), an alternative solution would be to do :

// do eventually some stuff

function FOO(){} // the generated main function.

__BRYTHON__.addModule(FOO); // will execute FOO at the correct time to add the module

With that, no needs for a top level await.
Still, a __BRYTHON__.whenReady() would be useful when working with JS.

@PierreQuentel
Copy link
Contributor

  1. ok, easy to implement and helpful for users

  2. javascript.import_modules is asynchronous (it takes an argument callback that is called when the JS modules are loaded). Since Python import is synchronous, it cannot be used to load JS modules

  3. once again, I don't want to encourage users to precompile Python code to Javascript. This is not how Brython is designed (code is translated on the fly, and possibly stored in an indexedDB cache). If you think I'm wrong to persist in this direction, which I completely accept, the best is maybe to create a fork of the CPython brython package to provide an alternative brython-cli tool that supports precompiling and "watch" options to track changes to source code

@denis-migdal
Copy link
Contributor Author

denis-migdal commented Oct 26, 2023

  1. javascript.import_modules is asynchronous (it takes an argument callback that is called when the JS modules are loaded). Since Python import is synchronous, it cannot be used to load JS modules

If it is only for TOP-level import, you can do TOP-level JS await (if webworker are constructed with type=module, and if generated JS code is put inside new <script type="module"> instead of being put inside eval).

But that's not a hill I'll die on xD.

  1. once again, I don't want to encourage users to precompile Python code to Javascript. This is not how Brython is designed (code is translated on the fly, and possibly stored in an indexedDB cache).

I suggested 2 output formats. The second one is doing nearly what brython-cli make_dist is doing, i.e. not precompiling but wrapping the python source code inside a JS file. So I think this one format is compliant with how Brython is designed.

The advantage compared to make_dist is that :

  • it enables to use existing bundlers, which would offers more liberty/features/customisations compared to the current make_dist.
  • it enables a watch mode (watch modes are cool).

Still, I think Brython should at least add a __BRYTHON__.whenReady() we could await, so that we could avoid timing issues when we do stuff with JS (e.g. testing stuff in the AST generated JS / testing with JSPerf / doing JS interactions).

For example, the following code won't work:

<script type="text/python" id="s1">
   def foo():
	   pass
</script>
<script>
const module = __BRYTHON__.getPythonModule('s1');
console.log(module); // returns undefined
</script>

Maybe also add a __BRYTHON__.getPythonModuleAsync('s1'); or __BRYTHON__.whenPythonModuleDeclared('s1'); that'd be resolved when the module is declared in Brython ?

Note: it seems there is a bug in the documentation example for getPythonModule().

Uncaught RangeError: NaN can't be converted to BigInt because it isn't an integer
    rich_op1 https://raw.githack.com/brython-dev/brython/master/www/src/brython.js:5845
    rich_op https://raw.githack.com/brython-dev/brython/master/www/src/brython.js:5823
    show_square3 https://raw.githack.com/brython-dev/brython/master/www/src/brython.js line 5137 > Function:36
    <anonymous> http://localhost:3000/test.html:23
brython.js:5845:57

@goffi-contrib
Copy link
Contributor

goffi-contrib commented Nov 6, 2023

Hello, really interesting issue to follow, I'm specially interested in async imports. Regarding non top level imports, it can be sometimes useful to use them to save resources (e.g. you need to import an heavy module only if some dynamically known condition are met). What about doing a Brython specific async import alternative, for instance in aio module?

@PierreQuentel
Copy link
Contributor

Note: it seems there is a bug in the documentation example for getPythonModule().

You're right, the error happens if there is nothing in the INPUT field, or if parseInt() doesn't return an integer. I have added a default value.

PierreQuentel added a commit that referenced this issue Nov 6, 2023
@denis-migdal
Copy link
Contributor Author

e.g. you need to import an heavy module only if some dynamically known condition are met

? You can already do that in Python/Brython.

Just put the import inside a function, or use importlib ?

PierreQuentel added a commit that referenced this issue Nov 6, 2023
@PierreQuentel
Copy link
Contributor

Still, I think Brython should at least add a BRYTHON.whenReady() we could await, so that we could avoid timing issues when we do stuff with JS (e.g. testing stuff in the AST generated JS / testing with JSPerf / doing JS interactions).

Good idea Denis ! In the commit above I have added a new core Brython script, brython_ready.js, that sets __BRYTHON__.whenReady(). Can you test it ? If it's ok I will document it.

With the following commit (abed0fb) you can test that the script is available with

document.getElementById('s1').addEventListener('load',
  function(ev){
      var v = document.getElementById('num').value
      __BRYTHON__.getPythonModule('s1').show_square(parseInt(v))
  }
)

@denis-migdal
Copy link
Contributor Author

Thanks, I'll take a look at it tomorrow ;)

@goffi-contrib
Copy link
Contributor

e.g. you need to import an heavy module only if some dynamically known condition are met

? You can already do that in Python/Brython.

Just put the import inside a function, or use importlib ?

I may have missed something, but I thought that non top level import were about to be deprecated with Brython:

Non top level sync import may not be supported in the future due to the evolution of web standards. Please import this module using a top level import if you want to use it.

And my suggestion was to replace blocking import by a Brython specific async import. Did I misunderstand?

@PierreQuentel
Copy link
Contributor

I may have missed something, but I thought that non top level import were about to be deprecated with Brython:

No, it's standard Python so it will remain supported in Brython.

@denis-migdal
Copy link
Contributor Author

I may have missed something, but I thought that non top level import were about to be deprecated with Brython:

Synchronous import currently relies on synchronous AJAX that are depreciated (but still implemented by browser).
So no, non top level import will not be depreciated in Brython. It's just that when synchronous AJAX will be removed from Browser (maybe in 10 years, in 200 years, we can't know), Brython will have an hard time.

And my suggestion was to replace blocking import by a Brython specific async import. Did I misunderstand?

Well we have no issue with async import, I think importutils already have one ?

@goffi-contrib
Copy link
Contributor

Synchronous import currently relies on synchronous AJAX that are depreciated (but still implemented by browser).
So no, non top level import will not be depreciated in Brython. It's just that when synchronous AJAX will be removed from Browser (maybe in 10 years, in 200 years, we can't know), Brython will have an hard time.

Indeed, I'm aware of the situation, and it's really good that you anticipate this (also good from a performance point of view).

Well we have no issue with async import, I think importutils already have one ?

Does it? I though that everything was blocking there, and after a quick glance at the doc, I haven't seen anything mentioning async import.

@PierreQuentel
Copy link
Contributor

Let's keep things simple. Brython is as compliant with standard Python as possible, so importing Python modules / packages is done with the keyword import, which works synchronously. This will remain until the Python language evolves to "async import" (I haven't seen any discussion about that).

In any case, if there was a need to import Python code asynchronously in Brython, it would be with a different syntax from the import statement.

For Javascript code

  • ES6 modules are "imported" with javascript.import_modules
  • non-modules used to be loaded with browser.load(), but this doesn't work for all kinds of scripts as reported is issue Can't load Swiper JS Library #2309. We should probably deprecate it - additionnaly, putting it in the browser module was a bad decision, and using eval is usually frowned upon (mal vu, quoi !)
  • for these "non-module" scripts, I have added a (so far undocumented) function javascript.import_scripts(script_urls, callback) with the same interface as import_modules. The function dynamically insert SCRIPT elements in the page and calls the callback function with these elements as arguments. I have tested it with the non-module version of Swiper v10, it worked (ie window.Swiper was set)
  • this feature will not work in web workers (no access to the DOM), but in this case there is importScripts... which works synchronously !

@ed2050
Copy link

ed2050 commented Nov 8, 2023

Pierre wrote:
3. once again, I don't want to encourage users to precompile Python code to Javascript. This is not how Brython is designed (code is translated on the fly, and possibly stored in an indexedDB cache).

Denis wrote:
I suggested 2 output formats. The second one is doing nearly what brython-cli make_dist is doing, i.e. not precompiling but wrapping the python source code inside a JS file. So I think this one format is compliant with how Brython is designed.

The advantage compared to make_dist is that :

  • it enables to use existing bundlers, which would offers more liberty/features/customisations compared to the current make_dist.
  • it enables a watch mode (watch modes are cool).

I have a third advantage to add: It would allow brython to be used to write browser extensions with manifest v3.

Manifest 2 vs 3

Brython works fine with the older manifest v2 standard. However google deprecated v2. They already stopped allowing extensions using manifest v2 being added to chrome web store, and plan to block manifest v2 extensions from working in a future version of chrome very soon (been delayed a few times but it's coming).

Manifest v3 locks extensions down much tighter. v3 requires all extension code to be static, ie only code in (read-only) extension files will run. Chrome doesn't recognize brython code until brython.js translates it to js code. This is forbidden in manifest v3.

Using brython with manifest v3 will unfortunately require precompilation to js. I know browser extensions aren't the main purpose of brython. But it's part of the web environment and very useful to have access to (many operations can only be achieved with extension-level access).

Bottom line

I understand Pierre's position. I too would prefer to see python scripts natively supported by all browsers. But we don't live in that world yet.

Preventing precompilation to js only limits the web platforms that brython can run on. And that limits brython's usefulness and growth.

@denis-migdal
Copy link
Contributor Author

I plan to make a tool for that.
Now that we will have a __BRYTHON__.whenReady(), it'll be possible to make AoT compilation (I have to test that).

Had some works these last days, still some stuff to do tomorrow morning, but then I'll try to work on it ;)

@denis-migdal
Copy link
Contributor Author

Trying to execute brython_standard_parser.js inside Deno, got some errors.

It seems you have a non-UTF8 character in brython_standard_parser.js file : throw Error('scope �trange')}.
Found with Linux command grep -axv '.*' ./www/src/brython_standard_parser.js.

Got also an error :

error: The module's source code could not be parsed: Expression expected at ./www/src/brython_standard_parser.js:9790:16

  if($B.imported[package]===undefined){
                              ~~~~~~~

@denis-migdal
Copy link
Contributor Author

denis-migdal commented Nov 8, 2023

It seems that package is a reserved word in JavaScript. All variables named packages should be renamed.

It seems I'll have to make some adjustment, else I think I can run it in deno. Which will enable me to convert Brython files to JS files from command line.

@denis-migdal
Copy link
Contributor Author

Started a project for that :
https://github.com/denis-migdal/Brython-AOT

For now the conversion is doing nothing, I have to see how I can execute brython_standard_parser.js (?) into Deno.

@denis-migdal
Copy link
Contributor Author

denis-migdal commented Nov 9, 2023

Line 250, line, idem
Line 3172, $PatternClassCtx isn't declared with let/const.
Line 6648, callable_iterator, idem
Line 9515, VFSLoader, idem
Line 12018, fast_float, idem
Line 12332, dict_view_op, idem
Line 17476, last_token, idem

error: Uncaught (in promise) TypeError: Cannot create property '__doc__' on boolean 'true'

Line 16619

First line should be :

var __BRYTHON__= globalThis.__BRYTHON__ ||{}

@denis-migdal
Copy link
Contributor Author

@PierreQuentel why do you use an eval line 17490 ?

eval(rule.action)

Can't you replace the rule.action string by a function rule.action() ?

Also, "a" isn't defined in $B._PyPegen.concatenate_strings(p, a).

@denis-migdal
Copy link
Contributor Author

Also, "a" isn't defined in $B._PyPegen.concatenate_strings(p, a).

This error prevents me from going further.

@PierreQuentel I'll need your help on this one, I don't know what is this "a" and where it should be defined.

@denis-migdal
Copy link
Contributor Author

brython_standard_parser.js is a generated file that hasn't been modified since 1 year.
Is it still use ?

@PierreQuentel
Copy link
Contributor

No, it is part of a set of scripts that I developed a few months ago to replace the current parser in py2js.js by a PEG parser based on the standard Python grammar file; brython_standard_parser.js is the equivalent of brython.js with this PEG parser. It worked, but was much slower, so I have kept the current version.

You can ignore brython_standard_parser.js and these scripts : string_parser.js, number_parser.js, action_helpers.js, python_parser.js, full_grammar.js

@denis-migdal
Copy link
Contributor Author

Ok, then I'll have to test again with brython.js.

Shouldn't you remove theses files, or put them in a PEG Parser (old) directory ?

@denis-migdal
Copy link
Contributor Author

@ed2050 You'll be happy, I'm making good progress on it ;)

Got some errors when executing the generated JS, but I'll work on fixing that ;)

@denis-migdal
Copy link
Contributor Author

denis-migdal commented Nov 9, 2023

Good idea Denis ! In the commit above I have added a new core Brython script, brython_ready.js, that sets __BRYTHON__.whenReady(). Can you test it ? If it's ok I will document it.

Tested a simple exemple :

(async function(){

__BRYTHON__.imported["exec"] = {};
__BRYTHON__.frames_stack = [];

// the generated JS code here

})();

Seems to work without whenReady.
I assume some recent commit may have fixed something, making the need of whenReady obsolete somehow.
Dunno what happened.

But yeah, with small adaptation the AoT seems to be working \o/

@denis-migdal
Copy link
Contributor Author

@ed2050 Brython-AOT is now working ;)
You'll need to get the Brython.js from my PR for it to work.
If you could test it a little, that'd be nice ;)

I will add several output options one day (an ES6 output could be nice), with or without Python code converted into JS AoT or on-demand, + some ES6 import for top level imports.

@denis-migdal
Copy link
Contributor Author

@PierreQuentel I submitted a PR, can you see if you can merge it ?
#2314

@1951FDG
Copy link

1951FDG commented Jan 4, 2024

Just would like to add some more context to previous comment #2292 (comment) by @ed2050

Although I got Brython-AOT working, and was able to output pre-compiled js, the precompiled file still requires Brython

Problem is that brython.js currently does not work for Manifest V3 (mv3)

brython.js uses eval or new Function(...), but these are only supported in sandboxed iframes for mv3

Uncaught EvalError: Refused to evaluate a string as JavaScript because 'unsafe-eval' is not an allowed source of script in the following Content Security Policy directive: "script-src 'self'".

If anyone has an idea of how to convert Python code to Javascript for usage in an mv3 extension, please do share !

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants