- This post is part of a series which dissects my method for creating a REST API using Python and Flask.
- Each post is written under the assumption that the reader has read and understood all previous posts in the series.
- I will attempt to explain every choice made along the way. If you feel I've left something out please mention it in a comment below.
- Each section explains why and how to use a specific technique or technology. Feel free to skip sections you're already comfortable with.
- The source code generated for this series can be found on GitHub.
By the end of this post you'll see the basics of how I set up a Python project and read some of my justifications for the base technologies and techniques. I meant for this post to get you up to a basic running Flask app with tests, but I'm on vacation and running out of time so that will be in the next post. Here's what I will cover:
- Project Structure
There are multiple techniques for creating a web API. I won't try and cover all the pros and cons of the different methods, you can write entire books comparing and contrasting. I use REST when creating APIs for two main reasons:
- Because it's a concept, not a technology, you can consume and produce REST APIs with basically any programming language.
- It's very mature meaning there is a ton of good tooling available
There are a ton of reasons to use Python (or not use Python). They can be found all over the internet in much more detail than I'll go into here. To stay true to my promise of explaining every design decision, here's a quick list of a few things that I like about it:
- Being interpreted, it's extremely flexible, cross platform, and very easy to debug
- There's a mature web framework ecosystem
- There are loads of packages for pretty much everything
- It's popular with data scientists, which could come in handy for those wanting to consume your API.
- I'm super comfortable with it because I use it for work
Most serious Python developers will have to manage multiple versions of Python on their development machine at a time. If you're using Poetry (which I do), you need to have some tool to do this. I use macOS, and the best way I've found to do this on that operating system is using a tool called pyenv. Getting started is really easy:
- Install HomeBrew:
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
- Install pyenv:
brew install pyenv
- Enable the shims:
echo -e 'if command -v pyenv 1>/dev/null 2>&1; then\n eval "$(pyenv init -)"\nfi' >> ~/.bash_profile
- Restart your shell
- Install the desired version of Python
pyenv install 3.7.3
Here's another divisive and subjective topic: editor. I use PyCharm by JetBrains. I tried a lot of different editors when I first starting writing Python and it was the one I liked the most. It has a ton of features, some of which I'll show you later on. If you want to try it out, there is a free version, otherwise just skip over the PyCharm-specific bits I mention or find the equivalent for your editor of choice.
This is a topic that I don't think gets talked about enough, project structure! Most Python tutorials just have you put a bunch of code in a folder and run it. This is great for getting someone writing usable code as fast as possible, but isn't sustainable. I structure all of my Python projects, regardless of their purpose, in what I call the "package structure". It looks a little something like this:
python-rest |-- README.md |-- pyproject.toml |-- python_rest | `-- __init__.py `-- tests `-- conftest.py
Here the "package name" is "python-rest". All of the logic of the application (the real code that runs) goes in the module "python_rest". Project metadata goes in the root of the package. Tests go in the tests directory. Note that package names are typically hyphen-delimited and module names are snake_case. Not everyone follows this convention but it's common enough that I picked it up. Here are some of the benefits to using a package structure for all your projects:
- It separates business logic from everything else
- It allows you to easily build your project into some sort of bundled format for publishing / deploying (e.g. wheel).
- Because everything is in a proper package, you can always use relative imports and proper name-spacing (issues pop up around this stuff all the time if you aren't consistent).
All of my justifications for using Poetry are already bundled up in a series about Python package management, check it out if you haven't already.
To get started with poetry:
- Install it:
curl -sSL https://raw.githubusercontent.com/sdispater/poetry/master/get-poetry.py | python
- Restart your shell
- Go to your project directory
- Create yourself a
- You'll get asked some questions, just fill them in
- Setup pyenv to use the correct version of Python:
pyenv local 3.7.3
- Configure Poetry to put your virtual environment in the project directory:
poetry config settings.virtualenvs.in-project true
- Create your virtual environment:
- Tell PyCharm to use that virtual environment
- Open preferences
- Go to Project > Project Interpreter
- Click the little gear in the top right, then Add
- Choose Virtual Environment > Existing Environment
- It often will auto-select the .venv folder. If it doesn't, you have to browse for /.venv/bin/python
Done! You're set up with a virtual environment and ready to start adding dependencies.
Flask is one of the most popular Python web frameworks (I think it's #2 right behind Django right now). Its design philosophy is to have a relatively small core with a huge library of extensions to add whatever functionality you want. It's not the easiest thing to get started with, but it's very powerful.
- Add flask to your project:
poetry add flask
You'll note that Poetry installs it to your virtual environment, adds it to your
pyproject.toml file as a dependency, and notes the specific versions and hashes of libraries you're using in the
Sorry this post doesn't get you anything you can run yet. As I said at the beginning, I ran out of time. I'll try to get the next post up faster so you can see how Flask actually works!