[Python-checkins] cpython (merge 3.4 -> default): #10510: Fix bug in forward port of 2.7 distutils patch.

r.david.murray python-checkins at python.org
Sun Sep 28 17:02:20 CEST 2014


https://hg.python.org/cpython/rev/90b07d422bd9
changeset:   92618:90b07d422bd9
parent:      92616:a4752c32cc79
parent:      92617:6375bf34fff6
user:        R David Murray <rdmurray at bitdance.com>
date:        Sun Sep 28 11:01:42 2014 -0400
summary:
  #10510: Fix bug in forward port of 2.7 distutils patch.

Pointed out by Arfrever.

files:
  Doc/faq/programming.rst            |  77 +++++++++++++++++-
  Lib/distutils/command/upload.py    |   1 -
  Lib/distutils/tests/test_upload.py |   2 +-
  3 files changed, 77 insertions(+), 3 deletions(-)


diff --git a/Doc/faq/programming.rst b/Doc/faq/programming.rst
--- a/Doc/faq/programming.rst
+++ b/Doc/faq/programming.rst
@@ -401,7 +401,7 @@
 
        # Calculate the value
        result = ... expensive computation ...
-       _cache[(arg1, arg2)] = result  # Store result in the cache
+       _cache[(arg1, arg2)] = result           # Store result in the cache
        return result
 
 You could use a global variable containing a dictionary instead of the default
@@ -448,6 +448,81 @@
 the values ``42``, ``314``, and ``somevar`` are arguments.
 
 
+Why did changing list 'y' also change list 'x'?
+------------------------------------------------
+
+If you wrote code like::
+
+   >>> x = []
+   >>> y = x
+   >>> y.append(10)
+   >>> y
+   [10]
+   >>> x
+   [10]
+
+you might be wondering why appending an element to ``y`` changed ``x`` too.
+
+There are two factors that produce this result:
+
+1) Variables are simply names that refer to objects.  Doing ``y = x`` doesn't
+   create a copy of the list -- it creates a new variable ``y`` that refers to
+   the same object ``x`` refers to.  This means that there is only one object
+   (the list), and both ``x`` and ``y`` refer to it.
+2) Lists are :term:`mutable`, which means that you can change their content.
+
+After the call to :meth:`~list.append`, the content of the mutable object has
+changed from ``[]`` to ``[10]``.  Since both the variables refer to the same
+object, accessing either one of them accesses the modified value ``[10]``.
+
+If we instead assign an immutable object to ``x``::
+
+   >>> x = 5  # ints are immutable
+   >>> y = x
+   >>> x = x + 1  # 5 can't be mutated, we are creating a new object here
+   >>> x
+   6
+   >>> y
+   5
+
+we can see that in this case ``x`` and ``y`` are not equal anymore.  This is
+because integers are :term:`immutable`, and when we do ``x = x + 1`` we are not
+mutating the int ``5`` by incrementing its value; instead, we are creating a
+new object (the int ``6``) and assigning it to ``x`` (that is, changing which
+object ``x`` refers to).  After this assignment we have two objects (the ints
+``6`` and ``5``) and two variables that refer to them (``x`` now refers to
+``6`` but ``y`` still refers to ``5``).
+
+Some operations (for example ``y.append(10)`` and ``y.sort()``) mutate the
+object, whereas superficially similar operations (for example ``y = y + [10]``
+and ``sorted(y)``) create a new object.  In general in Python (and in all cases
+in the standard library) a method that mutates an object will return ``None``
+to help avoid getting the two types of operations confused.  So if you
+mistakenly write ``y.sort()`` thinking it will give you a sorted copy of ``y``,
+you'll instead end up with ``None``, which will likely cause your program to
+generate an easily diagnosed error.
+
+However, there is one class of operations where the same operation sometimes
+has different behaviors with different types:  the augmented assignment
+operators.  For example, ``+=`` mutates lists but not tuples or ints (``a_list
++= [1, 2, 3]`` is equivalent to ``a_list.extend([1, 2, 3])`` and mutates
+``a_list``, whereas ``some_tuple += (1, 2, 3)`` and ``some_int += 1`` create
+new objects).
+
+In other words:
+
+* If we have a mutable object (:class:`list`, :class:`dict`, :class:`set`,
+  etc.), we can use some specific operations to mutate it and all the variables
+  that refer to it will see the change.
+* If we have an immutable object (:class:`str`, :class:`int`, :class:`tuple`,
+  etc.), all the variables that refer to it will always see the same value,
+  but operations that transform that value into a new value always return a new
+  object.
+
+If you want to know if two variables refer to the same object or not, you can
+use the :keyword:`is` operator, or the built-in function :func:`id`.
+
+
 How do I write a function with output parameters (call by reference)?
 ---------------------------------------------------------------------
 
diff --git a/Lib/distutils/command/upload.py b/Lib/distutils/command/upload.py
--- a/Lib/distutils/command/upload.py
+++ b/Lib/distutils/command/upload.py
@@ -162,7 +162,6 @@
                 if value and value[-1:] == b'\r':
                     body.write(b'\n')  # write an extra newline (lurve Macs)
         body.write(end_boundary)
-        body.write(b"\r\n")
         body = body.getvalue()
 
         msg = "Submitting %s to %s" % (filename, self.repository)
diff --git a/Lib/distutils/tests/test_upload.py b/Lib/distutils/tests/test_upload.py
--- a/Lib/distutils/tests/test_upload.py
+++ b/Lib/distutils/tests/test_upload.py
@@ -127,7 +127,7 @@
 
         # what did we send ?
         headers = dict(self.last_open.req.headers)
-        self.assertEqual(headers['Content-length'], '2163')
+        self.assertEqual(headers['Content-length'], '2161')
         content_type = headers['Content-type']
         self.assertTrue(content_type.startswith('multipart/form-data'))
         self.assertEqual(self.last_open.req.get_method(), 'POST')

-- 
Repository URL: https://hg.python.org/cpython


More information about the Python-checkins mailing list