diff --git a/lib/modules/manager/pip-compile/__snapshots__/artifacts.spec.ts.snap b/lib/modules/manager/pip-compile/__snapshots__/artifacts.spec.ts.snap
deleted file mode 100644
index 04c3f4a1b394e20213c4820edd873150c397b000..0000000000000000000000000000000000000000
--- a/lib/modules/manager/pip-compile/__snapshots__/artifacts.spec.ts.snap
+++ /dev/null
@@ -1,73 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`modules/manager/pip-compile/artifacts returns null if unchanged 1`] = `
-Array [
-  Object {
-    "cmd": "pip-compile requirements.in",
-    "options": Object {
-      "cwd": "/tmp/github/some/repo",
-      "encoding": "utf-8",
-      "env": Object {
-        "HOME": "/home/user",
-        "HTTPS_PROXY": "https://example.com",
-        "HTTP_PROXY": "http://example.com",
-        "LANG": "en_US.UTF-8",
-        "LC_ALL": "en_US",
-        "NO_PROXY": "localhost",
-        "PATH": "/tmp/path",
-        "PIP_CACHE_DIR": "/tmp/renovate/cache/others/pip",
-      },
-      "maxBuffer": 10485760,
-      "timeout": 900000,
-    },
-  },
-]
-`;
-
-exports[`modules/manager/pip-compile/artifacts returns updated requirements.txt 1`] = `
-Array [
-  Object {
-    "cmd": "pip-compile requirements.in",
-    "options": Object {
-      "cwd": "/tmp/github/some/repo",
-      "encoding": "utf-8",
-      "env": Object {
-        "HOME": "/home/user",
-        "HTTPS_PROXY": "https://example.com",
-        "HTTP_PROXY": "http://example.com",
-        "LANG": "en_US.UTF-8",
-        "LC_ALL": "en_US",
-        "NO_PROXY": "localhost",
-        "PATH": "/tmp/path",
-        "PIP_CACHE_DIR": "/tmp/renovate/cache/others/pip",
-      },
-      "maxBuffer": 10485760,
-      "timeout": 900000,
-    },
-  },
-]
-`;
-
-exports[`modules/manager/pip-compile/artifacts returns updated requirements.txt when doing lockfile maintenance 1`] = `
-Array [
-  Object {
-    "cmd": "pip-compile requirements.in",
-    "options": Object {
-      "cwd": "/tmp/github/some/repo",
-      "encoding": "utf-8",
-      "env": Object {
-        "HOME": "/home/user",
-        "HTTPS_PROXY": "https://example.com",
-        "HTTP_PROXY": "http://example.com",
-        "LANG": "en_US.UTF-8",
-        "LC_ALL": "en_US",
-        "NO_PROXY": "localhost",
-        "PATH": "/tmp/path",
-        "PIP_CACHE_DIR": "/tmp/renovate/cache/others/pip",
-      },
-      "maxBuffer": 10485760,
-      "timeout": 900000,
-    },
-  },
-]
-`;
diff --git a/lib/modules/manager/pip-compile/artifacts.spec.ts b/lib/modules/manager/pip-compile/artifacts.spec.ts
index 7fe989fd81babf4f1a80798c432907a921ad9ec6..6076f30988a7d574b3f1c8884df24804ee0d53a6 100644
--- a/lib/modules/manager/pip-compile/artifacts.spec.ts
+++ b/lib/modules/manager/pip-compile/artifacts.spec.ts
@@ -1,8 +1,7 @@
-import _fs from 'fs-extra';
 import { join } from 'upath';
 import { envMock, mockExecAll } from '../../../../test/exec-util';
 import { Fixtures } from '../../../../test/fixtures';
-import { env, git } from '../../../../test/util';
+import { env, fs, git } from '../../../../test/util';
 import { GlobalConfig } from '../../../config/global';
 import type { RepoGlobalConfig } from '../../../config/types';
 import { logger } from '../../../logger';
@@ -12,14 +11,12 @@ import type { UpdateArtifactsConfig } from '../types';
 import { constructPipCompileCmd } from './artifacts';
 import { updateArtifacts } from '.';
 
-jest.mock('fs-extra');
 jest.mock('../../../util/exec/env');
+jest.mock('../../../util/fs');
 jest.mock('../../../util/git');
 jest.mock('../../../util/host-rules');
 jest.mock('../../../util/http');
 
-const fs: jest.Mocked<typeof _fs> = _fs as any;
-
 const adminConfig: RepoGlobalConfig = {
   // `join` fixes Windows CI
   localDir: join('/tmp/github/some/repo'),
@@ -45,6 +42,7 @@ describe('modules/manager/pip-compile/artifacts', () => {
   });
 
   it('returns if no requirements.txt found', async () => {
+    const execSnapshots = mockExecAll();
     expect(
       await updateArtifacts({
         packageFileName: 'requirements.in',
@@ -53,12 +51,13 @@ describe('modules/manager/pip-compile/artifacts', () => {
         config,
       })
     ).toBeNull();
+    expect(execSnapshots).toEqual([]);
   });
 
   it('returns null if unchanged', async () => {
-    fs.readFile.mockResolvedValueOnce('content' as any);
+    fs.readLocalFile.mockResolvedValueOnce('content');
     const execSnapshots = mockExecAll();
-    fs.readFile.mockResolvedValueOnce('content' as any);
+    fs.readLocalFile.mockResolvedValueOnce('content');
     expect(
       await updateArtifacts({
         packageFileName: 'requirements.in',
@@ -67,16 +66,18 @@ describe('modules/manager/pip-compile/artifacts', () => {
         config,
       })
     ).toBeNull();
-    expect(execSnapshots).toMatchSnapshot();
+    expect(execSnapshots).toMatchObject([
+      { cmd: 'pip-compile requirements.in' },
+    ]);
   });
 
   it('returns updated requirements.txt', async () => {
-    fs.readFile.mockResolvedValueOnce('current requirements.txt' as any);
+    fs.readLocalFile.mockResolvedValueOnce('current requirements.txt');
     const execSnapshots = mockExecAll();
     git.getRepoStatus.mockResolvedValue({
       modified: ['requirements.txt'],
     } as StatusResult);
-    fs.readFile.mockResolvedValueOnce('New requirements.txt' as any);
+    fs.readLocalFile.mockResolvedValueOnce('New requirements.txt');
     expect(
       await updateArtifacts({
         packageFileName: 'requirements.in',
@@ -85,7 +86,9 @@ describe('modules/manager/pip-compile/artifacts', () => {
         config: { ...config, constraints: { python: '3.7' } },
       })
     ).not.toBeNull();
-    expect(execSnapshots).toMatchSnapshot();
+    expect(execSnapshots).toMatchObject([
+      { cmd: 'pip-compile requirements.in' },
+    ]);
   });
 
   it('supports docker mode', async () => {
@@ -94,7 +97,8 @@ describe('modules/manager/pip-compile/artifacts', () => {
     git.getRepoStatus.mockResolvedValue({
       modified: ['requirements.txt'],
     } as StatusResult);
-    fs.readFile.mockResolvedValueOnce('new lock' as any);
+    fs.readLocalFile.mockResolvedValueOnce('new lock');
+    fs.ensureCacheDir.mockResolvedValueOnce('/tmp/renovate/cache/others/pip');
     expect(
       await updateArtifacts({
         packageFileName: 'requirements.in',
@@ -127,13 +131,13 @@ describe('modules/manager/pip-compile/artifacts', () => {
     ]);
   });
 
-  it('supports iunstall mode', async () => {
+  it('supports install mode', async () => {
     GlobalConfig.set({ ...adminConfig, binarySource: 'install' });
     const execSnapshots = mockExecAll();
     git.getRepoStatus.mockResolvedValue({
       modified: ['requirements.txt'],
     } as StatusResult);
-    fs.readFile.mockResolvedValueOnce('new lock' as any);
+    fs.readLocalFile.mockResolvedValueOnce('new lock');
     expect(
       await updateArtifacts({
         packageFileName: 'requirements.in',
@@ -154,8 +158,9 @@ describe('modules/manager/pip-compile/artifacts', () => {
   });
 
   it('catches errors', async () => {
-    fs.readFile.mockResolvedValueOnce('Current requirements.txt' as any);
-    fs.outputFile.mockImplementationOnce(() => {
+    const execSnapshots = mockExecAll();
+    fs.readLocalFile.mockResolvedValueOnce('Current requirements.txt');
+    fs.writeLocalFile.mockImplementationOnce(() => {
       throw new Error('not found');
     });
     expect(
@@ -170,15 +175,16 @@ describe('modules/manager/pip-compile/artifacts', () => {
         artifactError: { lockFile: 'requirements.txt', stderr: 'not found' },
       },
     ]);
+    expect(execSnapshots).toEqual([]);
   });
 
   it('returns updated requirements.txt when doing lockfile maintenance', async () => {
-    fs.readFile.mockResolvedValueOnce('Current requirements.txt' as any);
+    fs.readLocalFile.mockResolvedValueOnce('Current requirements.txt');
     const execSnapshots = mockExecAll();
     git.getRepoStatus.mockResolvedValue({
       modified: ['requirements.txt'],
     } as StatusResult);
-    fs.readFile.mockReturnValueOnce('New requirements.txt' as any);
+    fs.readLocalFile.mockResolvedValueOnce('New requirements.txt');
     expect(
       await updateArtifacts({
         packageFileName: 'requirements.in',
@@ -187,7 +193,9 @@ describe('modules/manager/pip-compile/artifacts', () => {
         config: lockMaintenanceConfig,
       })
     ).not.toBeNull();
-    expect(execSnapshots).toMatchSnapshot();
+    expect(execSnapshots).toMatchObject([
+      { cmd: 'pip-compile requirements.in' },
+    ]);
   });
 
   it('uses pipenv version from config', async () => {
@@ -196,7 +204,8 @@ describe('modules/manager/pip-compile/artifacts', () => {
     git.getRepoStatus.mockResolvedValue({
       modified: ['requirements.txt'],
     } as StatusResult);
-    fs.readFile.mockResolvedValueOnce('new lock' as any);
+    fs.readLocalFile.mockResolvedValueOnce('new lock');
+    fs.ensureCacheDir.mockResolvedValueOnce('/tmp/renovate/cache/others/pip');
     expect(
       await updateArtifacts({
         packageFileName: 'requirements.in',
diff --git a/lib/modules/manager/poetry/__fixtures__/pyproject.10.toml b/lib/modules/manager/poetry/__fixtures__/pyproject.10.toml
index 14c589fab7736df2c332883be90d1d017f0975e4..65c5e06ba8d947daca002419a032a68c13d4a719 100644
--- a/lib/modules/manager/poetry/__fixtures__/pyproject.10.toml
+++ b/lib/modules/manager/poetry/__fixtures__/pyproject.10.toml
@@ -15,3 +15,7 @@ url = "last.url"
 
 [[tool.poetry.source]]
 name = "five"
+
+[build-system]
+requires = ["poetry_core>=1.0", "wheel"]
+build-backend = "poetry.masonry.api"
diff --git a/lib/modules/manager/poetry/__snapshots__/artifacts.spec.ts.snap b/lib/modules/manager/poetry/__snapshots__/artifacts.spec.ts.snap
deleted file mode 100644
index dd7b68266df9794d02ea9794fff88d55d77d3774..0000000000000000000000000000000000000000
--- a/lib/modules/manager/poetry/__snapshots__/artifacts.spec.ts.snap
+++ /dev/null
@@ -1,236 +0,0 @@
-// Jest Snapshot v1, https://goo.gl/fbAQLP
-
-exports[`modules/manager/poetry/artifacts catches errors 1`] = `
-Array [
-  Object {
-    "artifactError": Object {
-      "lockFile": "poetry.lock",
-      "stderr": "undefined
-undefined",
-    },
-  },
-]
-`;
-
-exports[`modules/manager/poetry/artifacts passes private credential environment vars 1`] = `
-Array [
-  Object {
-    "cmd": "poetry update --lock --no-interaction dep1",
-    "options": Object {
-      "cwd": "/tmp/github/some/repo",
-      "encoding": "utf-8",
-      "env": Object {
-        "HOME": "/home/user",
-        "HTTPS_PROXY": "https://example.com",
-        "HTTP_PROXY": "http://example.com",
-        "LANG": "en_US.UTF-8",
-        "LC_ALL": "en_US",
-        "NO_PROXY": "localhost",
-        "PATH": "/tmp/path",
-        "PIP_CACHE_DIR": "/tmp/cache/others/pip",
-        "POETRY_HTTP_BASIC_FOUR_OH_FOUR_PASSWORD": "passwordFour",
-        "POETRY_HTTP_BASIC_ONE_PASSWORD": "passwordOne",
-        "POETRY_HTTP_BASIC_ONE_USERNAME": "usernameOne",
-        "POETRY_HTTP_BASIC_TWO_USERNAME": "usernameTwo",
-      },
-      "maxBuffer": 10485760,
-      "timeout": 900000,
-    },
-  },
-]
-`;
-
-exports[`modules/manager/poetry/artifacts prioritizes pypi-scoped credentials 1`] = `
-Array [
-  Object {
-    "cmd": "poetry update --lock --no-interaction dep1",
-    "options": Object {
-      "cwd": "/tmp/github/some/repo",
-      "encoding": "utf-8",
-      "env": Object {
-        "HOME": "/home/user",
-        "HTTPS_PROXY": "https://example.com",
-        "HTTP_PROXY": "http://example.com",
-        "LANG": "en_US.UTF-8",
-        "LC_ALL": "en_US",
-        "NO_PROXY": "localhost",
-        "PATH": "/tmp/path",
-        "PIP_CACHE_DIR": "/tmp/cache/others/pip",
-        "POETRY_HTTP_BASIC_ONE_PASSWORD": "scoped-password",
-      },
-      "maxBuffer": 10485760,
-      "timeout": 900000,
-    },
-  },
-]
-`;
-
-exports[`modules/manager/poetry/artifacts returns null if unchanged 1`] = `
-Array [
-  Object {
-    "cmd": "poetry update --lock --no-interaction dep1",
-    "options": Object {
-      "cwd": "/tmp/github/some/repo",
-      "encoding": "utf-8",
-      "env": Object {
-        "HOME": "/home/user",
-        "HTTPS_PROXY": "https://example.com",
-        "HTTP_PROXY": "http://example.com",
-        "LANG": "en_US.UTF-8",
-        "LC_ALL": "en_US",
-        "NO_PROXY": "localhost",
-        "PATH": "/tmp/path",
-        "PIP_CACHE_DIR": "/tmp/cache/others/pip",
-      },
-      "maxBuffer": 10485760,
-      "timeout": 900000,
-    },
-  },
-]
-`;
-
-exports[`modules/manager/poetry/artifacts returns updated poetry.lock 1`] = `
-Array [
-  Object {
-    "cmd": "poetry update --lock --no-interaction dep1",
-    "options": Object {
-      "cwd": "/tmp/github/some/repo",
-      "encoding": "utf-8",
-      "env": Object {
-        "HOME": "/home/user",
-        "HTTPS_PROXY": "https://example.com",
-        "HTTP_PROXY": "http://example.com",
-        "LANG": "en_US.UTF-8",
-        "LC_ALL": "en_US",
-        "NO_PROXY": "localhost",
-        "PATH": "/tmp/path",
-        "PIP_CACHE_DIR": "/tmp/cache/others/pip",
-      },
-      "maxBuffer": 10485760,
-      "timeout": 900000,
-    },
-  },
-]
-`;
-
-exports[`modules/manager/poetry/artifacts returns updated poetry.lock using docker (constraints) 1`] = `
-Array [
-  Object {
-    "cmd": "docker pull renovate/sidecar",
-    "options": Object {
-      "encoding": "utf-8",
-    },
-  },
-  Object {
-    "cmd": "docker ps --filter name=renovate_sidecar -aq",
-    "options": Object {
-      "encoding": "utf-8",
-    },
-  },
-  Object {
-    "cmd": "docker run --rm --name=renovate_sidecar --label=renovate_child -v \\"/tmp/github/some/repo\\":\\"/tmp/github/some/repo\\" -v \\"/tmp/cache\\":\\"/tmp/cache\\" -e PIP_CACHE_DIR -e BUILDPACK_CACHE_DIR -w \\"/tmp/github/some/repo\\" renovate/sidecar bash -l -c \\"install-tool python 1.2.0 && pip install --user 'poetry>=1.0' && poetry update --lock --no-interaction dep1\\"",
-    "options": Object {
-      "cwd": "/tmp/github/some/repo",
-      "encoding": "utf-8",
-      "env": Object {
-        "BUILDPACK_CACHE_DIR": "/tmp/cache/buildpack",
-        "HOME": "/home/user",
-        "HTTPS_PROXY": "https://example.com",
-        "HTTP_PROXY": "http://example.com",
-        "LANG": "en_US.UTF-8",
-        "LC_ALL": "en_US",
-        "NO_PROXY": "localhost",
-        "PATH": "/tmp/path",
-        "PIP_CACHE_DIR": "/tmp/cache/others/pip",
-      },
-      "maxBuffer": 10485760,
-      "timeout": 900000,
-    },
-  },
-]
-`;
-
-exports[`modules/manager/poetry/artifacts returns updated poetry.lock using docker 1`] = `
-Array [
-  Object {
-    "cmd": "docker pull renovate/sidecar",
-    "options": Object {
-      "encoding": "utf-8",
-    },
-  },
-  Object {
-    "cmd": "docker ps --filter name=renovate_sidecar -aq",
-    "options": Object {
-      "encoding": "utf-8",
-    },
-  },
-  Object {
-    "cmd": "docker run --rm --name=renovate_sidecar --label=renovate_child -v \\"/tmp/github/some/repo\\":\\"/tmp/github/some/repo\\" -v \\"/tmp/cache\\":\\"/tmp/cache\\" -e PIP_CACHE_DIR -e BUILDPACK_CACHE_DIR -w \\"/tmp/github/some/repo\\" renovate/sidecar bash -l -c \\"install-tool python 1.2.0 && pip install --user 'poetry>=1.0' && poetry update --lock --no-interaction dep1\\"",
-    "options": Object {
-      "cwd": "/tmp/github/some/repo",
-      "encoding": "utf-8",
-      "env": Object {
-        "BUILDPACK_CACHE_DIR": "/tmp/cache/buildpack",
-        "HOME": "/home/user",
-        "HTTPS_PROXY": "https://example.com",
-        "HTTP_PROXY": "http://example.com",
-        "LANG": "en_US.UTF-8",
-        "LC_ALL": "en_US",
-        "NO_PROXY": "localhost",
-        "PATH": "/tmp/path",
-        "PIP_CACHE_DIR": "/tmp/cache/others/pip",
-      },
-      "maxBuffer": 10485760,
-      "timeout": 900000,
-    },
-  },
-]
-`;
-
-exports[`modules/manager/poetry/artifacts returns updated poetry.lock when doing lockfile maintenance 1`] = `
-Array [
-  Object {
-    "cmd": "poetry update --lock --no-interaction",
-    "options": Object {
-      "cwd": "/tmp/github/some/repo",
-      "encoding": "utf-8",
-      "env": Object {
-        "HOME": "/home/user",
-        "HTTPS_PROXY": "https://example.com",
-        "HTTP_PROXY": "http://example.com",
-        "LANG": "en_US.UTF-8",
-        "LC_ALL": "en_US",
-        "NO_PROXY": "localhost",
-        "PATH": "/tmp/path",
-        "PIP_CACHE_DIR": "/tmp/cache/others/pip",
-      },
-      "maxBuffer": 10485760,
-      "timeout": 900000,
-    },
-  },
-]
-`;
-
-exports[`modules/manager/poetry/artifacts returns updated pyproject.lock 1`] = `
-Array [
-  Object {
-    "cmd": "poetry update --lock --no-interaction dep1",
-    "options": Object {
-      "cwd": "/tmp/github/some/repo",
-      "encoding": "utf-8",
-      "env": Object {
-        "HOME": "/home/user",
-        "HTTPS_PROXY": "https://example.com",
-        "HTTP_PROXY": "http://example.com",
-        "LANG": "en_US.UTF-8",
-        "LC_ALL": "en_US",
-        "NO_PROXY": "localhost",
-        "PATH": "/tmp/path",
-        "PIP_CACHE_DIR": "/tmp/cache/others/pip",
-      },
-      "maxBuffer": 10485760,
-      "timeout": 900000,
-    },
-  },
-]
-`;
diff --git a/lib/modules/manager/poetry/artifacts.spec.ts b/lib/modules/manager/poetry/artifacts.spec.ts
index 49432cc37442bcdfa99cdb42dede11d5dff44d17..c524a84ba5e940de4af72cc20393f7431a20c6b4 100644
--- a/lib/modules/manager/poetry/artifacts.spec.ts
+++ b/lib/modules/manager/poetry/artifacts.spec.ts
@@ -1,8 +1,7 @@
-import _fs from 'fs-extra';
 import { join } from 'upath';
 import { envMock, mockExecAll } from '../../../../test/exec-util';
 import { Fixtures } from '../../../../test/fixtures';
-import { env, mocked } from '../../../../test/util';
+import { env, fs, mocked } from '../../../../test/util';
 import { GlobalConfig } from '../../../config/global';
 import type { RepoGlobalConfig } from '../../../config/types';
 import * as docker from '../../../util/exec/docker';
@@ -14,14 +13,13 @@ import { updateArtifacts } from '.';
 const pyproject1toml = Fixtures.get('pyproject.1.toml');
 const pyproject10toml = Fixtures.get('pyproject.10.toml');
 
-jest.mock('fs-extra');
 jest.mock('../../../util/exec/env');
+jest.mock('../../../util/fs');
 jest.mock('../../datasource');
 jest.mock('../../../util/host-rules');
 
 process.env.BUILDPACK = 'true';
 
-const fs: jest.Mocked<typeof _fs> = _fs as any;
 const datasource = mocked(_datasource);
 const hostRules = mocked(_hostRules);
 
@@ -41,6 +39,7 @@ describe('modules/manager/poetry/artifacts', () => {
   });
 
   it('returns null if no poetry.lock found', async () => {
+    const execSnapshots = mockExecAll();
     const updatedDeps = [{ depName: 'dep1' }];
     expect(
       await updateArtifacts({
@@ -50,9 +49,11 @@ describe('modules/manager/poetry/artifacts', () => {
         config,
       })
     ).toBeNull();
+    expect(execSnapshots).toEqual([]);
   });
 
   it('returns null if updatedDeps is empty', async () => {
+    const execSnapshots = mockExecAll();
     expect(
       await updateArtifacts({
         packageFileName: 'pyproject.toml',
@@ -61,12 +62,14 @@ describe('modules/manager/poetry/artifacts', () => {
         config,
       })
     ).toBeNull();
+    expect(execSnapshots).toEqual([]);
   });
 
   it('returns null if unchanged', async () => {
-    fs.readFile.mockReturnValueOnce('Current poetry.lock' as any);
     const execSnapshots = mockExecAll();
-    fs.readFile.mockReturnValueOnce('Current poetry.lock' as any);
+    fs.ensureCacheDir.mockResolvedValueOnce('/tmp/renovate/cache/others/pip');
+    fs.readLocalFile.mockResolvedValueOnce('Current poetry.lock');
+    fs.readLocalFile.mockResolvedValueOnce('Current poetry.lock');
     const updatedDeps = [{ depName: 'dep1' }];
     expect(
       await updateArtifacts({
@@ -76,13 +79,23 @@ describe('modules/manager/poetry/artifacts', () => {
         config,
       })
     ).toBeNull();
-    expect(execSnapshots).toMatchSnapshot();
+    expect(execSnapshots).toMatchObject([
+      {
+        cmd: 'poetry update --lock --no-interaction dep1',
+        options: {
+          cwd: '/tmp/github/some/repo',
+          env: { PIP_CACHE_DIR: '/tmp/renovate/cache/others/pip' },
+        },
+      },
+    ]);
   });
 
   it('returns updated poetry.lock', async () => {
-    fs.readFile.mockResolvedValueOnce('[metadata]\n' as never);
     const execSnapshots = mockExecAll();
-    fs.readFile.mockReturnValueOnce('New poetry.lock' as any);
+    // poetry.lock
+    fs.getSiblingFileName.mockReturnValueOnce('poetry.lock');
+    fs.readLocalFile.mockResolvedValueOnce('[metadata]\n');
+    fs.readLocalFile.mockResolvedValueOnce('New poetry.lock');
     const updatedDeps = [{ depName: 'dep1' }];
     expect(
       await updateArtifacts({
@@ -91,16 +104,29 @@ describe('modules/manager/poetry/artifacts', () => {
         newPackageFileContent: '{}',
         config,
       })
-    ).not.toBeNull();
-    expect(execSnapshots).toMatchSnapshot();
+    ).toEqual([
+      {
+        file: {
+          type: 'addition',
+          path: 'poetry.lock',
+          contents: 'New poetry.lock',
+        },
+      },
+    ]);
+    expect(execSnapshots).toMatchObject([
+      { cmd: 'poetry update --lock --no-interaction dep1' },
+    ]);
   });
 
   it('passes private credential environment vars', async () => {
-    // TODO #7154
-    fs.readFile.mockResolvedValueOnce(null as never);
-    fs.readFile.mockResolvedValueOnce('[metadata]\n' as never);
+    // poetry.lock
+    fs.getSiblingFileName.mockReturnValueOnce('poetry.lock');
+    fs.readLocalFile.mockResolvedValueOnce(null);
+    // pyproject.lock
+    fs.getSiblingFileName.mockReturnValueOnce('pyproject.lock');
+    fs.readLocalFile.mockResolvedValueOnce('[metadata]\n');
     const execSnapshots = mockExecAll();
-    fs.readFile.mockReturnValueOnce('New poetry.lock' as any);
+    fs.readLocalFile.mockResolvedValueOnce('New poetry.lock');
     hostRules.find.mockReturnValueOnce({
       username: 'usernameOne',
       password: 'passwordOne',
@@ -116,17 +142,30 @@ describe('modules/manager/poetry/artifacts', () => {
         newPackageFileContent: pyproject10toml,
         config,
       })
-    ).not.toBeNull();
+    ).toEqual([
+      {
+        file: {
+          type: 'addition',
+          path: 'pyproject.lock',
+          contents: 'New poetry.lock',
+        },
+      },
+    ]);
     expect(hostRules.find.mock.calls).toHaveLength(4);
-    expect(execSnapshots).toMatchSnapshot();
+    expect(execSnapshots).toMatchObject([
+      { cmd: 'poetry update --lock --no-interaction dep1' },
+    ]);
   });
 
   it('prioritizes pypi-scoped credentials', async () => {
-    // TODO #7154
-    fs.readFile.mockResolvedValueOnce(null as never);
-    fs.readFile.mockResolvedValueOnce(Buffer.from('[metadata]\n'));
     const execSnapshots = mockExecAll();
-    fs.readFile.mockResolvedValueOnce(Buffer.from('New poetry.lock'));
+    // poetry.lock
+    fs.getSiblingFileName.mockReturnValueOnce('poetry.lock');
+    fs.readLocalFile.mockResolvedValueOnce(null);
+    // pyproject.lock
+    fs.getSiblingFileName.mockReturnValueOnce('pyproject.lock');
+    fs.readLocalFile.mockResolvedValueOnce('[metadata]\n');
+    fs.readLocalFile.mockResolvedValueOnce('New poetry.lock');
     hostRules.find.mockImplementation((search) => ({
       password:
         search.hostType === 'pypi' ? 'scoped-password' : 'unscoped-password',
@@ -143,16 +182,29 @@ describe('modules/manager/poetry/artifacts', () => {
         `,
         config,
       })
-    ).not.toBeNull();
-    expect(execSnapshots).toMatchSnapshot();
+    ).toEqual([
+      {
+        file: {
+          type: 'addition',
+          path: 'pyproject.lock',
+          contents: 'New poetry.lock',
+        },
+      },
+    ]);
+    expect(execSnapshots).toMatchObject([
+      { cmd: 'poetry update --lock --no-interaction dep1' },
+    ]);
   });
 
   it('returns updated pyproject.lock', async () => {
-    // TODO #7154
-    fs.readFile.mockResolvedValueOnce(null as never);
-    fs.readFile.mockResolvedValueOnce('[metadata]\n' as never);
     const execSnapshots = mockExecAll();
-    fs.readFile.mockReturnValueOnce('New poetry.lock' as any);
+    // poetry.lock
+    fs.getSiblingFileName.mockReturnValueOnce('poetry.lock');
+    fs.readLocalFile.mockResolvedValueOnce(null);
+    // pyproject.lock
+    fs.getSiblingFileName.mockReturnValueOnce('pyproject.lock');
+    fs.readLocalFile.mockResolvedValueOnce('[metadata]\n');
+    fs.readLocalFile.mockResolvedValueOnce('New poetry.lock');
     const updatedDeps = [{ depName: 'dep1' }];
     expect(
       await updateArtifacts({
@@ -161,24 +213,28 @@ describe('modules/manager/poetry/artifacts', () => {
         newPackageFileContent: '{}',
         config,
       })
-    ).not.toBeNull();
-    expect(execSnapshots).toMatchSnapshot();
+    ).toEqual([
+      {
+        file: {
+          type: 'addition',
+          path: 'pyproject.lock',
+          contents: 'New poetry.lock',
+        },
+      },
+    ]);
+    expect(execSnapshots).toMatchObject([
+      { cmd: 'poetry update --lock --no-interaction dep1' },
+    ]);
   });
 
   it('returns updated poetry.lock using docker', async () => {
     GlobalConfig.set({ ...adminConfig, binarySource: 'docker' });
-    // poetry.lock
-    fs.readFile.mockResolvedValueOnce('[metadata]\n' as any);
     const execSnapshots = mockExecAll();
-    fs.readFile.mockReturnValueOnce('New poetry.lock' as any);
-    // poetry
-    datasource.getPkgReleases.mockResolvedValueOnce({
-      releases: [
-        { version: '1.0.0' },
-        { version: '1.1.0' },
-        { version: '1.2.0' },
-      ],
-    });
+    fs.ensureCacheDir.mockResolvedValueOnce('/tmp/renovate/cache/others/pip');
+    // poetry.lock
+    fs.getSiblingFileName.mockReturnValueOnce('poetry.lock');
+    fs.readLocalFile.mockResolvedValueOnce('[metadata]\n');
+    fs.readLocalFile.mockResolvedValueOnce('New poetry.lock');
     // python
     datasource.getPkgReleases.mockResolvedValueOnce({
       releases: [{ version: '2.7.5' }, { version: '3.4.2' }],
@@ -196,26 +252,49 @@ describe('modules/manager/poetry/artifacts', () => {
           },
         },
       })
-    ).not.toBeNull();
-    expect(execSnapshots).toMatchSnapshot();
+    ).toEqual([
+      {
+        file: {
+          type: 'addition',
+          path: 'poetry.lock',
+          contents: 'New poetry.lock',
+        },
+      },
+    ]);
+    expect(execSnapshots).toMatchObject([
+      { cmd: 'docker pull renovate/sidecar' },
+      { cmd: 'docker ps --filter name=renovate_sidecar -aq' },
+      {
+        cmd:
+          'docker run --rm --name=renovate_sidecar --label=renovate_child ' +
+          '-v "/tmp/github/some/repo":"/tmp/github/some/repo" ' +
+          '-v "/tmp/cache":"/tmp/cache" ' +
+          '-e PIP_CACHE_DIR ' +
+          '-e BUILDPACK_CACHE_DIR ' +
+          '-w "/tmp/github/some/repo" ' +
+          'renovate/sidecar ' +
+          'bash -l -c "' +
+          'install-tool python 3.4.2 ' +
+          '&& ' +
+          "pip install --user 'poetry>=1.0' " +
+          '&& ' +
+          'poetry update --lock --no-interaction dep1' +
+          '"',
+      },
+    ]);
   });
 
   it('returns updated poetry.lock using docker (constraints)', async () => {
     GlobalConfig.set({ ...adminConfig, binarySource: 'docker' });
+    const execSnapshots = mockExecAll();
+
+    fs.ensureCacheDir.mockResolvedValueOnce('/tmp/renovate/cache/others/pip');
     // poetry.lock
-    fs.readFile.mockResolvedValueOnce(
-      '[metadata]\npython-versions = "~2.7 || ^3.4"' as any
+    fs.getSiblingFileName.mockReturnValueOnce('poetry.lock');
+    fs.readLocalFile.mockResolvedValueOnce(
+      '[metadata]\npython-versions = "~2.7 || ^3.4"'
     );
-    const execSnapshots = mockExecAll();
-    fs.readFile.mockReturnValueOnce('New poetry.lock' as any);
-    // poetry
-    datasource.getPkgReleases.mockResolvedValueOnce({
-      releases: [
-        { version: '1.0.0' },
-        { version: '1.1.0' },
-        { version: '1.2.0' },
-      ],
-    });
+    fs.readLocalFile.mockResolvedValueOnce('New poetry.lock');
     // python
     datasource.getPkgReleases.mockResolvedValueOnce({
       releases: [{ version: '2.7.5' }, { version: '3.3.2' }],
@@ -231,26 +310,47 @@ describe('modules/manager/poetry/artifacts', () => {
           constraints: {},
         },
       })
-    ).not.toBeNull();
-    expect(execSnapshots).toMatchSnapshot();
+    ).toEqual([
+      {
+        file: {
+          type: 'addition',
+          path: 'poetry.lock',
+          contents: 'New poetry.lock',
+        },
+      },
+    ]);
+    expect(execSnapshots).toMatchObject([
+      { cmd: 'docker pull renovate/sidecar' },
+      { cmd: 'docker ps --filter name=renovate_sidecar -aq' },
+      {
+        cmd:
+          'docker run --rm --name=renovate_sidecar --label=renovate_child ' +
+          '-v "/tmp/github/some/repo":"/tmp/github/some/repo" ' +
+          '-v "/tmp/cache":"/tmp/cache" ' +
+          '-e PIP_CACHE_DIR ' +
+          '-e BUILDPACK_CACHE_DIR ' +
+          '-w "/tmp/github/some/repo" ' +
+          'renovate/sidecar ' +
+          'bash -l -c "' +
+          'install-tool python 3.3.2 ' +
+          '&& ' +
+          "pip install --user 'poetry>=1.0' " +
+          '&& ' +
+          'poetry update --lock --no-interaction dep1' +
+          '"',
+      },
+    ]);
   });
 
   it('returns updated poetry.lock using install mode', async () => {
     GlobalConfig.set({ ...adminConfig, binarySource: 'install' });
+    const execSnapshots = mockExecAll();
     // poetry.lock
-    fs.readFile.mockResolvedValueOnce(
-      '[metadata]\npython-versions = "~2.7 || ^3.4"' as any
+    fs.getSiblingFileName.mockReturnValueOnce('poetry.lock');
+    fs.readLocalFile.mockResolvedValueOnce(
+      '[metadata]\npython-versions = "~2.7 || ^3.4"'
     );
-    const execSnapshots = mockExecAll();
-    fs.readFile.mockReturnValueOnce('New poetry.lock' as any);
-    // poetry
-    datasource.getPkgReleases.mockResolvedValueOnce({
-      releases: [
-        { version: '1.0.0' },
-        { version: '1.1.0' },
-        { version: '1.2.0' },
-      ],
-    });
+    fs.readLocalFile.mockResolvedValueOnce('New poetry.lock');
     // python
     datasource.getPkgReleases.mockResolvedValueOnce({
       releases: [{ version: '2.7.5' }, { version: '3.3.2' }],
@@ -277,18 +377,18 @@ describe('modules/manager/poetry/artifacts', () => {
     ]);
 
     expect(execSnapshots).toMatchObject([
-      { cmd: 'install-tool python 1.2.0' },
+      { cmd: 'install-tool python 3.3.2' },
       { cmd: "pip install --user 'poetry>=1.0'" },
-      {
-        cmd: 'poetry update --lock --no-interaction dep1',
-        options: { cwd: '/tmp/github/some/repo' },
-      },
+      { cmd: 'poetry update --lock --no-interaction dep1' },
     ]);
   });
 
   it('catches errors', async () => {
-    fs.readFile.mockResolvedValueOnce('Current poetry.lock' as any);
-    fs.outputFile.mockImplementationOnce(() => {
+    const execSnapshots = mockExecAll();
+    // poetry.lock
+    fs.getSiblingFileName.mockReturnValueOnce('poetry.lock');
+    fs.readLocalFile.mockResolvedValueOnce('Current poetry.lock');
+    fs.writeLocalFile.mockImplementationOnce(() => {
       throw new Error('not found');
     });
     const updatedDeps = [{ depName: 'dep1' }];
@@ -299,13 +399,16 @@ describe('modules/manager/poetry/artifacts', () => {
         newPackageFileContent: '{}',
         config,
       })
-    ).toMatchSnapshot([{ artifactError: { lockFile: 'poetry.lock' } }]);
+    ).toMatchObject([{ artifactError: { lockFile: 'poetry.lock' } }]);
+    expect(execSnapshots).toMatchObject([]);
   });
 
   it('returns updated poetry.lock when doing lockfile maintenance', async () => {
-    fs.readFile.mockResolvedValueOnce('Old poetry.lock' as any);
     const execSnapshots = mockExecAll();
-    fs.readFile.mockReturnValueOnce('New poetry.lock' as any);
+    // poetry.lock
+    fs.getSiblingFileName.mockReturnValueOnce('poetry.lock');
+    fs.readLocalFile.mockResolvedValueOnce('Old poetry.lock');
+    fs.readLocalFile.mockResolvedValueOnce('New poetry.lock');
     expect(
       await updateArtifacts({
         packageFileName: 'pyproject.toml',
@@ -316,7 +419,17 @@ describe('modules/manager/poetry/artifacts', () => {
           updateType: 'lockFileMaintenance',
         },
       })
-    ).not.toBeNull();
-    expect(execSnapshots).toMatchSnapshot();
+    ).toEqual([
+      {
+        file: {
+          contents: 'New poetry.lock',
+          path: 'poetry.lock',
+          type: 'addition',
+        },
+      },
+    ]);
+    expect(execSnapshots).toMatchObject([
+      { cmd: 'poetry update --lock --no-interaction' },
+    ]);
   });
 });
diff --git a/lib/modules/manager/poetry/update-locked.spec.ts b/lib/modules/manager/poetry/update-locked.spec.ts
index 3118f8afe3b21113174360a8ad2734e63054995e..fe736a6444f7dcf07313ad4e6a08e10c69caa95b 100644
--- a/lib/modules/manager/poetry/update-locked.spec.ts
+++ b/lib/modules/manager/poetry/update-locked.spec.ts
@@ -29,4 +29,14 @@ describe('modules/manager/poetry/update-locked', () => {
     };
     expect(updateLockedDependency(config).status).toBe('unsupported');
   });
+
+  it('returns unsupported for mising locked content', () => {
+    const config: UpdateLockedConfig = {
+      packageFile,
+      lockFile,
+      depName: 'urllib3',
+      newVersion: '1.26.4',
+    };
+    expect(updateLockedDependency(config).status).toBe('unsupported');
+  });
 });