App Development Details
In this section, we'll dig into what actually happens inside an Edge application. A lot of the "heavy lifting" is done for you by Edge and the development tools. It's important to learn what those tools are and how they work so you can apply them correctly.
- By default, your app's file system isn't permanent. See Options for files and data storage.
- Be sure to run tests to verify that your app works before publishing to Edge.
About the "edm-rockylinux-8" Docker image
This base image includes the EDM deployment tool, which is used to
install Python packages. pip
is also available once you have created an
EDM-based Python environment. It is embedded in a Rockylinux-8 linux image.
ARG BASE_IMAGE=quay.io/enthought/edm-rockylinux-8:latest
About the Edge Supported Frameworks
The example app you built and deployed in the previous section has very little boilerplate code. This is partly due to the "edm-rockylinux-8" base image with EDM built-in, but also due to framework-specific support that is installed next to your application when it launches.
This framework support handles some of the "paperwork" involved with interfacing between your app and Edge. This includes:
- Handling the app's side of the OAuth authorization flow, so that only the user who launched the app can access it.
- Sending activity "pings" back to Edge as the app is used. This is so that Edge can eventually shut the app down if it isn't being used.
- Proxying your app and serving it on the standard port (8888) so that Edge can find it.
Using the Edge API (EdgeSession) inside your app
You can easily construct an EdgeSession object, by which your app can access APIs like data connectors or files. Because each user has their own running copy of your app, the EdgeSession will connect automatically under their account.
In your app's code, all you need to do is call the constructor with no arguments:
from edge.api import EdgeSession
mysession = EdgeSession()
That's it! The interface is ready to use. EdgeSession auto-configures by reading the user's API token, server location, etc., from the environment.
See the Streamlit example (or other examples in the online examples gallery) for tips on using EdgeSession locally in development mode. Briefly, when running your app outside of Edge you need to set some environment variables that will pass down your testing API token and a few other pieces of information.
Options for files and data storage
You can write to the local file system inside your app, but that local-disk storage is ephemeral... when the app is shut down and restarted, any changes will not be preserved. To persist data across application launches, you have a couple of options.
Store files on Edge
The first is to use the local drive as "scratch space", and save your data to
Files on Edge or another external storage service. If you want to do this,
create an EdgeSession object in your code and use the .files
property to
interact with the Edge file system. You can upload files from the local disk
to Edge, and download files from Edge to process them. For example:
from edge import EdgeSession
mysession = EdgeSession()
# Upload a file
with open("my-local-file.bin", "rb") as f:
mysession.files.upload("my-remote-file.bin", f)
# Download a file
mysession.files.download("my-dataset.bin")
Store files in a persistent volume
The second option is to request that Edge allocate a persistent volume for your
app. This approach is not currently exposed in the Edge user interface; you
will need to specify the volume parameters when registering your app version
via the Edge API. See the
Programmatically publishing versions
discussion below for how to use the Edge API to register an app version. You
will need to include two additional fields in AppVersion
constructor:
volume_mount_point
: a string with the name of an (empty) folder in your Docker image where you want the volume to be mounted. Keep in mind that since your startup script is in the home directory, you should not put/home/app
for this value. Instead, you can e.g. create a "workspace" folder and put/home/app/workspace
as the mount point.suggested_volume_size
: integer with the capacity of the volume in gigabytes (not bytes). For cost efficiency, you should use the smallest volume size possible.
Ports and environment variables
Your app should bind to 127.0.0.1:9000
. The Edge framework support will then
proxy your app to the standard port of 8888
. Your app should use plain HTTP
or websockets. Don't use SSL or certificates; Edge handles that for you.
A number of the standard JupyterHub environment variables are available, plus a few Edge-specific ones:
JUPYTERHUB_SERVICE_PREFIX
: If defined, has the full route to your app. If you're using a framework like Flask or FastAPI, you should respond to requests that start with this prefix. For example:PREFIX = os.environ.get("JUPYTERHUB_SERVICE_PREFIX", "/")
@app.route(PREFIX + "my-resource")
def get_my_resource():
...EDGE_API_TOKEN
orJUPYTERHUB_API_TOKEN
: Allows access to the Edge API. At least one of these will be defined when your app is running in Edge. EdgeSession automatically picks up this value, along with the following two variables.EDGE_API_ORG
: The "short name" (ID) of the user's organization.EDGE_API_SERVICE_URL
: The full URL of the Edge API root.
Continuous integration
Running tests
Each example includes a python -m ci run
command, which launches the
app in a local environment. This is useful for interactive development.
There's also a
Tools folder
in the example gallery which contains a testrun.py
test script. This
is a standalone script you can use in e.g. GitHub Actions to run
checks as part of your continuous-integration (CI) process. See the README
in that folder for instructions.
Programmatically publishing versions
You can automatically publish new versions of your app, for example in GitHub Actions as part of your continuous-integration workflow. To do so, create an EdgeSession within your build script and call the Edge "application" API:
from edge.api import EdgeSession
from edge.apps.application import Application
from edge.apps.app_version import AppKindEnum, AppFrameworkEnum, AppVersion
from edge.apps.server_info import ServerInfo
# Create an Edge session.
edge = EdgeSession(
service_url="https://edge.enthought.com/services/api",
organization="<YOUR_ORGANIZATION>",
api_token="<YOUR_EDGE_API_TOKEN>"
)
# Register the application version.
# Icon is a base64-encoded PNG or JPEG image.
# It should be square, any size; 64x64 or 128x128 are common.
ICON = (
"iVBORw0KGgoAAAANSUhEUgAAAIAAAACACAMAAAD04JH5AAAAk1BMVEUAAA"
"AMfIYAdnwAdnwAd3wAc3kAdntYsb8AdnwAdn0AdXwAdXsAd30Ad3xYsb8A"
"d30AdntYsL8Ad30AdnsAd30AdnwAd3wAd30AdnxYssBYsb5Ysr9Zs70Adn"
"tYsr9Ysb9Ysb9Ysr8AdnxXs75Ysb4AdXsAd3wAdnxYsb8AeoAAfYMAf4Vb"
"t8Zfv84Ad31ixtVevMuy+odXAAAAJ3RSTlMABPr88Shd+4pmkXyDbfB0Xy"
"dWTy0h9TLnkYv+IJmX9uigNzOIf5LOQ2WlAAAC/ElEQVR42sXX7VLiMBiG"
"4aTBDwQVEJSPXdkF3dAi1fM/uoXp7DyzIzHEm7bvT6Dc16SlDQbObG5anf"
"nHx4tpcea7q6sdWQPeXyyIgPc3+7nazQwY3N8sWhLM9uu/+Sd4NmBQvxog"
"YH0g4P2qLMFvAwb0WxE8q9+CwJqX96W6muV7U9fB8NfbsRV4u1ubhubHQf"
"C5PzQNjZXg/741dY8EdxKAPhTQPhfQPhfQPheQPhcM9wLa54KG+jYoCPZt"
"M4Llspn+KlRZr0PvrM7Z/7m9DHVCr19ub87Zd4UEX436hZeA9zPnJTix7z"
"IJcN97CU7tey8B70twev9Mgj+HvgQJ/fMIbqq+BCl9CXhfgpS+BLwvQUpf"
"At6XIN7nAvWTBOpDgfppgm6uPhGonyaw00LHAIH6qYLJNA8KeD8usF8Irk"
"E/SdChAvXbEajfjkD9dgTqtyNQv0XB9aFPBRcRAexzQXo/q1MQ77uyqHEN"
"4v2smPby0qUKHoOC1H7eGZtB4VMF47AgtX+hrU4Ngmj/cWzs+QU2rW/qFc"
"T7lSA/o6BvbEq/+rpRuuApiwvi/doE0X72pA/VJYj36xXE+7UL+sf7Tn0s"
"WPncBQTh51/Z0fV3oiDvBtegU/rQ/eC1OIpz5XRirEkQOFfch/8ulEfNru"
"gZcx8QVI+AuECnoGtM8NEc6N8aY0OC7ESB+qdvDdS33xQ8aH9A+0igfj74"
"Zh8K9BMEfSxwPgd9KKj6o8S+V58KaJ8LXJk/gD4WeM/6XMD6XDCC558LYJ"
"8LYJ8LQB8IYJ8IeD9+eG9LBPH7PxTgPhbQPhfwPhek932gDwTRfnjLyAW0"
"zwW8H5+IAPa5gPa5gPa5gPa5gPa5gPa5gPa5gPa5gPa5APa5gPW5YDJBfS"
"6YTkmfC1xZukb6NiiAfS4AfSAAfSQoHOpzwes2Q30+fQla6VsJQB8LeJ8L"
"wv0B6AMB6AMB6HMB7XMB7XMB7XMB7XMB7XMB7XMB7XMB7XMB7XMB7XMB7H"
"NBjvpccH0L+38B6kvWH2wXIe8AAAAASUVORK5CYII="
)
version = AppVersion(
app_id="my-app-id", # Specified when the app is created
version="1.0.0", # or whatever version you have
title="My Application Title",
description="This Is An Example Edge Application",
icon=ICON,
kind=AppKindEnum.Native,
framework=AppFrameworkEnum.Streamlit,
link="quay.io/enthought/YOUR_IMAGE_NAME_HERE:TAG",
recommended_profile="edge.medium"
)
edge.applications.add_app_version(version)