1

I created a basic application for personal use. The backed of my application uses Fast Api with a SQLite database. Usually to run my start up and run my backend server I have to use the following commands:

// Using Virtual ENV
source env/Scripts/activate

pip install -r requirements.txt
uvicorn main:app --reload

I have seen other people create a python executable before. I would like to do the same but I need it to start the uvicorn server. How do I create a python executable that runs a uvicorn server?

Or is it better to just write a batch script that does this?

ITM007
  • 117
  • 2
  • 9

3 Answers3

3

Somthing like

import uvicorn
from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

#  Import A module from my own project that has the routes defined
from redorg.routers import saved_items 

origins = [
    'http://localhost:8080',
]


webapp = FastAPI()
webapp.include_router(saved_items.router)
webapp.add_middleware(
    CORSMiddleware,
    allow_origins=origins,
    allow_credentials=True,
    allow_methods=['*'],
    allow_headers=['*'],
)


def serve():
    """Serve the web application."""
    uvicorn.run(webapp)

if __name__ == "__main__":
    serve()

If you need to pass arguments you can use something like argparse/click to expose a cli interface.

Vikash Balasubramanian
  • 2,921
  • 3
  • 33
  • 74
  • Thank you for answering so quickly... I am still a little confused, are you saying that this code will turn it into an executable? Or would I need to use a tool such as "pyinstaller"?... And for the code above i'm not understanding the ```from redorg.routers import saved_items``` What am i supposed to be importing there? For my backend I have three different files that build my sqlite database ```database.py```, ```models.py```, and ```main.py``` Am I supposed to be importing those files? – ITM007 May 09 '21 at 02:57
  • By executable do you mean an exe that can run on windows? Then yes, you need to use pyinstaller. That import is for the routes that you have defined: https://fastapi.tiangolo.com/tutorial/bigger-applications/ – Vikash Balasubramanian May 09 '21 at 03:00
0

Leveraging From Vikash Balasubramanian's Answer

Thanks for your helpful answer Vikash. For those coming here needing the same and more, I wanted to add a link with an example from that link for when one needs to pass additional parameters.

The link is Uvicorn Settings.

An example, that I just tested too, would be (leveraging from Vikash's code BUT changing webapp to app):

def serve():
    """Serve the web application."""
    uvicorn.run("Test1:app", port=8001, reload=True, access_log=False)

if __name__ == "__main__":
    serve()

I wanted to know right away after reading Vikash's answer HOW TO control the port number in case I had to run several APIs this same way.

NOTE how the first parameter must now be a string, due to the reload=True, and that Test1.py is the name of my script, so the first parameter string is therefore "Test1:app". This is ONLY for testing before you make the exe.

FOR PyInstaller work, you do NOT want to use "Test1:app" as your first parameter, and once you are committing to make an exe, you do NOT want to use reload=True anymore. SOOooo, your code would be ...

def serve():
    """Serve the web application."""
    uvicorn.run(app, port=8001)  # or whatever port number you want.

if __name__ == "__main__":
    serve()

And your pyinstaller command from a terminal would be ...

pyinstaller --onefile Test1.py

Otherwise, running Test1.exe will look for your Test1.py script, and then what was the point of creating an exe.

Thom Ives
  • 3,642
  • 3
  • 30
  • 29
0

The answer by Thom Ives works great. I just have one question:

When I run

pyinstaller --onefile Test1.py

The code runs very well. However I do not want the terminal to be shown to the user, so Im trying to create the exe file with:

pyinstaller --onefile --windowed Test1.py

The --windowed option should avoid opening the console when we run the program. However, When I run this the server never starts so I get a connection refused message on my browser when I try to connect.

I'm writing my code below for your reference:

from fastapi import FastAPI
import uvicorn
from fastapi.responses import HTMLResponse
from fastapi.templating import Jinja2Templates
from starlette.requests import Request
from fastapi.middleware.cors import CORSMiddleware
import streamlit as st
import os
import sys

def resource_path(relative_path):
    try:
        base_path = sys._MEIPASS2
    except Exception:
        base_path = os.path.abspath(".")

    return os.path.join(base_path, relative_path)


app = FastAPI()
origins = [
    '*',
]
app.add_middleware(
    CORSMiddleware,
    allow_origins=origins,
    allow_credentials=True,
    allow_methods=['*'],
    allow_headers=['*'],
)

templates = Jinja2Templates(directory=resource_path("templates"))

@app.get("/", response_class=HTMLResponse)
async def read_root(request: Request):
    return templates.TemplateResponse("home.html", {"request": request})

@app.get("/page2", response_class=HTMLResponse)
async def page_2(request: Request):
    return templates.TemplateResponse("page2.html", {"request": request})



import sys
from PyQt5.QtWidgets import QApplication, QMainWindow
from PyQt5.QtWebEngineWidgets import QWebEngineView
from PyQt5.QtCore import QUrl

class BrowserWindow(QMainWindow):
    def __init__(self, url):
        super().__init__()

        browser = QWebEngineView()
        browser.setUrl(QUrl(url))

        self.setCentralWidget(browser)
        self.show()


import threading


def run_server():
    st.write("Running")
    uvicorn.run(app, port=8000)
    
    
    # return 
def run_window():
    winapp = QApplication(sys.argv)
    window = BrowserWindow("http://127.0.0.1:8000/")
    sys.exit(winapp.exec_())



if __name__ == "__main__":
    thread1 = threading.Thread(target=run_server)
    thread2 = threading.Thread(target=run_window)

    # Start threads
    thread1.start()
    thread2.start()

    # Wait for both threads to complete before exiting
    thread1.join()
    thread2.join()

    print("Both threads have finished.")