diff --git a/lib/datasource/common.ts b/lib/datasource/common.ts
index ac486d3a49adc1b4820e888328923f2480622954..30ff87a415828b1ac1c3fc1d3d8dee879063c093 100644
--- a/lib/datasource/common.ts
+++ b/lib/datasource/common.ts
@@ -10,7 +10,6 @@ export interface DigestConfig extends Config {
 }
 
 interface ReleasesConfigBase {
-  constraints?: Record<string, string>;
   npmrc?: string;
   registryUrls?: string[];
   trustLevel?: 'low' | 'high';
@@ -27,6 +26,7 @@ export interface GetPkgReleasesConfig extends ReleasesConfigBase {
   lookupName?: string;
   versioning?: string;
   extractVersion?: string;
+  constraints?: Record<string, string>;
 }
 
 export function isGetPkgReleasesConfig(
@@ -48,6 +48,7 @@ export interface Release {
   releaseTimestamp?: any;
   version: string;
   newDigest?: string;
+  constraints?: Record<string, string[]>;
 }
 
 export interface ReleaseResult {
diff --git a/lib/datasource/index.ts b/lib/datasource/index.ts
index 222d41998259de0d4e701594834ce59443c1a5c8..875f22aad9b6ac810c7028d106c5f09536c62da7 100644
--- a/lib/datasource/index.ts
+++ b/lib/datasource/index.ts
@@ -298,6 +298,31 @@ export async function getPkgReleases(
         (findRelease) => findRelease.version === filterRelease.version
       ) === filterIndex
   );
+  // Filter releases for compatibility
+  for (const [constraintName, constraintValue] of Object.entries(
+    config.constraints || {}
+  )) {
+    // Currently we only support if the constraint is a plain version
+    // TODO: Support range/range compatibility filtering #8476
+    if (version.isVersion(constraintValue)) {
+      res.releases = res.releases.filter((release) => {
+        if (!is.nonEmptyArray(release.constraints?.[constraintName])) {
+          // A release with no constraints is OK
+          return true;
+        }
+        return release.constraints[constraintName].some(
+          // If any of the release's constraints match, then it's OK
+          (releaseConstraint) =>
+            !releaseConstraint ||
+            version.matches(constraintValue, releaseConstraint)
+        );
+      });
+    }
+  }
+  // Strip constraints from releases result
+  res.releases.forEach((release) => {
+    delete release.constraints; // eslint-disable-line no-param-reassign
+  });
   return res;
 }
 
diff --git a/lib/datasource/pypi/__snapshots__/index.spec.ts.snap b/lib/datasource/pypi/__snapshots__/index.spec.ts.snap
index ab246638c85cad433488192998a5dfb16b2f50ae..d7e84928680f3e3f761d45542a27ca98b71ccdfc 100644
--- a/lib/datasource/pypi/__snapshots__/index.spec.ts.snap
+++ b/lib/datasource/pypi/__snapshots__/index.spec.ts.snap
@@ -371,6 +371,9 @@ Object {
     Object {
       "version": "0.4.0",
     },
+    Object {
+      "version": "0.4.1",
+    },
     Object {
       "version": "0.30.3",
     },
diff --git a/lib/datasource/pypi/index.spec.ts b/lib/datasource/pypi/index.spec.ts
index 31e23adb33291b8f39d9e7516cae7a9d694d1315..7e17e21ff568b99745ba8a9e93acee03c3a63cfc 100644
--- a/lib/datasource/pypi/index.spec.ts
+++ b/lib/datasource/pypi/index.spec.ts
@@ -200,6 +200,7 @@ describe('datasource/pypi', () => {
             ],
             '0.31.1': [{ requires_python: '>=3.4' }],
             '0.4.0': [{ requires_python: '>=3.4' }, { requires_python: null }],
+            '0.4.1': [],
           },
         });
       expect(
diff --git a/lib/datasource/pypi/index.ts b/lib/datasource/pypi/index.ts
index 11e7b3bee1c2ebbea7650bb8ca50f342a17594c7..8bfcb2a08d04a98424df8a2df3d5a4e1eb934ca6 100644
--- a/lib/datasource/pypi/index.ts
+++ b/lib/datasource/pypi/index.ts
@@ -37,28 +37,9 @@ function normalizeName(input: string): string {
   return input.toLowerCase().replace(/(-|\.)/g, '_');
 }
 
-function compatibleVersions(
-  releases: Releases,
-  constraints: Record<string, string>
-): string[] {
-  const versions = Object.keys(releases);
-  if (!(constraints?.python && pep440.isVersion(constraints.python))) {
-    return versions;
-  }
-  return versions.filter((version) =>
-    releases[version].some((release) => {
-      if (!release.requires_python) {
-        return true;
-      }
-      return pep440.matches(constraints.python, release.requires_python);
-    })
-  );
-}
-
 async function getDependency(
   packageName: string,
-  hostUrl: string,
-  constraints: Record<string, string>
+  hostUrl: string
 ): Promise<ReleaseResult | null> {
   const lookupUrl = url.resolve(hostUrl, `${packageName}/json`);
   const dependency: ReleaseResult = { releases: null };
@@ -121,7 +102,7 @@ async function getDependency(
 
   dependency.releases = [];
   if (dep.releases) {
-    const versions = compatibleVersions(dep.releases, constraints);
+    const versions = Object.keys(dep.releases);
     dependency.releases = versions.map((version) => {
       const releases = dep.releases[version] || [];
       const { upload_time: releaseTimestamp } = releases[0] || {};
@@ -133,6 +114,10 @@ async function getDependency(
       if (isDeprecated) {
         result.isDeprecated = isDeprecated;
       }
+      // There may be multiple releases with different requires_python, so we return all in an array
+      result.constraints = {
+        python: releases.map(({ requires_python }) => requires_python),
+      };
       return result;
     });
   }
@@ -184,8 +169,7 @@ function cleanSimpleHtml(html: string): string {
 
 async function getSimpleDependency(
   packageName: string,
-  hostUrl: string,
-  constraints: Record<string, string>
+  hostUrl: string
 ): Promise<ReleaseResult | null> {
   const lookupUrl = url.resolve(hostUrl, `${packageName}`);
   const dependency: ReleaseResult = { releases: null };
@@ -214,7 +198,7 @@ async function getSimpleDependency(
       releases[version].push(release);
     }
   }
-  const versions = compatibleVersions(releases, constraints);
+  const versions = Object.keys(releases);
   dependency.releases = versions.map((version) => {
     const versionReleases = releases[version] || [];
     const isDeprecated = versionReleases.some(({ yanked }) => yanked);
@@ -222,13 +206,16 @@ async function getSimpleDependency(
     if (isDeprecated) {
       result.isDeprecated = isDeprecated;
     }
+    // There may be multiple releases with different requires_python, so we return all in an array
+    result.constraints = {
+      python: versionReleases.map(({ requires_python }) => requires_python),
+    };
     return result;
   });
   return dependency;
 }
 
 export async function getReleases({
-  constraints,
   lookupName,
   registryUrl,
 }: GetReleasesConfig): Promise<ReleaseResult | null> {
@@ -238,12 +225,12 @@ export async function getReleases({
   // not all simple indexes use this identifier, but most do
   if (hostUrl.endsWith('/simple/') || hostUrl.endsWith('/+simple/')) {
     logger.trace({ lookupName, hostUrl }, 'Looking up pypi simple dependency');
-    dependency = await getSimpleDependency(lookupName, hostUrl, constraints);
+    dependency = await getSimpleDependency(lookupName, hostUrl);
   } else {
     logger.trace({ lookupName, hostUrl }, 'Looking up pypi api dependency');
     try {
       // we need to resolve early here so we can catch any 404s and fallback to a simple lookup
-      dependency = await getDependency(lookupName, hostUrl, constraints);
+      dependency = await getDependency(lookupName, hostUrl);
     } catch (err) {
       if (err.statusCode !== 404) {
         throw err;
@@ -254,7 +241,7 @@ export async function getReleases({
         { lookupName, hostUrl },
         'Looking up pypi simple dependency via fallback'
       );
-      dependency = await getSimpleDependency(lookupName, hostUrl, constraints);
+      dependency = await getSimpleDependency(lookupName, hostUrl);
     }
   }
   return dependency;