Package: purly
PurlyControl the web with Python :snake:InstallTo install a stable version:pipinstallpurlyTo install a dev version:be sure to installnpmfirst!# clone the repositorygitclonehttps://github.com/rmorshea/purlycdpurly&&bashscripts/build.sh&&bashscripts/install.shGetting StartedRun the following snippet of code, and then navigate tohttp://127.0.0.1:8000/model/index.importpurly# Prepare your layoutpurly.state.Machine().run(debug=False)layout=purly.Layout('ws://127.0.0.1:8000/model/stream')# create your HTMLdiv=layout.html('div')div.style.update(height='20px',width='20px',background_color='coral')# add it to the layoutlayout.children.append(div)# and sync it!layout.sync()Now your creation should have magically appeared in the browser page you opened!Architecture (Not Final)Purly's fundamental goal is to give Python as much control of a webpage as possible, and do so in one incredibly simple package. There is one major problem that stands in the way of this goal - data synchronization. Purly's answer to this problem is itsmodel serverwhich acts as a "source of truth" about thestate of a webpagefor any clients which connect to it and adhere to itsprotocol.Model ServerPurly uses a web socket server to keep multiple concurrent clients in sync. The animation above shows 2 clients - a Python client pushing updates to a single Browser - however you could have more clients producing and / or consuming, model updates. Each client is associated with a single model (any JSON serializable object), however there can be multiple models that are stored on the server. Clients connect to a particular model by specifying its name in the socket route (e.g.ws://host:port/model/<model-name>/stream). Only clients that are connected to the same model communicate with each other via the server.Model SpecificationWhile the Model Server supports any JSON serializable model, Purly, as a framework for controlling the web must:Communicate as fully as possible the structure of DOM elements and their various interactions.Send updates to DOM models over a network in short and easy to interpret packages:Update messages must be small in size in order to reduce network traffic.To accomplish the goals defined above we propose a flat DOM model:Model={id:Element,# Maps a uniquely identifiable string to an Element.root:Element,# The id "root" should always indicate the outermost Element....}Element={tagName:string# Standard HTML tags like h1, table, div, etc.signature:string# The hash of this element attributes, and the hashes of its children.children:[string,# Any arbitrary string.{type:'ref','ref':string},# An object where the key "ref" refers to the "key" attribute....],attributes:{key:id,# The id that uniquely identifies this Element.parent_key:id# The unique id of this element's parent.attr:value,# Map any attribute name to any JSON serializable value.on<Event>:{# Specify an event callback with an attribute of the form "on<Event>".callback:uuid,# A unique identifier by which to refer to the callback function.keys:[...],# Details of the event to pass on to the callback.update:[...]# Any attributes that should be synced before the callback is triggered.}}}Purly Model ExampleThe following HTML<divkey='root'>Make a selection:<inputtype='text'key='abc123'></input><div>would be communicated with the following Purly model:{root:{tag:'div',elements:['Make a selection:'{'ref':'abc123'},]attributes:{'key':'root'}},abc123:{tag:'input',elements:[],attributes:{'key':'abc123','type':'text',},}}Communication ProtocolThe Purly model server sends and receives JSON serializable arrays which contain objects in the form of aMessage.[Message,# a dict conforming to the Message spec...]MessageThere are two types of messages - Updates and Signals - however both conform to the following format.Message={"header":{"type":"signal"or"update",# Message type (indicates the kind of content)."version":"0.1",# Message protocol version.}# Content which depends on the message type."content":dict,}SignalsA Signal does not modify the state of the model server.Signalcontentis distributed unmodified to other model clients.UpdatesThecontentfield specifies changes that will be merged into the model.Only the differences between the Updatecontentand the model are distributed to other clients.Update merges are performed in a nested fashionIf the current state of the model is{'a':{'b':1,'c':1,}}and an update message{'a':{'c':2}}is received, the resulting model state is{'a':{'b':1,'c':2,}}