[Python-checkins] peps (merge default -> default): Merge.

eric.smith python-checkins at python.org
Sat Aug 8 08:48:34 CEST 2015


https://hg.python.org/peps/rev/c147dcf14409
changeset:   5931:c147dcf14409
parent:      5930:0069da558ce6
parent:      5929:462b5e74a564
user:        Eric V. Smith <eric at trueblade.com>
date:        Sat Aug 08 02:48:37 2015 -0400
summary:
  Merge.

files:
  pep-0495.txt |  182 +++++++++++++++++++++++++++++++++++---
  pep-0499.txt |  124 ++++++++++++++++++++++++++
  2 files changed, 290 insertions(+), 16 deletions(-)


diff --git a/pep-0495.txt b/pep-0495.txt
--- a/pep-0495.txt
+++ b/pep-0495.txt
@@ -72,28 +72,170 @@
 
 The ``replace()`` methods of the ``datetime.time`` and
 ``datetime.datetime`` classes will get a new keyword-only argument
-called ``first`` with the default value ``True``.  The value of the
-``first`` argument will be used to set the value of the ``first``
-attribute in the returned instance.
+called ``first`` with the default value ``True``.  It will
+becave similarly to the other ``replace()`` arguments: if the ``first``
+argument is specified and given a boolean value, the new instance
+returned by ``replace()`` will have its ``first`` attribute set
+to that value.  In CPython, a non-boolean value of ``first`` will
+raise a ``TypeError``, but other implementations may allow the value
+``None`` to behave the same as when ``first`` is not given.  If the
+``first`` argument is not specified, the original value of the ``first``
+attribute is copied to the result.   
 
 Affected Behaviors
 ------------------
 
-The ``timestamp()`` method of ``datetime.datetime`` will return value
-advanced by 3600 if ``self`` represents an ambiguous hour and
-``first`` is False.
+Conversion from naive to aware
+..............................
+
+The ``astimezone()`` method will now work for naive ``self``.  The
+system local timezone will be assumed in this case and the ``first``
+flag will be used to determine which local timezone is in effect
+in the ambiguous case.
+
+For example, on a system set to US/Eastern timezone::
+
+  >>> dt = datetime(2014, 11, 2, 1, 30)
+  >>> dt.astimezone().strftime('%D %T %Z%z')
+  '11/02/14 01:30:00 EDT-0400'
+  >>> dt.replace(first=False).astimezone().strftime('%D %T %Z%z')
+  '11/02/14 01:30:00 EST-0500'
+
+Conversion to POSIX seconds from EPOCH
+......................................
+
+The ``timestamp()`` method of ``datetime.datetime`` will return different
+values for ``datetime.datetime`` instances that differ only by the value
+of their ``first`` attribute if and only if these instances represent an
+ambiguous or a non-existent value.
+
+When a ``datetime.datetime`` instance ``dt`` represents an ambiguous
+(repeated) time, there are two values ``s0`` and ``s1`` such that::
+
+  datetime.fromtimestamp(s0) == datetime.fromtimestamp(s1) == dt
+
+In this case, ``dt.timestamp()`` will return the smaller of ``s0``
+and ``s1`` values if ``dt.first == True`` and the larger otherwise.
+
+
+For example, on a system set to US/Eastern timezone::
+
+  >>> datetime(2014, 11, 2, 1, 30, first=True).timestamp()
+  1414906200.0
+  >>> datetime(2014, 11, 2, 1, 30, first=False).timestamp()
+  1414909800.0
+
+
+When a ``datetime.datetime`` instance ``dt`` represents an invalid
+time, there is no value ``s`` for which::
+
+  datetime.fromtimestamp(s) == dt
+
+but we can form two "nice to know" values of ``s`` that differ
+by the size of the gap in seconds.  One is the value of ``s``
+that would correspond to ``dt`` in a timezone where the UTC offset
+is always the same as the offset right before the gap and the
+other is the similar value but in a timezone the  UTC offset
+is always the same as the offset right after the gap.
+
+The value returned by ``dt.timestamp()`` given the invalid
+``dt`` will be the larger of the two "nice to know" values
+if ``dt.first  == True`` and the larger otherwise.
+
+For example, on a system set to US/Eastern timezone::
+
+  >>> datetime(2015, 3, 8, 2, 30, first=True).timestamp()
+  1425799800.0
+  >>> datetime(2015, 3, 8, 2, 30, first=False).timestamp()
+  1425796200.0
+
+
+Conversion from POSIX seconds from EPOCH
+........................................
+
 
 The ``fromtimestamp()`` static method of ``datetime.datetime`` will
 set the ``first`` attribute appropriately in the returned object.
 
+For example, on a system set to US/Eastern timezone::
 
-Implementations of tzinfo
-.........................
+  >>> datetime.fromtimestamp(1414906200)
+  datetime.datetime(2014, 11, 2, 1, 30)
+  >>> datetime.fromtimestamp(1414906200 + 3600)
+  datetime.datetime(2014, 11, 2, 1, 30, first=False)
 
-Subclasses of ``datetime.tzinfo`` will read the values of ``first`` in
-``utcoffset()`` and ``dst()`` methods and set it appropriately in the
-instances returned by the ``fromutc()`` method.  No change to the
-signatures of these methods is proposed.
+
+Implementations of tzinfo in stdlib
+...................................
+
+No new implementations of ``datetime.tzinfo`` abstract class are
+introduced in this PEP.  The existing (fixed offset) timezones do
+not introduce ambiguous local times and their ``utcoffset()``
+implementation will return the same constant value as they do now
+regardless of the value of ``first``. 
+
+New guidelines will be published for implementing concrete timezones
+with variable UTC offset.
+
+
+Guidelines for new tzinfo implementations
+-----------------------------------------
+
+Implementors of concrete ``datetime.tzinfo`` subclasses who want to
+support variable UTC offsets (due to DST and other causes) must follow
+these guidelines.
+
+New subclasses should override the baseclass ``fromutc()`` method so
+that in all cases where two UTC times ``u1`` and ``u2`` (``u1`` <``u2``)
+corespond to the same local time ``fromutc(u1)`` will return an instance
+with ``first=True`` and ``fromutc(u1)`` will return an instance
+with ``first=False``.
+
+New implementations of ``utcoffset()`` and ``dst()`` methods should
+ignore the value of ``first`` unless they are called on the ambiguous
+or invalid times.
+
+On an ambiguous time introduced at the end of DST, the values returned
+by ``utcoffset()`` and ``dst()`` methods should be as follows
+
++-----------------+----------------+------------------+
+|                 |   first=True   |    first=False   |
++-----------------+----------------+------------------+
+|   utcoff()      | stdoff + hour  |    stdoff        |
++-----------------+----------------+------------------+
+|    dst()        |     hour       |     zero         |
++-----------------+----------------+------------------+
+
+where ``stdoff`` is the standard (non-DST) offset,
+``hour = timedelta(hours=1)`` and ``zero = timedelta(0)``.
+
+On an invalid time introduced at the start of DST, the values returned
+by ``utcoffset()`` and ``dst()`` methods should	be as follows
+
+
++-----------------+----------------+------------------+
+|                 |   first=True   |    first=False   |
++-----------------+----------------+------------------+
+|   utcoff()      |     stdoff     |    stdoff + hour |
++-----------------+----------------+------------------+
+|    dst()        |     zero       |     hour         |
++-----------------+----------------+------------------+
+
+
+On ambiguous/invalid times introduced by the change in the standard time
+offset, the ``dst()`` method should return the same value regardless of
+the value of ``first`` and the ``utcoff()`` should return values
+according to the following table:
+
++-----------------+----------------+-----------------------------+
+|                 |   first=True   |    first=False              |
++-----------------+----------------+-----------------------------+
+|   ambiguous     |     oldoff     |     newoff = oldoff - delta |
++-----------------+----------------+-----------------------------+
+|   invalid       |     oldoff     |     newoff = oldoff + delta |
++-----------------+----------------+-----------------------------+
+
+
 
 Pickle size
 -----------
@@ -117,7 +259,7 @@
 --------------------
 
 The value of "first" will be ignored in all operations except those
-that involve conversion between timezones.
+that involve conversion between timezones. [#]_
 
 The result of addition (subtraction) of a timedelta to (from) a
 datetime will always have ``first`` set to ``True`` even if the
@@ -125,9 +267,17 @@
 
 (The only methods that will be able to produce non-default value of
 "first" are ``__new__``, and ``replace()`` methods of the
-``datetime.datetime`` and ``datetime.time`` classes ``now()`` and
-``fromtimestamp()`` methods of the ``datetime.datetime`` class, and
-``fromutc()`` method of some tzinfo implementations.)
+``datetime.datetime`` and ``datetime.time`` classes ``now()``,
+``astimezone()`` and ``fromtimestamp()`` methods of the
+``datetime.datetime`` class, and ``fromutc()`` method of some tzinfo
+implementations.)
+
+
+.. [#] As of Python 3.5, ``tzinfo`` is ignored whenever timedelta is
+   added or subtracted from a ``datetime.datetime`` instance or when
+   one ``datetime.datetime`` instance is subtracted from another with
+   the same (even not-None) ``tzinfo``.  This may change in the future,
+   but such changes are outside of the scope of this PEP.
 
 Comparison
 ----------
diff --git a/pep-0499.txt b/pep-0499.txt
new file mode 100644
--- /dev/null
+++ b/pep-0499.txt
@@ -0,0 +1,124 @@
+PEP: 499
+Title: ``python -m foo`` should bind ``sys.modules['foo']`` in additon to ``sys.modules['__main__']``
+Version: $Revision$
+Last-Modified: $Date$
+Author: Cameron Simpson <cs at zip.com.au>
+Status: Draft
+Type: Standards Track
+Content-Type: text/x-rst
+Created: 07-Aug-2015
+Python-Version: 3.6
+
+Abstract
+========
+
+When a module is used as a main program on the Python command line,
+such as by:
+
+    python -m module.name ...
+
+it is easy to accidentally end up with two independent instances
+of the module if that module is again imported within the program.
+This PEP proposes a way to fix this problem.
+
+When a module is invoked via Python's -m option the module is bound
+to ``sys.modules['__main__']`` and its ``.__name__`` attribute is set to
+``'__main__'``.
+This enables the standard "main program" boilerplate code at the
+bottom of many modules, such as::
+
+    if __name__ == '__main__':
+        sys.exit(main(sys.argv))
+
+However, when the above command line invocation is used it is a
+natural inference to presume that the module is actually imported
+under its official name ``module.name``,
+and therefore that if the program again imports that name
+then it will obtain the same module instance.
+
+That actuality is that the module was imported only as ``'__main__'``.
+Another import will obtain a distinct module instance, which can
+lead to confusing bugs.
+
+
+Proposal
+========
+
+It is suggested that to fix this situation all that is needed is a
+simple change to the way the ``-m`` option is implemented: in addition
+to binding the module object to ``sys.modules['__main__']``, it is also
+bound to ``sys.modules['module.name']``.
+
+Nick Coghlan has suggested that this is as simple as modifying the
+``runpy`` module's ``_run_module_as_main`` function as follows::
+
+    main_globals = sys.modules["__main__"].__dict__
+
+to instead be::
+
+    main_module = sys.modules["__main__"]
+    sys.modules[mod_spec.name] = main_module
+    main_globals = main_module.__dict__
+
+
+Considerations and Prerequisites
+================================
+
+Pickling Modules
+----------------
+
+Nick has mentioned `issue 19702`_ which proposes (quoted from the issue):
+
+- runpy will ensure that when __main__ is executed via the import
+  system, it will also be aliased in sys.modules as __spec__.name
+- if __main__.__spec__ is set, pickle will use __spec__.name rather
+  than __name__ to pickle classes, functions and methods defined in
+  __main__
+- multiprocessing is updated appropriately to skip creating __mp_main__
+  in child processes when __main__.__spec__ is set in the parent
+  process
+
+The first point above covers this PEP's specific proposal.
+
+
+Background
+==========
+
+`I tripped over this issue`_ while debugging a main program via a
+module which tried to monkey patch a named module, that being the
+main program module.  Naturally, the monkey patching was ineffective
+as it imported the main module by name and thus patched the second
+module instance, not the running module instance.
+
+However, the problem has been around as long as the ``-m`` command
+line option and is encountered regularly, if infrequently, by others.
+
+In addition to `issue 19702`_, the discrepancy around `__main__`
+is alluded to in PEP 451 and a similar proposal (predating PEP 451)
+is described in PEP 395 under `Fixing dual imports of the main module`_.
+
+
+References
+==========
+
+.. _issue 19702: http://bugs.python.org/issue19702
+
+.. _I tripped over this issue: https://mail.python.org/pipermail/python-list/2015-August/694905.html
+
+.. _Fixing dual imports of the main module: https://www.python.org/dev/peps/pep-0395/#fixing-dual-imports-of-the-main-module
+
+
+Copyright
+=========
+
+This document has been placed in the public domain.
+
+

+..
+   Local Variables:
+   mode: indented-text
+   indent-tabs-mode: nil
+   sentence-end-double-space: t
+   fill-column: 70
+   coding: utf-8
+   End:

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


More information about the Python-checkins mailing list