Safeguard
Developer Tools•

What do ~=, == and >= mean in a Python requirement?

A plain guide to Python version specifiers: what compatible release, exact match and minimum version allow, and which one to use in a project you ship.

Safeguard Security Team
4 min read

In a Python requirement, == asks for one exact version, >= accepts that version or anything newer, and ~= (compatible release) accepts newer versions that are expected to stay compatible. For example, ~=2.2 means at least 2.2 but below 3.0. Only an exact match gives the same install every time.

The operators in plain words

Python packaging defines these operators in the version specifiers specification. Here is what each one allows.

Written asMeansExample match
==1.4.2exactly this version1.4.2 only
==1.4.*any release that starts with 1.41.4.0, 1.4.9
>=1.4this version or newer1.4, 2.0, 9.9
~=2.2at least 2.2 and below 3.02.2, 2.7
!=1.5anything except this version1.4, 1.6

The specification describes the compatible release operator as approximately equivalent to a pair of clauses. Its own example says ~=2.2 is the same as >=2.2 together with ==2.*. It also describes a rarely needed triple equals form as a plain string comparison and says its use is heavily discouraged.

Which one should you write

It depends on what the file is for. A package other people install should declare loose, honest ranges, because a library that pins an exact version of everything makes it hard for anyone to combine it with other code. A deployed application wants the opposite: exact versions for every package, including the ones you never chose directly.

That is why two files usually exist. The ranges live in your project metadata. The exact versions live in a lock file or a pinned requirements file generated from those ranges.

Why a minimum version is not a pin

A requirement such as requests>=2.0 is satisfied by whatever is newest the day you run the install. Two builds a week apart can pull different code. If a new release is broken, or worse, malicious, the next fresh install picks it up without anyone editing a file.

A compatible release range narrows that window but does not close it. A new patch release still arrives automatically. Pinning with == plus a hash check closes it, at the cost of having to update on purpose. See pip constraints files vs requirements files for a way to pin transitive packages without listing them as direct dependencies.

A quick way to audit a file

  1. Open your requirements file or project dependency list.
  2. Mark every line that is not an exact match.
  3. For an application, replace those lines by generating a fully pinned file.
  4. For a library, keep ranges, but set a lower bound you have tested and an upper bound only where you know of a break.

Try it yourself

Click through it at your own pace, or open it full screen. Look at how an inventory records the resolved version of each component, not the range you wrote.

Safeguard's SBOM and dependency scanning reads the versions that actually resolve, so a loose range does not hide what shipped. You can try a free scan on your own manifest.

Quick check

Is ~=1.4.2 the same as ==1.4.2?

No. It allows 1.4.3 and later patch releases, up to but not including 1.5.0.

Should a library pin exact versions?

Generally no. Libraries declare ranges so users can combine them with other packages. Applications pin.

Sources

Last reviewed: 19 October 2026.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.

Self-healing security runs on Safeguard.

Your first fix PR is minutes away.

No sales call required, even your agent can complete the purchase over MCP.