Nick Coghlan | 1 Feb 01:35
Picon
Gravatar

Re: Store timestamps as decimal.Decimal objects

On Wed, Feb 1, 2012 at 8:58 AM, Mark Shannon <mark <at> hotpy.org> wrote:
> Why not add a new function rather than modifying time.time()?
> (after all its just a timestamp, does it really need nanosecond precision?)
>
> For those who do want super-accuracy then add a new function
> time.picotime() (it could be nanotime but why not future proof it :) )
> which returns an int represent the number of picoseconds since the
> epoch. ints never loose precision and never overflow.

Because the problem is broader than that - it affects os.stat(), too,
along with a number of the other time module APIs that produce
timestamp values.

That's where Alexander's suggestion of a separate "hirestime" module
comes in - it would be based on the concept of *always* using a high
precision type in the API (probably decimal.Decimal()). Conceptually,
it's a very clean approach, and obviously has zero performance impact
on existing APIs, but the idea of adding
yet-another-time-related-module to the standard library is rather
questionable. Such an approach is also likely to lead to a lot of
duplicated code.

Victor's current approach, unfortunately, is a bit of a
"worst-of-both-worlds" approach. It couples the time and os modules to
various other currently unrelated modules (such as datetime and
decimal), but still doesn't provide a particularly extensible API
(whether indicated by flags or strings, each new supported output type
must be special cased in time and os).

Perhaps more fruitful would be to revisit the original idea from the
(Continue reading)

Antoine Pitrou | 1 Feb 03:35

Re: Store timestamps as decimal.Decimal objects

On Wed, 1 Feb 2012 10:35:08 +1000
Nick Coghlan <ncoghlan <at> gmail.com> wrote:
> 
> With this approach, API usage might end up looking something like:
> 
>    >>> time.time()
>    1328006975.681211
>    >>> time.time(convert=time.as_float)
>    1328006975.681211
>    >>> time.time(convert=time.as_int)
>    1328006979
>    >>> time.time(convert=time.as_tuple)
>    (1328006975, 681211, -9)
>    >>> time.time(convert=decimal.Decimal.from_components)
>    Decimal('1328006983.761119000')

It strikes me as inelegant to have to do so much typing for something
as simple as getting the current time. We should approach the
simplicity of ``time.time(format='decimal')`` or
``time.decimal_time()``.

(and I think the callback thing is overkill)

Regards

Antoine.

PJ Eby | 1 Feb 03:40
Gravatar

Re: Store timestamps as decimal.Decimal objects

On Tue, Jan 31, 2012 at 7:35 PM, Nick Coghlan <ncoghlan <at> gmail.com> wrote:
Such a protocol can easily be extended to any other type - the time
module could provide conversion functions for integers and float
objects (meaning results may have lower precision than the underlying
system calls), while the existing "fromtimestamp" APIs in datetime can
be updated to accept the new optional arguments (and perhaps an
appropriate class method added to timedelta, too). A class method
could also be added to the decimal module to construct instances from
integer components (as shown above), since that method of construction
isn't actually specific to timestamps.

Why not just make it something like __fromfixed__() and make it a standard protocol, implemented on floats, ints, decimals, etc.  Then the API is just "time.time(type)", where type is any object providing a __fromfixed__ method.  ;-)
 
Matt Joiner | 1 Feb 04:02
Picon
Gravatar

Re: Store timestamps as decimal.Decimal objects

Analysis paralysis commence. +1 for separate module using decimal.

On Feb 1, 2012 1:44 PM, "PJ Eby" <pje <at> telecommunity.com> wrote:
On Tue, Jan 31, 2012 at 7:35 PM, Nick Coghlan <ncoghlan <at> gmail.com> wrote:
Such a protocol can easily be extended to any other type - the time
module could provide conversion functions for integers and float
objects (meaning results may have lower precision than the underlying
system calls), while the existing "fromtimestamp" APIs in datetime can
be updated to accept the new optional arguments (and perhaps an
appropriate class method added to timedelta, too). A class method
could also be added to the decimal module to construct instances from
integer components (as shown above), since that method of construction
isn't actually specific to timestamps.

Why not just make it something like __fromfixed__() and make it a standard protocol, implemented on floats, ints, decimals, etc.  Then the API is just "time.time(type)", where type is any object providing a __fromfixed__ method.  ;-)
 

_______________________________________________
Python-Dev mailing list
Python-Dev <at> python.org
http://mail.python.org/mailman/listinfo/python-dev
Unsubscribe: http://mail.python.org/mailman/options/python-dev/anacrolix%40gmail.com

Nick Coghlan | 1 Feb 05:08
Picon
Gravatar

Re: Store timestamps as decimal.Decimal objects

On Wed, Feb 1, 2012 at 12:35 PM, Antoine Pitrou <solipsis <at> pitrou.net> wrote:
> It strikes me as inelegant to have to do so much typing for something
> as simple as getting the current time. We should approach the
> simplicity of ``time.time(format='decimal')`` or
> ``time.decimal_time()``.

Getting the current time is simple (you can already do it), getting
access to high precision time without performance regressions or
backwards incompatiblities or excessive code duplication is hard.

There's a very simple rule in large scale software development:
coupling is bad and you should do everything you can to minimise it.
Victor's approach throws that out the window by requiring that time
and os know about every possible output format for time values.

That's why protocols are so valuable: instead of having MxN points of
interconnection, you just define a standard protocol as the basis for
interaction, and the consumer of the protocol doesn't need to care
about the details of the provider, they just care about the protocol
itself.

So, the question becomes how to solve the problem of exposing high
resolution timestamps to Python code in a way that:
- is applicable not just to time.time(), but also to os.stat(),
time.clock(), time.wall_clock() and any other timestamp sources I've
forgotten.
- is backwards compatible for all those use cases
- doesn't cause a significant performance regression for any of those use cases
- doesn't cause excessive coupling between the time and os modules and
other parts of Python
- doesn't excessively duplicate code
- doesn't add too much machinery for a relatively minor problem

The one key aspect that I think Victor's suggestion gets right is that
we want a way to request high precision time from the *existing* APIs,
and that this needs to be selected on a per call basis rather than
globally for the whole application.

The big advantage of going with a callback based approach is that it
gives you flexibility and low coupling without any additional
supporting infrastructure, and you have the full suite of Python tools
available to deal with any resulting verbosity issues.

For example, it would become *trivial* to write Alexander's suggested
"hirestime" module that always returned decimal.Decimal objects:

    _hires = decimal.Decimal.from_components

    def time():
        return time.time(convert=_hires)

    def clock():
        return time.clock(convert=_hires)

    def stat(path):
        return os.stat(path, timestamps=_hires)

    # etc...

PJE is quite right that using a new named protocol rather than a
callback with a particular signature could also work, but I don't see
a lot of advantages in doing so.

On the other hand, if you go with the "named output format",
"hires=True" or new API approaches, you end up having to decide what
additional coupling you're going to introduce to time and os. Now, in
this case, I actually think there *is* a reasonable option available
if we decide to go down that path:
- incorporate Stefan Krah's cdecimal work into the standard library
- add a "hires=False" flag to affected APIs
- return a Decimal instance with full available precision if
"hires=True" is passed in.
- make time and os explicitly depend on the ability to create
decimal.Decimal instances

A hirestime module is even easier to implement in that case:

    def time():
        return time.time(hires=True)

    def clock():
        return time.clock(hires=True)

    def stat(path):
        return os.stat(path, hires=True)

    # etc...

All of the other APIs (datetime, timedelta, etc) can then just be
updated to also accept a Decimal object as input, rather than handling
the (integer, fraction, exponent) callback signature I suggested.

Either extreme (full flexibility via a callback API or protocol, or
else settling specifically on decimal.Decimal and explicitly making
time and os dependent on that type) makes sense to me. A wishy-washy
middle ground that introduces a dependency from time and os onto
multiple other modules *without* making the API user extensible
doesn't seem reasonable at all.

Cheers,
Nick.

--

-- 
Nick Coghlan   |   ncoghlan <at> gmail.com   |   Brisbane, Australia
Ethan Furman | 1 Feb 04:57
Picon
Gravatar

PEP 409 - final?

I haven't seen any further discussion here or in the bug tracker.  Below 
is the latest version of this PEP, now with a section on Language Details.

Who makes the final call on this?  Any idea how long that will take? 
(Not that I'm antsy, or anything... ;)

PEP: 409
Title: Suppressing exception context
Version: $Revision$
Last-Modified: $Date$
Author: Ethan Furman <ethan <at> stoneleaf.us>
Status: Draft
Type: Standards Track
Content-Type: text/x-rst
Created: 26-Jan-2012
Post-History: 30-Aug-2002, 01-Feb-2012

Abstract
========

One of the open issues from PEP 3134 is suppressing context:  currently
there is no way to do it.  This PEP proposes one.

Rationale
=========

There are two basic ways to generate exceptions:

  1) Python does it (buggy code, missing resources, ending loops, etc.)

  2) manually (with a raise statement)

When writing libraries, or even just custom classes, it can become
necessary to raise exceptions; moreover it can be useful, even
necessary, to change from one exception to another.  To take an example
from my dbf module::

     try:
         value = int(value)
     except Exception:
         raise DbfError(...)

Whatever the original exception was (``ValueError``, ``TypeError``, or
something else) is irrelevant.  The exception from this point on is a
``DbfError``, and the original exception is of no value.  However, if
this exception is printed, we would currently see both.

Alternatives
============
Several possibilities have been put forth:

  * ``raise as NewException()``

    Reuses the ``as`` keyword; can be confusing since we are not really
    reraising the originating exception

  * ``raise NewException() from None``

    Follows existing syntax of explicitly declaring the originating
    exception

  * ``exc = NewException(); exc.__context__ = None; raise exc``

    Very verbose way of the previous method

  * ``raise NewException.no_context(...)``

    Make context suppression a class method.

All of the above options will require changes to the core.

Proposal
========

I proprose going with the second option::

     raise NewException from None

It has the advantage of using the existing pattern of explicitly setting
the cause::

     raise KeyError() from NameError()

but because the 'cause' is ``None`` the previous context, while retained,
is not displayed by the default exception printing routines.

Language Details
================

Currently, ``__context__`` and ``__cause__`` start out as None, and then get
set as exceptions occur.

To support ``from None``, ``__context__`` will stay as it is, but
``__cause__`` will start out as ``False``, and will change to ``None``
when the ``raise ... from None`` method is used.

The default exception printing routine will then:

  * If ``__cause__`` is ``False`` the ``__context__`` (if any) will be 
printed.

  * If ``__cause__`` is ``None`` the ``__context__`` will not be printed.

  * if ``__cause__`` is anything else, ``__cause__`` will be printed.

This has the benefit of leaving the ``__context__`` intact for future
logging, querying, etc., while suppressing its display if it is not caught.
This is important for those times when trying to debug poorly written
libraries with `bad error messages`_.

Patches
=======
There is a patch for CPython implementing this attached to `Issue 6210`_.

References
==========

Discussion and refinements in this `thread on python-dev`_.

.. _bad error messages: http://bugs.python.org/msg152294
.. _Issue 6210: http://bugs.python.org/issue6210
.. _thread on python-dev: 
http://mail.python.org/pipermail/python-dev/2012-January/115838.html

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:

Ethan Furman | 1 Feb 04:58
Picon
Gravatar

PEP 409 - now properly formatted (sorry for the noise)

PEP: 409
Title: Suppressing exception context
Version: $Revision$
Last-Modified: $Date$
Author: Ethan Furman <ethan <at> stoneleaf.us>
Status: Draft
Type: Standards Track
Content-Type: text/x-rst
Created: 26-Jan-2012
Post-History: 30-Aug-2002, 01-Feb-2012

Abstract
========

One of the open issues from PEP 3134 is suppressing context:  currently
there is no way to do it.  This PEP proposes one.

Rationale
=========

There are two basic ways to generate exceptions:

  1) Python does it (buggy code, missing resources, ending loops, etc.)

  2) manually (with a raise statement)

When writing libraries, or even just custom classes, it can become
necessary to raise exceptions; moreover it can be useful, even
necessary, to change from one exception to another.  To take an example
from my dbf module::

     try:
         value = int(value)
     except Exception:
         raise DbfError(...)

Whatever the original exception was (``ValueError``, ``TypeError``, or
something else) is irrelevant.  The exception from this point on is a
``DbfError``, and the original exception is of no value.  However, if
this exception is printed, we would currently see both.

Alternatives
============
Several possibilities have been put forth:

  * ``raise as NewException()``

    Reuses the ``as`` keyword; can be confusing since we are not really
    reraising the originating exception

  * ``raise NewException() from None``

    Follows existing syntax of explicitly declaring the originating
    exception

  * ``exc = NewException(); exc.__context__ = None; raise exc``

    Very verbose way of the previous method

  * ``raise NewException.no_context(...)``

    Make context suppression a class method.

All of the above options will require changes to the core.

Proposal
========

I proprose going with the second option::

     raise NewException from None

It has the advantage of using the existing pattern of explicitly setting
the cause::

     raise KeyError() from NameError()

but because the 'cause' is ``None`` the previous context, while retained,
is not displayed by the default exception printing routines.

Language Details
================

Currently, ``__context__`` and ``__cause__`` start out as None, and then get
set as exceptions occur.

To support ``from None``, ``__context__`` will stay as it is, but
``__cause__`` will start out as ``False``, and will change to ``None``
when the ``raise ... from None`` method is used.

The default exception printing routine will then:

  * If ``__cause__`` is ``False`` the ``__context__`` (if any) will be
printed.

  * If ``__cause__`` is ``None`` the ``__context__`` will not be printed.

  * if ``__cause__`` is anything else, ``__cause__`` will be printed.

This has the benefit of leaving the ``__context__`` intact for future
logging, querying, etc., while suppressing its display if it is not caught.
This is important for those times when trying to debug poorly written
libraries with `bad error messages`_.

Patches
=======
There is a patch for CPython implementing this attached to `Issue 6210`_.

References
==========

Discussion and refinements in this `thread on python-dev`_.

.. _bad error messages: http://bugs.python.org/msg152294
.. _Issue 6210: http://bugs.python.org/issue6210
.. _thread on python-dev:
http://mail.python.org/pipermail/python-dev/2012-January/115838.html

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:

Nick Coghlan | 1 Feb 06:14
Picon
Gravatar

Re: PEP 409 - final?

On Wed, Feb 1, 2012 at 1:57 PM, Ethan Furman <ethan <at> stoneleaf.us> wrote:
> I haven't seen any further discussion here or in the bug tracker.  Below is
> the latest version of this PEP, now with a section on Language Details.
>
> Who makes the final call on this?  Any idea how long that will take? (Not
> that I'm antsy, or anything... ;)

Guido still has the final say on PEP approvals as BDFL - it's just
that sometimes he'll tap someone else and say "Your call!" (thus
making them a BDFOP - Benevolent Dictator for One PEP).

FWIW, I'm personally +1 on the latest version of this.

Cheers,
Nick.

--

-- 
Nick Coghlan   |   ncoghlan <at> gmail.com   |   Brisbane, Australia
Ethan Furman | 1 Feb 06:07
Picon
Gravatar

docs fixes and PEP 409

I'm looking at the docs to make the relevant changes due to PEP 409, and 
I'm noticing some problems.

E.g. The PyException_Get|Set_Context|Cause all talk about using NULL to 
clear the related attribute, when actually in should be Py_None.

Only PyException_GetCause is directly related to PEP 409 -- should I 
only fix that one, and open up a new issue on the tracker for the other 
three, or should I fix all four now?

~Ethan~
Nick Coghlan | 1 Feb 06:57
Picon
Gravatar

Re: docs fixes and PEP 409

On Wed, Feb 1, 2012 at 3:07 PM, Ethan Furman <ethan <at> stoneleaf.us> wrote:
> I'm looking at the docs to make the relevant changes due to PEP 409, and I'm
> noticing some problems.
>
> E.g. The PyException_Get|Set_Context|Cause all talk about using NULL to
> clear the related attribute, when actually in should be Py_None.
>
> Only PyException_GetCause is directly related to PEP 409 -- should I only
> fix that one, and open up a new issue on the tracker for the other three, or
> should I fix all four now?

Passing in NULL is the right way to clear them using those APIs - the
descriptors in exceptions.c then control how "not set" is exposed at
the Python layer.

So only Get/SetCause should need updating for PEP 409 to say to pass
in NULL to clear the cause and fall back on displaying the context and
Py_None to suppress the context in the default display.

Cheers,
Nick.

--

-- 
Nick Coghlan   |   ncoghlan <at> gmail.com   |   Brisbane, Australia

Gmane