|
22 | 22 | import os.path |
23 | 23 | import platform |
24 | 24 | import re |
| 25 | +import shlex |
25 | 26 | import shutil |
26 | 27 | import subprocess |
27 | 28 | import tempfile |
@@ -183,10 +184,22 @@ def start(self: T) -> T: |
183 | 184 | # while waiting for the launcher subprocess to indicate readiness, |
184 | 185 | # but by the time polling begins the launcher has already exited |
185 | 186 | # so the loop never sees a live process and times out. |
186 | | - # Running the command synchronously via the shell lets cmd.exe |
187 | | - # handle quoting and lets pg_ctl itself act as the readiness |
188 | | - # barrier, which is more reliable than mirakuru's approach here. |
189 | | - result = subprocess.run(self.command, shell=True, check=False) |
| 187 | + # Run pg_ctl synchronously as an argv list (no shell) so quoting |
| 188 | + # is handled safely by subprocess, mirroring the stop() approach. |
| 189 | + postgres_options_str = f" {self.postgres_options}" if self.postgres_options else "" |
| 190 | + pg_options = ( |
| 191 | + f"-F -p {self.port} -c log_destination=stderr " |
| 192 | + f"-c logging_collector=off{postgres_options_str}" |
| 193 | + ) |
| 194 | + args = [ |
| 195 | + self.executable, |
| 196 | + "start", |
| 197 | + "-D", self.datadir, |
| 198 | + "-o", pg_options, |
| 199 | + "-l", self.logfile, |
| 200 | + *shlex.split(self.startparams), |
| 201 | + ] |
| 202 | + result = subprocess.run(args, check=False, env=self.envvars) |
190 | 203 | if result.returncode != 0: |
191 | 204 | raise ProcessFinishedWithError(self, result.returncode) |
192 | 205 | return self |
@@ -268,16 +281,22 @@ def running(self) -> bool: |
268 | 281 | return result.returncode == 0 |
269 | 282 |
|
270 | 283 | def check_subprocess(self) -> bool: |
271 | | - """Check if PostgreSQL server is running via pg_ctl status. |
272 | | -
|
273 | | - Overrides mirakuru's default check which requires the launcher |
274 | | - subprocess (pg_ctl start -w) to still be alive. On all platforms |
275 | | - pg_ctl start -w exits as soon as the server is ready, so the |
276 | | - subprocess is always dead by the time mirakuru polls. Using |
277 | | - pg_ctl status instead makes start() and stopped() reliable on |
278 | | - Windows where the subprocess exit races the polling interval. |
| 284 | + """Check whether the PostgreSQL server is ready. |
| 285 | +
|
| 286 | + On Windows the launcher (pg_ctl start -w) is invoked synchronously |
| 287 | + in start(), so mirakuru's polling loop is never reached and this |
| 288 | + method is called only from running()-style checks. Returning |
| 289 | + self.running() (pg_ctl status) is appropriate there. |
| 290 | +
|
| 291 | + On non-Windows, mirakuru's TCPExecutor.check_subprocess() is the |
| 292 | + correct check: it verifies both that the launcher subprocess is still |
| 293 | + alive (enabling fast ProcessFinishedWithError on pg_ctl failures) and |
| 294 | + that the TCP port is accepting connections. Delegating to super() |
| 295 | + preserves that failure-detection behaviour. |
279 | 296 | """ |
280 | | - return self.running() |
| 297 | + if platform.system() == "Windows": |
| 298 | + return self.running() |
| 299 | + return super().check_subprocess() |
281 | 300 |
|
282 | 301 | def _windows_terminate_process(self, _sig: Optional[int] = None) -> None: |
283 | 302 | """Terminate process on Windows. |
|
0 commit comments