Skip to content

Commit 0210c89

Browse files
committed
Refine Windows start() and check_subprocess() in executor
- start() Windows branch: replace shell=True/self.command with an argv list built from the same pieces used in WINDOWS_PROC_START_COMMAND (executable, datadir, port, logfile, startparams, postgres_options). Pass env=self.envvars so locale variables reach pg_ctl. Mirrors the list-form invocation already used in stop(). - check_subprocess(): only return self.running() on Windows (where start() is synchronous and mirakuru's loop is bypassed). On all other platforms delegate to super().check_subprocess() so TCPExecutor's port-reachability check and subprocess-alive check remain in effect, enabling fast ProcessFinishedWithError detection when pg_ctl fails to start. - Add missing shlex import. Made-with: Cursor
1 parent 5534379 commit 0210c89

1 file changed

Lines changed: 32 additions & 13 deletions

File tree

pytest_postgresql/executor.py

Lines changed: 32 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
import os.path
2323
import platform
2424
import re
25+
import shlex
2526
import shutil
2627
import subprocess
2728
import tempfile
@@ -183,10 +184,22 @@ def start(self: T) -> T:
183184
# while waiting for the launcher subprocess to indicate readiness,
184185
# but by the time polling begins the launcher has already exited
185186
# 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)
190203
if result.returncode != 0:
191204
raise ProcessFinishedWithError(self, result.returncode)
192205
return self
@@ -268,16 +281,22 @@ def running(self) -> bool:
268281
return result.returncode == 0
269282

270283
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.
279296
"""
280-
return self.running()
297+
if platform.system() == "Windows":
298+
return self.running()
299+
return super().check_subprocess()
281300

282301
def _windows_terminate_process(self, _sig: Optional[int] = None) -> None:
283302
"""Terminate process on Windows.

0 commit comments

Comments
 (0)