Skip to content

bpo-16500: Don't use string constants for os.register_at_fork() behavior#1834

Merged
gpshead merged 14 commits into
python:masterfrom
gpshead:atfork-when-cleanup-16500
May 29, 2017
Merged

bpo-16500: Don't use string constants for os.register_at_fork() behavior#1834
gpshead merged 14 commits into
python:masterfrom
gpshead:atfork-when-cleanup-16500

Conversation

@gpshead

@gpshead gpshead commented May 27, 2017

Copy link
Copy Markdown
Member

Add os module constants and updates all uses and documentation instead of string literals.
This allows for cleaner code with less chance of a typo in a string value showing up at runtime.
Linters and similar tools can flag incorrect constant names before run time.

@pitrou

pitrou commented May 27, 2017

Copy link
Copy Markdown
Member

I'm not sure I like this. This is longer to type, and doesn't seem to bring anything over human-readable string constants (compare with, say, SEET_SET and friends which are opaque integer values). If you make a typo, since os.register_at_fork will typically be called at module import, that typo will probably show up immediately.

@1st1 ?

@gpshead

gpshead commented May 27, 2017

Copy link
Copy Markdown
Member Author

I wouldn't expect register_at_fork() to be called at import time in most non-stdlib use cases. An application is more likely to call it later as it starts running things that need special treatment (especially for crazy developers who refuse to learn that mixing fork and threads is a bad idea).

My main complaint is the use of magic string values.

Alternatives would be

  • To make three functions instead of having a when parameter.
  • Or to mirror pthread_atfork()'s API and accept three callables (keyword args only at that point). I kind of like that idea. A single register_at_fork() function, with three optional named parameters.

@pitrou

pitrou commented May 27, 2017

Copy link
Copy Markdown
Member

Well, I would rather have constants than your two other suggestions :-)

@gpshead

gpshead commented May 27, 2017

Copy link
Copy Markdown
Member Author
os.register_at_fork(before=prepare_for_launch,
                    after_parent=launch_cleanup,
                    after_child=reinitialize_stuff)

Looks very readable to me.

@pitrou

pitrou commented May 27, 2017

Copy link
Copy Markdown
Member

Why not. It's much better than having three separate functions, at least.

@gpshead gpshead self-assigned this May 27, 2017
@gpshead gpshead changed the title bpo-16500: Use constants for os.register_at_fork() bpo-16500: Don't use string constants for os.register_at_fork() behavior May 28, 2017
@gpshead gpshead requested a review from serhiy-storchaka May 28, 2017 08:00
Comment thread Doc/library/os.rst Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is "if control is expected to return to the Python interpreter" related to other arguments? _posixsubprocess.fork_exec() doesn't call any of handlers if preexec_fn is None.

And the same question about the documentation of PyOS_AfterFork_Child() vs other functions.

Comment thread Lib/test/test_posix.py Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is not clear what argument triggered an error. Use three different invocations with three assertRaises.

Comment thread Lib/test/test_posix.py Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The same as above.

Comment thread Modules/posixmodule.c Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Double backticks don't look nice in text output.

Comment thread Modules/posixmodule.c Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This means that os.register_at_fork(before=lambda:None, after_in_child=42, after_in_parent='spam') is successful.

I would change this condition to just (!(before || after_in_child || after_in_parent)) or (!before && !after_in_child && !after_in_parent) and add separate PyCallable_Check() checks for every non-NULL argument.

Since this case was not caught by tests, needed a new test for it.

Comment thread Modules/posixmodule.c Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The check for before != NULL can be moved into register_at_forker() together with a PyCallable_Check() check. Unless you want to produce more specific error messages.

Comment thread Doc/c-api/sys.rst Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

HAVE_FORK is internal CPython define. It is not a part of any API.

@1st1

1st1 commented May 28, 2017

Copy link
Copy Markdown
Member

Having linters and IDEs helping you with the register_at_fork signature is a good idea, although I still like the original API proposed by @pitrou.

+1 to go with keyword arguments, instead of constants.

os.register_at_fork(before=prepare_for_launch,
                    after_parent=launch_cleanup,
                    after_child=reinitialize_stuff)

@gpshead

gpshead commented May 28, 2017

Copy link
Copy Markdown
Member Author

Thanks @serhiy-storchaka I believe I've addressed all of those suggestions.

gpshead added 10 commits May 28, 2017 13:20
Adds the constants and updates all uses and documentation.
This switches from using str constants or os module constants
on a warn parameter to using named keyword only arguments
specifying the intent of the callable being passed in.  It avoids
the need for string literals or new os module constants and is
generally more readable.  This is more similar to how the libc
pthread_atfork() API works.
We don't want to call PyOS_BeforeFork() or PyOS_AfterFork_Parent()
if we never manage to make it to the fork() call it self, erroring
out instead.
The statement about re-entering the interpreter applies to all three
calls, not just after_in_child.
Comment thread Modules/posixmodule.c Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error message looks like:

'before' must be callable, not <class 'str'>

It is more common to include the type name rather than repr.

'before' must be callable, not str

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

%s and Py_TYPE(obj)->tp_name solves that. thanks.

Comment thread Modules/posixmodule.c Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is more common to return either

  • 1 on success, 0 on failure, or
  • 0 on success, -1 on failure.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it doesn't really matter. 0 on success and non-zero on failure is the real idiom. 1 and -1 are both non-zero and pass a truth test.

Comment thread Modules/posixmodule.c Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This partially repeats descriptions of arguments ('before' is called before forking, etc).

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed some of the redundancy in the docstring.

Comment thread Modules/posixmodule.c Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"in the parent" perhaps is redundant.

And "Zero arg" may be redundant in the docstring. This detail can be explained in the module documentation. Shortened "arg" is a jargon word.

Comment thread Modules/posixmodule.c Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Callables.

Comment thread Doc/library/os.rst Outdated

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

os.fork() is not the only forking function. There are also os.fork1(), os.forkpty() and _posixsubprocess.fork_exec(). And third-party code can call fork().

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is impossible and unmaintainable to list them all, I've generalized the wording.

@serhiy-storchaka

Copy link
Copy Markdown
Member

I don't know why, but the diff now shows already merged changes, including unrelated to this issue. This makes reviewing hard.

@gpshead

gpshead commented May 28, 2017

Copy link
Copy Markdown
Member Author

I don't know why git did that. I followed the https://cpython-devguide.readthedocs.io/gitbootcamp.html#syncing-with-upstream instructions to pull in recent changes so the tests would pass and I could update Lib/threading.py to use this API. :(

@gpshead gpshead force-pushed the atfork-when-cleanup-16500 branch from 9c9dbac to 22a40c9 Compare May 28, 2017 22:52
@gpshead

gpshead commented May 28, 2017

Copy link
Copy Markdown
Member Author

Okay, I figured out how to do a proper rebasing and the diffs for this PR look sane again without including merged changes from others.

(for reference, the git instructions I followed were based on https://www.digitalocean.com/community/tutorials/how-to-rebase-and-update-a-pull-request)

Comment thread Modules/clinic/posixmodule.c.h Outdated
"--\n"
"\n"
"Register a callable object to be called when forking.\n"
"Registers callables to be called when forking a new process.\n"

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As a style rule, CPython uses infinitives in documentation, not present tense.

@pitrou

pitrou commented May 29, 2017

Copy link
Copy Markdown
Member

btw, if you don't want to mess around with git rebases, git pull origin master should be sufficient to merge the latest changes from master into your current branch (replace "origin" with whatever the upstream CPython is named in your working clone).

@gpshead gpshead dismissed serhiy-storchaka’s stale review May 29, 2017 16:57

I believe all comments have been addressed. Anything else can be addressed in follow up commits.

@gpshead gpshead merged commit 163468a into python:master May 29, 2017
@gpshead gpshead deleted the atfork-when-cleanup-16500 branch May 29, 2017 17:04
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants