Shinylive for Python (1): On-Premise
Why Python?
print("Hello!")
There really is no single, specialized task at which Python is the best1. That said, it is quite capable at most things. Python can be very helpful for a small team managing a wide range of needs. The team I'm part of uses Python for scripts, automation, workflows, integrations, and internal web applications.
Python: one core skillset; applicable to many domains (bonus: enjoyable to use)
Why Shiny?
from shiny import ui
Shiny for Python allows for the creation of web applications using only Python. Its doesn't include the flexibility of other approaches, but it is perfectly capable for creating dynamic, real-time, internal business apps and utilities. And, if corners of your web app do start to outgrow the framework's built-in features there are plenty of supported "escape hatches" to add features using straight HTML, CSS, and Javascript.
Shinylive is a deployment method for Shiny built on Pyodide, where the application files are "exported" into a format that's suitable to be served statically. The details of this technology aren't the focus here, but the Pyodide foundation means the entire Shiny application (yes, including the Python code) within a web browser. And the ability to service the application as static files means that each web app and utility can be isolated into it's own small deployment, with it's own isolated logic and concerns. This allows us to move away from a monolithic, server side code base containing ever-accumulating and complex UI application logic for numerous, separate web apps and utilities.
Why Shinylive Instead of Standard Shiny?
While Shiny for Python and Shinylive are deeply connected, there are differences.
Shiny
Standard Shiny functions very much as a server-side rendered website (like a Flask, Django or FastAPI web application). When you go to the URL in a web browser the application server receives the request, processes the logic on the backend, and returns a pre-rendered website (and then renders and returns bits and pieces of updated sections when performing dynamic actions).
Highlights:
All end-user connections are managed on the same application server. While there are ways to scale this horizontally, my main concern is for internal business applications where this isn't necessary..
User sessions need to be managed correctly to ensure they remain isolated from each other.
All individual utilities are combined into a more complex monolith2
Any change made to a single Shiny app requires redeploying the entire monolith. This means even a small change to one utility can put all other web app tools at risk. Redeploying the entire application also adds a lot of overhead. This can include tasks like rerunning all tests, a more involved PR approval process, rebuilding and storing the updated image, redeploying the container, and application downtime.
Shinylive + FastAPI
The development experience for a Shinylive app is very similar to Shiny. However, when it comes to deployment and functionality, Shinylive for Python is more like a SPA (single-page application) in JavaScript, where each application runs independently in local web browsers. Since Shinylive apps don't need server-side rendering and can be served statically, they can be deployed from anywhere. For example, if you already maintain a REST API endpoints application, Shinylive apps can be added to that existing app and served as static files.
Highlights:
All enduser sessions are run independently on their own computers. No shared resources. And there is little to no worry about server load, so far as the web applications themselves are concerned.
All enduser "connections" are fully separate and isolated. All application logic takes place on their local computers. Any external connections made, such as to Rest API endpoints, could be designed to be stateless.
Each Shinylive app is separate. This means when looking at the code base for the web application logic itself one is only looking that specific app (sometimes it can means looking through a few hundred lines of code, opposed to thousands or tens of thousands of lines of code). Creating new apps is easier too.
Changes to web application logic usually only require that specific Shinylive app needs to be redeployed3. In our environment that's as simple as "exporting" the app as static files, and "pushing" those files to a directory on the web server. No server or container level redeployments. No full application restarts. No interrupting the work of folks using other apps and utilities. Less risk; less impacted productivity; fewer annoyed developers and endusers.
Unlike the Shiny server side method of deployment, there is no "additional" server or container application that needs to be maintained. Shinylive apps can be served from any existing web server.
The Challenge: On-Premise in a Cloud-First World
Figuring out how to set everything up took some time—from development and testing to reliable and repeatable deployments. This is partly because we live in an increasingly "cloud-first" world when it comes to technology stacks.
I work on a team that primarily deploys infrastructure on-premise (for reasons not discussed here). This means we often have to be creative to use more "modern" software technology.
Shinylive falls into the same category. Most of the documentation, deployment examples, and suggestions involve some form of cloud storage or computing.
In this series, I'll discuss how I solved these challenges. The solutions include general hosting and deployment setups, as well as Python packages created to support related workflows.
The next article is currently in progress: On-Premise Hosting with FastAPI
1. There is plenty of room for nuance here. ↩
2. I'm not against monoliths at all. I work in a small enterprise team within a mid-sized organization. As a starting point, I prefer monoliths. Microservices can add a lot of maintenance overhead for a single team to handle. When comparing the two options, the distinction isn't just about one large app versus multiple small apps. ↩
3. Obviously, it's not always this simple. If the Shinylive app depends on an application that serves endpoints, sometimes the endpoint server will still need to be redeployed (e.g., a new Shinylive app requires the creation of an additional endpoint). That said, with careful planning, changes to that part of the stack shouldn't happen often. I find it easier to reliably create and test server-side Rest API endpoints than full-fledged GUI application logic. This means that the new endpoint can be created, tested, and deployed safely with minimal risk to other endpoints and other Shinylive apps. After that, the new Shinylive app can be developed and deployed in relative "isolation." ↩
Subscribe to my newsletter
Read articles from Ben Hammond directly inside your inbox. Subscribe to the newsletter, and don't miss out.
Written by