diff --git a/lib/manager/pip_requirements/artifacts.spec.ts b/lib/manager/pip_requirements/artifacts.spec.ts
new file mode 100644
index 0000000000000000000000000000000000000000..573e158ea568058a3dea7d0abaff429618b40db1
--- /dev/null
+++ b/lib/manager/pip_requirements/artifacts.spec.ts
@@ -0,0 +1,64 @@
+import _fs from 'fs-extra';
+import { updateArtifacts } from './artifacts';
+
+const fs: jest.Mocked<typeof _fs> = _fs as any;
+
+jest.mock('fs-extra');
+jest.mock('child_process');
+jest.mock('../../util/exec');
+
+const config = {};
+
+const newPackageFileContent = `atomicwrites==1.4.0 \
+--hash=sha256:03472c30eb2c5d1ba9227e4c2ca66ab8287fbfbbda3888aa93dc2e28fc6811b4 \
+--hash=sha256:75a9445bac02d8d058d5e1fe689654ba5a6556a1dfd8ce6ec55a0ed79866cfa6`;
+
+describe('.updateArtifacts()', () => {
+  beforeEach(() => {
+    jest.resetAllMocks();
+    jest.resetModules();
+  });
+  it('returns null if no updatedDeps were provided', async () => {
+    expect(
+      await updateArtifacts({
+        packageFileName: 'requirements.txt',
+        updatedDeps: [],
+        newPackageFileContent,
+        config,
+      })
+    ).toBeNull();
+  });
+  it('returns null if unchanged', async () => {
+    fs.readFile.mockResolvedValueOnce(newPackageFileContent as any);
+    expect(
+      await updateArtifacts({
+        packageFileName: 'requirements.txt',
+        updatedDeps: ['atomicwrites'],
+        newPackageFileContent,
+        config,
+      })
+    ).toBeNull();
+  });
+  it('returns updated file', async () => {
+    fs.readFile.mockResolvedValueOnce('new content' as any);
+    expect(
+      await updateArtifacts({
+        packageFileName: 'requirements.txt',
+        updatedDeps: ['atomicwrites'],
+        newPackageFileContent,
+        config,
+      })
+    ).toHaveLength(1);
+  });
+  it('catches and returns errors', async () => {
+    fs.readFile.mockResolvedValueOnce('new content' as any);
+    expect(
+      await updateArtifacts({
+        packageFileName: null,
+        updatedDeps: ['atomicwrites'],
+        newPackageFileContent,
+        config,
+      })
+    ).toHaveLength(1);
+  });
+});
diff --git a/lib/manager/pip_requirements/artifacts.ts b/lib/manager/pip_requirements/artifacts.ts
new file mode 100644
index 0000000000000000000000000000000000000000..3ee8206455e0d4f4173d7ecf499eb91fea19320b
--- /dev/null
+++ b/lib/manager/pip_requirements/artifacts.ts
@@ -0,0 +1,65 @@
+import is from '@sindresorhus/is';
+import { logger } from '../../logger';
+import { ExecOptions, exec } from '../../util/exec';
+import { readLocalFile } from '../../util/fs';
+import { UpdateArtifact, UpdateArtifactsResult } from '../common';
+
+export async function updateArtifacts({
+  packageFileName,
+  updatedDeps,
+  newPackageFileContent,
+  config,
+}: UpdateArtifact): Promise<UpdateArtifactsResult[] | null> {
+  logger.debug(`pip_requirements.updateArtifacts(${packageFileName})`);
+  if (!is.nonEmptyArray(updatedDeps)) {
+    logger.debug('No updated pip_requirements deps - returning null');
+    return null;
+  }
+  try {
+    const cmd: string[] = [];
+    const rewrittenContent = newPackageFileContent.replace(/\\\n/g, '');
+    const lines = rewrittenContent.split('\n').map((line) => line.trim());
+    for (const dep of updatedDeps) {
+      const hashLine = lines.find(
+        (line) => line.startsWith(`${dep}==`) && line.includes('--hash=')
+      );
+      if (hashLine) {
+        const depConstraint = hashLine.split(' ')[0];
+        cmd.push(`hashin ${depConstraint} -r ${packageFileName}`);
+      }
+    }
+    const execOptions: ExecOptions = {
+      cwdFile: packageFileName,
+      docker: {
+        image: 'renovate/python',
+        tagScheme: 'pip_requirements',
+        preCommands: ['pip install hashin'],
+      },
+    };
+    await exec(cmd, execOptions);
+    const newContent = await readLocalFile(packageFileName, 'utf8');
+    if (newContent === newPackageFileContent) {
+      logger.debug(`${packageFileName} is unchanged`);
+      return null;
+    }
+    logger.debug(`Returning updated ${packageFileName}`);
+    return [
+      {
+        file: {
+          name: packageFileName,
+          contents: newContent,
+        },
+      },
+    ];
+  } catch (err) {
+    logger.debug({ err }, `Failed to update ${packageFileName} file`);
+    return [
+      {
+        artifactError: {
+          lockFile: packageFileName,
+          stderr: err.stdout + '\n' + err.stderr,
+        },
+      },
+    ];
+  }
+}
diff --git a/lib/manager/pip_requirements/extract.ts b/lib/manager/pip_requirements/extract.ts
index 7901a0d9437b7accc315835b008a855a70558d87..acde62f0b3486bf593f3c994c37a24b3e5fe35bf 100644
--- a/lib/manager/pip_requirements/extract.ts
+++ b/lib/manager/pip_requirements/extract.ts
@@ -57,7 +57,7 @@ export function extractPackageFile(
         dep.skipReason = SkipReason.Ignored;
       }
       regex.lastIndex = 0;
-      const matches = regex.exec(line);
+      const matches = regex.exec(line.split(' \\')[0]);
       if (!matches) {
         return null;
       }
diff --git a/lib/manager/pip_requirements/index.ts b/lib/manager/pip_requirements/index.ts
index 3b321a2fc92ff20714f4524711238150c69af460..b2c951aa37c73d56d8e0aeb2daddb53671ef39aa 100644
--- a/lib/manager/pip_requirements/index.ts
+++ b/lib/manager/pip_requirements/index.ts
@@ -1,5 +1,6 @@
 import { LANGUAGE_PYTHON } from '../../constants/languages';
 
+export { updateArtifacts } from './artifacts';
 export { extractPackageFile } from './extract';
 export { getRangeStrategy } from './range';