On Tue, May 19, 2015 at 2:18 AM, Steven D'Aprano
The way to make this work would be two-fold. Firstly, an incorrect __future__ directive would have to no longer be a SyntaxError; and secondly, __future__ directives would have to be permitted after a try statement (currently, they're not allowed to follow anything, so the 'try' would have to be special-cased to be allowed in).
It's not enough to merely change the wording of the error from SyntaxError to something else. You have to change when it occurs: it can no longer be raised at compile time, but has to happen at run time. That means that __future__ imports have to have compile to something which runs at run time, instead of just being a directive to the compiler.
Precisely. I'm not saying that the incorrect future directive would be some other sort of error instead of SyntaxError - for this to be possible, it would have to *not be an error at all* at compile time, leaving the faulty directive unannounced until it gets to the second step (actual run-time importing of the __future__ module) to catch errors. (Hence the side effect that "from __future__ import all_feature_names" would actually not be an error; to the compiler, it's an unknown future directive and thus ignored, and to the run-time, it's a perfectly valid way to grab the list of features.)
As for the changes necessary to the compiler, I have no idea how extensive they would be, but my guess is "extremely".
Actually, not much. Since it's just the nerfing of one error, it can be done fairly easily - as proof of concept, I just commented out lines 50 through 54 of future.c (the "else" block that raises an error) and compiled: rosuav@sikorsky:~/cpython$ cat futuredemo.py from __future__ import generator_stop from __future__ import all_feature_names from __future__ import oops rosuav@sikorsky:~/cpython$ ./python futuredemo.py Traceback (most recent call last): File "futuredemo.py", line 3, in <module> from __future__ import oops ImportError: cannot import name 'oops'
Also, consider that once you are allowing __future__ directives to occur after a try statement, expect there to be a lot more pressure to allow it after any arbitrary code. After all, I might want to write:
if sys.version != '3.7' and read_config('config.ini')['allow_jabberwocky']: from __future__ import jabberwocky
so you're opening the doors to a LOT more complexity.
Yes, now that is a much bigger concern. I did say that the "try:" part of a try block would have to be deemed not-code, as a special case. Simply nerfing that error (in compile.c and future.c) does make for a viable proof-of-concept, though, so it's still nothing that requires extensive changes to the compiler. However...
Which, as far as I am concerned, is a good thing, because it makes the chances of this actually happening to be somewhere between Buckley's and none *wink*
... this I agree with. I don't think the feature is all that useful, and while it might well not be all that hard to implement, it would complicate things somewhat, and that's not good. (It also may end up being quite hard, and more so depending on the complexity of the definition of what's allowed prior to a __future__ directive.) I can imagine, for instance, a special case given to this precise structure: try: from __future__ import feature except: pass which would then be an "optional future import"; but again, how often is it even useful, much less necessary? ChrisA