We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
Travis ppc64le network issue work-around
### Description

Hi,

I currently see network issues with docker containers on Travis CI ppc64le builders.
Essentially, connections seem to time out, e.g., on `yum update`.

The same problem seems to be known in conda-forge and adding `--network=host` to `docker run` seems to solve the issue.

Do you know how I can most simply apply the same work-around in cibuildwheel?

X-ref:
- https://github.com/conda-forge/conda-smithy/pull/1520

### Build log

X-ref:
- https://github.com/openPMD/openPMD-api/pull/1136

### CI config

- https://github.com/openPMD/openPMD-api/tree/wheels

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `cibuildwheel/docker_container.py`
Content:
```
1 import io
2 import json
3 import os
4 import shlex
5 import subprocess
6 import sys
7 import uuid
8 from pathlib import Path, PurePath
9 from types import TracebackType
10 from typing import IO, Dict, List, Optional, Sequence, Type, cast
11 
12 from .typing import PathOrStr, PopenBytes
13 
14 
15 class DockerContainer:
16     """
17     An object that represents a running Docker container.
18 
19     Intended for use as a context manager e.g.
20     `with DockerContainer(docker_image = 'ubuntu') as docker:`
21 
22     A bash shell is running in the remote container. When `call()` is invoked,
23     the command is relayed to the remote shell, and the results are streamed
24     back to cibuildwheel.
25     """
26 
27     UTILITY_PYTHON = "/opt/python/cp38-cp38/bin/python"
28 
29     process: PopenBytes
30     bash_stdin: IO[bytes]
31     bash_stdout: IO[bytes]
32 
33     def __init__(
34         self, *, docker_image: str, simulate_32_bit: bool = False, cwd: Optional[PathOrStr] = None
35     ):
36         if not docker_image:
37             raise ValueError("Must have a non-empty docker image to run.")
38 
39         self.docker_image = docker_image
40         self.simulate_32_bit = simulate_32_bit
41         self.cwd = cwd
42         self.name: Optional[str] = None
43 
44     def __enter__(self) -> "DockerContainer":
45         self.name = f"cibuildwheel-{uuid.uuid4()}"
46         cwd_args = ["-w", str(self.cwd)] if self.cwd else []
47         shell_args = ["linux32", "/bin/bash"] if self.simulate_32_bit else ["/bin/bash"]
48         subprocess.run(
49             [
50                 "docker",
51                 "create",
52                 "--env=CIBUILDWHEEL",
53                 f"--name={self.name}",
54                 "--interactive",
55                 "--volume=/:/host",  # ignored on CircleCI
56                 *cwd_args,
57                 self.docker_image,
58                 *shell_args,
59             ],
60             check=True,
61         )
62         self.process = subprocess.Popen(
63             [
64                 "docker",
65                 "start",
66                 "--attach",
67                 "--interactive",
68                 self.name,
69             ],
70             stdin=subprocess.PIPE,
71             stdout=subprocess.PIPE,
72         )
73 
74         assert self.process.stdin and self.process.stdout
75         self.bash_stdin = self.process.stdin
76         self.bash_stdout = self.process.stdout
77 
78         # run a noop command to block until the container is responding
79         self.call(["/bin/true"])
80 
81         return self
82 
83     def __exit__(
84         self,
85         exc_type: Optional[Type[BaseException]],
86         exc_val: Optional[BaseException],
87         exc_tb: Optional[TracebackType],
88     ) -> None:
89 
90         self.bash_stdin.close()
91         self.process.terminate()
92         self.process.wait()
93 
94         assert isinstance(self.name, str)
95 
96         subprocess.run(["docker", "rm", "--force", "-v", self.name], stdout=subprocess.DEVNULL)
97         self.name = None
98 
99     def copy_into(self, from_path: Path, to_path: PurePath) -> None:
100         # `docker cp` causes 'no space left on device' error when
101         # a container is running and the host filesystem is
102         # mounted. https://github.com/moby/moby/issues/38995
103         # Use `docker exec` instead.
104 
105         if from_path.is_dir():
106             self.call(["mkdir", "-p", to_path])
107             subprocess.run(
108                 f"tar cf - . | docker exec -i {self.name} tar -xC {shell_quote(to_path)} -f -",
109                 shell=True,
110                 check=True,
111                 cwd=from_path,
112             )
113         else:
114             subprocess.run(
115                 f'cat {shell_quote(from_path)} | docker exec -i {self.name} sh -c "cat > {shell_quote(to_path)}"',
116                 shell=True,
117                 check=True,
118             )
119 
120     def copy_out(self, from_path: PurePath, to_path: Path) -> None:
121         # note: we assume from_path is a dir
122         to_path.mkdir(parents=True, exist_ok=True)
123 
124         subprocess.run(
125             f"docker exec -i {self.name} tar -cC {shell_quote(from_path)} -f - . | tar -xf -",
126             shell=True,
127             check=True,
128             cwd=to_path,
129         )
130 
131     def glob(self, path: PurePath, pattern: str) -> List[PurePath]:
132         glob_pattern = os.path.join(str(path), pattern)
133 
134         path_strings = json.loads(
135             self.call(
136                 [
137                     self.UTILITY_PYTHON,
138                     "-c",
139                     f"import sys, json, glob; json.dump(glob.glob({glob_pattern!r}), sys.stdout)",
140                 ],
141                 capture_output=True,
142             )
143         )
144 
145         return [PurePath(p) for p in path_strings]
146 
147     def call(
148         self,
149         args: Sequence[PathOrStr],
150         env: Optional[Dict[str, str]] = None,
151         capture_output: bool = False,
152         cwd: Optional[PathOrStr] = None,
153     ) -> str:
154 
155         chdir = f"cd {cwd}" if cwd else ""
156         env_assignments = (
157             " ".join(f"{shlex.quote(k)}={shlex.quote(v)}" for k, v in env.items())
158             if env is not None
159             else ""
160         )
161         command = " ".join(shlex.quote(str(a)) for a in args)
162         end_of_message = str(uuid.uuid4())
163 
164         # log the command we're executing
165         print(f"    + {command}")
166 
167         # Write a command to the remote shell. First we change the
168         # cwd, if that's required. Then, we use the `env` utility to run
169         # `command` inside the specified environment. We use `env` because it
170         # can cope with spaces and strange characters in the name or value.
171         # Finally, the remote shell is told to write a footer - this will show
172         # up in the output so we know when to stop reading, and will include
173         # the return code of `command`.
174         self.bash_stdin.write(
175             bytes(
176                 f"""(
177             {chdir}
178             env {env_assignments} {command}
179             printf "%04d%s\n" $? {end_of_message}
180         )
181         """,
182                 encoding="utf8",
183                 errors="surrogateescape",
184             )
185         )
186         self.bash_stdin.flush()
187 
188         if capture_output:
189             output_io: IO[bytes] = io.BytesIO()
190         else:
191             output_io = sys.stdout.buffer
192 
193         while True:
194             line = self.bash_stdout.readline()
195 
196             if line.endswith(bytes(end_of_message, encoding="utf8") + b"\n"):
197                 # fmt: off
198                 footer_offset = (
199                     len(line)
200                     - 1  # newline character
201                     - len(end_of_message)  # delimiter
202                     - 4  # 4 return code decimals
203                 )
204                 # fmt: on
205                 return_code_str = line[footer_offset : footer_offset + 4]
206                 return_code = int(return_code_str)
207                 # add the last line to output, without the footer
208                 output_io.write(line[0:footer_offset])
209                 break
210             else:
211                 output_io.write(line)
212 
213         if isinstance(output_io, io.BytesIO):
214             output = str(output_io.getvalue(), encoding="utf8", errors="surrogateescape")
215         else:
216             output = ""
217 
218         if return_code != 0:
219             raise subprocess.CalledProcessError(return_code, args, output)
220 
221         return output
222 
223     def get_environment(self) -> Dict[str, str]:
224         env = json.loads(
225             self.call(
226                 [
227                     self.UTILITY_PYTHON,
228                     "-c",
229                     "import sys, json, os; json.dump(os.environ.copy(), sys.stdout)",
230                 ],
231                 capture_output=True,
232             )
233         )
234         return cast(Dict[str, str], env)
235 
236     def environment_executor(self, command: List[str], environment: Dict[str, str]) -> str:
237         # used as an EnvironmentExecutor to evaluate commands and capture output
238         return self.call(command, env=environment, capture_output=True)
239 
240 
241 def shell_quote(path: PurePath) -> str:
242     return shlex.quote(str(path))
243
```
--- 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/cibuildwheel/docker_container.py b/cibuildwheel/docker_container.py
--- a/cibuildwheel/docker_container.py
+++ b/cibuildwheel/docker_container.py
@@ -1,6 +1,7 @@
 import io
 import json
 import os
+import platform
 import shlex
 import subprocess
 import sys
@@ -9,6 +10,8 @@
 from types import TracebackType
 from typing import IO, Dict, List, Optional, Sequence, Type, cast
 
+from cibuildwheel.util import CIProvider, detect_ci_provider
+
 from .typing import PathOrStr, PopenBytes
 
 
@@ -44,6 +47,15 @@
     def __enter__(self) -> "DockerContainer":
         self.name = f"cibuildwheel-{uuid.uuid4()}"
         cwd_args = ["-w", str(self.cwd)] if self.cwd else []
+
+        # work-around for Travis-CI PPC64le Docker runs since 2021:
+        # this avoids network splits
+        # https://github.com/pypa/cibuildwheel/issues/904
+        # https://github.com/conda-forge/conda-smithy/pull/1520
+        network_args = []
+        if detect_ci_provider() == CIProvider.travis_ci and platform.machine() == "ppc64le":
+            network_args = ["--network=host"]
+
         shell_args = ["linux32", "/bin/bash"] if self.simulate_32_bit else ["/bin/bash"]
         subprocess.run(
             [
@@ -53,6 +65,7 @@
                 f"--name={self.name}",
                 "--interactive",
                 "--volume=/:/host",  # ignored on CircleCI
+                *network_args,
                 *cwd_args,
                 self.docker_image,
                 *shell_args,

{"golden_diff": "diff --git a/cibuildwheel/docker_container.py b/cibuildwheel/docker_container.py\n--- a/cibuildwheel/docker_container.py\n+++ b/cibuildwheel/docker_container.py\n@@ -1,6 +1,7 @@\n import io\n import json\n import os\n+import platform\n import shlex\n import subprocess\n import sys\n@@ -9,6 +10,8 @@\n from types import TracebackType\n from typing import IO, Dict, List, Optional, Sequence, Type, cast\n \n+from cibuildwheel.util import CIProvider, detect_ci_provider\n+\n from .typing import PathOrStr, PopenBytes\n \n \n@@ -44,6 +47,15 @@\n     def __enter__(self) -> \"DockerContainer\":\n         self.name = f\"cibuildwheel-{uuid.uuid4()}\"\n         cwd_args = [\"-w\", str(self.cwd)] if self.cwd else []\n+\n+        # work-around for Travis-CI PPC64le Docker runs since 2021:\n+        # this avoids network splits\n+        # https://github.com/pypa/cibuildwheel/issues/904\n+        # https://github.com/conda-forge/conda-smithy/pull/1520\n+        network_args = []\n+        if detect_ci_provider() == CIProvider.travis_ci and platform.machine() == \"ppc64le\":\n+            network_args = [\"--network=host\"]\n+\n         shell_args = [\"linux32\", \"/bin/bash\"] if self.simulate_32_bit else [\"/bin/bash\"]\n         subprocess.run(\n             [\n@@ -53,6 +65,7 @@\n                 f\"--name={self.name}\",\n                 \"--interactive\",\n                 \"--volume=/:/host\",  # ignored on CircleCI\n+                *network_args,\n                 *cwd_args,\n                 self.docker_image,\n                 *shell_args,\n", "issue": "Travis ppc64le network issue work-around\n### Description\r\n\r\nHi,\r\n\r\nI currently see network issues with docker containers on Travis CI ppc64le builders.\r\nEssentially, connections seem to time out, e.g., on `yum update`.\r\n\r\nThe same problem seems to be known in conda-forge and adding `--network=host` to `docker run` seems to solve the issue.\r\n\r\nDo you know how I can most simply apply the same work-around in cibuildwheel?\r\n\r\nX-ref:\r\n- https://github.com/conda-forge/conda-smithy/pull/1520\r\n\r\n### Build log\r\n\r\nX-ref:\r\n- https://github.com/openPMD/openPMD-api/pull/1136\r\n\r\n### CI config\r\n\r\n- https://github.com/openPMD/openPMD-api/tree/wheels\n", "before_files": [{"content": "import io\nimport json\nimport os\nimport shlex\nimport subprocess\nimport sys\nimport uuid\nfrom pathlib import Path, PurePath\nfrom types import TracebackType\nfrom typing import IO, Dict, List, Optional, Sequence, Type, cast\n\nfrom .typing import PathOrStr, PopenBytes\n\n\nclass DockerContainer:\n    \"\"\"\n    An object that represents a running Docker container.\n\n    Intended for use as a context manager e.g.\n    `with DockerContainer(docker_image = 'ubuntu') as docker:`\n\n    A bash shell is running in the remote container. When `call()` is invoked,\n    the command is relayed to the remote shell, and the results are streamed\n    back to cibuildwheel.\n    \"\"\"\n\n    UTILITY_PYTHON = \"/opt/python/cp38-cp38/bin/python\"\n\n    process: PopenBytes\n    bash_stdin: IO[bytes]\n    bash_stdout: IO[bytes]\n\n    def __init__(\n        self, *, docker_image: str, simulate_32_bit: bool = False, cwd: Optional[PathOrStr] = None\n    ):\n        if not docker_image:\n            raise ValueError(\"Must have a non-empty docker image to run.\")\n\n        self.docker_image = docker_image\n        self.simulate_32_bit = simulate_32_bit\n        self.cwd = cwd\n        self.name: Optional[str] = None\n\n    def __enter__(self) -> \"DockerContainer\":\n        self.name = f\"cibuildwheel-{uuid.uuid4()}\"\n        cwd_args = [\"-w\", str(self.cwd)] if self.cwd else []\n        shell_args = [\"linux32\", \"/bin/bash\"] if self.simulate_32_bit else [\"/bin/bash\"]\n        subprocess.run(\n            [\n                \"docker\",\n                \"create\",\n                \"--env=CIBUILDWHEEL\",\n                f\"--name={self.name}\",\n                \"--interactive\",\n                \"--volume=/:/host\",  # ignored on CircleCI\n                *cwd_args,\n                self.docker_image,\n                *shell_args,\n            ],\n            check=True,\n        )\n        self.process = subprocess.Popen(\n            [\n                \"docker\",\n                \"start\",\n                \"--attach\",\n                \"--interactive\",\n                self.name,\n            ],\n            stdin=subprocess.PIPE,\n            stdout=subprocess.PIPE,\n        )\n\n        assert self.process.stdin and self.process.stdout\n        self.bash_stdin = self.process.stdin\n        self.bash_stdout = self.process.stdout\n\n        # run a noop command to block until the container is responding\n        self.call([\"/bin/true\"])\n\n        return self\n\n    def __exit__(\n        self,\n        exc_type: Optional[Type[BaseException]],\n        exc_val: Optional[BaseException],\n        exc_tb: Optional[TracebackType],\n    ) -> None:\n\n        self.bash_stdin.close()\n        self.process.terminate()\n        self.process.wait()\n\n        assert isinstance(self.name, str)\n\n        subprocess.run([\"docker\", \"rm\", \"--force\", \"-v\", self.name], stdout=subprocess.DEVNULL)\n        self.name = None\n\n    def copy_into(self, from_path: Path, to_path: PurePath) -> None:\n        # `docker cp` causes 'no space left on device' error when\n        # a container is running and the host filesystem is\n        # mounted. https://github.com/moby/moby/issues/38995\n        # Use `docker exec` instead.\n\n        if from_path.is_dir():\n            self.call([\"mkdir\", \"-p\", to_path])\n            subprocess.run(\n                f\"tar cf - . | docker exec -i {self.name} tar -xC {shell_quote(to_path)} -f -\",\n                shell=True,\n                check=True,\n                cwd=from_path,\n            )\n        else:\n            subprocess.run(\n                f'cat {shell_quote(from_path)} | docker exec -i {self.name} sh -c \"cat > {shell_quote(to_path)}\"',\n                shell=True,\n                check=True,\n            )\n\n    def copy_out(self, from_path: PurePath, to_path: Path) -> None:\n        # note: we assume from_path is a dir\n        to_path.mkdir(parents=True, exist_ok=True)\n\n        subprocess.run(\n            f\"docker exec -i {self.name} tar -cC {shell_quote(from_path)} -f - . | tar -xf -\",\n            shell=True,\n            check=True,\n            cwd=to_path,\n        )\n\n    def glob(self, path: PurePath, pattern: str) -> List[PurePath]:\n        glob_pattern = os.path.join(str(path), pattern)\n\n        path_strings = json.loads(\n            self.call(\n                [\n                    self.UTILITY_PYTHON,\n                    \"-c\",\n                    f\"import sys, json, glob; json.dump(glob.glob({glob_pattern!r}), sys.stdout)\",\n                ],\n                capture_output=True,\n            )\n        )\n\n        return [PurePath(p) for p in path_strings]\n\n    def call(\n        self,\n        args: Sequence[PathOrStr],\n        env: Optional[Dict[str, str]] = None,\n        capture_output: bool = False,\n        cwd: Optional[PathOrStr] = None,\n    ) -> str:\n\n        chdir = f\"cd {cwd}\" if cwd else \"\"\n        env_assignments = (\n            \" \".join(f\"{shlex.quote(k)}={shlex.quote(v)}\" for k, v in env.items())\n            if env is not None\n            else \"\"\n        )\n        command = \" \".join(shlex.quote(str(a)) for a in args)\n        end_of_message = str(uuid.uuid4())\n\n        # log the command we're executing\n        print(f\"    + {command}\")\n\n        # Write a command to the remote shell. First we change the\n        # cwd, if that's required. Then, we use the `env` utility to run\n        # `command` inside the specified environment. We use `env` because it\n        # can cope with spaces and strange characters in the name or value.\n        # Finally, the remote shell is told to write a footer - this will show\n        # up in the output so we know when to stop reading, and will include\n        # the return code of `command`.\n        self.bash_stdin.write(\n            bytes(\n                f\"\"\"(\n            {chdir}\n            env {env_assignments} {command}\n            printf \"%04d%s\\n\" $? {end_of_message}\n        )\n        \"\"\",\n                encoding=\"utf8\",\n                errors=\"surrogateescape\",\n            )\n        )\n        self.bash_stdin.flush()\n\n        if capture_output:\n            output_io: IO[bytes] = io.BytesIO()\n        else:\n            output_io = sys.stdout.buffer\n\n        while True:\n            line = self.bash_stdout.readline()\n\n            if line.endswith(bytes(end_of_message, encoding=\"utf8\") + b\"\\n\"):\n                # fmt: off\n                footer_offset = (\n                    len(line)\n                    - 1  # newline character\n                    - len(end_of_message)  # delimiter\n                    - 4  # 4 return code decimals\n                )\n                # fmt: on\n                return_code_str = line[footer_offset : footer_offset + 4]\n                return_code = int(return_code_str)\n                # add the last line to output, without the footer\n                output_io.write(line[0:footer_offset])\n                break\n            else:\n                output_io.write(line)\n\n        if isinstance(output_io, io.BytesIO):\n            output = str(output_io.getvalue(), encoding=\"utf8\", errors=\"surrogateescape\")\n        else:\n            output = \"\"\n\n        if return_code != 0:\n            raise subprocess.CalledProcessError(return_code, args, output)\n\n        return output\n\n    def get_environment(self) -> Dict[str, str]:\n        env = json.loads(\n            self.call(\n                [\n                    self.UTILITY_PYTHON,\n                    \"-c\",\n                    \"import sys, json, os; json.dump(os.environ.copy(), sys.stdout)\",\n                ],\n                capture_output=True,\n            )\n        )\n        return cast(Dict[str, str], env)\n\n    def environment_executor(self, command: List[str], environment: Dict[str, str]) -> str:\n        # used as an EnvironmentExecutor to evaluate commands and capture output\n        return self.call(command, env=environment, capture_output=True)\n\n\ndef shell_quote(path: PurePath) -> str:\n    return shlex.quote(str(path))\n", "path": "cibuildwheel/docker_container.py"}], "after_files": [{"content": "import io\nimport json\nimport os\nimport platform\nimport shlex\nimport subprocess\nimport sys\nimport uuid\nfrom pathlib import Path, PurePath\nfrom types import TracebackType\nfrom typing import IO, Dict, List, Optional, Sequence, Type, cast\n\nfrom cibuildwheel.util import CIProvider, detect_ci_provider\n\nfrom .typing import PathOrStr, PopenBytes\n\n\nclass DockerContainer:\n    \"\"\"\n    An object that represents a running Docker container.\n\n    Intended for use as a context manager e.g.\n    `with DockerContainer(docker_image = 'ubuntu') as docker:`\n\n    A bash shell is running in the remote container. When `call()` is invoked,\n    the command is relayed to the remote shell, and the results are streamed\n    back to cibuildwheel.\n    \"\"\"\n\n    UTILITY_PYTHON = \"/opt/python/cp38-cp38/bin/python\"\n\n    process: PopenBytes\n    bash_stdin: IO[bytes]\n    bash_stdout: IO[bytes]\n\n    def __init__(\n        self, *, docker_image: str, simulate_32_bit: bool = False, cwd: Optional[PathOrStr] = None\n    ):\n        if not docker_image:\n            raise ValueError(\"Must have a non-empty docker image to run.\")\n\n        self.docker_image = docker_image\n        self.simulate_32_bit = simulate_32_bit\n        self.cwd = cwd\n        self.name: Optional[str] = None\n\n    def __enter__(self) -> \"DockerContainer\":\n        self.name = f\"cibuildwheel-{uuid.uuid4()}\"\n        cwd_args = [\"-w\", str(self.cwd)] if self.cwd else []\n\n        # work-around for Travis-CI PPC64le Docker runs since 2021:\n        # this avoids network splits\n        # https://github.com/pypa/cibuildwheel/issues/904\n        # https://github.com/conda-forge/conda-smithy/pull/1520\n        network_args = []\n        if detect_ci_provider() == CIProvider.travis_ci and platform.machine() == \"ppc64le\":\n            network_args = [\"--network=host\"]\n\n        shell_args = [\"linux32\", \"/bin/bash\"] if self.simulate_32_bit else [\"/bin/bash\"]\n        subprocess.run(\n            [\n                \"docker\",\n                \"create\",\n                \"--env=CIBUILDWHEEL\",\n                f\"--name={self.name}\",\n                \"--interactive\",\n                \"--volume=/:/host\",  # ignored on CircleCI\n                *network_args,\n                *cwd_args,\n                self.docker_image,\n                *shell_args,\n            ],\n            check=True,\n        )\n        self.process = subprocess.Popen(\n            [\n                \"docker\",\n                \"start\",\n                \"--attach\",\n                \"--interactive\",\n                self.name,\n            ],\n            stdin=subprocess.PIPE,\n            stdout=subprocess.PIPE,\n        )\n\n        assert self.process.stdin and self.process.stdout\n        self.bash_stdin = self.process.stdin\n        self.bash_stdout = self.process.stdout\n\n        # run a noop command to block until the container is responding\n        self.call([\"/bin/true\"])\n\n        return self\n\n    def __exit__(\n        self,\n        exc_type: Optional[Type[BaseException]],\n        exc_val: Optional[BaseException],\n        exc_tb: Optional[TracebackType],\n    ) -> None:\n\n        self.bash_stdin.close()\n        self.process.terminate()\n        self.process.wait()\n\n        assert isinstance(self.name, str)\n\n        subprocess.run([\"docker\", \"rm\", \"--force\", \"-v\", self.name], stdout=subprocess.DEVNULL)\n        self.name = None\n\n    def copy_into(self, from_path: Path, to_path: PurePath) -> None:\n        # `docker cp` causes 'no space left on device' error when\n        # a container is running and the host filesystem is\n        # mounted. https://github.com/moby/moby/issues/38995\n        # Use `docker exec` instead.\n\n        if from_path.is_dir():\n            self.call([\"mkdir\", \"-p\", to_path])\n            subprocess.run(\n                f\"tar cf - . | docker exec -i {self.name} tar -xC {shell_quote(to_path)} -f -\",\n                shell=True,\n                check=True,\n                cwd=from_path,\n            )\n        else:\n            subprocess.run(\n                f'cat {shell_quote(from_path)} | docker exec -i {self.name} sh -c \"cat > {shell_quote(to_path)}\"',\n                shell=True,\n                check=True,\n            )\n\n    def copy_out(self, from_path: PurePath, to_path: Path) -> None:\n        # note: we assume from_path is a dir\n        to_path.mkdir(parents=True, exist_ok=True)\n\n        subprocess.run(\n            f\"docker exec -i {self.name} tar -cC {shell_quote(from_path)} -f - . | tar -xf -\",\n            shell=True,\n            check=True,\n            cwd=to_path,\n        )\n\n    def glob(self, path: PurePath, pattern: str) -> List[PurePath]:\n        glob_pattern = os.path.join(str(path), pattern)\n\n        path_strs = json.loads(\n            self.call(\n                [\n                    self.UTILITY_PYTHON,\n                    \"-c\",\n                    f\"import sys, json, glob; json.dump(glob.glob({glob_pattern!r}), sys.stdout)\",\n                ],\n                capture_output=True,\n            )\n        )\n\n        return [PurePath(p) for p in path_strs]\n\n    def call(\n        self,\n        args: Sequence[PathOrStr],\n        env: Optional[Dict[str, str]] = None,\n        capture_output: bool = False,\n        cwd: Optional[PathOrStr] = None,\n    ) -> str:\n\n        chdir = f\"cd {cwd}\" if cwd else \"\"\n        env_assignments = (\n            \" \".join(f\"{shlex.quote(k)}={shlex.quote(v)}\" for k, v in env.items())\n            if env is not None\n            else \"\"\n        )\n        command = \" \".join(shlex.quote(str(a)) for a in args)\n        end_of_message = str(uuid.uuid4())\n\n        # log the command we're executing\n        print(f\"    + {command}\")\n\n        # Write a command to the remote shell. First we change the\n        # cwd, if that's required. Then, we use the `env` utility to run\n        # `command` inside the specified environment. We use `env` because it\n        # can cope with spaces and strange characters in the name or value.\n        # Finally, the remote shell is told to write a footer - this will show\n        # up in the output so we know when to stop reading, and will include\n        # the returncode of `command`.\n        self.bash_stdin.write(\n            bytes(\n                f\"\"\"(\n            {chdir}\n            env {env_assignments} {command}\n            printf \"%04d%s\\n\" $? {end_of_message}\n        )\n        \"\"\",\n                encoding=\"utf8\",\n                errors=\"surrogateescape\",\n            )\n        )\n        self.bash_stdin.flush()\n\n        if capture_output:\n            output_io: IO[bytes] = io.BytesIO()\n        else:\n            output_io = sys.stdout.buffer\n\n        while True:\n            line = self.bash_stdout.readline()\n\n            if line.endswith(bytes(end_of_message, encoding=\"utf8\") + b\"\\n\"):\n                # fmt: off\n                footer_offset = (\n                    len(line)\n                    - 1  # newline character\n                    - len(end_of_message)  # delimiter\n                    - 4  # 4 returncode decimals\n                )\n                # fmt: on\n                returncode_str = line[footer_offset : footer_offset + 4]\n                returncode = int(returncode_str)\n                # add the last line to output, without the footer\n                output_io.write(line[0:footer_offset])\n                break\n            else:\n                output_io.write(line)\n\n        if isinstance(output_io, io.BytesIO):\n            output = str(output_io.getvalue(), encoding=\"utf8\", errors=\"surrogateescape\")\n        else:\n            output = \"\"\n\n        if returncode != 0:\n            raise subprocess.CalledProcessError(returncode, args, output)\n\n        return output\n\n    def get_environment(self) -> Dict[str, str]:\n        env = json.loads(\n            self.call(\n                [\n                    self.UTILITY_PYTHON,\n                    \"-c\",\n                    \"import sys, json, os; json.dump(os.environ.copy(), sys.stdout)\",\n                ],\n                capture_output=True,\n            )\n        )\n        return cast(Dict[str, str], env)\n\n    def environment_executor(self, command: List[str], environment: Dict[str, str]) -> str:\n        # used as an EnvironmentExecutor to evaluate commands and capture output\n        return self.call(command, env=environment, capture_output=True)\n\n\ndef shell_quote(path: PurePath) -> str:\n    return shlex.quote(str(path))\n", "path": "cibuildwheel/docker_container.py"}]}