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 as | Means | Example match |
|---|---|---|
| ==1.4.2 | exactly this version | 1.4.2 only |
| ==1.4.* | any release that starts with 1.4 | 1.4.0, 1.4.9 |
| >=1.4 | this version or newer | 1.4, 2.0, 9.9 |
| ~=2.2 | at least 2.2 and below 3.0 | 2.2, 2.7 |
| !=1.5 | anything except this version | 1.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
- Open your requirements file or project dependency list.
- Mark every line that is not an exact match.
- For an application, replace those lines by generating a fully pinned file.
- 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.