sanic's mypy is silenced for six modules and its ruff runs at 79 columns
Accelerate your web app development | Build fast. Run fast.
At a glance
- What is it?
- Sanic is a Python 3.10+ async web server and framework that is also ASGI compliant, distributed with two optional compiled accelerators and a dual release line. The parts a reader can actually check are in the configuration: a ruff line length of 79, type checking muted for six third-party modules including the router and the HTML tagger, a committed bandit baseline of accepted security findings, and a changelog assembled from fragments at release time.
- Who is it for?
- Sanic fits a Python team that wants an async web framework with a built-in server and a documented path to running under someone else's ASGI server, and that is willing to pin to a maintained major line. It does not fit a team whose tooling assumes a modernised lint and type configuration, since the line length is the PEP 8 default and six third-party modules have missing-import errors suppressed in mypy.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 108 days ago.
- What is it written in?
- Mainly Python, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on November 14, 2026, and from our analysis. They are not legal advice.
Star history
- sanic-org/sanic18,633 ★ · -3
| Repository | 2026-09-21 | 2026-11-14 |
|---|---|---|
| sanic-org/sanic | 18.6k | 18.6k |
-3 Stars in the last 54 days, measured from the daily GitHub snapshot. Compare with other projects
Editorial analysis
Two major lines were patched on the same afternoon
The three most recent releases are v25.12.1 on 2026-05-31 at 19:42:26, v24.12.1 on 2026-05-31 at 19:39:04, and v25.12.0 on 2025-12-31.
So there are two version lines, 24.x and 25.x, and both received a patch release three minutes apart on the same evening in May 2026. That is dual maintenance rather than a single moving line, and it is the arrangement a library uses when a meaningful number of deployments are on the previous major.
The version numbers are also dated. v25.12.0 is 25.12, released on the last day of December 2025, which is a calendar scheme rather than a semantic one. A team that reads version numbers as feature signals has to learn the convention first.
The last push to the default branch was on 2026-07-29, two months after the newest release, so the branch is ahead of both published lines. For an adopter the practical question is which line to pin, and the answer this repository supports is either, with the caveat that 24.x is the line receiving maintenance patches and 25.x is the line receiving them too.
The floor is Python 3.10 and the async sentence is about Python 3.5
The README states the minimum version twice, as a Python 3.10+ web server and web framework and again as a standalone line reading minimum Python version is 3.10. Both agree.
The sentence explaining why is the one that has not moved. Sanic allows the usage of the async/await syntax added in Python 3.5, which makes your code non-blocking and speedy. That framing dates the text to a decade before the floor it is announcing, and it is the project's primary justification repeated in the first paragraph a reader sees.
A second dated reference sits in the installation notes. There is a note that if you are running on a clean install of Fedora 28 or above, you should make sure you have the `redhat-rpm-config` package installed in case you want to use `sanic` with the `ujson` dependency.
None of this is a technical problem. The floor is stated correctly and twice, and a Fedora note about a build dependency is the kind of thing that gets written once and never revisited. It is worth naming because it is a reliable signal: when a README's explanatory sentences lag its own requirements by ten years, the prose around them is not a description of the current project.
uvloop and ujson each have an environment variable that turns them off
The install is one command:
pip install sanicAnd the performance dependencies are optional by environment variable. The README says Sanic makes use of `uvloop` and `ujson` to help with performance, and that if you do not want to use those packages you simply add an environmental variable, `SANIC_NO_UVLOOP=true` or `SANIC_NO_UJSON=true`, at install time. The shell block that follows sets both and then runs:
$ pip install --no-binary :all: sanicSo the shape is: two compiled accelerators that are on by default, two environment variables that remove them, and a `--no-binary :all:` path for building from source.
The opt-out exists for a concrete reason. Both packages ship as compiled extensions with platform wheels, and a platform without one, or a user with a policy against opaque binaries, is exactly the case the variables cover. Setting them at install time rather than at runtime means the choice is recorded in the environment that built the artefact.
The Fedora note attaches to the same mechanism, since a missing `redhat-rpm-config` is a reason the `ujson` build would fail.
ruff runs at 79 columns and mypy is muted for six modules
The lint and type configuration is where this project's conventions are stated precisely, and one number in it is unusual.
`[tool.ruff]` sets `target-version = "py310"` and `line-length = 79`. Seventy-nine is the PEP 8 default and one of the tightest settings a Python project can choose, so every line in `sanic/` is held to a width that many teams relax. The selected rules are the pycodestyle set, pyflakes, isort and the pycodestyle warnings, with E203 ignored, the formatter uses double quotes and space indentation, and isort is configured with `sanic` as first party and `pytest` as third party.
The type configuration is the more interesting part. There is a mypy overrides block listing six modules with `ignore_missing_imports = true`: `httptools`, `trustme`, `sanic_routing`, `aioquic`, `html5tagger` and `tracerite`.
Three of those six are not incidental. `sanic_routing` is, by its name, the router, and `html5tagger` and `tracerite` are third-party packages in the same list. So mypy is configured not to report missing imports or stubs for those packages, the router among them. There is also a per-file ignore giving `sanic/__main__.py` both E402 and I001, so the command line entry point is exempt from import ordering.
bandit.baseline is a committed list of accepted security findings
There is a file called `bandit.baseline` at the repository root, next to `SECURITY.md`, `CONTRIBUTING.md` and `CODE_OF_CONDUCT.md`.
Bandit is a Python security scanner, and a baseline file is the record of findings a project has seen and chosen not to fix. It is normally generated once and then used to suppress known issues so a run reports only new ones.
Committing it is a stronger position than most projects take. The alternative, which is to run the scanner with no baseline, produces a list that is noisy from the first run and therefore gets ignored. The cost of the committed baseline is that the accepted findings are now a public document, which means anyone can see what the maintainers decided not to address, and there is nothing hidden about it.
For an adopter that is useful information rather than a warning. A web framework that handles untrusted request bodies will have scanner findings, and a project that publishes which ones it accepts is telling you where its judgement was applied. `SECURITY.md` sits beside it for the reporting path, and the two together are the project's security posture in two files.
The Makefile spells the default goal .DEFAULT, and the first target is clean
The Makefile opens by naming six directories for ruff and then sets a default goal:
RUFF_FORMATTED_FOLDERS = sanic examples scripts tests guide docs
.DEFAULT: helpThe first line is conventional. The second is not. The conventional spelling is `.DEFAULT_GOAL`, assigned as a variable, and this file writes `.DEFAULT` with a colon and a prerequisite instead.
`help` is the first ordinary target in the file, so a bare `make` runs it; `clean` comes after it. The `clean` target runs a `find` for `.pyc` files, a `find` for `.pyo` files, a `find` for `.coverage`, then removes the contents of `build/` and `dist/`.
So the help text that the file goes to real trouble to write, listing install, docker-test, fix, format, pretty, docs, clean-docs, docs-test, changelog and release with a description for each, is what a bare `make` prints. One mismatch is worth knowing: the menu says clean-docs, while the target is defined as docs-clean.
The rest of the Makefile is a good summary of how the project works: `install` is `python -m pip install .`, `docker-test` builds `docker/Dockerfile` into a test image and runs tox in it, `view-coverage` is `sanic ./coverage --simple`, so the project's own CLI reads the coverage file, and `docs-serve` is `sphinx-autobuild`.
setup.py reads the version out of a Python file with a regex
The packaging is declared in `pyproject.toml`, which requires `setuptools` and `wheel` with the `setuptools.build_meta` backend, and there is also a `setup.py`.
The setup file does three things worth naming. It opens `sanic/__version__.py` and extracts the version with a regular expression, raising `RuntimeError("Unable to determine version.")` if the pattern does not match. It reads `README.rst` for the long description, which is consistent with the README being reStructuredText with a badge table at the top. And it defines a `PyTest` class that subclasses `setuptools.command.test` so that `setup.py` can run pytest, with a `pytest-args` user option and a `run_tests` method that shells the arguments through `shlex` and exits with pytest's return code.
So the version has exactly one home, in a Python file, and no version management tool owns it. The consequence is that every build, every install and every publish reads the same string by pattern match, and the failure mode when the pattern stops matching is a build error rather than a wrong number.
The `str_to_bool` helper in the same file accepts a long list of yes-values and no-values and raises `ValueError` for anything else, which is a small sign of the kind of environment-variable handling this project does elsewhere, where the install notes turn features off with `SANIC_NO_UVLOOP` and `SANIC_NO_UJSON`.
Two documentation hosts, a third-party awesome list, and a changelog from fragments
The documentation surface is spread across more hosts than a reader expects, and the badge table at the top is where the spread shows first.
The Documentation badge points at `sanic.readthedocs.io`. The body text then says User Guide, Changelog, and API Documentation can be found at `sanic.dev`, and the User Guide link in the README body targets `sanicframework.org`. The forums are at `community.sanicframework.org`, and Discord is a separate invite. So documentation is spread over sanic.readthedocs.io, sanic.dev and sanicframework.org, with the forums on a separate subdomain and Discord as another entry point.
The badge table also has an Awesome entry, and it points at `github.com/mekicha/awesome-sanic`, which is not maintained in this repository. It advertises monthly downloads, weekly downloads and conda-forge downloads, so usage volume is a headline metric the project chooses to display, and there is a code style ruff badge alongside.
Two other things in the tree explain the rest. There is a `crowdin.yml`, so translations are managed through Crowdin. And there is both a `CHANGELOG.md` and a `changelogs/` directory, with a Makefile target whose description is to generate the changelog to prepare for a new release, which is the fragment-then-assemble pattern.
The repository also carries two continuous integration systems: a `tests.yml` workflow under `.github/` and an `.appveyor.yml`, which is the Windows one.
Editorial conclusion
Sanic fits a Python team that wants an async web framework with a built-in server and a documented path to running under someone else's ASGI server, and that is willing to pin to a maintained major line. It does not fit a team whose tooling assumes a modernised lint and type configuration, since the line length is the PEP 8 default and six third-party modules have missing-import errors suppressed in mypy. Before adopting, check three things: which major line you are on, because v24.12.1 and v25.12.1 were both patched on 2026-05-31 so the older line is still maintained, whether `uvloop` and `ujson` have wheels for your platform, and what the committed `bandit.baseline` records, because that file is the project's list of security findings it has chosen to accept.
Frequently asked questions
How do I install Sanic?
`pip install sanic`. The package uses `uvloop` and `ujson` to help with performance, and you can opt out of either with an environment variable set at install time, `SANIC_NO_UVLOOP=true` or `SANIC_NO_UJSON=true`. To build without binary wheels the README gives `pip install --no-binary :all: sanic`, and it notes that on a clean Fedora 28 or above you may need the `redhat-rpm-config` package for the ujson dependency.
What is Sanic?
Sanic is a Python 3.10+ web server and web framework written to go fast, using the async/await syntax so your code is non-blocking. It is also ASGI compliant, so you can deploy it with an alternative ASGI webserver, and the project describes itself as maintained by the community, for the community, with funding handled through Open Collective.
Can Sanic be deployed behind another web server?
The README states that Sanic is ASGI compliant so you can deploy it with an alternative ASGI webserver, and it links a deployment page covering running under ASGI. The examples directory includes `run_asgi.py` alongside `hello_world.py` and `run_async.py`, and the repository carries a `docker/` directory and an `examples/Dockerfile`.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/sanic-org-sanic)Star history chart for your README
[](https://hysenlabs.com/projects/sanic-org-sanic)Was this useful?