Open denis-migdal opened 8 months ago
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']
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.
Okay, some stuff that can be interesting regardless :
<select>
to choose a Brython version).import
instead of having to use javascript.import_modules()
(?).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 :
@PierreQuentel What do you think now ?
EDIT: To enable better bundling and module dependencies resolution during build time :
(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 Githubissues.
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 usingeval()
. Would avoid exposing the current scope, and would authorize top levelawait
andimport
if we wishes to use it.[ ] When generating JS code, add a
build_context
parameter, contaning abuild_context.async: [true or false]
option. By default, all python code would be generated with abuild_context.async = true
, but when imported by$B.import()
or$B.import_from()
, the imported module would be built with abuild_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 levelawait
, I suggest using a$B.waitMyTurn()
function returning a resolver. Add this to the generated JS code only whenbuild_context.async == true
:[ ] 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 functionsawait $B.importAsync()
andawait $B.import_from()
. Then the dependancies (if not already known ofc) would be built withbuild_context.async == true
, and theirwaitMyTurn()
would be immediately resolved (and set tonull
in thewaitingList
- 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:
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 importimport {} 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 (usingionotify_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 usebrython.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 ofbrython.js
) at the start of files produced bybrython-cli build/watch
:import __BRYTHON__ from '$BRYTHON_PATH/brython_runtime.mjs'
Could be an alias ofbrython.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 ofbrython.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 :build_context.aot = [true|false]
. Whenfalse
, do like the current behavior ofmake_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 ?