@@ -465,6 +465,8 @@ PyOS_AfterFork_Child(void)
465465static int
466466register_at_forker (PyObject * * lst , PyObject * func )
467467{
468+ if (func == NULL ) /* nothing to register? do nothing. */
469+ return 0 ;
468470 if (* lst == NULL ) {
469471 * lst = PyList_New (0 );
470472 if (* lst == NULL )
@@ -5309,52 +5311,67 @@ os_spawnve_impl(PyObject *module, int mode, path_t *path, PyObject *argv,
53095311
53105312
53115313#ifdef HAVE_FORK
5314+
5315+ /* Helper function to validate arguments.
5316+ Returns 0 on success. non-zero on failure with a TypeError raised.
5317+ If obj is non-NULL it must be callable. */
5318+ static int
5319+ check_null_or_callable (PyObject * obj , const char * obj_name )
5320+ {
5321+ if (obj && !PyCallable_Check (obj )) {
5322+ PyErr_Format (PyExc_TypeError , "'%s' must be callable, not %s" ,
5323+ obj_name , Py_TYPE (obj )-> tp_name );
5324+ return -1 ;
5325+ }
5326+ return 0 ;
5327+ }
5328+
53125329/*[clinic input]
53135330os.register_at_fork
53145331
5315- func: object
5316- Function or callable
5317- /
5318- when: str
5319- 'before', 'child' or 'parent'
5332+ *
5333+ before: object=NULL
5334+ A callable to be called in the parent before the fork() syscall.
5335+ after_in_child: object=NULL
5336+ A callable to be called in the child after fork().
5337+ after_in_parent: object=NULL
5338+ A callable to be called in the parent after fork().
53205339
5321- Register a callable object to be called when forking.
5340+ Register callables to be called when forking a new process .
53225341
5323- 'before' callbacks are called in reverse order before forking.
5324- 'child' callbacks are called in order after forking, in the child process.
5325- 'parent' callbacks are called in order after forking, in the parent process.
5342+ 'before' callbacks are called in reverse order.
5343+ 'after_in_child' and 'after_in_parent' callbacks are called in order.
53265344
53275345[clinic start generated code]*/
53285346
53295347static PyObject *
5330- os_register_at_fork_impl (PyObject * module , PyObject * func , const char * when )
5331- /*[clinic end generated code: output=8943be81a644750c input=5fc05efa4d42eb84]*/
5348+ os_register_at_fork_impl (PyObject * module , PyObject * before ,
5349+ PyObject * after_in_child , PyObject * after_in_parent )
5350+ /*[clinic end generated code: output=5398ac75e8e97625 input=cd1187aa85d2312e]*/
53325351{
53335352 PyInterpreterState * interp ;
5334- PyObject * * lst ;
53355353
5336- if (!PyCallable_Check (func )) {
5337- PyErr_Format (PyExc_TypeError ,
5338- "expected callable object, got %R" , Py_TYPE (func ));
5354+ if (!before && !after_in_child && !after_in_parent ) {
5355+ PyErr_SetString (PyExc_TypeError , "At least one argument is required." );
5356+ return NULL ;
5357+ }
5358+ if (check_null_or_callable (before , "before" ) ||
5359+ check_null_or_callable (after_in_child , "after_in_child" ) ||
5360+ check_null_or_callable (after_in_parent , "after_in_parent" )) {
53395361 return NULL ;
53405362 }
53415363 interp = PyThreadState_Get ()-> interp ;
53425364
5343- if (!strcmp (when , "before" ))
5344- lst = & interp -> before_forkers ;
5345- else if (!strcmp (when , "child" ))
5346- lst = & interp -> after_forkers_child ;
5347- else if (!strcmp (when , "parent" ))
5348- lst = & interp -> after_forkers_parent ;
5349- else {
5350- PyErr_Format (PyExc_ValueError , "unexpected value for `when`: '%s'" ,
5351- when );
5365+ if (register_at_forker (& interp -> before_forkers , before )) {
53525366 return NULL ;
53535367 }
5354- if (register_at_forker (lst , func ))
5368+ if (register_at_forker (& interp -> after_forkers_child , after_in_child )) {
53555369 return NULL ;
5356- else
5357- Py_RETURN_NONE ;
5370+ }
5371+ if (register_at_forker (& interp -> after_forkers_parent , after_in_parent )) {
5372+ return NULL ;
5373+ }
5374+ Py_RETURN_NONE ;
53585375}
53595376#endif /* HAVE_FORK */
53605377
0 commit comments