Home Web Development Python Web Development with Sanic

Python Web Development with Sanic

By Adam Hopkins
books-svg-icon Book
eBook $35.99 $24.99
Print $43.99
Subscription $15.99 $10 p/m for three months
$10 p/m for first 3 months. $15.99 p/m after that. Cancel Anytime!
What do you get with a Packt Subscription?
This book & 7000+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with a Packt Subscription?
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with eBook + Subscription?
Download this book in EPUB and PDF formats, plus a monthly download credit
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with a Packt Subscription?
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with eBook?
Download this book in EPUB and PDF formats
Access this title in our online reader
DRM FREE - Read whenever, wherever and however you want
Online reader with customised display settings for better reading experience
What do you get with video?
Download this video in MP4 format
Access this title in our online reader
DRM FREE - Watch whenever, wherever and however you want
Online reader with customised display settings for better learning experience
What do you get with video?
Stream this video
Access this title in our online reader
DRM FREE - Watch whenever, wherever and however you want
Online reader with customised display settings for better learning experience
What do you get with Audiobook?
Download a zip folder consisting of audio files (in MP3 Format) along with supplementary PDF
What do you get with Exam Trainer?
Flashcards, Mock exams, Exam Tips, Practice Questions
Access these resources with our interactive certification platform
Mobile compatible-Practice whenever, wherever, however you want
BUY NOW $10 p/m for first 3 months. $15.99 p/m after that. Cancel Anytime!
eBook $35.99 $24.99
Print $43.99
Subscription $15.99 $10 p/m for three months
What do you get with a Packt Subscription?
This book & 7000+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with a Packt Subscription?
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with eBook + Subscription?
Download this book in EPUB and PDF formats, plus a monthly download credit
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with a Packt Subscription?
This book & 6500+ ebooks & video courses on 1000+ technologies
60+ curated reading lists for various learning paths
50+ new titles added every month on new and emerging tech
Early Access to eBooks as they are being written
Personalised content suggestions
Customised display settings for better reading experience
50+ new titles added every month on new and emerging tech
Playlists, Notes and Bookmarks to easily manage your learning
Mobile App with offline access
What do you get with eBook?
Download this book in EPUB and PDF formats
Access this title in our online reader
DRM FREE - Read whenever, wherever and however you want
Online reader with customised display settings for better reading experience
What do you get with video?
Download this video in MP4 format
Access this title in our online reader
DRM FREE - Watch whenever, wherever and however you want
Online reader with customised display settings for better learning experience
What do you get with video?
Stream this video
Access this title in our online reader
DRM FREE - Watch whenever, wherever and however you want
Online reader with customised display settings for better learning experience
What do you get with Audiobook?
Download a zip folder consisting of audio files (in MP3 Format) along with supplementary PDF
What do you get with Exam Trainer?
Flashcards, Mock exams, Exam Tips, Practice Questions
Access these resources with our interactive certification platform
Mobile compatible-Practice whenever, wherever, however you want
  1. Free Chapter
    Chapter 1: Introduction to Sanic and Async Frameworks
About this book
Today’s developers need something more powerful and customizable when it comes to web app development. They require effective tools to build something unique to meet their specific needs, and not simply glue a bunch of things together built by others. This is where Sanic comes into the picture. Built to be unopinionated and scalable, Sanic is a next-generation Python framework and server tuned for high performance. This Sanic guide starts by helping you understand Sanic’s purpose, significance, and use cases. You’ll learn how to spot different issues when building web applications, and how to choose, create, and adapt the right solution to meet your requirements. As you progress, you’ll understand how to use listeners, middleware, and background tasks to customize your application. The book will also take you through real-world examples, so you will walk away with practical knowledge and not just code snippets. By the end of this web development book, you’ll have gained the knowledge you need to design, build, and deploy high-performance, scalable, and maintainable web applications with the Sanic framework.
Publication date:
March 2022
Publisher
Packt
Pages
504
ISBN
9781801814416

 

Chapter 1: Introduction to Sanic and Async Frameworks

There should be one—and preferably only one—obvious way to do it.

– Tim Peters, The Zen of Python

Too often, this maxim of Python is taken to mean that there must be only one way to do something. Any Python web developer can simply look at the number of web frameworks that exist and tell you that the choice is not so simple. There are dozens of web frameworks on PyPI, and within the ecosystem of any single framework, you will find even more options to solve a single problem. Go ahead and type authentication into the search bar at https://pypi.org. Looking at the number of results, stating that there is only "[one] obvious way to do it" does not seem so obvious. Maybe this sentence needs to be changed. Perhaps it could read, "There should be one … obvious way for you to do it." Why? Because adding to the context that we are talking about your specific application brings us to the next level.

This is Sanic, and this is the goal of this book.

What may be obvious for someone building a stock portfolio tracker will not be obvious to someone building a streaming media player. So, to figure out what the obvious solution is, we must first understand the problem. And to understand the problem, we must be hyper-aware of our specific use case.

When we're trying to find a solution to a problem, many other tools and frameworks respond by saying: here is how you should do it. Do you want to read data from your web request? Here's how to validate it. Do you need cross-site request forgery (CSRF) protection? Here's the snippet you need to add. This approach fails to make you a better developer and fails to find the optimal solution for your use case.

Why should I validate my data this way? Why do I need this snippet to protect myself? Because someone else decided for you. You cannot answer these questions. All you know is that the framework documentation—or some blog on the internet—told you to do this, so you did it.

And this is why Sanic—and indeed, this book—takes a different approach. By the end of this book, we want you to know how to spot peculiar use cases, as well as how to bend the tooling to meet your needs. You should be able to think through different types of implementations and select one that is most meaningful for your needs. This will be the obvious solution.

Sanic prides itself on being unopinionated. That is not to say that the maintainers of the project do not have strong opinions. I welcome you to engage me or anyone in the community in a discussion about proxy forwarding, deployment strategies, authentication schemes, and more. You will certainly find passionate opinions. By "unopinionated," we mean to say that Sanic's job is to take care of the plumbing so that all you need to do is build the logic. The decision of how to tackle problems is not the domain of the framework.

You will find that Sanic developers are mostly keen to find solutions that are hyper-focused on solving the particular challenges that they face. Developers use Sanic because it is fast and simple. However, you will also find that using Sanic means the obvious solution to a problem is not based upon Sanic but upon your unique application requirements.

The other side of the story is that sometimes, your use case doesn't need a hyper-focused solution. This is also fine. For this reason, you will find several plugins (many of which are supported by active members of the Sanic core developer team) or off-the-shelf solutions. We wholly support your adoption of them and their patterns. Throughout this book, our examples will steer away from implementations that require plugins. However, where there are popular solutions that include plugins, we will also point them out to you for reference.

Our goal in this book is to help you learn how to identify your unique application requirements and match them with the tools at your disposal. This will help make your applications better and make you a better developer.

In this chapter, we will begin by building the foundational understanding that's needed to read this book. To do this, we will cover the following topics:

  • What is Sanic?
  • Leveling up
  • Framework versus server
  • Why we use Sanic—build fast, run fast
 

Technical requirements

This chapter will include some basic Python and Terminal usage. To follow along with the examples, make sure that your computer is setup with at least Python version 3.7, but Python version 3.8 or newer is recommended. You will also need to have curl, or a similar program, installed so that you can easily make and inspect HTTP requests. If you are unfamiliar with curl, it is a program that's executed from a Terminal session that allows you to make HTTP requests. It should be available on most macOS and Linux installations by default and can be installed on Windows machines.

 

What is Sanic?

Since you're reading this book, you're probably familiar with Python and may even know about some of the popular tools that are used to build web applications using Python. As for Sanic, you've either heard of it or have used it and want to improve your skills and understanding of it. You may know that Sanic is unlike traditional tools. Built 100% from the ground up, it's been developed with the idea of asynchronous Python in mind. From the very beginning, Sanic set out to be fast. It is one of the critical decisions that drives much of its development.

To truly understand the Sanic project, we'd benefit from a history lesson. Sanic was the first legitimate attempt to bring asynchronous Python to a web framework. It started as a proof-of-concept, as a hobby project. So, let's set the stage.

The most fundamental building block of Sanic is the asyncio module from Python's standard library. The Sanic project was born during the early stages of the module's release and has matured alongside the module.

Python 3.4—released in early 2014—was the first step to introduce the concept of coroutines into the standard library in the newly added asyncio module. Using standard Python generators, a function's execution can be halted while something else happens, and then data can be injected back into that function to allow it to resume execution. If there was an object that "looped" through a list of tasks that needed work, we could duck in and out of the execution of multiple functions at the same time. This could achieve "concurrency" in a single thread and is the basis of the idea of asyncio.

In the early days, this was mainly a toy and there was no widespread adoption of coroutines. Of course, legitimate application needs were being solved, but the concept was still very early in its development and not fully developed. The concept was refined over the next several Python releases to give us the asyncio module we know today.

Here's a quick look at what asynchronous programming looked like in Python 3.4:

import asyncio
@asyncio.coroutine
def get_value():
    yield from asyncio.sleep(1)
    return 123
@asyncio.coroutine
def slow_operation():
    value = yield from get_value()
    print(">>", value)
loop = asyncio.get_event_loop()
loop.run_until_complete(slow_operation())
loop.close()

While we will not delve into how this works, it is worth mentioning that asynchronous Python is built on the premise of generators. The idea is that a generator function can yield back to some "loop" to allow it to duck in and out of execution.

Important Note

Sanic can be run with the standard library asyncio loop. However, out of the box, it uses an alternative loop called uvloop that operates significantly faster.

The language and syntax from the new asyncio module were both extremely powerful and a bit clunky. Generators are usually a bit mysterious and difficult for less seasoned Python developers. What exactly is yield from? This stuff looked alien to many people; Python needed a better syntax.

Before continuing, it is worth providing a quick side note if you are unfamiliar with generators. Python has a special type of function that returns a generator. That generator can be used to partially execute a function and suspend its operation by yielding a value until it is needed again. Generators can also be bi-directional, in that data can be sent back into them during execution. Again, the specifics of this are beyond the scope of this book since it is not entirely pertinent, but it is helpful to know that this is what yield from helps us achieve. Using the bidirectional functionality of generators, Python was able to build the capability for asynchronous coroutines.

Because this implementation was complex and slightly more difficult for newcomers when Python 3.5 was released, it included a much simpler and cleaner version:

async def get_value():
    await asyncio.sleep(1)
    return 123
async def slow_operation():
    value = await get_value()
    print(">>", value)

One of the main benefits of this style of programming is that it helps you block code due to input and output. This is known as I/O-bound. A classical example of an I/O-bound application is a web server. So, it was a natural fit for this new asyncio module to be built with the idea of creating protocols for interacting with networking traffic.

At the time, however, there were no frameworks or web servers that adopted this approach. The Python web ecosystem was built upon a premise that is fundamentally at odds with asynchronous Python.

Important Note

The classical Python pattern for integrating an application with a Python web server is known as the Web Server Gateway Interface (WSGI). This is not within the scope of this book. While it is possible to shoehorn Sanic into WSGI, it is generally frowned upon. The problem with WSGI is that the entire premise of it is blocking. A web server receives a request, processes it, and sends a response all within a single execution. This means that the server can only process one request at a time. Using Sanic with a WSGI server destroys the asynchronous ability to efficiently handle multiple requests concurrently.

Classical Django and Flask could not adopt this pattern. Looking back after 6 years, those projects eventually did find ways to introduce async/await. However, it is not the natural use pattern for these frameworks and came at the expense of an extraordinary effort over many years.

As the asyncio module was being released, there was a lack of web frameworks to fill this new use case. Even the concept that eventually came to be known as Asynchronous Server Gateway Interface (ASGI) did not exist.

Important Note

The ASGI is the corollary to WSGI, except for asynchronous Python. It is possible—although not required—to use it for Sanic, and we will discuss it further in Chapter 8, Running a Sanic Server.

In the summer of 2016, Sanic was built to explore the gap. The idea was simple: could we take an application with a simplistic-looking API from Flask and make it async/await?

Somehow, the idea took off and gained traction. It was not a project that was initially set out with the goal of redoing how Python applications handled web requests. It very much was a case of accidental exposure. The project exploded and created excitement very quickly. There was a lot of appeal in making Flask adopt this new pattern; but, since Flask was incapable of doing so itself, many people thought that Sanic could be the async version of Flask.

Developers were excited for this new opportunity to use the latest in Python to bring a whole new level of performance to their applications. Early benchmarks showed Sanic running circles around Flask and Django.

Like so many projects, however, the initial burst of energy died off. The original project was meant to answer the question, "could a Flask-like framework exist?" The answer was a resounding yes. However, being a one-person project that had no intention of handling the level of support and attention that it received, the project started to gather dust. Pull requests started piling up. Issues went unanswered.

Through 2017 and 2018, the ecosystem for asynchronous Python was still very immature. It lacked production-worthy platforms that would be supported, maintained, and viable for both personal and professional web applications. Furthermore, there was still a bit of an identity question about Sanic. Some people felt that it should be a very niche piece of software, while others felt it could have broad applicability.

The result of the months of frustration and lack of responses from the maintainers of the project resulted in the Sanic Task Force. This precursor to the Sanic Community Organization (SCO) was a loose collective of developers that were interested in finding a future for the project, which was on the verge of failing. There was a desire to stabilize the API and answer all of the outstanding identity questions. For several months in mid-2018, a debate brewed about how to move the project forward, and how to make sure the project would not suffer the same fate again.

One of the most fundamental questions was whether the project should be forked. Since no one on the Sanic Task Force had admin access to the repository or other assets—and the only person who did was non-responsive—the only option was to fork and rebrand the project. However, Sanic had already existed for 2 years at that time and was known within the Python community as a viable (and fast) option for building asynchronous web applications. Dropping the existing project name would have been a huge blow toward ever getting the new project back up off the ground. Nonetheless, this was the only remaining solution. On the eve of forking the project to a new GitHub repository, the original maintainer offered up access to the repository and the SCO was born. The team worked to reorganize the operation of the community with the following goals:

  • Regular and predictable releases, as well as deprecations
  • Accountability and responsibility for responding to issues and support
  • Structure for reviewing code and making decisions

In December 2018, the SCO released its first community version of Sanic: 18.12 LTS.

With this new structure in place, the SCO turned to its next question: what is Sanic? Ultimately, the decision was to break with any attempt at Flask parity. While it may be said that the original project was a "Flask clone," this is no longer true. You will still hear it being called "Flask-like," but that is a fair comparison, only because they look similar on the surface. Their features and behaviors are fundamentally different, and the likeness stops there. I try to steer away from this comparison because it diminishes the effort and improvements that hundreds of contributors have made to let Sanic stand on its own. Now that you know some of the history behind Sanic, we turn to one of the goals of this book: making you a better developer.

 

Leveling up

Sanic encourages experimentation, customization, and, most of all, curiosity.

It is probably not the best tool for Python beginners since it assumes that they have some knowledge of both Python and web development. That is not to say that the project discourages newcomers, or people just getting into web development. Indeed, Sanic is a wonderful place to start learning about web development for those that truly want to understand the practice. Much of web development is learning to balance competing decisions. Sanic development often includes learning about these decision points.

This touches upon the goal of this book: answering the question, "what is the obvious path to building my Sanic application?" This book intends to explore different patterns that could be used in Sanic. While we will learn about the concepts that relate to Sanic, the principles can be abstracted and applied to building an application with any other framework or language. It is critical to remember that there is no "right" or "wrong" way. Online forums are filled with "what is the right way?" questions that assume that there is standard practice. The answers to these questions point to the implication that if they are not following a particular pattern, then their application is wrong.

For example, someone may ask the following questions:

  • What is the right way to serve internationalized content?
  • What is the right way to deploy my web app?
  • What is the right way to handle long-running operations?

These "right way" questions suffer from a critical flaw: the belief that there is only a single obvious solution. Structuring a question like this is the domain of opinionated frameworks that do not teach developers to think on their own. Ask these same questions on the Sanic forums, and you'll probably receive an "it depends" as your answer. Opinionated frameworks hinder creativity and design. They ultimately drive the developer into making choices based upon the constraints provided by the framework and not the constraints of the application's needs.

Instead, Sanic provides a set of tools to help developers craft the solutions that work for their use cases without mandating certain practices. This is why the built-in features of Sanic focus on functionality and the request/response cycle, and not on implementation details such as validation, cross-origin resource sharing (CORS), CSRF, and database management. All of that other stuff is, of course, important, and we will explore this in later chapters. But this book intends to look at the issues and see how you might solve the problem. Web applications and web APIs have differing needs, so you, as a developer, should be allowed to make the choices that are best suited (and obvious) to solving your problems.

Revisiting the previous questions, a better way to phrase them would be as follows:

  • How can I serve internationalized content?
  • Which deployment strategy will work for me, considering my constraints?
  • What are the trade-offs to consider for handling long-running operations?

By the end of this book, you will be able to spot these questions and use your creativity to come up with appropriate solutions. You will learn about some of the powerful strategies that Sanic has to offer. But by no means think that any given solution is the only way to do something. Just because it is outlined here does not mean that it is the right way, or that the tools being used only apply to one particular situation.

The right way is to use the tools that Sanic and Python provide to solve your problems. If you were tasked with making soup, you would find that there is no single way to do it. You could look up some recipes and try to learn some basic patterns: boil water, add ingredients, and so on. Ultimately, to master the art of soup-making, you need to cook within the constraints of your kitchen, equipment, ingredients, and the people you are serving. This is what I want you to learn about web development: master your tools, environment, and requirements to build what you need for your specific users.

While reading this book, you should both be learning and analyzing the patterns and code. Try to generalize the concepts and think about how to use similar ideas down the road in your code. We encourage you to read this book in its entirety or duck in and out of chapters if they apply to you. Both are valid approaches.

Let's take a closer look at the question about internationalization to see how framing the question differently impacts our application and our knowledge:

  • BAD: What is the right way to serve internationalized content?
  • GOOD: How can I serve internationalized content?

The answer to the first question would likely include a snippet of code from someone who has had this problem in the past. The developer would copy/paste and move on, not learning anything in the process (and therefore will be unable to generalize and apply knowledge to similar problems in the future). At best, the solution is passably acceptable. At worst, the solution is harmful to the overall design of the application and the developer doesn't even know it.

Framing the question as "How can I…" leaves open the idea that there may be multiple avenues to the same destination. The bad question style is narrow and drives our attention to a single approach. The good style opens up the possibilities to explore different solutions and weigh them against their merits. After asking the question, our job now is to figure out those possible solutions, determine the potential trade-offs, and then come to a conclusion. In this process, we can draw upon our own past experiences, examples from other developers, and resource materials such as this book.

We may think about possible solutions involving the following:

  • Middleware (catch the headers or path and reroute the request to a different handler)
  • Routing (embed the language code in the URL path and extract the language from the route)
  • Functional programming (use different functional handlers to generate separate responses)
  • Decorators (execute some logic before [or after] the actual handler is run)

But which solution should be used? We need to know about the specifics of our application. Here are some important questions to keep in mind:

  • Who is developing it? What is their experience level? How many developers are on the team? What tools will they be working with? Who will be maintaining it?
  • Who will be using the application? Will it be consumed from a frontend JavaScript (JS) framework? A mobile app? Third-party integrations?
  • How big will it scale? What sort of content needs to be delivered?

These questions are the domain of this book. We intend to ask these questions and determine the reasons behind making particular design pattern decisions. Of course, we cannot be exhaustive. Instead, we hope to inspire your journey to use as many tools and creative solutions as you can.

Few projects can match the amount of literature that has been written about Django. But precisely because Sanic does not require specific patterns, there is no need for such expansive amounts of documentation. The only prerequisite is knowing Python. The depth of API-specific knowledge that's needed to be successful with Sanic is not large. Do you know how to instantiate objects, pass values, and access properties? Of course, you do—it's just Python!

Two obvious valuable Sanic-specific resources are the User Guide (https://sanic.dev) and the API documentation (https://sanic.readthedocs.io). We will refer to both of these heavily in this book. But equally important is any other source on the web or in print that you have used up to this point to learn Python.

Coming back to the question of the obvious way to handle some tasks within Sanic: use the resources and tools that exist. There is a wealth of information on StackOverflow and the Sanic Community Forums. The Discord server is an active live discussion channel. Make yourself known, make your voice heard.

Don't ask the right way questions. Instead, ask the how can I questions. Next, we will learn a little bit about where Sanic falls in the web ecosystem, and its unusual place as both a framework and server.

 

Framework versus server

Sanic calls itself both a web framework and a web server. What does this mean? And more importantly, why is this important? Before we can explore this, we must understand what these terms mean, and why they exist.

Web server

A web server is a piece of software that is designed to deliver documents and data via the HTTP protocol. Its function is to accept an incoming HTTP request, decode the message to understand what the request is trying to accomplish, and deliver an appropriate response. The language of web servers is the HTTP protocol.

We will get into the specifics later, but for now, we will set up a simple Sanic server, issue a request from curl, and look at the message:

  1. Create a file called server.py and run it in your Terminal:
    from sanic import Sanic, text, Request
    app = Sanic(__name__)
    @app.post("/")
    async def handler(request: Request):
        message = (
            request.head + b"\n\n" + request.body
        ).decode("utf-8")
        print(message)
        return text("Done")
    app.run(port=9999, debug=True)
  2. Now, we will send a request to our API:
    $ curl localhost:9999 -d '{"foo": "bar"}'

In our console, we should see the following HTTP request message:

POST / HTTP/1.1
Host: localhost:9999
User-Agent: curl/7.76.1
Accept: */*
Content-Length: 14
Content-Type: application/x-www-form-urlencoded
{"foo": "bar"}

What we can see here is three components:

  • The first line contains the HTTP method, the path, and the HTTP protocol that's being used.
  • Next is a list of HTTP headers, one per line in key: value format.
  • Last is the HTTP body, preceded by a blank line.

HTTP responses are very similar:

HTTP/1.1 200 OK
content-length: 4
connection: keep-alive
content-type: text/plain; charset=utf-8
Done

The three components are now as follows:

  • The first line contains the HTTP protocol, followed by the HTTP status, and then a status description.
  • Next is a list of HTTP headers, one per line in key: value format.
  • Last is the HTTP body (if there is one), preceded by a blank line.

Though this is the language of web servers, it is very cumbersome to write all of that. This is why tools such as web browsers and HTTP client libraries were created—to build and parse these messages for us. Next we will explore how servers deal with this same problem, and how web frameworks solve it.

Web framework

We could, of course, write a program in Python that receives these raw HTTP messages, decodes them, and returns an appropriate HTTP response message. However, this would require a lot of boilerplate, be difficult to scale, and be prone to mistakes.

Certain tools do this for us: web frameworks. The job of a web framework is to build the HTTP message and handle the request appropriately. Many frameworks go further by providing conveniences and utilities to make the process simpler.

There are many web frameworks in the Python ecosystem that do this work to varying degrees. Some provide a huge number of features, while some are very sparse in terms of what they offer. Some are very strict, while some are more open. Sanic tries to fall on the continuum of being feature-rich, but only so far as what's required to not get in the way of the developer.

One of the features that Sanic provides is that it is both a web framework and a web server.

If you survey the web frameworks on PyPI, you will find that most of them require a separate web server to be installed. When it comes to deploying most Python applications, there is a hard line between the persistent operation that runs on the machine and the tooling that's used to develop response handlers. We will not delve too deeply into WSGI since it doesn't apply to Sanic. However, the paradigm that there is a server that calls a single input function, passes information about the request, and then expects a response is important to understand. Everything that happens in-between is the framework.

Narrowing our focus to projects that support async/await style coroutine handlers, the vast majority require you to run an ASGI server. It follows a similar pattern: an ASGI-ready server calls into an ASGI ready framework. These two components operate with one another using a specific protocol. There are currently three popular ASGI servers: uvicorn, hypercorn, and daphne.

Precisely because Sanic was born during the era that predated ASGI, it needed a server. Over time, this has become one of its greatest assets and is in large part why it outperforms most other Python frameworks. Development of the Sanic server is hyper-focused on performance and minimizing the request/response cycle. However, in recent years, Sanic has also adopted an ASGI interface to allow it to be run by an ASGI web server.

However, for the majority of this book, you can assume that when we are talking about running Sanic, we mean using the internal web server. It is production-ready and remains one of the best methods for deploying Sanic. Later, in Chapter 8, Running a Sanic Server, we will discuss all of the potential choices and help you come up with the questions to ask when you're deciding which solution is obvious for your needs. Now that you know the what of Sanic, we turn to the why.

 

Why we use Sanic—build fast, run fast

Let's begin by looking at Sanic's goal:

To provide a simple way to get a highly performant HTTP server up and running that is easy to build, expand, and, ultimately, scale.

Source: https://sanic.dev/en/guide/#goal.

Most people who are familiar with the Sanic project will tell you that its defining feature is its performance. While this is important, it is only a single part of the core philosophies of the Sanic project.

The Sanic tagline is: "Build fast. Run fast." This highlights the performance orientation of the project. It also speaks to the goal that building an application in Sanic is meant to be intuitive. Getting an application up and running should not mean learning a complex set of APIs and having a near-constant second browser window open to the documentation. While other tools make heavy usage of "black box" type features such as global variables, "magic" imports, and monkey patching, Sanic generally prefers to head in the direction of well-written, clean, and idiomatic Python (that is, Pythonic code). If you know Python, you can build a web API with Sanic.

If performance alone is not the defining feature, then what is? The front page of the Sanic project's website gives six reasons for us to explore:

  • Simple and lightweight
  • Unopinionated and flexible
  • Performant and scalable
  • Production-ready
  • Trusted by millions
  • Community-driven

Simple and lightweight

The API is intentionally lightweight. This means that the most common properties and methods are easily accessible and that you do not need to spend a long time memorizing a particular call stack. Early on in the history of the project, there were discussions about adding certain features. But often, adding features can lead to bloat. The SCO decided that it was more important to focus on the specifics of providing a quality developer experience than on providing "bells and whistles" features.

For example, if my application is meant to be consumed by third-party applications, then why does it need CORS? There are so many differing needs for web applications that it was decided that these features are better left to plugins and developers. This leads to the next reason.

Unopinionated and flexible

Being unopinionated is a huge asset. It means that the developer decides if they want sessions or token authentication and if they want an ORM, raw SQL queries, a NoSQL database, a combination, or even no data store at all. That is not to say that these things can't be achieved in other frameworks. But there are certain design decisions to take into account. Rather than focusing on all these features, Sanic would rather provide you with the tools to implement the features you need, and nothing more. Tooling beats features. Borrowing from a popular proverb: Sanic does not give you the fish, it teaches you how to fish.

Performant and scalable

This is what Sanic is well-known for. With a performance-first approach toward development and implementations that include tools such as uvloop and ujson, Sanic tends to outperform other asynchronous Python frameworks. We will not spend too much time on benchmarks because I tend to feel they have limited use when it comes to comparing frameworks. Something more important regarding performance is the ability to build fast and scale fast. Sanic makes it simple to run multiple server instances from a single deployment. Chapter 8, Running a Sanic Server, will talk more about scaling. It's also important to note that Sanic is very well suited to building monolithic applications, microservices, and everything in-between because of the intentional flexibility of the API.

Production-ready

It is common for frameworks to ship with a development server. These development servers make the process of building simpler by including features such as auto-reload while you are working on a project. However, these servers don't tend to be ready for production environments. Sanic's web server is intentionally built to be the primary strategy for deployment in production systems. This leads to the next reason.

Trusted by millions

Sanic is installed in and powers many applications, both large and small. It is used in corporate-built web applications and personal web projects. It tends to be one of the most downloaded frameworks from PyPI. Between April 2019 and April 2020, there were 48 million downloads of Django. Sanic, in that same period, had about 44 million downloads. It is a project with high visibility and widespread adoption in a variety of use cases.

Community-driven

Since its move from an individual repository to a community organization in 2018, decision-making has become shared across the members of the community in what they call "lazy consensus." Here's what the SCO's website says:

In general, so long as nobody explicitly opposes a proposal or patch, it is recognized as having the support of the community. This is called lazy consensus; that is, those who have not stated their opinion explicitly have implicitly agreed to the implementation of the proposal.

Source: https://sanic.dev/en/org/scope.html#lazy-consensus

Another important factor of the community is the ability of all members (whether a regular contributor or a first-time user) to enter the conversation and input valuable information into the conversation. As much as possible, Sanic attempts to be "of the community, by the community" to ensure its stability, feature set, and future.

The insistence on a community-first organization is meant to create a level of stability. All the work on the project is done by volunteers. It is a labor of love. With that said, projects driven by passion alone are at risk of becoming unmaintained if it rests upon the shoulders of a single person. This is exactly the scenario Sanic was trying to escape when the SCO was created. Being a project "of the community" means that multiple people are willing and capable to help carry the torch forward. Sanic achieves this with a rotating set of developers and balances long-term stability with staggered terms.

More on the SCO

If you want to learn more about the structure of the SCO and how you can get involved, check out the Sanic Community Organization Policy E-manual (SCOPE): https://sanic.dev/en/org/scope.html#lazy-consensus.

What drives code decisions?

Although not exactly formalized, there is an underlying set of principles that the architects and engineers of Sanic use when making coding decisions. Keeping in mind that this project is built by the hands of many people, from many backgrounds and experience levels, it is no surprise to learn that maintaining a set of consistent coding practices is itself a challenge.

I am not specifically talking about things such as formatting—tools such as black, isort, flake8, and mypy have abstracted that away. Rather, what should the code look like, how should it be organized, and what patterns should be followed?

The principles behind developing Sanic's code base are as follows:

  • Performance
  • Usability (unopinionated)
  • Simplicity

Any line of code that is going to run during the execution of the request/response cycle will be highly scrutinized for its performance impacts. When faced with a question that puts two or more of these core philosophies in opposition, the performance consideration will almost always win. However, there are times when a slower alternative must be used. This will help not force developers into an awkward development pattern or add an undue level of complexity for developers. Part of the "speed" of Sanic is not just application performance, but also development performance.

When using Sanic, we can feel confident that there is a team of developers scrutinizing every line of code and its impact on performance, usability, and simplicity.

Let's imagine you are being asked to build an API by a project manager, who has a deadline in mind. To meet that goal, you want to get up and running as quickly as possible. However, you also want to make sure that you will have the freedom to iterate on the problems that face you without fear of being boxed into making bad decisions. One of the goals of this book is to help you identify useful patterns to adapt to help you get there.

 

Summary

It is helpful to understand the history and decisions behind Sanic to understand its feature set and implementation. Often, Sanic will be seen as an attempt to bring async/await style programming to a Flask app. While this may be a fair point of the original proof-of-concept, Sanic has developed upon a very divergent path, with the goal and impact of becoming a powerful tool designed for performance applications.

Due to this, Sanic is typically used by developers and teams that are looking to build a rich environment that addresses the unique—and obvious—design patterns that are required by their application's needs. The project intends to take away the difficult or cumbersome parts of building a web server and provide the tools to create performant and scalable web applications.

Now that we have learned about the background of Sanic, we should understand and appreciate the flexibility of using Sanic as a web framework. It is helpful to know the context in which Sanic was developed so that we can learn how to use it in our projects. The next step—beginning with Chapter 2, Organizing a Project—is to start learning about some of the foundational decisions we should make we're when starting any new web development project.

About the Author
  • Adam Hopkins

    Adam Hopkins is a self-taught programmer. He started programming in high school and has been using Python and building websites for more than 25 years. He is a licensed attorney and practiced law before transitioning into software engineering as a second career. Adam is the core developer and project maintainer of Sanic, a popular asynchronous Python framework and web server. Currently, he is a software engineering manager at PacketFabric, where he leads a team building backend web services. He is passionate about open source contributions and helping other developers to grow and learn. Adam lives in his desert home in the south of Israel with his wife and five children.

    Browse publications by this author
Python Web Development with Sanic
Unlock this book and the full library FREE for 7 days
Start now