Python Web Development with Sanic

By Adam Hopkins

Early Access

This is an Early Access product. Early Access chapters haven’t received a final polish from our editors yet. Every effort has been made in the preparation of these chapters to ensure the accuracy of the information presented. However, the content in this book will evolve and be updated during the development process.

Learn more
    Advance your knowledge in tech with a Packt subscription

  • Instant online access to over 7,500+ books and videos
  • Constantly updated with 100+ new titles each month
  • Breadth and depth in over 1,000+ technologies
  1. 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 want the tools to build something, and not simply glue a bunch of things together built by others.

This is where Sanic comes into the picture. It’s a next-generation Python framework and server tuned for high performance and built to be unopinionated and scalable.

This official Sanic guide starts with discussing Sanic’s purpose, significance, and use cases. You’ll learn to spot different issues when building web applications, and how to choose, create, and adapt the right solution to meet your needs. As you progress, you’ll understand how to use listeners, middleware, and background tasks to customize your application. The book will take you through real-world examples, so you walk away with practical knowledge and not just code snippets.

By the end of this web development book, you will have gained the knowledge you need to design, build, and deploy high-performance, scalable, and maintainable web applications with the Sanic framework.

Publication date:
April 2022
Publisher
Packt
Pages
454
ISBN
9781801814416

 

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, that there is only “[one] obvious way to do it” does not seem so obvious. Maybe this sentence needs a change. Perhaps it could read, “There should be one … obvious way for you to do it.” Why? Because adding in 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. Therefore, 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 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 made the decision 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, the book—takes a different approach. By the end of this book, we want you to know how to spot peculiar use cases, and 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, etc. You will certainly find passionate opinions. By “unopinionated”, we mean to say that Sanic’s job is to take care of the plumbing so 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 most 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. But, 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 is not in need of a hyper-focused solution. This is also fine. For this reason, you will find a number of 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 including plugins, we will also point them out to you for reference.

Our goal in this book is to learn to identify your unique application requirements and match them with the tools at our disposal to: (1) make our applications better; and (2) make us better developers.

In this chapter, we will begin building the foundational understanding needed to read through this book, by working through the following topics:

  • What is Sanic?
  • Leveling up
  • Framework vs server
  • Why 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 set up with Python version 3.7 or newer. You will also need to have curl, or a similar program, installed so that we can easily make and inspect HTTP requests. If you are unfamiliar with curl, it is a program 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 some popular tools 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 drive 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 out as a proof-of-concept, as a hobby project. 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 along with 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 then 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 not widespread adoption of coroutines. Of course, there were legitimate application needs being solved, but the concept was still very early in its development and not fully developed. The concept was refined over the course of 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

We will talk about how you can use alternative loops (such as trio) later on, but this book will assume we are using asyncio.

The language and syntax from the new asyncio module were both extremely powerful, and a bit clunky. Generators have usually been 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 making 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 execute partially and suspend its operation by yielding a value until it is further needed. Generators also have the ability to 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 to 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 eases up the blocking of code due to input and output. This is known as io-bound. A classical example of an io-bound application is a web server. It was, therefore, 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 webserver 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 process only one request at a time. Using Sanic with a WSGI server completely destroys the asynchronous ability to efficiently handle multiple requests concurrently.

Classical Django and Flask could not adopt this pattern. Looking back 6+ years later, those projects eventually did find ways to introduce async/await. But, 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 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 to making Flask adopt this new pattern; but, since Flask was itself incapable of doing so, 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 frustrations and lack of responses from the maintainers of the project resulted in the Sanic Task Force. This precursor to the Sanic Community Organization was a loose collective of developers that were interested in finding a future for the project that 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 two years at that time and was known within the Python community as a viable (and fast) option to build asynchronous web applications. Dropping the existing project name would have been a huge blow towards 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, and deprecations;
  • Accountability and responsibility for responding to issues and support; and
  • 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 called “Flask-like”, but that is a fair comparison only because they look similar on the surface. The features and behaviors are fundamentally different, and the likeness stops there. Personally, 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.

 

Leveling up

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

It probably is not the best tool for Python beginners since it assumes 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 actually a wonderful place to start learning 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 the concepts as they 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 might 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 the developer craft the solutions that work for their use case 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 like validation, cross-origin resource sharing (CORS), CSRF, and database management. All of that other stuff is of course important, and we will explore them in later chapters. But the intent of this book is to look at the issues and see how you might solve the problem. Web applications and web APIs have differing needs, and therefore you as the developer should be allowed to make the choices that are best suited (and obvious) to solving your problems.

Revisiting those questions above, a better way to phrase them would be:

  • “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 the questions and use your creativity to come up with appropriate solutions. You will learn some of the powerful strategies that Sanic has to offer. But, by no means think that any given solution is the only way. Just because it is outlined here does not mean that it is the right way. Or, that the tools used only apply in 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 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, etc. 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 own code. We encourage you to read the book in its entirety or duck in and out of chapters as they may apply to you. Both are valid approaches.

Let’s take a closer look that 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 likely will include a snippet of code from someone who has had this problem in the past. The developer will 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 does not 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 like this book.

We might 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 JS framework? A mobile app? Third-party integrations?
  • How large will it scale? What sort of content needs to be delivered?

These questions are the questions that are the domain of this book. We intend to ask the 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 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://sanicframework.org) and the API documentation (https://sanic.readthedocs.io). We will refer to both of these heavily in this book. But, just as equally important are 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 what is 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.

 

Framework v 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 first 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 more 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 then 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 send a request to our API:

    $ curl localhost:9999 -d '{"foo": "bar"}'

In our console, we should now see the 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 see here are three components:

  • The first line contains the HTTP method, the path, and the HTTP protocol is 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 now are:

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

Though this is the language of web servers, it is very cumbersome to write all of that. Therefore, tools like web browsers and HTTP client libraries were created to build and parse these messages for us.

Web framework

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

There are tools that do this for us: web frameworks. The job of a web framework is to make the work of building the HTTP message and appropriately handling the request. 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, and some are very sparse in their offering. Some are very strict, and some are more open. Sanic tries to fall on the continuum of being feature-rich only so far as 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 perform a survey of web frameworks on PyPI, you will find that most of them require the installation of a separate web server. When deploying most Python applications, there is a hard line between the persistent operation that runs on the machine, and the tooling used to develop response handlers. We will not delve too deeply into WSGI since it is not applicable to Sanic. However, the paradigm that there is a server that calls a single input function passing it information about the request and 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 currently are three popular ASGI servers: uvicorn, hypercorn, and daphne.

Precisely because Sanic was born during the era that predated ASGI, it needed its own 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 minimization of the request/response cycle. However, in recent years Sanic 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 still remains one of the best methods for deploying Sanic. Later in Chapter 8, Running a server, we will discuss all of the potential choices, and help you to come up with the questions to ask when deciding which solution is obvious for your needs.

 

Why we use Sanic—Build fast. Run fast.

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

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

Source: https://sanicframework.org/en/guide/#goal

Most people familiar with the Sanic project will tell you that its defining feature is performance. While this is of course 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 of course 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 a near constant second browser window opened to the documentation. While other tools make heavy usage of “black box” type features like global variables, “magic” imports, and monkey-patching, Sanic generally prefers to head in the direction of well-written, clean, and idiomatic Python (aka 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 lengthy period of 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 do does it need CORS? There are so many differing needs for web applications that it was decided 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 db, a combination, or even no data store at all. That is not to say these things are not achievable 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 most known for. With a performance-first approach towards development and implementations that include tools like uvloop and ujson, Sanic tends to outperform other asynchronous Python frameworks. We will not spend much time on benchmarks, because I tend to feel they have limited use when comparing frameworks. More important to performance is the ability to build fast and to scale fast. Sanic makes it simple to run multiple server instances from a single deployment. Chapter 8, Running a server, will talk more about scaling. Also important to note is 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 like auto-reload while you are working on it. However, these servers tend to not be ready for production environments. Sanic 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 and powering a number of 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 wide-spread adoption in a variety of use cases.

Community driven

Since the 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 (https://sanicframework.org/en/guide/project/scope.html#lazy-consensus) says:

In general, as 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.

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 itself 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 there are multiple people 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 (aka SCOPE): https://sanicframework.org/en/guide/project/scope.html.

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 like formatting–tools like 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?

These principles behind developing Sanic’s code base are:

  • performance,
  • usability (unopinionated), and
  • 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 to either: (1) not force developers into an awkward development pattern, or (2) 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. But, you also want to make sure 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.

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

Now that we have learned the background of Sanic, we can 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 in Chapter 2, Organizing a project, —is to start learning some of the foundational decisions we should make 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 20 years. He is the Core developer and project maintainer of Sanic, one of the most popular asynchronous Python framework and webserver. Currently, he is a Lead Senior Software Engineer at PacketFabric. He is also an open-source contributor and a conference speaker at the Python Web Conference.

    Browse publications by this author
Python Web Development with Sanic
Unlock this book and the full library for $5 a month*
Start now