Skip to main content

Python server-side DOM synchronised with Browser

Project description

domsync

domsync is a library for building responsive web UIs in Python. A DOM document containing the whole UI is built and updated on the Python server side, changes to this DOM are synchronised efficiently to the Browser. Events that happen on the Browser-side trigger callbacks on the Python-side. This allows you to keep what the user sees in your Python process, close to your existing Python logic, eliminating the need for creating and maintaining a separate Javascript client application and building an API interface to communicate with the client.

The syntax of domsync closely follows the core Javascript syntax for manipulating a DOM document: we got getElementById, createElement, appendChild, setAttribute, addEventListener, and so on. Every change to the Python domsync document generates Javascript code which is almost equivalent to the Python domsync call, this allows users to clearly understand and control what is happening to the DOM document on the Browser-side.

Installation

Install domsync with:

pip install domsync

Basic example

This Python domsync app shows the current time:

import asyncio
from datetime import datetime
from domsync.domsync_server import DomsyncServer

async def connection_handler(server, client):
    """
    connection_handler is called when a client connects to the server
    :param server: is the DomsyncServer instance
    :param client: is a websocket client connection instance
    """

    # get the client's domsync Document
    document = server.get_document(client)

    # add a div to the root element
    root_element = document.getElementById(document.getRootId())
    div_element = document.createElement('div')
    root_element.appendChild(div_element)

    while True:
        # update the text of the div to the current time
        div_element.innerText = 'The current time is: ' + datetime.utcnow().isoformat()

        # send updates to the client
        await server.flush(client)

        # wait a bit
        await asyncio.sleep(0.1)

async def main():
    # start a domsync server on localhost port 8888
    await DomsyncServer(connection_handler, 'localhost', 8888).serve()

if __name__ == "__main__":
    asyncio.get_event_loop().run_until_complete(main())
    asyncio.get_event_loop().run_forever()

Let's take a look at what happens here.

  1. await DomsyncServer(connection_handler, 'localhost', 8888).serve() starts a domsync server which is essentially a websocket server with a domsync Document instance for each connected client.
  2. async def connection_handler(server, client) is the handler that runs when a new client connects to the server. The arguments of this function are the DomsyncServer instance and the websocket client connection instance.
  3. doc = server.get_document(client) gets the domsync Document associated with the client which contains the DOM. Each client has it's separate Document that can be manipulated separately.
  4. root_element = document.getElementById(document.getRootId()) gets the root element of the Document which corresponds to the <div id='domsync_root_id'></div> element in the client-side HTML.
    div_element = document.createElement('div') creates a new div element in the document.
    root_element.appendChild(div_element) appends the div element under the root element as a child.
    div_element.innerText = 'The current time is: ' + datetime.utcnow().isoformat() updates the text of the div element to the current time.
    These operations modify the domsync Document in memory but also generate Javascript code which is saved in an internal buffer of the Document. At this point the content of the buffer is this generated Javascript code:
    var __domsync__ = [];
    __domsync__["domsync_root_id"] = document.getElementById("domsync_root_id");
    __domsync__["__domsync_el_0"] = document.createElement("div");
    __domsync__["__domsync_el_0"].setAttribute("id","__domsync_el_0");
    __domsync__["domsync_root_id"].appendChild(__domsync__["__domsync_el_0"]);
    __domsync__["__domsync_el_0"].innerText = `The current time is: 2022-06-08T03:23:14.818841`;
    
  5. await server.flush(client) sends the contents of the Javascript buffer to the client where it gets evaluated and as a result the current time appears on the screen.
  6. As the while loop progresses, the Document is modified and the generated Javascript code is sent to the client continuously. However, domsync is efficient in the sense that it only sends changes for those elements that have actually changed, in this example this is the only line of generated Javascript that is sent by the next await server.flush(client):
    __domsync__["__domsync_el_0"].innerText = `The current time is: 2022-06-08T03:23:14.925521`;
    

This is the generic Browser-side domsync client:

<html>

  <!-- domsync will render into this element -->
  <body><div id='domsync_root_id'></div></body>

  <script type = "text/javascript">

    // server -> client: DOM changes are coming from websocket as javascript code and are eval'ed here
    socket = new WebSocket("ws://localhost:8888");
    socket.onmessage = function(event) { (function(){eval.apply(this, arguments);}(event.data)); };

    // client -> server: ws_send is called by event handlers to send event messages to the server
    function ws_send(msg) { socket.send(JSON.stringify(msg)); };

  </script>
</html>

The client connects to the domsync server running on localhost port 8888 over websocket. The domsync server sends javascript code containing DOM operations that are evaluated in socket.onmessage. The ws_send function is used as an event callback to send events back to the server.

This example is in examples/example_clock.py with the client-side html in examples/client.html.

Read the docs: https://domsync.readthedocs.io/

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

domsync-0.1.2.tar.gz (20.3 kB view details)

Uploaded Source

Built Distribution

domsync-0.1.2-py3-none-any.whl (16.9 kB view details)

Uploaded Python 3

File details

Details for the file domsync-0.1.2.tar.gz.

File metadata

  • Download URL: domsync-0.1.2.tar.gz
  • Upload date:
  • Size: 20.3 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.1 CPython/3.8.10

File hashes

Hashes for domsync-0.1.2.tar.gz
Algorithm Hash digest
SHA256 dd0c94ecc035a97e518ad6621e3ada86dd9e71a396a4c850ea65e90863e58642
MD5 ed7e68788701a26899bc77e90eac072c
BLAKE2b-256 f91223439cf0eb00e7bac38cb4baedd52070077d2a243f700f2e4c36d3abf21c

See more details on using hashes here.

File details

Details for the file domsync-0.1.2-py3-none-any.whl.

File metadata

  • Download URL: domsync-0.1.2-py3-none-any.whl
  • Upload date:
  • Size: 16.9 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/4.0.1 CPython/3.8.10

File hashes

Hashes for domsync-0.1.2-py3-none-any.whl
Algorithm Hash digest
SHA256 d04ecd6990d729853c72974a341aa276a73868a1e96a9e2ec7eb7d4b67e04d78
MD5 505c1d14db8d449ebd8e80cfe6f6607d
BLAKE2b-256 0595d1231f10430e93c75fc7334a9c524d507e9f16933c188dfe50f187049ff7

See more details on using hashes here.

Supported by

AWS AWS Cloud computing and Security Sponsor Datadog Datadog Monitoring Fastly Fastly CDN Google Google Download Analytics Microsoft Microsoft PSF Sponsor Pingdom Pingdom Monitoring Sentry Sentry Error logging StatusPage StatusPage Status page