We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
`borg with-lock` raises `FileNotFoundError`
Hey,
I just want to find out what `with-lock` does. I tried some things and I ended up with an exception :)

```bash
kmille@linbox:~ borg --verbose with-lock /home/kmille/tmp/borgrepo list
Local Exception
Traceback (most recent call last):
  File "/usr/lib/python3.11/site-packages/borg/archiver.py", line 5343, in main
    exit_code = archiver.run(args)
                ^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/site-packages/borg/archiver.py", line 5263, in run
    return set_ec(func(args))
                  ^^^^^^^^^^
  File "/usr/lib/python3.11/site-packages/borg/archiver.py", line 189, in wrapper
    return method(self, args, repository=repository, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/site-packages/borg/archiver.py", line 1853, in do_with_lock
    return subprocess.call([args.command] + args.args, env=env)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/subprocess.py", line 389, in call
    with Popen(*popenargs, **kwargs) as p:
         ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "/usr/lib/python3.11/subprocess.py", line 1026, in __init__
    self._execute_child(args, executable, preexec_fn, close_fds,
  File "/usr/lib/python3.11/subprocess.py", line 1950, in _execute_child
    raise child_exception_type(errno_num, err_msg, err_filename)
FileNotFoundError: [Errno 2] No such file or directory: 'list'

Platform: Linux linbox 6.6.8-hardened1-1-hardened #1 SMP PREEMPT_DYNAMIC Thu, 21 Dec 2023 23:45:24 +0000 x86_64
Linux: Unknown Linux  
Borg: 1.2.7  Python: CPython 3.11.6 msgpack: 1.0.5 fuse: llfuse 1.5.0 [pyfuse3,llfuse]
PID: 186644  CWD: /home/kmille
sys.argv: ['/usr/bin/borg', '--verbose', 'with-lock', '/home/kmille/tmp/borgrepo', 'list']
SSH_ORIGINAL_COMMAND: None
```

I don't understand the [docs](https://borgbackup.readthedocs.io/en/stable/usage/lock.html). After reading, I ask myself:
- What's the purpose of a lock if I can run other borg commands in the meantime
- "It will first try to acquire the lock": Does this just mean: wait until it's free?
- 

Maybe it makes sense to update the docs :)

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `src/borg/archiver/lock_cmds.py`
Content:
```
1 import argparse
2 import subprocess
3 
4 from ._common import with_repository
5 from ..cache import Cache
6 from ..constants import *  # NOQA
7 from ..helpers import prepare_subprocess_env, set_ec
8 from ..manifest import Manifest
9 
10 from ..logger import create_logger
11 
12 logger = create_logger()
13 
14 
15 class LocksMixIn:
16     @with_repository(manifest=False, exclusive=True)
17     def do_with_lock(self, args, repository):
18         """run a user specified command with the repository lock held"""
19         # for a new server, this will immediately take an exclusive lock.
20         # to support old servers, that do not have "exclusive" arg in open()
21         # RPC API, we also do it the old way:
22         # re-write manifest to start a repository transaction - this causes a
23         # lock upgrade to exclusive for remote (and also for local) repositories.
24         # by using manifest=False in the decorator, we avoid having to require
25         # the encryption key (and can operate just with encrypted data).
26         data = repository.get(Manifest.MANIFEST_ID)
27         repository.put(Manifest.MANIFEST_ID, data)
28         # usually, a 0 byte (open for writing) segment file would be visible in the filesystem here.
29         # we write and close this file, to rather have a valid segment file on disk, before invoking the subprocess.
30         # we can only do this for local repositories (with .io), though:
31         if hasattr(repository, "io"):
32             repository.io.close_segment()
33         env = prepare_subprocess_env(system=True)
34         try:
35             # we exit with the return code we get from the subprocess
36             rc = subprocess.call([args.command] + args.args, env=env)
37             set_ec(rc)
38         finally:
39             # we need to commit the "no change" operation we did to the manifest
40             # because it created a new segment file in the repository. if we would
41             # roll back, the same file would be later used otherwise (for other content).
42             # that would be bad if somebody uses rsync with ignore-existing (or
43             # any other mechanism relying on existing segment data not changing).
44             # see issue #1867.
45             repository.commit(compact=False)
46 
47     @with_repository(lock=False, manifest=False)
48     def do_break_lock(self, args, repository):
49         """Break the repository lock (e.g. in case it was left by a dead borg."""
50         repository.break_lock()
51         Cache.break_lock(repository)
52 
53     def build_parser_locks(self, subparsers, common_parser, mid_common_parser):
54         from ._common import process_epilog
55 
56         break_lock_epilog = process_epilog(
57             """
58         This command breaks the repository and cache locks.
59         Please use carefully and only while no borg process (on any machine) is
60         trying to access the Cache or the Repository.
61         """
62         )
63         subparser = subparsers.add_parser(
64             "break-lock",
65             parents=[common_parser],
66             add_help=False,
67             description=self.do_break_lock.__doc__,
68             epilog=break_lock_epilog,
69             formatter_class=argparse.RawDescriptionHelpFormatter,
70             help="break repository and cache locks",
71         )
72         subparser.set_defaults(func=self.do_break_lock)
73 
74         with_lock_epilog = process_epilog(
75             """
76         This command runs a user-specified command while locking the repository. For example:
77 
78         ::
79 
80             $ BORG_REPO=/mnt/borgrepo borg with-lock rsync -av /mnt/borgrepo /somewhere/else/borgrepo
81 
82         It will first try to acquire the lock (make sure that no other operation is
83         running in the repo), then execute the given command as a subprocess and wait
84         for its termination, release the lock and return the user command's return
85         code as borg's return code.
86 
87         .. note::
88 
89             If you copy a repository with the lock held, the lock will be present in
90             the copy. Thus, before using borg on the copy from a different host,
91             you need to use "borg break-lock" on the copied repository, because
92             Borg is cautious and does not automatically remove stale locks made by a different host.
93         """
94         )
95         subparser = subparsers.add_parser(
96             "with-lock",
97             parents=[common_parser],
98             add_help=False,
99             description=self.do_with_lock.__doc__,
100             epilog=with_lock_epilog,
101             formatter_class=argparse.RawDescriptionHelpFormatter,
102             help="run user command with lock held",
103         )
104         subparser.set_defaults(func=self.do_with_lock)
105         subparser.add_argument("command", metavar="COMMAND", help="command to run")
106         subparser.add_argument("args", metavar="ARGS", nargs=argparse.REMAINDER, help="command arguments")
107
```

Path: `src/borg/testsuite/archiver/lock_cmds.py`
Content:
```
1 import os
2 
3 from ...constants import *  # NOQA
4 from . import cmd, generate_archiver_tests, RK_ENCRYPTION
5 
6 pytest_generate_tests = lambda metafunc: generate_archiver_tests(metafunc, kinds="local,remote,binary")  # NOQA
7 
8 
9 def test_break_lock(archivers, request):
10     archiver = request.getfixturevalue(archivers)
11     cmd(archiver, "rcreate", RK_ENCRYPTION)
12     cmd(archiver, "break-lock")
13 
14 
15 def test_with_lock(archivers, request):
16     archiver = request.getfixturevalue(archivers)
17     cmd(archiver, "rcreate", RK_ENCRYPTION)
18     lock_path = os.path.join(archiver.repository_path, "lock.exclusive")
19     command = "python3", "-c", 'import os, sys; sys.exit(42 if os.path.exists("%s") else 23)' % lock_path
20     cmd(archiver, "with-lock", *command, fork=True, exit_code=42)
21
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/src/borg/archiver/lock_cmds.py b/src/borg/archiver/lock_cmds.py
--- a/src/borg/archiver/lock_cmds.py
+++ b/src/borg/archiver/lock_cmds.py
@@ -4,7 +4,7 @@
 from ._common import with_repository
 from ..cache import Cache
 from ..constants import *  # NOQA
-from ..helpers import prepare_subprocess_env, set_ec
+from ..helpers import prepare_subprocess_env, set_ec, CommandError
 from ..manifest import Manifest
 
 from ..logger import create_logger
@@ -35,6 +35,8 @@
             # we exit with the return code we get from the subprocess
             rc = subprocess.call([args.command] + args.args, env=env)
             set_ec(rc)
+        except (FileNotFoundError, OSError, ValueError) as e:
+            raise CommandError(f"Error while trying to run '{args.command}': {e}")
         finally:
             # we need to commit the "no change" operation we did to the manifest
             # because it created a new segment file in the repository. if we would
diff --git a/src/borg/testsuite/archiver/lock_cmds.py b/src/borg/testsuite/archiver/lock_cmds.py
--- a/src/borg/testsuite/archiver/lock_cmds.py
+++ b/src/borg/testsuite/archiver/lock_cmds.py
@@ -18,3 +18,10 @@
     lock_path = os.path.join(archiver.repository_path, "lock.exclusive")
     command = "python3", "-c", 'import os, sys; sys.exit(42 if os.path.exists("%s") else 23)' % lock_path
     cmd(archiver, "with-lock", *command, fork=True, exit_code=42)
+
+
+def test_with_lock_non_existent_command(archivers, request):
+    archiver = request.getfixturevalue(archivers)
+    cmd(archiver, "rcreate", RK_ENCRYPTION)
+    command = ["non_existent_command"]
+    cmd(archiver, "with-lock", *command, fork=True, exit_code=EXIT_ERROR)

{"golden_diff": "diff --git a/src/borg/archiver/lock_cmds.py b/src/borg/archiver/lock_cmds.py\n--- a/src/borg/archiver/lock_cmds.py\n+++ b/src/borg/archiver/lock_cmds.py\n@@ -4,7 +4,7 @@\n from ._common import with_repository\n from ..cache import Cache\n from ..constants import *  # NOQA\n-from ..helpers import prepare_subprocess_env, set_ec\n+from ..helpers import prepare_subprocess_env, set_ec, CommandError\n from ..manifest import Manifest\n \n from ..logger import create_logger\n@@ -35,6 +35,8 @@\n             # we exit with the return code we get from the subprocess\n             rc = subprocess.call([args.command] + args.args, env=env)\n             set_ec(rc)\n+        except (FileNotFoundError, OSError, ValueError) as e:\n+            raise CommandError(f\"Error while trying to run '{args.command}': {e}\")\n         finally:\n             # we need to commit the \"no change\" operation we did to the manifest\n             # because it created a new segment file in the repository. if we would\ndiff --git a/src/borg/testsuite/archiver/lock_cmds.py b/src/borg/testsuite/archiver/lock_cmds.py\n--- a/src/borg/testsuite/archiver/lock_cmds.py\n+++ b/src/borg/testsuite/archiver/lock_cmds.py\n@@ -18,3 +18,10 @@\n     lock_path = os.path.join(archiver.repository_path, \"lock.exclusive\")\n     command = \"python3\", \"-c\", 'import os, sys; sys.exit(42 if os.path.exists(\"%s\") else 23)' % lock_path\n     cmd(archiver, \"with-lock\", *command, fork=True, exit_code=42)\n+\n+\n+def test_with_lock_non_existent_command(archivers, request):\n+    archiver = request.getfixturevalue(archivers)\n+    cmd(archiver, \"rcreate\", RK_ENCRYPTION)\n+    command = [\"non_existent_command\"]\n+    cmd(archiver, \"with-lock\", *command, fork=True, exit_code=EXIT_ERROR)\n", "issue": "`borg with-lock` raises `FileNotFoundError`\nHey,\r\nI just want to find out what `with-lock` does. I tried some things and I ended up with an exception :)\r\n\r\n```bash\r\nkmille@linbox:~ borg --verbose with-lock /home/kmille/tmp/borgrepo list\r\nLocal Exception\r\nTraceback (most recent call last):\r\n  File \"/usr/lib/python3.11/site-packages/borg/archiver.py\", line 5343, in main\r\n    exit_code = archiver.run(args)\r\n                ^^^^^^^^^^^^^^^^^^\r\n  File \"/usr/lib/python3.11/site-packages/borg/archiver.py\", line 5263, in run\r\n    return set_ec(func(args))\r\n                  ^^^^^^^^^^\r\n  File \"/usr/lib/python3.11/site-packages/borg/archiver.py\", line 189, in wrapper\r\n    return method(self, args, repository=repository, **kwargs)\r\n           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n  File \"/usr/lib/python3.11/site-packages/borg/archiver.py\", line 1853, in do_with_lock\r\n    return subprocess.call([args.command] + args.args, env=env)\r\n           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n  File \"/usr/lib/python3.11/subprocess.py\", line 389, in call\r\n    with Popen(*popenargs, **kwargs) as p:\r\n         ^^^^^^^^^^^^^^^^^^^^^^^^^^^\r\n  File \"/usr/lib/python3.11/subprocess.py\", line 1026, in __init__\r\n    self._execute_child(args, executable, preexec_fn, close_fds,\r\n  File \"/usr/lib/python3.11/subprocess.py\", line 1950, in _execute_child\r\n    raise child_exception_type(errno_num, err_msg, err_filename)\r\nFileNotFoundError: [Errno 2] No such file or directory: 'list'\r\n\r\nPlatform: Linux linbox 6.6.8-hardened1-1-hardened #1 SMP PREEMPT_DYNAMIC Thu, 21 Dec 2023 23:45:24 +0000 x86_64\r\nLinux: Unknown Linux  \r\nBorg: 1.2.7  Python: CPython 3.11.6 msgpack: 1.0.5 fuse: llfuse 1.5.0 [pyfuse3,llfuse]\r\nPID: 186644  CWD: /home/kmille\r\nsys.argv: ['/usr/bin/borg', '--verbose', 'with-lock', '/home/kmille/tmp/borgrepo', 'list']\r\nSSH_ORIGINAL_COMMAND: None\r\n```\r\n\r\nI don't understand the [docs](https://borgbackup.readthedocs.io/en/stable/usage/lock.html). After reading, I ask myself:\r\n- What's the purpose of a lock if I can run other borg commands in the meantime\r\n- \"It will first try to acquire the lock\": Does this just mean: wait until it's free?\r\n- \r\n\r\nMaybe it makes sense to update the docs :)\n", "before_files": [{"content": "import argparse\nimport subprocess\n\nfrom ._common import with_repository\nfrom ..cache import Cache\nfrom ..constants import *  # NOQA\nfrom ..helpers import prepare_subprocess_env, set_ec\nfrom ..manifest import Manifest\n\nfrom ..logger import create_logger\n\nlogger = create_logger()\n\n\nclass LocksMixIn:\n    @with_repository(manifest=False, exclusive=True)\n    def do_with_lock(self, args, repository):\n        \"\"\"run a user specified command with the repository lock held\"\"\"\n        # for a new server, this will immediately take an exclusive lock.\n        # to support old servers, that do not have \"exclusive\" arg in open()\n        # RPC API, we also do it the old way:\n        # re-write manifest to start a repository transaction - this causes a\n        # lock upgrade to exclusive for remote (and also for local) repositories.\n        # by using manifest=False in the decorator, we avoid having to require\n        # the encryption key (and can operate just with encrypted data).\n        data = repository.get(Manifest.MANIFEST_ID)\n        repository.put(Manifest.MANIFEST_ID, data)\n        # usually, a 0 byte (open for writing) segment file would be visible in the filesystem here.\n        # we write and close this file, to rather have a valid segment file on disk, before invoking the subprocess.\n        # we can only do this for local repositories (with .io), though:\n        if hasattr(repository, \"io\"):\n            repository.io.close_segment()\n        env = prepare_subprocess_env(system=True)\n        try:\n            # we exit with the return code we get from the subprocess\n            rc = subprocess.call([args.command] + args.args, env=env)\n            set_ec(rc)\n        finally:\n            # we need to commit the \"no change\" operation we did to the manifest\n            # because it created a new segment file in the repository. if we would\n            # roll back, the same file would be later used otherwise (for other content).\n            # that would be bad if somebody uses rsync with ignore-existing (or\n            # any other mechanism relying on existing segment data not changing).\n            # see issue #1867.\n            repository.commit(compact=False)\n\n    @with_repository(lock=False, manifest=False)\n    def do_break_lock(self, args, repository):\n        \"\"\"Break the repository lock (e.g. in case it was left by a dead borg.\"\"\"\n        repository.break_lock()\n        Cache.break_lock(repository)\n\n    def build_parser_locks(self, subparsers, common_parser, mid_common_parser):\n        from ._common import process_epilog\n\n        break_lock_epilog = process_epilog(\n            \"\"\"\n        This command breaks the repository and cache locks.\n        Please use carefully and only while no borg process (on any machine) is\n        trying to access the Cache or the Repository.\n        \"\"\"\n        )\n        subparser = subparsers.add_parser(\n            \"break-lock\",\n            parents=[common_parser],\n            add_help=False,\n            description=self.do_break_lock.__doc__,\n            epilog=break_lock_epilog,\n            formatter_class=argparse.RawDescriptionHelpFormatter,\n            help=\"break repository and cache locks\",\n        )\n        subparser.set_defaults(func=self.do_break_lock)\n\n        with_lock_epilog = process_epilog(\n            \"\"\"\n        This command runs a user-specified command while locking the repository. For example:\n\n        ::\n\n            $ BORG_REPO=/mnt/borgrepo borg with-lock rsync -av /mnt/borgrepo /somewhere/else/borgrepo\n\n        It will first try to acquire the lock (make sure that no other operation is\n        running in the repo), then execute the given command as a subprocess and wait\n        for its termination, release the lock and return the user command's return\n        code as borg's return code.\n\n        .. note::\n\n            If you copy a repository with the lock held, the lock will be present in\n            the copy. Thus, before using borg on the copy from a different host,\n            you need to use \"borg break-lock\" on the copied repository, because\n            Borg is cautious and does not automatically remove stale locks made by a different host.\n        \"\"\"\n        )\n        subparser = subparsers.add_parser(\n            \"with-lock\",\n            parents=[common_parser],\n            add_help=False,\n            description=self.do_with_lock.__doc__,\n            epilog=with_lock_epilog,\n            formatter_class=argparse.RawDescriptionHelpFormatter,\n            help=\"run user command with lock held\",\n        )\n        subparser.set_defaults(func=self.do_with_lock)\n        subparser.add_argument(\"command\", metavar=\"COMMAND\", help=\"command to run\")\n        subparser.add_argument(\"args\", metavar=\"ARGS\", nargs=argparse.REMAINDER, help=\"command arguments\")\n", "path": "src/borg/archiver/lock_cmds.py"}, {"content": "import os\n\nfrom ...constants import *  # NOQA\nfrom . import cmd, generate_archiver_tests, RK_ENCRYPTION\n\npytest_generate_tests = lambda metafunc: generate_archiver_tests(metafunc, kinds=\"local,remote,binary\")  # NOQA\n\n\ndef test_break_lock(archivers, request):\n    archiver = request.getfixturevalue(archivers)\n    cmd(archiver, \"rcreate\", RK_ENCRYPTION)\n    cmd(archiver, \"break-lock\")\n\n\ndef test_with_lock(archivers, request):\n    archiver = request.getfixturevalue(archivers)\n    cmd(archiver, \"rcreate\", RK_ENCRYPTION)\n    lock_path = os.path.join(archiver.repository_path, \"lock.exclusive\")\n    command = \"python3\", \"-c\", 'import os, sys; sys.exit(42 if os.path.exists(\"%s\") else 23)' % lock_path\n    cmd(archiver, \"with-lock\", *command, fork=True, exit_code=42)\n", "path": "src/borg/testsuite/archiver/lock_cmds.py"}], "after_files": [{"content": "import argparse\nimport subprocess\n\nfrom ._common import with_repository\nfrom ..cache import Cache\nfrom ..constants import *  # NOQA\nfrom ..helpers import prepare_subprocess_env, set_ec, CommandError\nfrom ..manifest import Manifest\n\nfrom ..logger import create_logger\n\nlogger = create_logger()\n\n\nclass LocksMixIn:\n    @with_repository(manifest=False, exclusive=True)\n    def do_with_lock(self, args, repository):\n        \"\"\"run a user specified command with the repository lock held\"\"\"\n        # for a new server, this will immediately take an exclusive lock.\n        # to support old servers, that do not have \"exclusive\" arg in open()\n        # RPC API, we also do it the old way:\n        # re-write manifest to start a repository transaction - this causes a\n        # lock upgrade to exclusive for remote (and also for local) repositories.\n        # by using manifest=False in the decorator, we avoid having to require\n        # the encryption key (and can operate just with encrypted data).\n        data = repository.get(Manifest.MANIFEST_ID)\n        repository.put(Manifest.MANIFEST_ID, data)\n        # usually, a 0 byte (open for writing) segment file would be visible in the filesystem here.\n        # we write and close this file, to rather have a valid segment file on disk, before invoking the subprocess.\n        # we can only do this for local repositories (with .io), though:\n        if hasattr(repository, \"io\"):\n            repository.io.close_segment()\n        env = prepare_subprocess_env(system=True)\n        try:\n            # we exit with the return code we get from the subprocess\n            rc = subprocess.call([args.command] + args.args, env=env)\n            set_ec(rc)\n        except (FileNotFoundError, OSError, ValueError) as e:\n            raise CommandError(f\"Error while trying to run '{args.command}': {e}\")\n        finally:\n            # we need to commit the \"no change\" operation we did to the manifest\n            # because it created a new segment file in the repository. if we would\n            # roll back, the same file would be later used otherwise (for other content).\n            # that would be bad if somebody uses rsync with ignore-existing (or\n            # any other mechanism relying on existing segment data not changing).\n            # see issue #1867.\n            repository.commit(compact=False)\n\n    @with_repository(lock=False, manifest=False)\n    def do_break_lock(self, args, repository):\n        \"\"\"Break the repository lock (e.g. in case it was left by a dead borg.\"\"\"\n        repository.break_lock()\n        Cache.break_lock(repository)\n\n    def build_parser_locks(self, subparsers, common_parser, mid_common_parser):\n        from ._common import process_epilog\n\n        break_lock_epilog = process_epilog(\n            \"\"\"\n        This command breaks the repository and cache locks.\n        Please use carefully and only while no borg process (on any machine) is\n        trying to access the Cache or the Repository.\n        \"\"\"\n        )\n        subparser = subparsers.add_parser(\n            \"break-lock\",\n            parents=[common_parser],\n            add_help=False,\n            description=self.do_break_lock.__doc__,\n            epilog=break_lock_epilog,\n            formatter_class=argparse.RawDescriptionHelpFormatter,\n            help=\"break repository and cache locks\",\n        )\n        subparser.set_defaults(func=self.do_break_lock)\n\n        with_lock_epilog = process_epilog(\n            \"\"\"\n        This command runs a user-specified command while locking the repository. For example:\n\n        ::\n\n            $ BORG_REPO=/mnt/borgrepo borg with-lock rsync -av /mnt/borgrepo /somewhere/else/borgrepo\n\n        It will first try to acquire the lock (make sure that no other operation is\n        running in the repo), then execute the given command as a subprocess and wait\n        for its termination, release the lock and return the user command's return\n        code as borg's return code.\n\n        .. note::\n\n            If you copy a repository with the lock held, the lock will be present in\n            the copy. Thus, before using borg on the copy from a different host,\n            you need to use \"borg break-lock\" on the copied repository, because\n            Borg is cautious and does not automatically remove stale locks made by a different host.\n        \"\"\"\n        )\n        subparser = subparsers.add_parser(\n            \"with-lock\",\n            parents=[common_parser],\n            add_help=False,\n            description=self.do_with_lock.__doc__,\n            epilog=with_lock_epilog,\n            formatter_class=argparse.RawDescriptionHelpFormatter,\n            help=\"run user command with lock held\",\n        )\n        subparser.set_defaults(func=self.do_with_lock)\n        subparser.add_argument(\"command\", metavar=\"COMMAND\", help=\"command to run\")\n        subparser.add_argument(\"args\", metavar=\"ARGS\", nargs=argparse.REMAINDER, help=\"command arguments\")\n", "path": "src/borg/archiver/lock_cmds.py"}, {"content": "import os\n\nfrom ...constants import *  # NOQA\nfrom . import cmd, generate_archiver_tests, RK_ENCRYPTION\n\npytest_generate_tests = lambda metafunc: generate_archiver_tests(metafunc, kinds=\"local,remote,binary\")  # NOQA\n\n\ndef test_break_lock(archivers, request):\n    archiver = request.getfixturevalue(archivers)\n    cmd(archiver, \"rcreate\", RK_ENCRYPTION)\n    cmd(archiver, \"break-lock\")\n\n\ndef test_with_lock(archivers, request):\n    archiver = request.getfixturevalue(archivers)\n    cmd(archiver, \"rcreate\", RK_ENCRYPTION)\n    lock_path = os.path.join(archiver.repository_path, \"lock.exclusive\")\n    command = \"python3\", \"-c\", 'import os, sys; sys.exit(42 if os.path.exists(\"%s\") else 23)' % lock_path\n    cmd(archiver, \"with-lock\", *command, fork=True, exit_code=42)\n\n\ndef test_with_lock_non_existent_command(archivers, request):\n    archiver = request.getfixturevalue(archivers)\n    cmd(archiver, \"rcreate\", RK_ENCRYPTION)\n    command = [\"non_existent_command\"]\n    cmd(archiver, \"with-lock\", *command, fork=True, exit_code=EXIT_ERROR)\n", "path": "src/borg/testsuite/archiver/lock_cmds.py"}]}