2

I recently discovered that the round function available in future does not support negative digit rounding, which is incompatible with builtin round:

>>> from builtins import round
>>> round(4781, -2)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/lib/python2.7/dist-packages/future/builtins/newround.py", line 33, in newround
    raise NotImplementedError('negative ndigits not supported yet')
NotImplementedError: negative ndigits not supported yet

This somewhat limits the usefulness of the Python-Future quick-start recommendation:

The easiest way is to start each new module with these lines:

from __future__ import (absolute_import, division,
                        print_function, unicode_literals)
from builtins import *

Then write standard Python 3 code.

I can't find the round incompatibility documented anywhere, and wonder what other functions or types behave differently or have non-implemented features. What other gotchas are there? Where are these incompatibilities documented?

smci
  • 32,567
  • 20
  • 113
  • 146
Lee Hachadoorian
  • 364
  • 2
  • 15
  • 3
    Note that the `future` module isn't part of core Python, so you shouldn't expect to find its features or deficiencies documented in the official Python documentation. I'd suggest opening an issue on the [bug tracker](https://github.com/PythonCharmers/python-future/issues) – Mark Dickinson Jan 20 '18 at 19:22
  • The [`future` documentation on `round()`](http://python-future.org/reference.html#round) is not very forthcoming on this, I see: *See the `newround` module docstring for more details.*, and [that module](https://github.com/PythonCharmers/python-future/blob/fe494df5d66db19af94a1fdc32afe0e2e52dcf66/src/future/builtins/newround.py) offers almost zero detail. – Martijn Pieters Jan 20 '18 at 19:31
  • 1
    There are other problems with `future`s version of `round` that might be worth a bug report. For example: `round(1e100, 2)` raises a `decimal.InvalidOperation` exception. – Mark Dickinson Jan 20 '18 at 19:31
  • 1
    Opened https://github.com/PythonCharmers/python-future/issues/322 – Mark Dickinson Jan 20 '18 at 19:46
  • Thank you for clarifying that Python Future is distinct. I found references to future imports in official Python documentation, and looked for more info there. I have removed that remark from the question. – Lee Hachadoorian Jan 20 '18 at 19:54
  • 1
    @LeeHachadoorian: Right, the `__future__` (pseudo)module *is* part of the Python core. But the `future` package isn't. You shouldn't be seeing any occurrences of `import future` or `from future import ...` in the offiical Python docs. – Mark Dickinson Jan 20 '18 at 19:55
  • Thanks for that clarification. I did not understand that distinction. – Lee Hachadoorian Jan 20 '18 at 19:56
  • This was filed as [Issue #322 on Python-Future](https://github.com/PythonCharmers/python-future/issues/322) – smci Jan 20 '18 at 19:59

1 Answers1

4

There is no such list.

The Python-Future project is entirely separate from the Python project, so you indeed won't find any implementation gaps in the Python-Future project listed in the official Python documentation.

It is unfortunate that the reference documentation for round() fails to mention this gap in the implementation. An oblique reference to the newround module docstring isn't helpful either, as it too is very scant on details.

You'll have to ask the Python-Future project for such a list, you could try to file an issue to ask them to make such a list.

In the intervening time, you could search for NotImplementedError references in the source code. This will yield an incomplete list, as there may be short-comings in the implementation not covered by raising that exception.


On a personal note, I would recommend against using Python-Future; the project's philosophy of backporting everything without regard for suitability or performance is ill-suited for production code; for example, their super() implementation has to rely on full scans of all attributes on the class MRO to locate the relevant class to use as the first argumant, making it slow and cumbersome. Just because you can make it work somehow doesn't mean you should.

That their implementations are incomplete without clear indication where the gaps are only makes it harder to change my view on the project.

Martijn Pieters
  • 1,048,767
  • 296
  • 4,058
  • 3,343
  • Regarding your personal note: Point taken. In this situation I am teaching a course using a software that uses Python2 as a built-in scripting language, but I want my students to learn Python3. So this is a compromise for pedagogical purposes, and would not be used for production. – Lee Hachadoorian Jan 20 '18 at 19:49
  • [Issue #322 on Python-Future](https://github.com/PythonCharmers/python-future/issues/322) was already filed – smci Jan 20 '18 at 19:56
  • 1
    While I would also avoid using python-future, this bit - "their super() implementation has to rely on full scans of the class MRO" - is something [the real Python 3 super() does too](https://github.com/python/cpython/blob/master/Objects/typeobject.c#L7512), just in C. I'd say the bigger problems with the python-future implementation are that the scan fails with decorated methods or with functions that appear in multiple class dicts, and that the scan also needs to traverse the entire dict of each class until it finds the method. – user2357112 Jan 20 '18 at 19:56
  • @user2357112: no, it does not. The Python 3 implementation picks up the current class from the `__class__` closure the compiler adds. I'm not talking about resolving the attribute here, I'm referring to the search for the first argument value to `super()`. – Martijn Pieters Jan 20 '18 at 19:59
  • @user2357112: context: [Why is Python 3.x's super() magic?](//stackoverflow.com/a/19609168) – Martijn Pieters Jan 20 '18 at 20:05
  • @MartijnPieters: I'm aware that the Python 3 implementation picks up the current class from a closure variable, but they both need to scan the MRO eventually. The Python 3 implementation doesn't get to completely avoid MRO traversals, so saying that the python-future implementation is bad because it "has to rely on full scans of the class MRO" gives a misleading impression of the Python 3 implementation. There are much worse downsides of the python-future implementation to cite. – user2357112 Jan 20 '18 at 20:06
  • @user2357112: Using `super()` with arguments in Python 2, or using `super()` in Python 3 does not need to peek into the Python stack frame or scan `type(self)` in full (testing *every single attribute of every object in the MRO*, including poking at property and other descriptor objects) to find which one might be the class defining the current function object. That full scan is executed in Python byte code every single time you call a method using `super()`. I find that a very high cost to have to pay, when you could just use `super(ClassReference, self)`. – Martijn Pieters Jan 20 '18 at 20:12
  • @user2357112: the scan that `super().__getattr__()` has to execute is much, much cheaper; it only has to test for the attribute name in each `__dict__` mapping on the MRO, so worst case the cost is O(len(MRO)). Doing this is C code also makes it far cheaper than testing each and every attribute separately on the MRO (so O(total_attribute_count_on_the_MRO). That loop has to be executed regardless, the loop that Future adds here is *on top of that cost*. The project does a very poor job of making it clear that this cost exists. – Martijn Pieters Jan 20 '18 at 20:14
  • @MartijnPieters: The python-future implementation's scan is far more expensive than the `super.__getattribute__` scan, but the answer doesn't say anything about that; the answer just criticizes the python-future implementation for scanning the MRO at all. I'd say something like "their super() implementation has to rely on a slow, unreliable heuristic scan of the entire dict of every class in the MRO until it finds the calling method". – user2357112 Jan 20 '18 at 20:27
  • @user2357112: right, I was aiming to be succinct. Updated. – Martijn Pieters Jan 20 '18 at 20:42