I just encountered code like this:
if hasattr(x, 'initialize'):
x.initialize()
The type of x was an ABC but it doesn't include initialize. This can be easily worked around by rewriting it like this:
if hasattr(x, 'initialize'):
cast(Any, x).initialize()
However, mypy could do better, plausibly. For example:
x has a union type, infer only union item types with attribute initialize after the hasattr check. So if type of x is Union[str, X] and X has initialize, infer type of x to be X in the if body.hasattr check (a little like Optional[...] requiring something like an is not None check). Not sure what the syntax for this would be like. It would be nice to support these in ABCs as well.I ran into an instance of this in the Zulip test runner code, where we add an attribute to a test method via a decorator to track the fact it's expected to be slow, and then check it like this:
def enforce_timely_test_completion(test_method, test_name, delay):
# type: (Callable[[], None], str, float) -> None
if hasattr(test_method, 'expected_run_time'):
# Allow for tests to run 50% slower than normal due
# to random variations.
max_delay = 1.5 * test_method.expected_run_time
See https://github.com/zulip/zulip/blob/master/zerver/lib/test_runner.py for the full code.
In general i despise hasattr() checks; it's usually much better to add a
class level initialization to None and check for that.
On Saturday, June 4, 2016, Tim Abbott [email protected] wrote:
I ran into an instance of this in the Zulip test runner code, where we add
an attribute to a test method via a decorator to track the fact it's
expected to be slow, and then check it like this:def enforce_timely_test_completion(test_method, test_name, delay):
# type: (Callable[[], None], str, float) -> None
if hasattr(test_method, 'expected_run_time'):
# Allow for tests to run 50% slower than normal due
# to random variations.
max_delay = 1.5 * test_method.expected_run_timeSee https://github.com/zulip/zulip/blob/master/zerver/lib/test_runner.py
for the full code.—
You are receiving this because you are subscribed to this thread.
Reply to this email directly, view it on GitHub
https://github.com/python/mypy/issues/1424#issuecomment-223795217, or mute
the thread
https://github.com/notifications/unsubscribe/ACwrMgWW3Km5ug5KWyGjSWJtCKdAsPpuks5qIme4gaJpZM4INlY-
.
--Guido (mobile)
Just to be clear, these are attributes monkey-added to a function, not a class, so I don't think that solution would work here.
So there's no place to declare their type anyway. Maybe using three-arg getattr is better?
If x has a union type, infer only union item types with attribute initialize after the hasattr check. So if type of x is Union[str, X] and X has initialize, infer type of x to be X in the if body.
Likely has false positives if one of the elements on the union is an ABC
I was wondering whether the protocol/structural subtyping work could be used on a hasattr call to in some way to coerce the type into one that accepts the attribute within that scope?
This also affects using features from future python versions (I'm aware that we can do sys.version_info compare but IMHO this case shouldn't give errors)
import ast
if hasattr(ast, "unparse"):
print(ast.unparse)
else:
print("no")
Most helpful comment
I was wondering whether the protocol/structural subtyping work could be used on a
hasattrcall to in some way to coerce the type into one that accepts the attribute within that scope?