JSPyBridge icon indicating copy to clipboard operation
JSPyBridge copied to clipboard

Bridge either returns old state or times out

Open jonarmani opened this issue 1 year ago • 2 comments

I'm managing Automerge documents through JSPyBridge.

from javascript import require
automerge = require('@automerge/automerge')

doc = automerge.init()  # I also give it a key, "log", holding a list, using the separate automerge.load()

Updating a document requires sending the document and a change function into the .change() call, which returns the changed document. This is my best attempt using the bridge:

def append_message(doc):
    doc['log'].append("message")
    return doc
                
new_doc = automerge.change(doc, append_message)

but the new_doc always appears identical to doc for some reason.

I tried a different approach using eval_js():

from javascript import eval_js

d = doc
new_doc = eval_js('''
    return automerge.change(d, doc => {
        doc['log'].push("message")
    })
''')

which does update the new state into new_doc, but this doesn't seem to be the intended way to use this library. Additionally, I start facing lots of bridge proxy timeout errors this way:

message = "foo"

new_doc = eval_js('''
    let msg = await message.valueOf();
    return automerge.change(d, doc => {
        doc['log'].push(msg)
    })
''')

this error throws when awaiting the valueOf() when shuffling string data into eval_js() with any reasonable frequency::

Exception in thread Thread-6 (process_queue):
Traceback (most recent call last):
  File "/usr/local/lib/python3.11/threading.py", line 1038, in _bootstrap_inner
    self.run()
  File "/usr/local/lib/python3.11/threading.py", line 975, in run
    self._target(*self._args, **self._kwargs)
  File "/home/jon/projects/project/file.py", line 80, in process_queue
    new_doc = eval_js('''
        ^^^^^^^^^^^
  File "/home/jon/.local/lib/python3.11/site-packages/javascript/__init__.py", line 53, in eval_js
    rv = config.global_jsi.evaluateWithContext(js, local_vars, forceRefs=True)
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/jon/.local/lib/python3.11/site-packages/javascript/proxy.py", line 213, in __call__
    else self._exe.callProp(
         ^^^^^^^^^^^^^^^^^^^
  File "/home/jon/.local/lib/python3.11/site-packages/javascript/proxy.py", line 153, in callProp
    resp = self.pcall(ffid, "call", method, args, timeout=timeout, forceRefs=forceRefs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/home/jon/.local/lib/python3.11/site-packages/javascript/proxy.py", line 132, in pcall
    raise Exception(
Exception: Call to 'evaluateWithContext' timed out. Increase the timeout by setting the `timeout` keyword argument.

jonarmani avatar Aug 09 '23 00:08 jonarmani

I've ultimately found something that works well enough (so far):

message = query['message']
d = self.doc
js_code = f'''
    let msg = {json.dumps(message)}; ''' + '''
    const new_doc = automerge.change(d, doc => {
        doc['log'].push(msg)
    });
    return new_doc;
'''
self.doc = eval_js(js_code)

This directly includes the data of the message as part of the code to run, instead of relying on async transfers.

The ''' + ''' avoids confusion around JS's curly braces while allowing direct variable assignment before the break with f''' ... '''

I somehow needed d = self.doc since passing self.doc directly to eval_js() failed (can't handle periods? I'm missing some braces?).

jonarmani avatar Aug 09 '23 06:08 jonarmani