import subprocess
from pyngrok import ngrok
from git import Repo
import re
import os
import shutil
from pathlib import Path
[docs]class Host:
"""Base class for hosting any python application.
Given `port` number it will expose the port to internet.
Given `requirements` will install them using `pip install`.
Given `git_url` it will clone the repo for you.
Parameters:
port : int, optional
requirements : List[str] or str, optional
List[str]: list of package requirements for hosting.
str: requirements file path to install requirements from.
git_url : str, optional
"""
def __init__(
self, port: int = 1000, requirements: list or str = None, git_url: str = None
):
super().__init__()
if isinstance(git_url, str):
self._clone_repo(git_url)
self._install_requirements(requirements)
self.port = port
self._start_tunnel()
def _clone_repo(self, git_url) -> Repo:
folder_name = re.search(r"[^/]+$", git_url).group().replace(".git", "")
folder = Path(folder_name)
if folder.exists() and folder.is_dir():
shutil.rmtree(folder)
repo = Repo.clone_from(git_url, folder)
self.repo = repo
os.chdir(self.repo.working_dir)
def _install_requirements(self, requirements: list or str):
subprocess.run(f"pip install --upgrade pip".split(), stdout=subprocess.PIPE)
if not isinstance(requirements, (str, list)):
return
if isinstance(requirements, str):
requirements_file = f"{self.repo.working_dir}/{requirements}"
subprocess.run(
["pip", "install", "-r", requirements_file], stdout=subprocess.PIPE
)
return
for requirement in requirements:
subprocess.run(f"pip install {requirement}".split(), stdout=subprocess.PIPE)
def _start_tunnel(self):
active_tunnels = ngrok.get_tunnels()
for tunnel in active_tunnels:
public_url = tunnel.public_url
ngrok.disconnect(public_url)
url = ngrok.connect(port=self.port, options={"bind_tls": True})
print(f"Hosted Server can be accessed on: {url}")
[docs]class SimpleHttpServer(Host):
"""Class to expose simple file server application.
Parameters:
port : int, optional
"""
def __init__(self, port: int = 1000):
super().__init__(port)
self._start_server()
def _start_server(self):
subprocess.run(
f"python -m http.server {self.port}".split(), stdout=subprocess.PIPE
)
[docs]class JupyterNotebook(Host):
"""Class to expose Jupyter Notebook IDE on browser.
Parameters:
port : int, optional
requirements : List[str], optional
Defaults to `["notebook"]` and you can include
other packages to include with this. For example
notebook extension, theme, etc
"""
def __init__(self, port: int = 1000, requirements: list = ["notebook"]):
super().__init__(port, requirements)
self._start_server()
def _start_server(self):
subprocess.run(
f"python -m jupyter notebook --allow-root --ip=0.0.0.0 --port {self.port}".split(),
stdout=subprocess.PIPE,
)
[docs]class JupyterLab(Host):
"""Class to expose Jupyter Lab IDE on browser.
Parameters:
port : int, optional
requirements : List[str], optional
Defaults to `["jupyterlab"]` and you can include
other packages to include with this. For example
notebook extension, theme, etc
"""
def __init__(self, port: int = 1000, requirements: list = ["jupyterlab"]):
super().__init__(port, requirements)
self._start_server()
def _start_server(self):
subprocess.run(
f"python -m jupyter lab --allow-root --ip=0.0.0.0 --port {self.port}".split(),
stdout=subprocess.PIPE,
)
[docs]class FlaskApp(Host):
"""Class to expose python Flask or Gunicorn application.
Parameters:
port : int, optional
app : str, optional
Definition of your python gunicorn app. (Defaults to `"main:app"`).
git_url : str, optional
Git URL to clone your repo containing application.
(Defaults to `"https://github.com/PuneethaPai/colab_host_flask_demo"`).
requirements_file: str, optional
Name of file in repo `git_url` containing requirements for hosting the
application. (Defaults to `"requirements.txt"`).
"""
def __init__(
self,
port: int = 1000,
app="main:app",
git_url="https://github.com/PuneethaPai/colab_host_flask_demo",
requirements_file: str = "requirements.txt",
):
self.port = port
self.app = app
super().__init__(port, requirements_file, git_url)
self._start_server()
def _start_server(self):
subprocess.run(
f"gunicorn --bind 0.0.0.0:{self.port} {self.app}".split(),
stdout=subprocess.PIPE,
)
[docs]class UvicornApp(Host):
"""Class to expose python FastApi or Uvicorn application.
Parameters:
port : int, optional
app : str, optional
Definition of your python gunicorn app. (Defaults to `"main:app"`).
git_url : str, optional
Git URL to clone your repo containing application.
(Defaults to `"https://github.com/PuneethaPai/colab_host_uvicorn_demo"`).
requirements_file: str, optional
Name of file in repo `git_url` containing requirements for hosting the
application. (Defaults to `"requirements.txt"`).
"""
def __init__(
self,
port: int = 1000,
app="main:app",
git_url="https://github.com/PuneethaPai/colab_host_flask_demo",
requirements_file: str = "requirements.txt",
):
self.port = port
self.app = app
super().__init__(port, requirements_file, git_url)
self._start_server()
def _start_server(self):
subprocess.run(
f"uvicorn --host 0.0.0.0 --port {self.port} {self.app}".split(),
stdout=subprocess.PIPE,
)