Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Incorrect (misleading) statement in the execution model documentation #68317

Closed
ilevkivskyi opened this issue May 5, 2015 · 18 comments
Closed
Labels
docs Documentation in the Doc dir type-feature A feature request or enhancement

Comments

@ilevkivskyi
Copy link
Member

BPO 24129
Nosy @arigo, @rhettinger, @ncoghlan, @bitdancer, @ericsnowcurrently, @ilevkivskyi
Files
  • classdoc.patch: Imptoved wording for docs on execution model (class statement)
  • classdoc_v2.patch: Made changes sugested by Eric
  • classdoc-v3.patch: A bit more extensive patch; main improvment is separating doc into subsections
  • classdoc-v4.patch: Comments of Nick and Eric are taken into account.
  • Note: these values reflect the state of the issue at the time it was migrated and might not reflect the current state.

    Show more details

    GitHub fields:

    assignee = None
    closed_at = <Date 2015-08-05.14:47:23.300>
    created_at = <Date 2015-05-05.20:51:59.401>
    labels = ['type-feature', 'docs']
    title = 'Incorrect (misleading) statement in the execution model documentation'
    updated_at = <Date 2015-08-05.14:55:27.564>
    user = 'https://github.com/ilevkivskyi'

    bugs.python.org fields:

    activity = <Date 2015-08-05.14:55:27.564>
    actor = 'ncoghlan'
    assignee = 'docs@python'
    closed = True
    closed_date = <Date 2015-08-05.14:47:23.300>
    closer = 'ncoghlan'
    components = ['Documentation']
    creation = <Date 2015-05-05.20:51:59.401>
    creator = 'levkivskyi'
    dependencies = []
    files = ['39383', '39744', '39749', '39759']
    hgrepos = []
    issue_num = 24129
    keywords = ['patch']
    message_count = 18.0
    messages = ['242619', '243258', '245514', '245517', '245524', '245533', '245535', '245545', '245548', '245554', '245562', '245596', '245697', '246031', '247800', '247938', '248039', '248045']
    nosy_count = 8.0
    nosy_names = ['arigo', 'rhettinger', 'ncoghlan', 'r.david.murray', 'abacabadabacaba', 'docs@python', 'eric.snow', 'levkivskyi']
    pr_nums = []
    priority = 'normal'
    resolution = 'fixed'
    stage = 'resolved'
    status = 'closed'
    superseder = None
    type = 'enhancement'
    url = 'https://bugs.python.org/issue24129'
    versions = ['Python 3.4', 'Python 3.5', 'Python 3.6']

    @ilevkivskyi
    Copy link
    Member Author

    The documentation on execution model https://docs.python.org/3/reference/executionmodel.html contains the statement
    """
    A class definition is an executable statement that may use and define names. These references follow the normal rules for name resolution. The namespace of the class definition becomes the attribute dictionary of the class. Names defined at the class scope are not visible in methods.
    """
    However, the following code (taken from http://lackingrhoticity.blogspot.ch/2008/08/4-python-variable-binding-oddities.html):

    x = "xtop"
    y = "ytop"
    def func():
        x = "xlocal"
        y = "ylocal"
        class C:
            print(x)
            print(y)
            y = 1
    func()

    prints

    xlocal
    ytop

    In case of "normal rules for name resolution" it should rise UnboundLocalError.

    I suggest replacing the mentioned statement with the following:
    """
    A class definition is an executable statement that may use and define names. Free variables follow the normal rules for name resolution, bound variables are looked up in the global namespace. The namespace of the class definition becomes the attribute dictionary of the class. Names defined at the class scope are not visible in methods.
    """
    or a similar one.

    @ilevkivskyi ilevkivskyi added the type-bug An unexpected behavior, bug, or error label May 5, 2015
    @ilevkivskyi ilevkivskyi added the docs Documentation in the Doc dir label May 5, 2015
    @ilevkivskyi
    Copy link
    Member Author

    Since no one proposed alternative ideas, I am submitting my proposal as a patch, with the following wording:

    """
    A class definition is an executable statement that may use and define names. Free variables follow the normal rules for name resolution, while unbound local variables are looked up in the global namespace. The namespace of the class definition becomes the attribute dictionary of the class. Names defined at the class scope are not visible in methods
    """

    @ilevkivskyi
    Copy link
    Member Author

    Should I invite someone to review the patch or just wait? How the things are organized here?

    @bitdancer
    Copy link
    Member

    In this particular case, just wait (now that you have pinged the issue). Raymond is the most likely person to figure out how to phrase this better, but it isn't obvious what the best way to explain this is. I don't think your explanation is exactly correct, but I don't know enough about how class name resolution is implemented to explain what's wrong with it, I just know it doesn't feel quite right :) (Of course, I might be wrong.)

    Ping the issue again in a few weeks if there is no action.

    @ericsnowcurrently
    Copy link
    Member

    I've left a review. That said, we need to be sure this behavior is intentional. The fact that it skips the "nonlocal" scope(s) smells like a bug to me.

    @ilevkivskyi
    Copy link
    Member Author

    Eric, thank you for the review. I have incorporated proposed changes in second version of the patch.
    Concerning the question whether it is a bug, it also smells like a bug to me, but Guido said 13 years ago that this should not be changed: https://mail.python.org/pipermail/python-dev/2002-April/023428.html and it stayed like this since then. However, things changed a bit in Python 3.4 with the introduction of the LOAD_CLASSDEREF opcode. Perhaps, we should ask Guido again :) What do you think?

    @ericsnowcurrently
    Copy link
    Member

    I expect you'll get the same response, especially given potential (though slight) chance for backward-compatibility issues. What I find curious is Guido's reference to "the rule that class bodies don't play the nested
    scopes game" (and his subsequent explanation). Is there something about that in the language reference? If so, the patch should be updated to link to that section. If not then it should be added to the language reference.

    That said, it wouldn't hurt to ask on python-dev, particularly in light of that new opcode.

    @ncoghlan
    Copy link
    Contributor

    The "normal rules for name resolution" reference here is referring to the name lookup rules as they existed prior to the introduction of lexical scoping for functions. It's a dated way of describing it, as the current behaviour of functions has now been around long enough that a lot of folks will consider *that* normal, and the module, class and exec scoping rules to be the unusual case (as levkivskyi has here).

    However, I've spent far too many hours staring at CPython compiler internals to be able to suggest a helpful rewording that will make sense to folks that *haven't* done that, so I'll instead provide the relevant background info to see if others can come up with a concise rewording of the reference docs :)

    Prior to Python 2.1, Python didn't have closure support, and hence nested functions and classes couldn't see variables in outer scopes at all - they could see their local scope, the module globals, and the builtins. That changed with the introduction of nested scopes as a __future__ import in Python 2.1 and the default behaviour in 2.2: https://www.python.org/dev/peps/pep-0227/

    As a result of that change, the compiler now keeps track of "function locals" at compile time, and *emits different code for references to them*. Where early versions of CPython only had LOAD_NAME and LOAD_GLOBAL in the bytecode, these days we now also have LOAD_FAST (function local), LOAD_CLOSURE (function local referenced as a nonlocal), LOAD_DEREF (function nonlocal) and LOAD_CLASSDEREF (class nonlocal). The latter four opcodes will *only* be emitted in a function body - they'll never be emitted for module level code (include the bodies of module level class definitions). If you attempt to reference a function local before a value has been assigned, you'll get UnboundLocalError rather than NameError.

    The name lookup rules used for execution of class bodies are thus the same ones used for the exec() builtin with two namespace arguments: there is a local namespace where name assignments happen, and name lookups check the local, global and builtin namespaces in that order. The code is executed line by line, so if a name is referenced before it has been assigned locally, then it may find a global or builtin of that name. Classes that are defined inside a function may refer to lexically scoped local variables from the class body, but class variables are not themselves visible to function definitions nested inside a class scope (i.e. method definitions).

    These rules are also used for module level execution and exec() with a single namespace argument, except that the local namespace and the global namespace refer to the same namespace.

    @ilevkivskyi
    Copy link
    Member Author

    Eric, the "rule" that classes don't play the nested scopes game is explained at beginning of the same section, but the explanation is "one sided" it only explains that names defined in classes are not visible inside functions.
    Nick, thank you for the thorough explanation. I will try to improve the wording. It looks like a bit more substantial changes are needed.

    @arigo
    Copy link
    Mannequin

    arigo mannequin commented Jun 20, 2015

    Related to http://bugs.python.org/issue19979 and others mentioned there.

    @ilevkivskyi
    Copy link
    Member Author

    Eric, I have submitted a new version of the patch. Could you please make a review? Nick, it will be interesting to hear your opinion too.

    I tried to follow such rules:

    1. Explanation should be succinct yet clear
    2. It should tell as less as possible about implementation details
    3. Minimize necessary changes

    It turns out that these goals could be achieved by
    a) simply reshuffling and structuring the existing text to separate the exceptions (classes, etc.) from the general case;
    and
    b) adding some minor clarifications.

    Armin, thank you for the link. It looks like this is a really old discussion.

    PS: Unfortunately, the diff after reshuffling of the text looks big and cumbersome, in fact the changes are minimal.

    @ilevkivskyi
    Copy link
    Member Author

    Nick, thank you for a review, I have made a new patch with all the previous comments taken into account.

    @ilevkivskyi
    Copy link
    Member Author

    It looks like on python-dev (http://www.mail-archive.com/python-dev@python.org/msg88256.html) there is an agreement that this behavior should not be changed (at least not in the nearest future). If there are no more comments/suggestions, then maybe one could accept the latest patch?

    @ilevkivskyi
    Copy link
    Member Author

    What holds the patch now? Should I do something or just wait?

    @ilevkivskyi
    Copy link
    Member Author

    I am sorry but I still don't get how things are organized here, so pinging this up. What is the next step? Should I wait for another review?

    @bitdancer
    Copy link
    Member

    Your ping after a month is very appropriate. It looks like yes, this is waiting for another review. Based on the fact that the previous patches were reviewed by core devs and you have responded, I'm moving it to 'commit review', but I haven't looked at the patch myself.

    @ncoghlan
    Copy link
    Contributor

    ncoghlan commented Aug 5, 2015

    I merged Ivan's latest patch to 3.4/3.5/default. We're unlikely to ever be able to make these docs completely intuitive (as name resolution is genuinely complex), but Ivan's revisions at least mean we're no longer assuming readers know how the name resolution worked prior to the introduction of lexical scoping, and a couple of tricky cases now have inline examples.

    I also noticed an existing paragraph in the docs that *I* didn't understand, and filed issue bpo-24796 to cover that. I'm not sure if we should just delete the paragraph, or if we accidentally dropped a compile time error check that didn't have any tests to ensure we were detecting the problem.

    @ncoghlan ncoghlan closed this as completed Aug 5, 2015
    @ncoghlan ncoghlan added type-feature A feature request or enhancement and removed type-bug An unexpected behavior, bug, or error labels Aug 5, 2015
    @ncoghlan
    Copy link
    Contributor

    ncoghlan commented Aug 5, 2015

    The issue tracker was having issues and didn't automatically register the commits. Links:

    3.4: https://hg.python.org/cpython/rev/94e215a5e24b
    3.5: https://hg.python.org/cpython/rev/5e4d21311772
    default: https://hg.python.org/cpython/rev/e75881393cf2

    @ezio-melotti ezio-melotti transferred this issue from another repository Apr 10, 2022
    Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
    Labels
    docs Documentation in the Doc dir type-feature A feature request or enhancement
    Projects
    None yet
    Development

    No branches or pull requests

    4 participants