]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
[3.13] gh-70870: Clarify dual usage of 'free variable' (GH-122545) (#125088)
authorMiss Islington (bot) <31488909+miss-islington@users.noreply.github.com>
Tue, 8 Oct 2024 07:58:47 +0000 (09:58 +0200)
committerGitHub <noreply@github.com>
Tue, 8 Oct 2024 07:58:47 +0000 (17:58 +1000)
The term "free variable" has unfortunately become genuinely
ambiguous over the years (presumably due to the names of
some relevant code object instance attributes).

While we can't eliminate that ambiguity at this late date, we can
at least alert people to the potential ambiguity by describing
both the formal meaning of the term and the common
alternative use as a direct synonym for "closure variable".

---------

(cherry picked from commit 27390990fa9306e2a797a4eb2bd83c5bfc7cb186)

Co-authored-by: Alyssa Coghlan <ncoghlan@gmail.com>
Co-authored-by: Carol Willing <carolcode@willingconsulting.com>
Doc/c-api/code.rst
Doc/glossary.rst
Doc/library/dis.rst
Doc/library/functions.rst
Doc/library/symtable.rst
Doc/library/types.rst
Doc/reference/datamodel.rst
Doc/reference/executionmodel.rst
Misc/NEWS.d/next/Documentation/2024-08-01-17-18-21.gh-issue-70870.fZnBM9.rst [new file with mode: 0644]

index 6ae6bfe4aa6ab44d5695289fca0644216a4b34ff..6eae24b38fae48a4b35dedef19cf6e3ad4d3a7ff 100644 (file)
@@ -32,11 +32,13 @@ bound into a function.
 
 .. c:function:: Py_ssize_t PyCode_GetNumFree(PyCodeObject *co)
 
-   Return the number of free variables in a code object.
+   Return the number of :term:`free (closure) variables <closure variable>`
+   in a code object.
 
 .. c:function:: int PyUnstable_Code_GetFirstFree(PyCodeObject *co)
 
-   Return the position of the first free variable in a code object.
+   Return the position of the first :term:`free (closure) variable <closure variable>`
+   in a code object.
 
    .. versionchanged:: 3.13
 
@@ -144,7 +146,8 @@ bound into a function.
 
    Equivalent to the Python code ``getattr(co, 'co_freevars')``.
    Returns a new reference to a :c:type:`PyTupleObject` containing the names of
-   the free variables. On error, ``NULL`` is returned and an exception is raised.
+   the :term:`free (closure) variables <closure variable>`. On error, ``NULL`` is returned
+   and an exception is raised.
 
    .. versionadded:: 3.11
 
index 17461e23e715570d4a9b3e624fc2f999e4ad3d24..7a319e1376aa859303d921f4b9930e89454dea18 100644 (file)
@@ -226,6 +226,28 @@ Glossary
       A variable defined in a class and intended to be modified only at
       class level (i.e., not in an instance of the class).
 
+   closure variable
+      A :term:`free variable` referenced from a :term:`nested scope` that is defined in an outer
+      scope rather than being resolved at runtime from the globals or builtin namespaces.
+      May be explicitly defined with the :keyword:`nonlocal` keyword to allow write access,
+      or implicitly defined if the variable is only being read.
+
+      For example, in the ``inner`` function in the following code, both ``x`` and ``print`` are
+      :term:`free variables <free variable>`, but only ``x`` is a *closure variable*::
+
+          def outer():
+              x = 0
+              def inner():
+                  nonlocal x
+                  x += 1
+                  print(x)
+              return inner
+
+      Due to the :attr:`codeobject.co_freevars` attribute (which, despite its name, only
+      includes the names of closure variables rather than listing all referenced free
+      variables), the more general :term:`free variable` term is sometimes used even
+      when the intended meaning is to refer specifically to closure variables.
+
    complex number
       An extension of the familiar real number system in which all numbers are
       expressed as a sum of a real part and an imaginary part.  Imaginary
@@ -444,6 +466,13 @@ Glossary
       the :term:`global interpreter lock` which allows only one thread to
       execute Python bytecode at a time.  See :pep:`703`.
 
+   free variable
+      Formally, as defined in the :ref:`language execution model <bind_names>`, a free
+      variable is any variable used in a namespace which is not a local variable in that
+      namespace. See :term:`closure variable` for an example.
+      Pragmatically, due to the name of the :attr:`codeobject.co_freevars` attribute,
+      the term is also sometimes used as a synonym for :term:`closure variable`.
+
    function
       A series of statements which returns some value to a caller. It can also
       be passed zero or more :term:`arguments <argument>` which may be used in
index c5507e89a527edda3fe2a3d1beade2032bb7c5a6..ad8ef5dfc76ab8fc4046fb6e6891014e9df6e83e 100644 (file)
@@ -1431,7 +1431,7 @@ iterations of the loop.
    slot ``i`` of the "fast locals" storage in this mapping.
    If the name is not found there, loads it from the cell contained in
    slot ``i``, similar to :opcode:`LOAD_DEREF`. This is used for loading
-   free variables in class bodies (which previously used
+   :term:`closure variables <closure variable>` in class bodies (which previously used
    :opcode:`!LOAD_CLASSDEREF`) and in
    :ref:`annotation scopes <annotation-scopes>` within class bodies.
 
@@ -1460,8 +1460,8 @@ iterations of the loop.
 
 .. opcode:: COPY_FREE_VARS (n)
 
-   Copies the ``n`` free variables from the closure into the frame.
-   Removes the need for special code on the caller's side when calling
+   Copies the ``n`` :term:`free (closure) variables <closure variable>` from the closure
+   into the frame. Removes the need for special code on the caller's side when calling
    closures.
 
    .. versionadded:: 3.11
@@ -1917,10 +1917,10 @@ instructions:
 
 .. data:: hasfree
 
-   Sequence of bytecodes that access a free variable. 'free' in this
-   context refers to names in the current scope that are referenced by inner
-   scopes or names in outer scopes that are referenced from this scope.  It does
-   *not* include references to global or builtin scopes.
+   Sequence of bytecodes that access a :term:`free (closure) variable <closure variable>`.
+   'free' in this context refers to names in the current scope that are
+   referenced by inner scopes or names in outer scopes that are referenced
+   from this scope.  It does *not* include references to global or builtin scopes.
 
 
 .. data:: hasname
index 8d023ebf48a3d53bd4d77b2112f9655816cd4ce2..798d96e846293e0821a8be94408b5578ef857b90 100644 (file)
@@ -678,9 +678,10 @@ are always available.  They are listed here in alphabetical order.
    ``__builtins__`` dictionary into *globals* before passing it to :func:`exec`.
 
    The *closure* argument specifies a closure--a tuple of cellvars.
-   It's only valid when the *object* is a code object containing free variables.
-   The length of the tuple must exactly match the number of free variables
-   referenced by the code object.
+   It's only valid when the *object* is a code object containing
+   :term:`free (closure) variables <closure variable>`.
+   The length of the tuple must exactly match the length of the code object'S
+   :attr:`~codeobject.co_freevars` attribute.
 
    .. audit-event:: exec code_object exec
 
index ca7eaef45f6c4f291fcdc3c42f2255e0c0fb5d1f..56c62d8e610c934aaf64d2537f01764817114ce8 100644 (file)
@@ -167,11 +167,12 @@ Examining Symbol Tables
 
    .. method:: get_nonlocals()
 
-      Return a tuple containing names of nonlocals in this function.
+      Return a tuple containing names of explicitly declared nonlocals in this function.
 
    .. method:: get_frees()
 
-      Return a tuple containing names of free variables in this function.
+      Return a tuple containing names of :term:`free (closure) variables <closure variable>`
+      in this function.
 
 
 .. class:: Class
index 3c3c760c206ff2d6104a2b4195e4cfcfaafa251b..84b80ec6efd59f1731d59d85920ed143282e1be1 100644 (file)
@@ -199,7 +199,7 @@ Standard names are defined for the following types:
 .. data:: CellType
 
    The type for cell objects: such objects are used as containers for
-   a function's free variables.
+   a function's :term:`closure variables <closure variable>`.
 
    .. versionadded:: 3.8
 
index 57d9b286c6c554f6fb1761f90ee5c89886ffa90c..d9783875cab803492c570c05d246600bc1191936 100644 (file)
@@ -561,8 +561,9 @@ Special read-only attributes
        in which the function was defined.
 
    * - .. attribute:: function.__closure__
-     - ``None`` or a :class:`tuple` of cells that contain bindings for the
-       function's free variables.
+     - ``None`` or a :class:`tuple` of cells that contain bindings for the names specified
+       in the :attr:`~codeobject.co_freevars` attribute of the function's
+       :attr:`code object <function.__code__>`.
 
        A cell object has the attribute ``cell_contents``.
        This can be used to get the value of the cell, as well as set the value.
@@ -1238,10 +1239,14 @@ Special read-only attributes
 
    * - .. attribute:: codeobject.co_cellvars
      - A :class:`tuple` containing the names of :ref:`local variables <naming>`
-       that are referenced by nested functions inside the function
+       that are referenced from at least one :term:`nested scope` inside the function
 
    * - .. attribute:: codeobject.co_freevars
-     - A :class:`tuple` containing the names of free variables in the function
+     - A :class:`tuple` containing the names of
+       :term:`free (closure) variables <closure variable>` that a :term:`nested scope`
+       references in an outer scope. See also :attr:`function.__closure__`.
+
+       Note: references to global and builtin names are *not* included.
 
    * - .. attribute:: codeobject.co_code
      - A string representing the sequence of :term:`bytecode` instructions in
index c6d98fc2e7013d262f99a19235c7218e4bee75d0..1cf543a4432a6eb36a6adc56a890a044b4b6c66d 100644 (file)
@@ -90,7 +90,7 @@ If a name is bound in a block, it is a local variable of that block, unless
 declared as :keyword:`nonlocal` or :keyword:`global`.  If a name is bound at
 the module level, it is a global variable.  (The variables of the module code
 block are local and global.)  If a variable is used in a code block but not
-defined there, it is a :dfn:`free variable`.
+defined there, it is a :term:`free variable`.
 
 Each occurrence of a name in the program text refers to the :dfn:`binding` of
 that name established by the following name resolution rules.
@@ -330,6 +330,9 @@ enclosing namespace, but in the global namespace.  [#]_ The :func:`exec` and
 :func:`eval` functions have optional arguments to override the global and local
 namespace.  If only one namespace is specified, it is used for both.
 
+.. XXX(ncoghlan) above is only accurate for string execution. When executing code objects,
+   closure cells may now be passed explicitly to resolve co_freevars references.
+   Docs issue: https://github.com/python/cpython/issues/122826
 
 .. _exceptions:
 
diff --git a/Misc/NEWS.d/next/Documentation/2024-08-01-17-18-21.gh-issue-70870.fZnBM9.rst b/Misc/NEWS.d/next/Documentation/2024-08-01-17-18-21.gh-issue-70870.fZnBM9.rst
new file mode 100644 (file)
index 0000000..ba607bf
--- /dev/null
@@ -0,0 +1,3 @@
+Clarified the dual usage of the term "free variable" (both the formal
+meaning of any reference to names defined outside the local scope, and the
+narrower pragmatic meaning of nonlocal variables named in ``co_freevars``).