| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -3367,25 +3367,41 @@ written in Python, such as a mail server's external command delivery program. | |||
| 3367 | 3367 | ||
| 3368 | 3368 | .. function:: posix_spawn(path, argv, env, file_actions=None) | |
| 3369 | 3369 | ||
| 3370 | - Wraps the posix_spawn() C library API for use from Python. | ||
| 3370 | + Wraps the :c:func:`posix_spawn` C library API for use from Python. | ||
| 3371 | 3371 | ||
| 3372 | - Most users should use :class:`subprocess.run` instead of posix_spawn. | ||
| 3372 | + Most users should use :func:`subprocess.run` instead of :func:`posix_spawn`. | ||
| 3373 | 3373 | ||
| 3374 | 3374 | The *path*, *args*, and *env* arguments are similar to :func:`execve`. | |
| 3375 | 3375 | ||
| 3376 | 3376 | The *file_actions* argument may be a sequence of tuples describing actions | |
| 3377 | 3377 | to take on specific file descriptors in the child process between the C | |
| 3378 | - library implementation's fork and exec steps. The first item in each tuple | ||
| 3379 | - must be one of the three type indicator listed below describing the | ||
| 3380 | - remaining tuple elements: | ||
| 3378 | + library implementation's :c:func:`fork` and :c:func:`exec` steps. | ||
| 3379 | + The first item in each tuple must be one of the three type indicator | ||
| 3380 | + listed below describing the remaining tuple elements: | ||
| 3381 | 3381 | ||
| 3382 | - (os.POSIX_SPAWN_OPEN, fd, path, open flags, mode) | ||
| 3383 | - (os.POSIX_SPAWN_CLOSE, fd) | ||
| 3384 | - (os.POSIX_SPAWN_DUP2, fd, new_fd) | ||
| 3382 | + .. data:: POSIX_SPAWN_OPEN | ||
| 3385 | 3383 | ||
| 3386 | - These tuples correspond to the C library posix_spawn_file_actions_addopen, | ||
| 3387 | - posix_spawn_file_actions_addclose, and posix_spawn_file_actions_adddup2 API | ||
| 3388 | - calls used to prepare for the posix_spawn call itself. | ||
| 3384 | + (``os.POSIX_SPAWN_OPEN``, *fd*, *path*, *flags*, *mode*) | ||
| 3385 | + | ||
| 3386 | + Performs ``os.dup2(os.open(path, flags, mode), fd)``. | ||
| 3387 | + | ||
| 3388 | + .. data:: POSIX_SPAWN_CLOSE | ||
| 3389 | + | ||
| 3390 | + (``os.POSIX_SPAWN_CLOSE``, *fd*) | ||
| 3391 | + | ||
| 3392 | + Performs ``os.close(fd)``. | ||
| 3393 | + | ||
| 3394 | + .. data:: POSIX_SPAWN_DUP2 | ||
| 3395 | + | ||
| 3396 | + (``os.POSIX_SPAWN_DUP2``, *fd*, *new_fd*) | ||
| 3397 | + | ||
| 3398 | + Performs ``os.dup2(fd, new_fd)``. | ||
| 3399 | + | ||
| 3400 | + These tuples correspond to the C library | ||
| 3401 | + :c:func:`posix_spawn_file_actions_addopen`, | ||
| 3402 | + :c:func:`posix_spawn_file_actions_addclose`, and | ||
| 3403 | + :c:func:`posix_spawn_file_actions_adddup2` API calls used to prepare | ||
| 3404 | + for the :c:func:`posix_spawn` call itself. | ||
| 3389 | 3405 | ||
| 3390 | 3406 | .. versionadded:: 3.7 | |
| 3391 | 3407 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -177,22 +177,6 @@ def test_fexecve(self): | |||
| 177 | 177 | os.close(fp) | |
| 178 | 178 | ||
| 179 | 179 | ||
| 180 | - @unittest.skipUnless(hasattr(os, 'posix_spawn'), "test needs os.posix_spawn") | ||
| 181 | - def test_posix_spawn(self): | ||
| 182 | - pid = posix.posix_spawn(sys.executable, [sys.executable, "-c", "pass"], os.environ,[]) | ||
| 183 | - self.assertEqual(os.waitpid(pid,0),(pid,0)) | ||
| 184 | - | ||
| 185 | - | ||
| 186 | - @unittest.skipUnless(hasattr(os, 'posix_spawn'), "test needs os.posix_spawn") | ||
| 187 | - def test_posix_spawn_file_actions(self): | ||
| 188 | - file_actions = [] | ||
| 189 | - file_actions.append((0,3,os.path.realpath(__file__),0,0)) | ||
| 190 | - file_actions.append((os.POSIX_SPAWN_CLOSE,2)) | ||
| 191 | - file_actions.append((os.POSIX_SPAWN_DUP2,1,4)) | ||
| 192 | - pid = posix.posix_spawn(sys.executable, [sys.executable, "-c", "pass"], os.environ, file_actions) | ||
| 193 | - self.assertEqual(os.waitpid(pid,0),(pid,0)) | ||
| 194 | - | ||
| 195 | - | ||
| 196 | 180 | @unittest.skipUnless(hasattr(posix, 'waitid'), "test needs posix.waitid()") | |
| 197 | 181 | @unittest.skipUnless(hasattr(os, 'fork'), "test needs os.fork()") | |
| 198 | 182 | def test_waitid(self): | |
@@ -1437,9 +1421,168 @@ def test_setgroups(self): | |||
| 1437 | 1421 | posix.setgroups(groups) | |
| 1438 | 1422 | self.assertListEqual(groups, posix.getgroups()) | |
| 1439 | 1423 | ||
| 1424 | + | ||
| 1425 | + @unittest.skipUnless(hasattr(os, 'posix_spawn'), "test needs os.posix_spawn") | ||
| 1426 | + class TestPosixSpawn(unittest.TestCase): | ||
| 1427 | + def test_returns_pid(self): | ||
| 1428 | + pidfile = support.TESTFN | ||
| 1429 | + self.addCleanup(support.unlink, pidfile) | ||
| 1430 | + script = f"""if 1: | ||
| 1431 | + import os | ||
| 1432 | + with open({pidfile!r}, "w") as pidfile: | ||
| 1433 | + pidfile.write(str(os.getpid())) | ||
| 1434 | + """ | ||
| 1435 | + pid = posix.posix_spawn(sys.executable, | ||
| 1436 | + [sys.executable, '-c', script], | ||
| 1437 | + os.environ) | ||
| 1438 | + self.assertEqual(os.waitpid(pid, 0), (pid, 0)) | ||
| 1439 | + with open(pidfile) as f: | ||
| 1440 | + self.assertEqual(f.read(), str(pid)) | ||
| 1441 | + | ||
| 1442 | + def test_no_such_executable(self): | ||
| 1443 | + no_such_executable = 'no_such_executable' | ||
| 1444 | + try: | ||
| 1445 | + pid = posix.posix_spawn(no_such_executable, | ||
| 1446 | + [no_such_executable], | ||
| 1447 | + os.environ) | ||
| 1448 | + except FileNotFoundError as exc: | ||
| 1449 | + self.assertEqual(exc.filename, no_such_executable) | ||
| 1450 | + else: | ||
| 1451 | + pid2, status = os.waitpid(pid, 0) | ||
| 1452 | + self.assertEqual(pid2, pid) | ||
| 1453 | + self.assertNotEqual(status, 0) | ||
| 1454 | + | ||
| 1455 | + def test_specify_environment(self): | ||
| 1456 | + envfile = support.TESTFN | ||
| 1457 | + self.addCleanup(support.unlink, envfile) | ||
| 1458 | + script = f"""if 1: | ||
| 1459 | + import os | ||
| 1460 | + with open({envfile!r}, "w") as envfile: | ||
| 1461 | + envfile.write(os.environ['foo']) | ||
| 1462 | + """ | ||
| 1463 | + pid = posix.posix_spawn(sys.executable, | ||
| 1464 | + [sys.executable, '-c', script], | ||
| 1465 | + {'foo': 'bar'}) | ||
| 1466 | + self.assertEqual(os.waitpid(pid, 0), (pid, 0)) | ||
| 1467 | + with open(envfile) as f: | ||
| 1468 | + self.assertEqual(f.read(), 'bar') | ||
| 1469 | + | ||
| 1470 | + def test_empty_file_actions(self): | ||
| 1471 | + pid = posix.posix_spawn( | ||
| 1472 | + sys.executable, | ||
| 1473 | + [sys.executable, '-c', 'pass'], | ||
| 1474 | + os.environ, | ||
| 1475 | + [] | ||
| 1476 | + ) | ||
| 1477 | + self.assertEqual(os.waitpid(pid, 0), (pid, 0)) | ||
| 1478 | + | ||
| 1479 | + def test_multiple_file_actions(self): | ||
| 1480 | + file_actions = [ | ||
| 1481 | + (os.POSIX_SPAWN_OPEN, 3, os.path.realpath(__file__), os.O_RDONLY, 0), | ||
| 1482 | + (os.POSIX_SPAWN_CLOSE, 0), | ||
| 1483 | + (os.POSIX_SPAWN_DUP2, 1, 4), | ||
| 1484 | + ] | ||
| 1485 | + pid = posix.posix_spawn(sys.executable, | ||
| 1486 | + [sys.executable, "-c", "pass"], | ||
| 1487 | + os.environ, file_actions) | ||
| 1488 | + self.assertEqual(os.waitpid(pid, 0), (pid, 0)) | ||
| 1489 | + | ||
| 1490 | + def test_bad_file_actions(self): | ||
| 1491 | + with self.assertRaises(TypeError): | ||
| 1492 | + posix.posix_spawn(sys.executable, | ||
| 1493 | + [sys.executable, "-c", "pass"], | ||
| 1494 | + os.environ, [None]) | ||
| 1495 | + with self.assertRaises(TypeError): | ||
| 1496 | + posix.posix_spawn(sys.executable, | ||
| 1497 | + [sys.executable, "-c", "pass"], | ||
| 1498 | + os.environ, [()]) | ||
| 1499 | + with self.assertRaises(TypeError): | ||
| 1500 | + posix.posix_spawn(sys.executable, | ||
| 1501 | + [sys.executable, "-c", "pass"], | ||
| 1502 | + os.environ, [(None,)]) | ||
| 1503 | + with self.assertRaises(TypeError): | ||
| 1504 | + posix.posix_spawn(sys.executable, | ||
| 1505 | + [sys.executable, "-c", "pass"], | ||
| 1506 | + os.environ, [(12345,)]) | ||
| 1507 | + with self.assertRaises(TypeError): | ||
| 1508 | + posix.posix_spawn(sys.executable, | ||
| 1509 | + [sys.executable, "-c", "pass"], | ||
| 1510 | + os.environ, [(os.POSIX_SPAWN_CLOSE,)]) | ||
| 1511 | + with self.assertRaises(TypeError): | ||
| 1512 | + posix.posix_spawn(sys.executable, | ||
| 1513 | + [sys.executable, "-c", "pass"], | ||
| 1514 | + os.environ, [(os.POSIX_SPAWN_CLOSE, 1, 2)]) | ||
| 1515 | + with self.assertRaises(TypeError): | ||
| 1516 | + posix.posix_spawn(sys.executable, | ||
| 1517 | + [sys.executable, "-c", "pass"], | ||
| 1518 | + os.environ, [(os.POSIX_SPAWN_CLOSE, None)]) | ||
| 1519 | + with self.assertRaises(ValueError): | ||
| 1520 | + posix.posix_spawn(sys.executable, | ||
| 1521 | + [sys.executable, "-c", "pass"], | ||
| 1522 | + os.environ, | ||
| 1523 | + [(os.POSIX_SPAWN_OPEN, 3, __file__ + '\0', | ||
| 1524 | + os.O_RDONLY, 0)]) | ||
| 1525 | + | ||
| 1526 | + def test_open_file(self): | ||
| 1527 | + outfile = support.TESTFN | ||
| 1528 | + self.addCleanup(support.unlink, outfile) | ||
| 1529 | + script = """if 1: | ||
| 1530 | + import sys | ||
| 1531 | + sys.stdout.write("hello") | ||
| 1532 | + """ | ||
| 1533 | + file_actions = [ | ||
| 1534 | + (os.POSIX_SPAWN_OPEN, 1, outfile, | ||
| 1535 | + os.O_WRONLY | os.O_CREAT | os.O_TRUNC, | ||
| 1536 | + stat.S_IRUSR | stat.S_IWUSR), | ||
| 1537 | + ] | ||
| 1538 | + pid = posix.posix_spawn(sys.executable, | ||
| 1539 | + [sys.executable, '-c', script], | ||
| 1540 | + os.environ, file_actions) | ||
| 1541 | + self.assertEqual(os.waitpid(pid, 0), (pid, 0)) | ||
| 1542 | + with open(outfile) as f: | ||
| 1543 | + self.assertEqual(f.read(), 'hello') | ||
| 1544 | + | ||
| 1545 | + def test_close_file(self): | ||
| 1546 | + closefile = support.TESTFN | ||
| 1547 | + self.addCleanup(support.unlink, closefile) | ||
| 1548 | + script = f"""if 1: | ||
| 1549 | + import os | ||
| 1550 | + try: | ||
| 1551 | + os.fstat(0) | ||
| 1552 | + except OSError as e: | ||
| 1553 | + with open({closefile!r}, 'w') as closefile: | ||
| 1554 | + closefile.write('is closed %d' % e.errno) | ||
| 1555 | + """ | ||
| 1556 | + pid = posix.posix_spawn(sys.executable, | ||
| 1557 | + [sys.executable, '-c', script], | ||
| 1558 | + os.environ, | ||
| 1559 | + [(os.POSIX_SPAWN_CLOSE, 0),]) | ||
| 1560 | + self.assertEqual(os.waitpid(pid, 0), (pid, 0)) | ||
| 1561 | + with open(closefile) as f: | ||
| 1562 | + self.assertEqual(f.read(), 'is closed %d' % errno.EBADF) | ||
| 1563 | + | ||
| 1564 | + def test_dup2(self): | ||
| 1565 | + dupfile = support.TESTFN | ||
| 1566 | + self.addCleanup(support.unlink, dupfile) | ||
| 1567 | + script = """if 1: | ||
| 1568 | + import sys | ||
| 1569 | + sys.stdout.write("hello") | ||
| 1570 | + """ | ||
| 1571 | + with open(dupfile, "wb") as childfile: | ||
| 1572 | + file_actions = [ | ||
| 1573 | + (os.POSIX_SPAWN_DUP2, childfile.fileno(), 1), | ||
| 1574 | + ] | ||
| 1575 | + pid = posix.posix_spawn(sys.executable, | ||
| 1576 | + [sys.executable, '-c', script], | ||
| 1577 | + os.environ, file_actions) | ||
| 1578 | + self.assertEqual(os.waitpid(pid, 0), (pid, 0)) | ||
| 1579 | + with open(dupfile) as f: | ||
| 1580 | + self.assertEqual(f.read(), 'hello') | ||
| 1581 | + | ||
| 1582 | + | ||
| 1440 | 1583 | def test_main(): | |
| 1441 | 1584 | try: | |
| 1442 | - support.run_unittest(PosixTester, PosixGroupsTester) | ||
| 1585 | + support.run_unittest(PosixTester, PosixGroupsTester, TestPosixSpawn) | ||
| 1443 | 1586 | finally: | |
| 1444 | 1587 | support.reap_children() | |
| 1445 | 1588 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -0,0 +1 @@ | |||
| 1 | + Improved error handling and fixed a reference leak in :func:`os.posix_spawn()`. | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1742,7 +1742,7 @@ PyDoc_STRVAR(os_posix_spawn__doc__, | |||
| 1742 | 1742 | " env\n" | |
| 1743 | 1743 | " Dictionary of strings mapping to strings.\n" | |
| 1744 | 1744 | " file_actions\n" | |
| 1745 | - " FileActions object."); | ||
| 1745 | + " A sequence of file action tuples."); | ||
| 1746 | 1746 | ||
| 1747 | 1747 | #define OS_POSIX_SPAWN_METHODDEF \ | |
| 1748 | 1748 | {"posix_spawn", (PyCFunction)os_posix_spawn, METH_FASTCALL, os_posix_spawn__doc__}, | |
@@ -6589,4 +6589,4 @@ os_getrandom(PyObject *module, PyObject *const *args, Py_ssize_t nargs, PyObject | |||
| 6589 | 6589 | #ifndef OS_GETRANDOM_METHODDEF | |
| 6590 | 6590 | #define OS_GETRANDOM_METHODDEF | |
| 6591 | 6591 | #endif /* !defined(OS_GETRANDOM_METHODDEF) */ | |
| 6592 | - /*[clinic end generated code: output=fc603214822bdda6 input=a9049054013a1b77]*/ | ||
| 6592 | + /*[clinic end generated code: output=8d3d9dddf254c3c2 input=a9049054013a1b77]*/ | ||
| Back | FazBrowse Home | New Git URL |
0 commit comments