Skip to content

Commit

Permalink
PEP 615: Updates in response to Steering Council feedback (python#1350)
Browse files Browse the repository at this point in the history
* Rename nocache to no_cache

This is more consistent with the snake_case naming convention in
from_file and clear_cache.

* Add section on clear_cache

* Improve string representation section.

In response to suggestions by Barry Warsaw:

python/steering-council#22 (comment)

* Specify failure behavior for time zone data.

From discussion with Barry Warsaw:
python/steering-council#22 (comment)

* Add a footnote regarding the term "first-party"

Per comments from Barry Warsaw:
python/steering-council#22 (comment)
  • Loading branch information
pganssle committed Apr 4, 2020
1 parent 42e4ce4 commit 226d6ef
Showing 1 changed file with 80 additions and 12 deletions.
92 changes: 80 additions & 12 deletions pep-0615.rst
Expand Up @@ -18,7 +18,7 @@ This proposes adding a module, ``zoneinfo``, to provide a concrete time zone
implementation supporting the IANA time zone database. By default,
``zoneinfo`` will use the system's time zone data if available; if no system
time zone data is available, the library will fall back to using the
first-party package ``tzdata``, deployed on PyPI.
first-party package ``tzdata``, deployed on PyPI. [d]_

Motivation
==========
Expand Down Expand Up @@ -97,6 +97,11 @@ read from the data source (usually a TZif file) upon construction, and may
not change during the lifetime of the object (this restriction applies to all
``ZoneInfo`` constructors).

In the event that no matching file is found on the search path (either because
the system does not supply time zone data or because the key is invalid), the
constructor will raise a ``zoneinfo.ZoneInfoNotFoundError``, which will be a
subclass of ``KeyError``.

One somewhat unusual guarantee made by this constructor is that calls with
identical arguments must return *identical* objects. Specifically, for all
values of ``key``, the following assertion must always be valid [b]_::
Expand Down Expand Up @@ -132,7 +137,7 @@ behavior for end users.

.. code-block::
ZoneInfo.nocache(key: str)
ZoneInfo.no_cache(key: str)
This is an alternate constructor that bypasses the constructor's cache. It is
identical to the primary constructor, but returns a new object on each call.
Expand All @@ -145,7 +150,7 @@ should always be true:

.. code-block::
>>> a = ZoneInfo.nocache(key)
>>> a = ZoneInfo.no_cache(key)
>>> b = ZoneInfo(key)
>>> a is not b
Expand All @@ -164,7 +169,7 @@ stream objects have mutable state and so determining whether two inputs are
identical is difficult or impossible, and it is likely that users constructing
from a file specifically want to load from that file and not a cache.

As with ``ZoneInfo.nocache``, objects constructed by this method must not be
As with ``ZoneInfo.no_cache``, objects constructed by this method must not be
added to the cache.

Behavior during data updates
Expand All @@ -187,7 +192,7 @@ should come from the updated data source.
This means that the point at which a ``ZoneInfo`` file is updated depends
primarily on the semantics of the caching behavior. The only guaranteed way to
get a ``ZoneInfo`` file from an updated data source is to induce a cache miss,
either by bypassing the cache and using ``ZoneInfo.nocache`` or by clearing the
either by bypassing the cache and using ``ZoneInfo.no_cache`` or by clearing the
cache.

.. note::
Expand All @@ -197,15 +202,59 @@ cache.
recommended) to eagerly pre-populate the cache with time zones that have
never been constructed.

Deliberate cache invalidation
#############################

In addition to ``ZoneInfo.no_cache``, which allows a user to *bypass* the
cache, ``ZoneInfo`` also exposes a ``clear_cache`` method to deliberately
invalidate either the entire cache or selective portions of the cache::

ZoneInfo.clear_cache(*, only_keys: Iterable[str]=None) -> None

If no arguments are passed, all caches are invalidated and the first call for
each key to the primary ``ZoneInfo`` constructor after the cache has been
cleared will return a new instance.

.. code-block::
>>> NYC0 = ZoneInfo("America/New_York")
>>> NYC0 is ZoneInfo("America/New_York")
True
>>> ZoneInfo.clear_cache()
>>> NYC1 = ZoneInfo("America/New_York")
>>> NYC0 is NYC1
False
>>> NYC1 is ZoneInfo("America/New_York")
True
An optional parameter, ``only_keys``, takes an iterable of keys to clear from
the cache, otherwise leaving the cache intact.

.. code-block::
>>> NYC0 = ZoneInfo("America/New_York")
>>> LA0 = ZoneInfo("America/Los_Angeles")
>>> ZoneInfo.clear_cache(only_keys=["America/New_York"])
>>> NYC1 = ZoneInfo("America/New_York")
>>> LA0 = ZoneInfo("America/Los_Angeles")
>>> NYC0 is NYC1
False
>>> LA0 is LA1
True
Manipulation of the cache behavior is expected to be a niche use case; this
function is primarily provided to facilitate testing, and to allow users with
unusual requirements to tune the cache invalidation behavior to their needs.

.. _Representations:

String representation
#####################

The ``ZoneInfo`` class's ``__str__`` representation will be drawn from the
``key`` parameter. This is partially because the ``key`` represents a
``key`` parameter. This is partially because the ``key`` represents a
human-readable "name" of the string, but also because it is a useful parameter
that users will want exposed. It is necessary to provide a mechanism to expose
that users will want exposed. It is necessary to provide a mechanism to expose
the key for serialization between languages and because it is also a primary
key for localization projects like CLDR (the Unicode Common Locale Data
Repository [#cldr]_).
Expand All @@ -218,6 +267,11 @@ An example:
>>> str(zone)
'Pacific/Kwajalein'
>>> dt = datetime(2020, 4, 1, 3, 15, tzinfo=zone)
>>> f"{dt.isoformat()} [{dt.tzinfo}]"
'2020-04-01T03:15:00+12:00 [Pacific/Kwajalein]'
When a ``key`` is not specified, the ``str`` operation should not fail, but
should return the file's ``__repr__``::

Expand All @@ -227,7 +281,15 @@ should return the file's ``__repr__``::

The ``__repr__`` for a ``ZoneInfo`` is implementation-defined and not
necessarily stable between versions, but it must not be a valid ``ZoneInfo``
key.
key, to avoid confusion between a key-derived ``ZoneInfo`` with a valid
``__str__`` and a file-derived ``ZoneInfo`` which has fallen through to the
``__repr__``.

Since the use of ``str()`` to access the key provides no easy way to check
for the *presence* of a key (the only way is to try constructing a ``ZoneInfo``
from it and detect whether it raises an exception), ``ZoneInfo`` objects will
also expose a read-only ``key`` attribute, which will be ``None`` in the event
that no key was supplied.

Pickle serialization
####################
Expand All @@ -253,11 +315,11 @@ The behavior of a ``ZoneInfo`` file depends on how it was constructed:
>>> a is b
True
2. ``ZoneInfo.nocache(key)``: When constructed from the cache-bypassing
2. ``ZoneInfo.no_cache(key)``: When constructed from the cache-bypassing
constructor, the ``ZoneInfo`` object will still be serialized by key, but
when deserialized, it will use the cache bypassing constructor. If
``europe_berlin_pkl_nc`` is a string containing a pickle constructed from
``ZoneInfo.nocache("Europe/Berlin")``, one would expect the following
``ZoneInfo.no_cache("Europe/Berlin")``, one would expect the following
behavior:

.. code-block::
Expand Down Expand Up @@ -295,7 +357,7 @@ to use system-deployed time zone data wherever possible. However, not all
systems ship a publicly accessible time zone database — notably Windows uses a
different system for managing time zones — and so if available ``zoneinfo``
falls back to an installable first-party package, ``tzdata``, available on
PyPI. If no system zoneinfo files are found but ``tzdata`` is installed, the
PyPI. [d]_ If no system zoneinfo files are found but ``tzdata`` is installed, the
primary ``ZoneInfo`` constructor will use ``tzdata`` as the time zone source.

System time zone information
Expand Down Expand Up @@ -352,7 +414,7 @@ The ``tzdata`` Python package
In order to ensure easy access to time zone data for all end users, this PEP
proposes to create a data-only package ``tzdata`` as a fallback for when system
data is not available. The ``tzdata`` package would be distributed on PyPI as
a "first party" package, maintained by the CPython development team.
a "first party" package [d]_, maintained by the CPython development team.

The ``tzdata`` package contains only data and metadata, with no public-facing
functions or classes. It will be designed to be compatible with both newer
Expand Down Expand Up @@ -792,6 +854,12 @@ Footnotes
we do not need to worry about the possibly more serious issue that a given
``datetime`` object's hash would change during its lifetime.
.. [d]
The term "first party" here is distinguished from "third party" in that,
although it is is distributed via PyPI and is not currently included in
Python by default, it is to be considered an official sub-project of
CPython rather than a "blessed" third-party package.
References
==========

Expand Down

0 comments on commit 226d6ef

Please sign in to comment.