-
Notifications
You must be signed in to change notification settings - Fork 182
Allow __future__ in doctest #43
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
fwiw, since contributors now need to be explicit to avoid maintainers fiddling with commits, I am capable of rebasing my own changesets if it is required, so please dont mess up my workflows by doing it for me. |
2976f24
to
1591a65
Compare
@futuresAllowed.setter | ||
def futuresAllowed(self, value): | ||
"""Disable permitting `__future__` in the current context.""" | ||
self.futuresAllowed # trigger consistent deprecation warning |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
bah, this wont work with stack levels
1591a65
to
9ec74d5
Compare
|
||
def test_futureImportUsed(self): | ||
"""XXX This test can't work in a doctest""" | ||
pass |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this class (and while we're at it, maybe the one above) just be removed? Unless I'm missing something, not much point to an empty test class.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
No. The mixin means this empty class runs all of the TestImports tests with a different self.flakes method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, of course. Perhaps instead of pass
we can have a short docstring saying just that?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Sure; I'll update this patch after #42 is merged, copying the docstring that is merged for that test case.
Anything else?
What's the essential change being made here? I thought maybe def f():
"""
>>> from __future__ import print_function
>>> 'a'
""" |
Did you enable doctests when you tested that? |
Apparently not. Definitely need more coffee this morning :) What do you think about dropping |
I dont see how dropping it simplifies the code significantly. It is only 14 lines of deprecation code for the property. The underlying assumption about As a result, I could easily be convinced that
Not deprecating it implies this fix could only be released in pyflakes 2.0, if this project is using semver. |
9ec74d5
to
48db10f
Compare
It's not using semver, so I wouldn't worry about it too much. We could probably do better at clearly delineating the public interface. It's probably just:
It would be nice to preserve the Perhaps it would help me to understand your objective if you could elaborate on what behaviors the tests are asserting. If the problem is that putting a |
As explained in the commit message, There are projects using |
What? I took a quick look at flake8 and it's not using this attribute. What other projects use pyflakes via |
"per PEP8" = as defined by PEP8. It should use underscore prefixes for private variables.
I assume you mean "What other projects use pyflakes via https://github.com/rdunklau/Gedit-checkpython has used |
While that project uses |
Pylama seems to be maintained and using the Checker class without using futuresAllowed: https://github.com/klen/pylama/blob/develop/pylama/lint/pylama_pyflakes.py#L42 |
(My point here is that I'd bet 99% of people using the class use it only to access the |
@sigmavirus24 , that line of reasoning is beating around the bush. It is not possible to know all uses; at best we could look at all usage in open source projects published and findable via Google. That is why there are conventions for public interfaces. Is your input in this code review that you want me to break backwards compatibility with regards to |
|
It isn't beating around the bush. It's pragmatic instead of ideological.
My input is that @bitglue's input makes sense and is reasonable (as they are the primary maintainer). Short of writing a new class to take the Finally, there is no documentation around futuresAllowed. Your argument is invalid. If what we can see indicates that people are primarily using the |
51da74d
to
f67709e
Compare
not self.scope._futures_allowed or | ||
any(not isinstance(binding, FutureImportation) | ||
for binding in self.scope.values())): | ||
self.scope._futures_allowed = False |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can this (and the very similar logic above) be broken up to make it easier to digest please?
And maybe it can be consolidated. If I'm following the logic correctly, the only reason we can't simply check scope._futures_allowed
here is that above, where its set in handleNode()
, there's a possibility there was some from foo import bar
or similar. That would have met the isinstance(node, ast.ImportFrom)
condition, and so would not have set self.scope._futures_allowed = False
It would seem though that if we extend that condition just a bit, to match only from __future__ ...
, then we can have the complete logic for determining if __future__
imports are allowed in just one place. What do you think?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The check for "any other binding in scope" because this method currently can not follow the old pattern
if node.module == '__future__':
...
else:
self.futuresAllowed = False
"self.futuresAllowed = False" can not become "self.scope._futures_allowed = False" , because the current scope may not be a ModuleScope/DocScope, so "self.scope._futures_allowed" would be an attribute error.
We could work around that by using a more complex else clause like
if node.module == '__future__':
...
elif hasattr(self.scope, '_futures_allowed'):
self.scope._futures_allowed = False
However that adds an extra branch to every 'import from' processed, whereas a "any other binding in scope" inside if node.module == '__future__'
achieves the same result and is only executed within the specific __future__
import nodes, so it will only negatively impact performance when a module has many __future__
imports.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I guess that makes sense, but if the code requires some explanation, then I'd like to improve the code so future readers won't run into the same difficulties I'm having understanding the code now.
What can be done to make this code more digestible? Can we:
- break up the compound conditional expressions into several
if
statements? - assign intermediate names to some of the subexpressions?
- extract some of the logic to well-named private methods or functions?
- consolidate the logic?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've added has_only_future_imports
to name one aspect of it.
Except for that, it is the same as the previous logic, excepting that
not self.futureAllowed
is now
not isinstance(self.scope, ModuleScope) or not self.scope._futures_allowed
.
I could name that compound conditional...
@property
def futuresAllowed(self):
return not isinstance(self.scope, ModuleScope) or not self.scope._futures_allowed
;-)
56da281
to
4e77475
Compare
self.futuresAllowed = False | ||
|
||
if (isinstance(self.scope, ModuleScope) and | ||
self.scope._futures_allowed and |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
would adding _futures_allowed = False
to the Scope
base class allow the isinstance(self.scope, ModuleScope)
to be dropped from this logic?
bfab363
to
2251d5a
Compare
So, now the logic is wrapped up in the |
@@ -314,6 +326,28 @@ def __init__(self, tree, filename='(none)', builtins=None, | |||
self.popScope() | |||
self.checkDeadScopes() | |||
|
|||
@property | |||
def futuresAllowed(self): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this can be made more efficient, and simpler.
also need to re-add docstrings, and comments.
2251d5a
to
1d9764d
Compare
|
Replaces plain attribute Checker.futuresAllowed with a property that supports __future__ in both module and doctest.
1d9764d
to
9c88c0e
Compare
Deprecate Checker.futuresAllowed as it is now unused,
and is typically unnecessary.