Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Conversation

@gpshead
Copy link
Member

@gpshead gpshead commented May 27, 2017

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
Copy link
Member

pitrou commented May 27, 2017

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
Copy link
Member Author

gpshead commented May 27, 2017

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
Copy link
Member

pitrou commented May 27, 2017

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

@gpshead
Copy link
Member Author

gpshead commented May 27, 2017

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

Looks very readable to me.

@pitrou
Copy link
Member

pitrou commented May 27, 2017

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
Copy link
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.

Copy link
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.

Copy link
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.

Copy link
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.

Copy link
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.

Copy link
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.

Copy link
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
Copy link
Member

1st1 commented May 28, 2017

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
Copy link
Member Author

gpshead commented May 28, 2017

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.
Copy link
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
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.

Copy link
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
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.

Copy link
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
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.

Copy link
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.

Copy link
Member

Choose a reason for hiding this comment

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

Callables.

Copy link
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
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
Member

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

@gpshead
Copy link
Member Author

gpshead commented May 28, 2017

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
Copy link
Member Author

gpshead commented May 28, 2017

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)

"--\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
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
Copy link
Member

pitrou commented May 29, 2017

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