Nano Templates
Minimal, fast, non-evaluating text templates for Python.
Install
Use pip, or your favorite package manager:
python -m pip install nano-template
Example
import nano_template as nt
template = nt.parse("Hello, {{ you }}!")
print(template.render({"you": "World"})) # Hello, World!
About
Nano Template is a small text templating engine, written as a Python C extension, with syntax familiar to anyone who's used Jinja/Minijinja, Django templates or Liquid.
Unlike those popular template engines, Nano Template forces you to keep application logic out of template text by implementing a reduced feature set. In this scenario, template authors and application developer are likely to be the same person (or team of people).
Syntax
Variables
<div>{{ some.variable }}</div>
<p>{{ other["variable with spaces or special characters"] }}</p>
Conditions
{% if some.variable %}
more markup
{% elif another.variable %}
alternative markup
{% else %}
default markup
{% endif %}
Loops
{% for x in y %}
more markup with {{ x }}
{% else %}
default markup (y was empty or not iterable)
{% endfor %}
Logical operators
Logical operators and, or and not use Python truthiness and precedence rules, and terms can be grouped with parentheses.
{% if not a and b %}
markup with {{ b }} and {{ c }}.
{% endif %}
Logical and and or have last value semantics.
Hello, {{ user.name or "guest" }}!
Strings
String literals and quoted variable path segments can use single or double quotes, and allow JSON-style escape sequences.
{{ greeting or "Hi \uD83D\uDE00!" }}
Output:
Hi 😀!
Whitespace control
Control whitespace before and after markup delimiters with - and ~. ~ will remove newlines but retain space and tab characters. - strips all whitespace.
<ul>
{% for x in y ~%}
<li>{{ x }}</li>
{% endfor -%}
</ul>
API
render
render(source, data) is a convenience function that parses and immediately renders a template to a string. Use this for testing or when you know you'll be rendering the template just the once.
import nano_template as nt
print(nt.render("Hello, {{ you }}!", {"you", "World"}))
render also accepts serializer and undefined keyword arguments.
parse
parse(source) parses template text and returns an instance of Template for later rendering with the render(self, data) method.
import nano_template as nt
template = nt.parse("Hello, {{ you }}!")
print(template.render({"you": "World"})) # Hello, World!
print(template.render({"you": "Sue"})) # Hello, Sue!
parse also accepts serializer and undefined keyword arguments.
Serializing objects
By default, when outputting an object with {{ and }}, lists, dictionaries and tuples are rendered in JSON format. For all other objects we render the result of str(obj).
You can change this behavior by passing a callable to parse or render as the serializer keyword argument. The callable should accept an object and return its string representation suitable for output.
This example shows how one might define a serializer that can dump data classes with json.dumps.
import json
from dataclasses import asdict
from dataclasses import dataclass
from dataclasses import is_dataclass
import nano_template as nt
@dataclass
class SomeData:
foo: str
bar: int
def json_default(obj: object) -> object:
if is_dataclass(obj) and not isinstance(obj, type):
return asdict(obj)
raise TypeError(f"Object of type {obj.__class__.__name__} is not JSON serializable")
def my_serializer(obj: object) -> str:
return (
json.dumps(obj, default=json_default)
if isinstance(obj, (list, dict, tuple))
else str(obj)
)
template = nt.parse("{{ some_object }}", serializer=my_serializer)
data = {"some_object": [SomeData("hello", 42)]}
print(template.render(data)) # [{"foo": "hello", "bar": 42}]
Undefined variables
When a template variable or property can't be resolved, an instance of the undefined type is used instead. That is, an instance of nano_template.Undefined or a subclass of it.
The default undefined type renders nothing when output, evaluates to False when tested for truthiness and is an empty iterable when looped over. You can pass an alternative undefined type as the undefined keyword argument to parse or render to change this behavior.
Here we use the built-in StrictUndefined.
import nano_template as nt
t = nt.parse("{{ foo.nosuchthing }}", undefined=nt.StrictUndefined)
print(t.render({"foo": {}}))
# nano_template._exceptions.UndefinedVariableError: 'foo.nosuchthing' is undefined
# -> '{{ foo.nosuchthing }}':1:3
# |
# 1 | {{ foo.nosuchthing }}
# | ^^^ 'foo.nosuchthing' is undefined
Or you can implement you own.
from typing import Iterator
import nano_template as nt
class MyUndefined(nt.Undefined):
def __str__(self) -> str:
return "<MISSING>"
def __bool__(self) -> bool:
return False
def __iter__(self) -> Iterator[object]:
yield from ()
t = nt.parse("{{ foo.nosuchthing }}", undefined=MyUndefined)
print(t.render({"foo": {}})) # <MISSING>
Preliminary benchmark
TODO: move this
(On an M2 Mac Mini with Python 3.13)
$ python scripts/benchmark.py
(001) 5 rounds with 10000 iterations per round.
parse c ext : best = 0.092587s | avg = 0.092743s
parse pure py : best = 2.378554s | avg = 2.385293s
just render c ext : best = 0.061812s | avg = 0.061850s
just render pure py : best = 0.314468s | avg = 0.315076s
just render jinja2 : best = 0.170373s | avg = 0.170706s
just render minijinja : best = 0.454723s | avg = 0.457256s
parse and render ext : best = 0.155797s | avg = 0.156455s
parse and render pure py : best = 2.733121s | avg = 2.745028s
parse and render jinja2 : <with caching disabled, I got bored waiting>
parse and render minijinja : best = 0.705995s | avg = 0.707589s
$ python scripts/benchmark_format.py
(002) 5 rounds with 1000000 iterations per round.
render template : best = 0.413830s | avg = 0.419547s
format string : best = 0.375050s | avg = 0.375237s
Contributing
TODO
Notes to self
TODO: move this
Python Debug Build
Build Python in debug mode from source in ~/python-debug:
./configure --prefix=$HOME/python-debug --with-pydebug
Tell uv to use the debug build instead of system or downloaded Python version:
uv sync -p <path/to/python debug binary>
Rebuild the extension in debug mode:
uv run python setup.py build_ext --inplace --force --debug
PYTHONMALLOC=debug
Use PYTHONMALLOC=debug python dev.py to activate Python's debug memory allocator, which inserts guard bytes, fills memory with known patterns, and performs validation to catch buffer overflows, use-after-free, and double frees when using Python memory APIs.
ASAN does not seem to catch these.
Manual reference leak check
(With a debug Python build.)
(A debug build will also report negative reference counts.)
If the delta is small and consistent, it's probably not a ref count leak.
import sys
from nano_template import render
before = sys.gettotalrefcount()
for i in range(10000):
render("{% if a %}a{% else %}c{% endif %}", {"a": False, "b": False})
if i % 1000 == 0:
print(f"Iteration {i}, total refcount={sys.gettotalrefcount()}")
after = sys.gettotalrefcount()
print("Refcount delta:", after - before)
Detecting leaks from reference cycles
(We shouldn't be creating any reference cycles in this project.)
import json
from nano_template import parse
import gc
# gc.set_debug(gc.DEBUG_LEAK)
# gc.set_debug(gc.DEBUG_SAVEALL)
with open("tests/fixtures/001/data.json") as fd:
data = json.load(fd)
with open("tests/fixtures/001/template.txt") as fd:
source = fd.read()
gc.disable()
gc.collect()
before = len(gc.get_objects())
parse(source).render(data)
gc.collect()
after = len(gc.get_objects())
print("Object delta:", after - before)
gc.enable()
# leaked = [o for o in gc.garbage if type(o).__module__ == "_nano_template"]
# for o in leaked:
# print(o, type(o))
ABI 3 Audit
Note that abi3audit ignores target ABI version when auditing .so files.
- Build a wheel locally with
python setup.py bdist_wheel - Run
abi3audit dist/<NAME>.whl --verbose
Example successful output:
[17:55:59] 💁 nano_template-0.1.0-cp39-abi3-linux_x86_64.whl: 1 extensions scanned; 0 ABI version mismatches and 0 ABI violations found
Release
- Manually run the Build wheels workflow.
- Clean
./dist/. - Download workflow artifacts.
- Extract artifacts to
./dist. - Build sdist with
uv run python -m build --sdist - Upload
uv run python -m twine upload dist/*
License
nano-template is distributed under the terms of the MIT license.
Metadata
Release files for nano-template 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| nano_template-0.1.1.tar.gz | 46.4 kB | Without provenance | Details for nano_template-0.1.1.tar.gz |
Built distributions (wheels)
Total release size: 1.4 MB
Release files / nano_template-0.1.1.tar.gz
| Download URL | nano_template-0.1.1.tar.gz |
|---|---|
| Size | 46.4 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
fc019a975f6cc68289e1469bcd8e0ffa50eca1bb49f8c8e11a5af790cb765905
|
|
BLAKE2b-256 checksum How to use checksums |
b9c953feb6a4c45e1847532aa2e008afa4081dbaad9afa2b4a99e2eb9461885c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.5
|
Release files / nano_template-0.1.1-cp314-cp314t-win_arm64.whl
| Download URL | nano_template-0.1.1-cp314-cp314t-win_arm64.whl |
|---|---|
| Size | 35.5 kB |
| Tags | CPython 3.14 CPython 3.14 free-threading Windows ARM64 |
|
SHA-256 checksum How to use checksums |
11fdb9948282760175e2d778dc3ec96c8c82f28fc6ec87dfad604db3ec8e04a4
|
|
BLAKE2b-256 checksum How to use checksums |
2d0552a9c9dc2ef4371d943cc179e22793230fae98517bf4338436ffcd5bbe14
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.5
|
Release files / nano_template-0.1.1-cp314-cp314t-win_amd64.whl
| Download URL | nano_template-0.1.1-cp314-cp314t-win_amd64.whl |
|---|---|
| Size | 38.0 kB |
| Tags | CPython 3.14 CPython 3.14 free-threading Windows x86-64 |
|
SHA-256 checksum How to use checksums |
5fb2897d633b374851e8f3ee4d42628af67bb0c1a9dfc227ebbc819b7b52b869
|
|
BLAKE2b-256 checksum How to use checksums |
e64e489eedf28a71a80b058d7de8471d4141023328bd7dc0462ff7b14e37da85
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.5
|
Release files / nano_template-0.1.1-cp314-cp314t-win32.whl
| Download URL | nano_template-0.1.1-cp314-cp314t-win32.whl |
|---|---|
| Size | 35.0 kB |
| Tags | CPython 3.14 CPython 3.14 free-threading Windows x86-32 |
|
SHA-256 checksum How to use checksums |
65933b1327f5db2cc2f658a658abdff288d1a4106a77b8c71c164dac5da0c816
|
|
BLAKE2b-256 checksum How to use checksums |
f56dc032cf59c0265299f2bbb0613dc0fba2edf70383be294109d2086c81d5b5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.5
|
Release files / nano_template-0.1.1-cp314-cp314t-musllinux_1_2_x86_64.whl
| Download URL | nano_template-0.1.1-cp314-cp314t-musllinux_1_2_x86_64.whl |
|---|---|
| Size | 150.5 kB |
| Tags | CPython 3.14 CPython 3.14 free-threading Linux musl 1.2+ x86-64 |
|
SHA-256 checksum How to use checksums |
7465d899bdd49a5186d6aebd48c177ad582fddb49954581ec8c3d00722befdc9
|
|
BLAKE2b-256 checksum How to use checksums |
c4c20e9509eedd27fe3499ebbee00c14abd89e26f8cffef8dfc6042be3278f91
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.5
|
Release files / nano_template-0.1.1-cp314-cp314t-musllinux_1_2_aarch64.whl
| Download URL | nano_template-0.1.1-cp314-cp314t-musllinux_1_2_aarch64.whl |
|---|---|
| Size | 151.9 kB |
| Tags | CPython 3.14 CPython 3.14 free-threading Linux musl 1.2+ ARM64 |
|
SHA-256 checksum How to use checksums |
6c18f927f98ea48c784a85995f437cf5106b66a5486ac8e92a87ab65062d739e
|
|
BLAKE2b-256 checksum How to use checksums |
1bd1899ae2ba29945a9537d363db40af84efeb53bf61d65ceb37b3d95bfa34e7
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.5
|
Release files / nano_template-0.1.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl
| Download URL | nano_template-0.1.1-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl |
|---|---|
| Size | 153.0 kB |
| Tags | CPython 3.14 CPython 3.14 free-threading Linux glibc 2.17+ ARM64 Linux glibc 2.28+ ARM64 |
|
SHA-256 checksum How to use checksums |
ba38cbeb2194a386033895d53d9e274d5eaf2dc2a547e1e61c349729e6ad3233
|
|
BLAKE2b-256 checksum How to use checksums |
7b03b7adf15eb3474bacc70e914bae07340245df2575e32221689ca5554da325
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.5
|
Release files / nano_template-0.1.1-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl
| Download URL | nano_template-0.1.1-cp314-cp314t-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl |
|---|---|
| Size | 149.9 kB |
| Tags | CPython 3.14 CPython 3.14 free-threading Linux glibc 2.28+ x86-64 Linux glibc 2.5+ x86-64 |
|
SHA-256 checksum How to use checksums |
55f855bda34ad8f23f2408f7d3c03a9c4b6366498e625f8a7d1cbf53e0691ca9
|
|
BLAKE2b-256 checksum How to use checksums |
d2709f9d798b2e98dfc53c5121e1130bdc81e657df3b7568ea405d48d987ad70
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.5
|
Release files / nano_template-0.1.1-cp314-cp314t-macosx_11_0_arm64.whl
| Download URL | nano_template-0.1.1-cp314-cp314t-macosx_11_0_arm64.whl |
|---|---|
| Size | 35.9 kB |
| Tags | CPython 3.14 CPython 3.14 free-threading macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
c7b1f380c48c031ef05c8d09636f408ec3a50b036d8a4b3c8fbe3d3b39be6154
|
|
BLAKE2b-256 checksum How to use checksums |
59a429ad3fac4709531716e3d2b4cc6a20e07bc01bc8b129701d08796eb70c25
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.5
|
Release files / nano_template-0.1.1-cp314-cp314t-macosx_10_15_x86_64.whl
| Download URL | nano_template-0.1.1-cp314-cp314t-macosx_10_15_x86_64.whl |
|---|---|
| Size | 35.8 kB |
| Tags | CPython 3.14 CPython 3.14 free-threading macOS 10.15+ x86-64 |
|
SHA-256 checksum How to use checksums |
dc0ebee681324244b467bc4837639f03f2f27fd2e425ba2adb5ca213cea8fe7f
|
|
BLAKE2b-256 checksum How to use checksums |
a34c6c5d2259cb4fa4c3244018ce9065f2ba641b0ffe28a81c85c609b10c3801
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.5
|
Release files / nano_template-0.1.1-cp39-abi3-win_arm64.whl
| Download URL | nano_template-0.1.1-cp39-abi3-win_arm64.whl |
|---|---|
| Size | 33.9 kB |
| Tags | CPython 3.9 Windows ARM64 abi3 |
|
SHA-256 checksum How to use checksums |
b143c78fb5c35bafb346fcdd3eaaf5740ddf21a41971b93dd2deaaf68f650d97
|
|
BLAKE2b-256 checksum How to use checksums |
f53e3e6cd71071c842627e5318d8050615b0e79e27d4b38861252656b7aa8088
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.5
|
Release files / nano_template-0.1.1-cp39-abi3-win_amd64.whl
| Download URL | nano_template-0.1.1-cp39-abi3-win_amd64.whl |
|---|---|
| Size | 35.5 kB |
| Tags | CPython 3.9 Windows x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
8bc905e11d9c8cb9343b4ce5db20562d0b0b631346b3a62aaf910788cd6014fd
|
|
BLAKE2b-256 checksum How to use checksums |
95392018e60083653df78c6ff4f23e803219edfde290418fe9dc9995c75bb82b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.5
|
Release files / nano_template-0.1.1-cp39-abi3-win32.whl
| Download URL | nano_template-0.1.1-cp39-abi3-win32.whl |
|---|---|
| Size | 32.5 kB |
| Tags | CPython 3.9 Windows x86-32 abi3 |
|
SHA-256 checksum How to use checksums |
9ad016097776114a2c0170f2cb30f0cf68caf2ffc1b05a06fd99cfba7dd3c864
|
|
BLAKE2b-256 checksum How to use checksums |
97d52e0ba4eea97d6eef55c0a7ebf2ab68159fb6c370054cd7014aac9fe8ddca
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.5
|
Release files / nano_template-0.1.1-cp39-abi3-musllinux_1_2_x86_64.whl
| Download URL | nano_template-0.1.1-cp39-abi3-musllinux_1_2_x86_64.whl |
|---|---|
| Size | 109.8 kB |
| Tags | CPython 3.9 Linux musl 1.2+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
ced2a53b3031c3d015580de6d66814304b491b951ff55584807a3ce35d3d8d5d
|
|
BLAKE2b-256 checksum How to use checksums |
961007365eeee8571b5198c5a839a7dd76f165e0bc9c45e7fad9a43936eb5f0c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.5
|
Release files / nano_template-0.1.1-cp39-abi3-musllinux_1_2_aarch64.whl
| Download URL | nano_template-0.1.1-cp39-abi3-musllinux_1_2_aarch64.whl |
|---|---|
| Size | 110.5 kB |
| Tags | CPython 3.9 Linux musl 1.2+ ARM64 abi3 |
|
SHA-256 checksum How to use checksums |
399f92a787f752b1fdfce39036cd24db22dab55daf7af2fe45ced8193553da26
|
|
BLAKE2b-256 checksum How to use checksums |
36685a8ed20fa6cc469f4d89110a808f07744a66538b0d3d1a94d164b181166b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.5
|
Release files / nano_template-0.1.1-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl
| Download URL | nano_template-0.1.1-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl |
|---|---|
| Size | 113.3 kB |
| Tags | CPython 3.9 Linux glibc 2.17+ ARM64 Linux glibc 2.28+ ARM64 abi3 |
|
SHA-256 checksum How to use checksums |
d41be82514d36c1b281de2922d1c650faaf175818b6288cb227ddbea9026c4b4
|
|
BLAKE2b-256 checksum How to use checksums |
f66bfe4efc39ba720ee12b6d049904cf97ffe1c4ff3d53a1ec006cc88a1bb530
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.5
|
Release files / nano_template-0.1.1-cp39-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl
| Download URL | nano_template-0.1.1-cp39-abi3-manylinux1_x86_64.manylinux_2_28_x86_64.manylinux_2_5_x86_64.whl |
|---|---|
| Size | 111.5 kB |
| Tags | CPython 3.9 Linux glibc 2.28+ x86-64 Linux glibc 2.5+ x86-64 abi3 |
|
SHA-256 checksum How to use checksums |
e0fb2b71d1d554dfe224bc1ae5ff2520caeefec0f7ea798768007d4e4001e03d
|
|
BLAKE2b-256 checksum How to use checksums |
0e79f224fde0a5f8b29b9ee2fd7f77e75f6d88477362a9b4769e8f4b7fc34c0a
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.5
|
Release files / nano_template-0.1.1-cp39-abi3-macosx_11_0_arm64.whl
| Download URL | nano_template-0.1.1-cp39-abi3-macosx_11_0_arm64.whl |
|---|---|
| Size | 34.5 kB |
| Tags | CPython 3.9 abi3 macOS 11.0+ ARM64 |
|
SHA-256 checksum How to use checksums |
c2bd8e2b1ee938170a5f80e0c5f1ed3c9950d446fdc8f7241d89f54c713e0a1c
|
|
BLAKE2b-256 checksum How to use checksums |
31f76827f83da6611dad436f6173b46e414efbf2af1251e48907479662d32dec
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.5
|
Release files / nano_template-0.1.1-cp39-abi3-macosx_10_9_x86_64.whl
| Download URL | nano_template-0.1.1-cp39-abi3-macosx_10_9_x86_64.whl |
|---|---|
| Size | 34.3 kB |
| Tags | CPython 3.9 abi3 macOS 10.9+ x86-64 |
|
SHA-256 checksum How to use checksums |
5cfec1fba73b13f57c664ef5d5b5d4fefe6e3698937c772691dacd8c34a80d6a
|
|
BLAKE2b-256 checksum How to use checksums |
f26e4aa47e84db0f4cf78d199e2f65db771b248630e8040659558c5e36fc99e8
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
twine/6.2.0 CPython/3.13.5
|
Security for nano-template 0.1.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.