This is part of a series of posts on learning Python and building a Python web service.
Scaffolding
Not knowing the best way to structure a Python web service project, I went searching on the web for good scaffolding examples for a flask app. I found a few and decided to go with this one. It seemed to lay a good foundation and could be customized as needed. Little did I know…
I ran into a couple of issues. The first was one of the dependencies of a dependency in this repo didn’t work with Python 3.13, in the version that was being installed. The maintainers had put out an update to fix it but the downstream package hadn’t started using it yet. Python 3.13 had just been released a few weeks earlier, which I naturally installed since it was the latest. That’s annoying but understandable.
Sidebar on how to install Python dependencies:
As with many things in the Python world, there are multiple ways you can manage project dependencies. The easiest way is to use pip. Like npm, you usually install it globally and then you can use it in every project to install packages. The way to do all of them at once is to list the packages you need and the version you want in a separate requirements.txt file, and then tell pip to install everything specified in that file. Another way is to use a tool called Poetry. It looks for a file named pyproject.toml that has a list of packages/versions plus general info on how to manage the project’s dependencies. It’s also more modern and full-featured than pip.
The scaffolding repo used Poetry and the fix for my first issue was to include the updated library that was the source of the problem as a special requirement before all other packages were installed. It took some research on the specs for the pyproject.toml file and trial and error to figure out the right entry to make, but that first error message eventually went away and all the packages got successfully installed. For the curious, the entry looked like this:
[tool.poetry.dependencies] python = "^3.12" # Needed for Python 3.13 greenlet = "^3.1.1"
The next issue I hit was more damaging. The scaffolding expected to use a particular HTTP server named gunicorn. There’s just one problem: it doesn’t run on Windows (kinda of lame, since IIS, Apache, and the various Node-based HTTP listeners I’ve used over the years all run fine on that OS). So trying to run my project based on this scaffolding blew up spectacularly.
It was at that point I decided to go back to square one and try to simplify things. I ended up starting with the new project template in PyCharm, and just adding an app.py
file and __init__.py
file. I copied over what was useful from my prior project, and augmented that with various examples I found via Google. It’s similar to the approach I took when creating Node web services for personal projects. I also wanted to find a good canonical example of scaffolding but I never did. I just ended up starting small and expanding from there. The problem is these tech stacks seem to change so often, and there are so many options, it would be hard to maintain a solid example over time. When I needed to spin up new Node apps I simply copied what I had done previously and modified the project as I added features or learned new Node best practices.
I eventually got a bare bones Python service running properly on my machine. I’m sure I’ll be adding to the project and re-arranging lots of things as I go.
[…] So that all made sense and I planned to create one for the Music Browser API. But again, that lead to the question of which tool to use to create one (another Node similarity: a dearth of tools available to do any particular thing). Python itself includes a module to create these environments name venv. It does the job but as I’m learning about this world, there are often better choices that offer more features and greater ease of development. After a bit of web searching I picked virtualenv. It works good, though I ended up building, destroying, and re-building the environment for the Music Browser API over and over, as various things I was trying went wrong (more on that here). […]