mrkeyoor.com_
Wed 23 Sept 00:34 UTC
PyPIUtilsupdated 22 Sept 2026

qrcode review

qrcode 8.2 is a Python encoder for standard QR Codes. Give it text or bytes and it builds the module grid, then hands that grid to a Pillow, PyPNG, SVG, terminal, or custom image factory. It does not scan photos or decode existing symbols. Release 8.2 made color-mask rendering faster and added the correctly spelled `embedded_image` and `embedded_image_path` arguments while retaining the old misspellings for compatibility. Our Python 3.12 install was a 1 MB pure-Python package that imported in 0.09 seconds; static typing remains informal because the distribution has no `py.typed` marker.

Verdict

qrcode 8.2 installed in 0.2 seconds, took 1 MB, and imported in 0.09 seconds in our sandbox; pip-audit found 0 known vulnerabilities, so standard QR generation adds little installation cost. Install it for controlled PNG, SVG, terminal, or matrix output; choose something else for decoding, Micro QR, strict declared typing, or decorative codes that nobody can scan-test.

We installed it

Lab card: what happened when we installed qrcodeScreenshot of qrcode documentation
Install✓ · 0.2s1 package on disk · 1 MB
Importimport qrcode in 0.09s · pure Python · requires Python >=3.9,<4.0
Known vulns0(pip-audit)

Answers from our run

Does qrcode install cleanly?

Yes. In a fresh container with an empty cache, pip install qrcode finished in 0.2s, leaving 1 package and 1 MB on disk. pip-audit reported no known vulnerabilities.

What does qrcode need to run?

Python >=3.9,<4.0, and nothing compiled: it is pure Python. In our run import qrcode succeeded in 0.09s.

qrcode or segno: which should you use?

segno: Micro QR support or standards-focused QR serialization is required. qrcode 8.2 installed in 0.2 seconds, took 1 MB, and imported in 0.09 seconds in our sandbox; pip-audit found 0 known vulnerabilities, so standard QR generation adds little installation cost.

When should you not use qrcode?

The input is a camera frame or an uploaded code that must be decoded. qrcode contains an encoder and image factories, with no reader API.

API stability4/5qrcode 8.2 retains the long-used `QRCode`, `add_data()`, `make()`, `make_image()`, factory, and `qr` command interfaces. Its only named compatibility change in the 8.2 notes corrects `embeded_image` and `embeded_image_path`; both old spellings still work for now. The main-branch changelog schedules those aliases for removal in version 9, so callers have a full released version in which to update keyword arguments.
Docs4/5The version 8.2 README explains the 1-to-40 size range, 4 error-correction choices, 4-module quiet zone, SVG factories, terminal output, Pillow styling, and the scan risk of altered modules. Its installation section is the weak spot. It says the standard package uses PyPNG, while the published metadata marks PyPNG and Pillow as optional extras. A reader must cross-check PyPI or test image creation to catch that difference.
Maintenance3/5Version 8.2 was published on May 1, 2025, and GitHub records the latest repository push on March 25, 2026. The project is unarchived, has 4,939 stars, 41 open issues, and 14 open pull requests. Work continued after the release, including merged fixes for `clear()`, thread safety, mask evaluation, invalid inline SVG markup, and zero-heavy data. None has reached a newer PyPI version, which lowers the score.
Ecosystem4/5qrcode receives roughly 7.0 million downloads in a week and has 4,939 GitHub stars. Its documented factories cover Pillow images, PyPNG output, full or fragment SVG, terminal characters, and a boolean matrix that a label printer or custom canvas can consume. Those paths cover common Python generation jobs. Reading codes, producing Micro QR, and supporting non-QR barcode families still require separate packages.

Use it if

  • Your Python job turns URLs, ticket references, Wi-Fi credentials, or short byte payloads into standard QR images.
  • You need to choose the error-correction level, quiet-zone width, pixel size, or a fixed QR version from 1 through 40.
  • The same encoder must feed PNG files, SVG markup, terminal output, or a printer-specific renderer built from the boolean matrix.
  • You want rounded modules, color masks, or a centered logo and can scan-test every design on the devices your users carry.
Skip it if

Setup reality

We installed qrcode 8.2 without a cache in an unprivileged Python 3.12 Bookworm container with 3 CPUs and 8 GB of RAM. Installation finished in 0.2 seconds and left 1 package occupying 1 MB. pip-audit found 0 known vulnerabilities, while import qrcode completed in 0.09 seconds. The distribution is pure Python, has 3 direct dependencies, requires Python >=3.9,<4.0, uses the BSD license, and omits py.typed.

There are no credentials or project config files. Image output is the part to settle before deployment. For qrcode 8.2, PyPI lists Pillow under the pil extra and PyPNG under png; all installs both. This conflicts with the README's claim that a standard install uses PyPNG. Install qrcode[pil] or qrcode[png] deliberately, then exercise the exact save path in CI. A successful import only proves that the encoder module loads.

Automatic sizing starts at QR version 1 and can grow through version 40 when version=None and fit=True. A fixed version with fit=False raises DataOverflowError when its payload will not fit. More error correction reduces available payload space. Keep the border at the documented minimum of 4 modules unless the downstream specification says otherwise. The constructor accepts smaller borders, so it will not enforce that quiet-zone rule for you.

PyPI's 8.2 code has a few fixes waiting on the main branch. clear() empties the data yet keeps the resolved version, so a reused encoder may stay larger than its next payload needs. The unreleased 8.x changelog also lists patches for shared bisect_left thread safety, full-symbol mask evaluation, and a glog(0) error on some zero-heavy bytes. Create a fresh QRCode per job, avoid sharing instances across threads, and regression-test binary payloads that contain long zero runs.

Patterns

Turn a URL into a PNG create-png

import qrcode

code = qrcode.make("https://example.com/check-in/8F3A")
code.save("check-in.png")

PNG rendering needs an installed image backend. Add `qrcode[pil]` for this Pillow-backed shortcut and test `save()` in the deployed environment.

Build a PNG response in memory return-png-bytes

from io import BytesIO

import qrcode

buffer = BytesIO()
qrcode.make(receipt_url).save(buffer, format="PNG")
png_bytes = buffer.getvalue()

Pillow can write to `BytesIO`, so a web handler does not need a temporary file. Install the `pil` extra before using this path.

Let the payload choose the matrix size fit-qr-version

import qrcode
from qrcode.constants import ERROR_CORRECT_Q

code = qrcode.QRCode(
    version=None,
    error_correction=ERROR_CORRECT_Q,
    box_size=8,
    border=4,
)
code.add_data(invitation_url)
code.make(fit=True)
image = code.make_image(fill_color="#12263a", back_color="white")

With `version=None`, `fit=True` searches standard QR versions 1 through 40. The 4-module border is the README's stated minimum.

Reject data that exceeds a fixed version detect-overflow

import qrcode
from qrcode.exceptions import DataOverflowError

code = qrcode.QRCode(version=4)
code.add_data(payload)
try:
    code.make(fit=False)
except DataOverflowError as error:
    raise ValueError("payload does not fit QR version 4") from error

`fit=False` preserves version 4 instead of silently selecting a larger matrix. A higher correction level leaves fewer bits for the payload.

Produce path-based SVG markup render-svg

import qrcode
from qrcode.image.svg import SvgPathImage

svg = qrcode.make(
    documentation_url,
    image_factory=SvgPathImage,
)
markup = svg.to_string(encoding="unicode")

`SvgPathImage` draws the dark modules as one path. The version 8.2 README recommends it to avoid white seams when an SVG is enlarged.

Capture a QR Code as terminal text print-terminal-code

from io import StringIO

import qrcode

code = qrcode.QRCode()
code.add_data("otpauth://totp/Example:alex")
text = StringIO()
code.print_ascii(out=text, invert=True)
terminal_code = text.getvalue()

`print_ascii()` does not need Pillow or PyPNG. The `invert` setting must match the terminal's foreground and background colors.

Draw rounded modules round-module-shapes

import qrcode
from qrcode.image.styledpil import StyledPilImage
from qrcode.image.styles.moduledrawers.pil import RoundedModuleDrawer

code = qrcode.QRCode(error_correction=qrcode.constants.ERROR_CORRECT_Q)
code.add_data(campaign_url)
image = code.make_image(
    image_factory=StyledPilImage,
    module_drawer=RoundedModuleDrawer(),
)

Version 8.2 keeps Pillow drawers under `moduledrawers.pil`. The README says styled codes are not guaranteed to work with every reader.

Apply the radial color mask apply-color-mask

from qrcode.image.styledpil import StyledPilImage
from qrcode.image.styles.colormasks import RadialGradiantColorMask

image = code.make_image(
    image_factory=StyledPilImage,
    color_mask=RadialGradiantColorMask(),
)

The public class in 8.2 is spelled `RadialGradiantColorMask`. Release 8.2 speeds up color-mask processing but does not certify the result for scanners.

Place a logo over the center embed-logo

import qrcode
from qrcode.constants import ERROR_CORRECT_H
from qrcode.image.styledpil import StyledPilImage

code = qrcode.QRCode(error_correction=ERROR_CORRECT_H)
code.add_data(landing_page)
code.make(fit=True)
image = code.make_image(
    image_factory=StyledPilImage,
    embedded_image_path="brand-mark.png",
)

qrcode 8 requires `ERROR_CORRECT_H` when an image covers the center. Version 8.2 accepts the correctly spelled `embedded_image_path` keyword.

Keep a byte payload in byte mode encode-raw-bytes

import qrcode

payload = bytes.fromhex("11aa22bb33cc44dd")
code = qrcode.QRCode(version=None)
code.add_data(payload, optimize=0)
code.make(fit=True)
matrix = code.get_matrix()

`optimize=0` stops qrcode from splitting the bytes into shorter mode-specific chunks. Version 8.2 can raise `glog(0)` on some zero-heavy binary inputs; the fix is merged after the release.

Pass the module grid to another renderer draw-custom-output

import qrcode

code = qrcode.QRCode(border=4)
code.add_data(printer_payload)
code.make(fit=True)

for row_number, row in enumerate(code.get_matrix()):
    printer.draw_row(row_number, row)

`get_matrix()` includes the configured border. With `border=4`, preserve all 4 blank modules around the symbol in the final output.

Generate output from the shell use-qr-command

qr --output=badge.png "employee:1042"
qr --factory=svg-path "https://example.com/help" > help.svg
qr --ascii "terminal payload" > code.txt

The `qr` command accepts `--output` for files and factory names for SVG or PNG. On PowerShell, use `--output` for binary PNG instead of redirection.

Alternatives

PackageRegistryPick it when
segnoPyPIPick it when Micro QR support or standards-focused QR serialization is required.
PyQRCodePyPIKeep it for an older codebase already tied to its SVG, EPS, PNG, and terminal methods.
treepoemPyPIUse it when one renderer must cover QR, Aztec, PDF417, Code 128, and many other barcode types.
pyzbarPyPIChoose it when the actual job is reading QR and one-dimensional barcodes from images.

More utils guides

lru-cache · ajv · type-fest · p-limit · find-up · js-yaml · the whole shelf →

How this guide is made: grounded in the library's documentation, release notes, changelog, and issue history, on a fixed rubric — not a hands-on install of every release. The 50 most-downloaded entries are additionally install-verified in clean containers. Corrections: contact the desk.