10aday › tools

Version Constraint Checker: npm, Cargo, Composer, pip & RubyGems compared

Type a version constraint such as ~1.2, ^0.3.1, ~> 2.1, ~=1.4.2 or >=1.2, <2. You see how each package manager reads it and which versions it accepts. The same string often means different things: ~1.2 stops before 1.3.0 in npm and Cargo, but allows every 1.x release in Composer.

Link to this result · Everything runs in your browser; nothing is sent anywhere.

Pre-releases: each tool has its own rule

ToolWhat this page shows for a pre-release such as 1.3.0-rc.1
npmA pre-release matches only if one comparator in the same range names the same major.minor.patch with a pre-release tag. For example, ^1.2.3-beta accepts 1.2.3-rc.1 but not 1.3.0-rc.1. Option includePrerelease is off.
CargoSame idea as npm (Rust crate semver): pre-releases are rejected unless a comparator names one on the same major.minor.patch.
ComposerThe constraint itself includes pre-releases: ^1.2 covers 1.3.0-RC1. At install time, minimum-stability (default stable) filters them out unless you lower it or add a flag such as @RC. This page shows the constraint check only.
pipThis page shows PEP 440 range membership with pre-releases allowed. pip itself installs a pre-release only with --pre, when the specifier names a pre-release, or when no final release matches. Note that <2.0 never matches 2.0.0a1.
RubyGemsPre-releases (1.3.0.rc1, or 1.3.0-rc.1 read as 1.3.0.pre.rc.1) sort before the release and are compared normally. gem install and Bundler only pick them when you ask for pre-releases.

What actually gets installed, tested with each real tool: pre-release versions in five package managers.

Cheat sheet: the same syntax in five package managers

Generated from the official implementations (node-semver 7.8.5, Rust semver 1.0.28, composer/semver 3.6.0, Python packaging 26.3, RubyGems 3.5.22). "invalid" means the tool rejects the constraint.

ConstraintnpmCargoComposerpipRubyGems
1.2.3 (bare)exactly 1.2.3^1.2.3 (≥1.2.3 <2.0.0)exactly 1.2.3invalid (needs ==)exactly 1.2.3
1.2 (bare)1.2.x (≥1.2.0 <1.3.0)^1.2 (≥1.2.0 <2.0.0)exactly 1.2.0invalidexactly 1.2 (= 1.2.0)
~1.2.3≥1.2.3 <1.3.0≥1.2.3 <1.3.0≥1.2.3 <1.3.0invalidinvalid
~1.2≥1.2.0 <1.3.0≥1.2.0 <1.3.0≥1.2.0 <2.0.0invalidinvalid
^1.2.3≥1.2.3 <2.0.0≥1.2.3 <2.0.0≥1.2.3 <2.0.0invalidinvalid
^0.2.3≥0.2.3 <0.3.0≥0.2.3 <0.3.0≥0.2.3 <0.3.0invalidinvalid
^0.0.3≥0.0.3 <0.0.4≥0.0.3 <0.0.4≥0.0.3 <0.0.4invalidinvalid
~> 1.2read as ~1.2: ≥1.2.0 <1.3.0invalidinvalidinvalid≥1.2 <2.0
~> 1.2.3≥1.2.3 <1.3.0invalidinvalidinvalid≥1.2.3 <1.3
~=1.4.2read as ~1.4.2: ≥1.4.2 <1.5.0invalidinvalid≥1.4.2, ==1.4.*invalid
~=1.4read as ~1.4: ≥1.4.0 <1.5.0invalidinvalid≥1.4, ==1.* (<2.0)invalid
1.*≥1.0.0 <2.0.0≥1.0.0 <2.0.0≥1.0.0 <2.0.0invalid (write ==1.*)invalid
1.x≥1.0.0 <2.0.0≥1.0.0 <2.0.0≥1.0.0 <2.0.0invalidthe literal version "1.x"
*anyanyanyinvalid (empty = any)invalid
>=1.2, <2 (comma)invalidANDANDANDAND (separate Gemfile arguments)
>=1.2 <2 (space)ANDinvalidANDinvalidinvalid
^1.2 || ^2ORinvalidORinvalidinvalid
1.2 - 2.3.4≥1.2.0 ≤2.3.4invalid≥1.2.0 ≤2.3.4invalidinvalid
==1.2.3invalidinvalidexactly 1.2.3exactly 1.2.3invalid
!=1.2.3invalidinvalidnot 1.2.3not 1.2.3not 1.2.3

Common questions

What is the difference between tilde (~) and caret (^) in npm?

~1.2.3 allows patch updates only (≥1.2.3 <1.3.0). ^1.2.3 allows minor and patch updates (≥1.2.3 <2.0.0). Below 1.0.0 the caret is stricter: ^0.2.3 means ≥0.2.3 <0.3.0, and ^0.0.3 means exactly the 0.0.3 line. Cargo reads both operators the same way.

What does tilde mean in Composer?

Composer's tilde lets the last number you wrote move: ~1.2 means ≥1.2 <2.0, while ~1.2.3 means ≥1.2.3 <1.3.0. Coming from npm, ~1.2 is the classic surprise.

What does ~> (pessimistic operator) mean in a Gemfile?

~> 2.1 means ≥2.1 and <3.0, and ~> 2.1.0 means ≥2.1.0 and <2.2. RubyGems drops the last number you wrote and bumps the one before it.

What does ~= (compatible release) mean in pip?

~=1.4.2 means ≥1.4.2 and ==1.4.*. ~=1.4 means ≥1.4 and ==1.* (any 1.x from 1.4). It works like Ruby's ~>. npm also accepts ~=, but reads it as its own tilde.

Guides

Sources