brython-dev / brython

Brython (Browser Python) is an implementation of Python 3 running in the browser

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Importing an inline script from a webworker module doesn't work

shlomil opened this issue · comments

The following is a script with a webworker module. In the webworker module I tried to import an inline script (embedded whithin a <script> tag with id="some_module2"), which is listed just above the webworker - It tries to fetch some_module2 from the webserver and eventually failed.

<!doctype html>
<html>
<head>
<meta charset="utf-8">

<!--
<script src="https://raw.githack.com/brython-dev/brython/master/www/src/brython.js"
        crossorigin="anonymous">
</script>
<script src="https://raw.githack.com/brython-dev/brython/master/www/src/brython_stdlib.js"
        crossorigin="anonymous">
</script>
-->
<script src="brython/src/brython.js"></script>
<script src="brython/src/brython_stdlib.js"></script>

</head>
<body>

<script type="text/python" id="some_module2">
def some_func2():
    return "some_text2"
</script>

<script type="text/python" class="webworker" id="worker">
from browser import bind, self
from some_module2 import some_func2

@bind(self, "message")
def message(evt):
    result = 'pong' if evt.data == 'ping' else evt.data
    workerResult = f'Result: {result}'
    self.send(workerResult)

</script>

<script type="text/python">
from browser import worker

def ready(w):
  print('ready')
  w.send('ping')

def message(ev):
  print(ev.data)

worker.create_worker("worker", onready=ready, onmessage=message)
</script>

</body>
</html>

Tested on the latest version from git (commit ef5a2a1) and some other past versions, got back to 3.8.x. This problem might have been there since ever.
Tested loading it from a subfolder (not from server root) - also fails.

You are right, the web worker cannot import a script embedded in the page, and it cannot because by design a web worker has no access to the document (the main program has). It can only load modules by an Ajax call, or of course if it is in the standard library.

The web worker communicates with the main program through messages, so you can implement a simple protocol to send the source code of embedded scripts, for instance with the code below (I had to make browser.run_script available in web workers in the commit referenced above to make it work)

<!doctype html>
<html>
<head>
<meta charset="utf-8">

<script src="https://raw.githack.com/brython-dev/brython/master/www/src/brython.js"
        crossorigin="anonymous">
</script>
<script src="https://raw.githack.com/brython-dev/brython/master/www/src/brython_stdlib.js"
        crossorigin="anonymous">
</script>


</head>
<body>

<script type="text/python" id="some_module2">
def some_func2():
    return "some_text2"
</script>

<script type="text/python" class="webworker" id="worker">
from browser import bind, self, run_script

@bind(self, "message")
def message(evt):
    if isinstance(evt.data, dict) and 'modules' in evt.data:
        # simulate module import
        for module in evt.data['modules']:
            run_script(evt.data['modules'][module], module)
    import some_module2
    print(some_module2.some_func2())

</script>

<script type="text/python">
from browser import worker, document, run_script

import some_module2

print(some_module2.some_func2())

def ready(w):
  print('ready')
  # send source code of embedded modules to worker
  w.send({"modules": {"some_module2": document['some_module2'].text}})

def message(ev):
  print(ev.data)

worker.create_worker("worker", onready=ready, onmessage=message)
</script>

</body>
</html>

Oh. Ok. It's not a bug but a feature then :)

It's actually understandable since there is some instantiation for embedded scripts that sets the "runtime environment" (worker/browser) that cannot be undone. I think that a side note in the documentation might also be helpful for people in the future. Right now there is a mention in the docs about webworker limitations that states about the inability to access the document but I didn't see anything about import limitations, or maybe I missed it.

As for my needs - I try to run complex code in the worker. Code that is split to several files. I can use the above technique or simply fetch it from the server which may result in quite a few fetches.

In that case, I think that a another cool solution , IMO, might be allowing to set something like class="webworker_space" for some_module2's script tag, which will allow it to be imported by the webworker in the same "runtime environment".

Thanks for the detailed explanation and the fix!

I don't think the use case is so widespread that it deserves a generic feature in Brython; but you can implement the solution you suggest by tweaking the protocol in my previous answer.

I am closing the issue, feel free to comment again if you wish.

I am closing the issue, feel free to comment again if you wish.

I'll take your kind offer to comment :-) ... and just add a note that while the above solution will work just fine for very trivial code running in the worker, it will still fail if the manually imported code itself needs to import a module, in example:

<script type="text/python" id="some_module3">
def some_func3():
    return "some_text3"
</script>

<script type="text/python" id="some_module2">
def some_func2():
    import some_module3
    return some_module3.some_func3()
    #return "some_text2"
</script>

This is again, In my opinion, not a show stopper issue. A workaround can always be found.