Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 18 additions & 18 deletions ptyprocess/ptyprocess.py
Original file line number Diff line number Diff line change
Expand Up @@ -275,30 +275,30 @@ def spawn(
for pair in zip([2] + spass_fds, spass_fds + [max_fd]):
os.closerange(pair[0]+1, pair[1])

if cwd is not None:
os.chdir(cwd)

if preexec_fn is not None:
try:
preexec_fn()
except Exception as e:
ename = type(e).__name__
tosend = '{}:0:{}'.format(ename, str(e))
if PY3:
tosend = tosend.encode('utf-8')

os.write(exec_err_pipe_write, tosend)
os.close(exec_err_pipe_write)
os._exit(1)

try:
if cwd is not None:
os.chdir(cwd)

if preexec_fn is not None:
try:
preexec_fn()
except Exception as e:
ename = type(e).__name__
tosend = '{}:0:{}'.format(ename, str(e))
if PY3:
tosend = tosend.encode('utf-8')

os.write(exec_err_pipe_write, tosend)
os.close(exec_err_pipe_write)
os._exit(1)

if env is None:
os.execv(command, argv)
else:
os.execvpe(command, argv, env)
except OSError as err:
# [issue #119] 5. If exec fails, the child writes the error
# code back to the parent using the pipe, then exits.
# [issue #119] 5. If chdir or exec fails, the child writes the
# error code back to the parent using the pipe, then exits.
tosend = 'OSError:{}:{}'.format(err.errno, str(err))
if PY3:
tosend = tosend.encode('utf-8')
Expand Down
72 changes: 72 additions & 0 deletions tests/test_cwd.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import errno
import os
import subprocess
import sys
import tempfile
import unittest

from ptyprocess import PtyProcess


class CwdTestCase(unittest.TestCase):
def _spawn_with_invalid_cwd(self, cwd):
# Isolate the call: an unreported child-side exception must not let the
# forked child continue running the test suite.
code = """
from ptyprocess import PtyProcess
import sys
try:
child = PtyProcess.spawn([sys.executable, '-c', 'pass'], cwd=sys.argv[1])
except OSError as err:
print('error:{}'.format(err.errno))
else:
child.close(force=True)
print('spawned')
"""
return subprocess.check_output(
[sys.executable, '-c', code, cwd],
cwd=os.path.dirname(os.path.dirname(__file__)),
timeout=10,
)

def test_missing_cwd(self):
with tempfile.TemporaryDirectory() as directory:
output = self._spawn_with_invalid_cwd(
os.path.join(directory, 'missing')
)
self.assertEqual(output, 'error:{}\n'.format(errno.ENOENT).encode())

def test_cwd_is_not_a_directory(self):
with tempfile.NamedTemporaryFile() as file:
output = self._spawn_with_invalid_cwd(file.name)
self.assertEqual(output, 'error:{}\n'.format(errno.ENOTDIR).encode())

def test_valid_cwd_before_preexec(self):
original_cwd = os.getcwd()
with tempfile.TemporaryDirectory() as directory:
def preexec():
with open('preexec-marker', 'w') as file:
file.write('ready')

child = PtyProcess.spawn(
[sys.executable, '-c', 'import os; print(os.getcwd())'],
cwd=directory,
preexec_fn=preexec,
)
try:
output = b''
while True:
try:
output += child.read()
except EOFError:
break
self.assertEqual(child.wait(), 0)
finally:
child.close(force=True)

self.assertEqual(
output.strip(), os.fsencode(os.path.realpath(directory))
)
with open(os.path.join(directory, 'preexec-marker')) as file:
self.assertEqual(file.read(), 'ready')
self.assertEqual(os.getcwd(), original_cwd)