| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
|
Any chance you can resolve the conflicts? |
Sorry, something went wrong.
| @@ -0,0 +1 @@ | |||
| STRICT_OPTIONAL = False | |||
There was a problem hiding this comment.
I don't think an adjective make a good module name. Can you make it a noun, e.g. 'experiments'?
Sorry, something went wrong.
|
Now allows non-Optional class variables to be initialized to None, though it doesn't check that they are set to something else in init (or a method init calls). |
Sorry, something went wrong.
|
I've figured out one of the mypy-on-mypy errors this introduces, though it's not entirely clear to me what the best solution is. Basically, the problem boils down to mypy inferring member variable types from isinstance calls but not from assignments. To be concrete: This results in an error: from typing import Union
class C:
x = "" # type: Union[int, str]
def f(self) -> None:
self.x = 1
self.x + 1 # E: Unsupported operand types for + ("Union[int, str]" and "int")But this works: def f(x: Union[int, str]) -> None:
x = 1
x + 1And so does this: from typing import Union
class C:
x = "" # type: Union[int, str]
def f(self) -> None:
if isinstance(self.x, int):
self.x + 1This problem was exposed because we have code that looks like this: class C:
x = ""
def f(self) -> None:
if self.x is not None:
return
self.x = "foo"
self.x.capitalize() # E: None has no attribute "capitalize"With this PR, mypy now understands that returning when self.x is not None implies that self.x = None for the rest of the function (because it understands foo is not None as an isinstance-style check). Mypy continues to not understand that self.x = "foo" means x becomes a str. It's not generally sound to assume that instance variables don't change their type within a function after assignment (within the constraints of their declared type), because method calls might mutate them. So it's not obvious what the best fix here is. |
Sorry, something went wrong.
really not convinced that the line length is set right
|
On further investigation, not inferring local types from member assignment appears to not be intended behavior, but rather the result of a bug. Here's what I've traced so far:
|
Sorry, something went wrong.
| return None | ||
|
|
||
| if typ and isinstance(typ.ret_type, UnboundType): | ||
| typ.ret_type.ret_type = True |
There was a problem hiding this comment.
This is confusing; maybe name the new field of UnboundType is_ret_type?
Sorry, something went wrong.
There was a problem hiding this comment.
Yeah, that's much better.
Sorry, something went wrong.
|
I assume we intend to support non-strict optional checking for the foreseeable future; if not then perhaps the following comments aren't relevant. Seeing many occurrences of if experiments.STRICT_OPTIONAL:
return UninhabitedType()
else:
return NoneTyp()made me think about the following reorganization. Instead of NoneTyp and UninhabitedType, have BottomType and NoneTyp, where
Ideally the only difference between the two modes would be whether None is given type NoneTyp or type BottomType. This way we'll have a single type hierarchy to think about, and my hope is to remove the dependencies of modules join, meet, solve, and subtypes on the setting of strict optional checking. I haven't yet read the whole diff carefully, so I'm not sure how this will work out in practice. |
Sorry, something went wrong.
|
Something we might want to think about before too long is whether it makes sense to check some parts of a program (e.g., some of its dependencies) with strict optional checking and others without, and how we might plan to support that. |
Sorry, something went wrong.
|
Honestly I would be happier if this was for a brief period only. But not sure how realistic that I. |
Sorry, something went wrong.
|
I agree with Guido: I think it would be best to target having this only for a brief period. @rwbarton: I've thought about your suggestion, and I think it can work, but I actually find the current way of thinking about things to be easier. Making sure BottomType behaves appropriately in both interpretations could get a bit complicated, I think, especially as relates to random weird edge cases in the type system (where things don't completely behave as expected). |
Sorry, something went wrong.
|
I've updated the new isinstance checking to only be active with --strict-optional enabled so this PR doesn't have to block on #1568, which is a bit complicated. |
Sorry, something went wrong.
| if not x: | ||
| g(x) # E: Argument 1 to "g" has incompatible type "Optional[int]"; expected None | ||
| else: | ||
| f(x) |
There was a problem hiding this comment.
Reveal the type of x here to make sure it's not Any or similar.
Sorry, something went wrong.
|
It would be nice to have test cases for various things that have been fixed. |
Sorry, something went wrong.
| def assign_type(self, expr: Node, | ||
| type: Type, | ||
| declared_type: Type, | ||
| restrict_any: bool = False) -> None: |
There was a problem hiding this comment.
Add docstring (at least explain declared_type and what it's used for).
Sorry, something went wrong.
|
Docstring of NoneTyp doesn't seem to be up to date: """The type of 'None'.
This is only used internally during type inference. Programs
cannot declare a variable of this type, and the type checker
refuses to infer this type for a variable. However, subexpressions
often have this type. Note that this is not used as the result
type when calling a function with a void type, even though
semantically such a function returns a None value; the void type
is used instead so that we can report an error if the caller tries
to do anything with the return value.
"""
|
Sorry, something went wrong.
|
I'd like a more detailed docstring for UninhabitedType. For example, it could answer questions like these:
Also, add unit tests to mypy/test/testtypes.py that test various type operations with UninhabitedType. |
Sorry, something went wrong.
|
One additional thing to test: see if overloaded functions work with None. If this doesn't work, it's okay to create a separate issue for it. |
Sorry, something went wrong.
| class A: | ||
| def f(self) -> None: | ||
| self.x = None # E: Need type annotation for variable | ||
| self.x = None |
There was a problem hiding this comment.
This should still produce Need type annotation for variable. Reasoning:
Sorry, something went wrong.
There was a problem hiding this comment.
Also, it would be useful to have a similar test case with strict optional enabled.
Sorry, something went wrong.
There was a problem hiding this comment.
The Need type annotation for variable error didn't go away, it just needed to be moved to the [out] section. The only thing that changed is that we can now infer that x is int for the body of g(), so there's an additional int not callable error.
Sorry, something went wrong.
There was a problem hiding this comment.
With strict optional checking enabled, None will be inferred as a type if no other information is available other than the None assignment (and this is already tested).
Sorry, something went wrong.
|
This is good to rebase and land after resolving the 'Need type annotation for variable' issue. |
Sorry, something went wrong.
First pass strict Optional checking. Adds the experimental `--strict-optional` flag. Fixes #1450. With --strict-optional: - "None" in type annotations refers to NoneTyp, except as a return value, where it still refers to Void - None and List[None] will now be inferred as a type if no other information is available. - Class variables may be initialized to None without having an Optional type. Mypy does not currently check that they're assigned to in __init__ or elsewhere before use. See #1450 for more details. This also fixes the bug where mypy didn't understand that x.y = "foo" implied that x.y would be a str for the remaineder of that block. This isn't entirely sound, but is in line with the way we do isinstance checks.
| Back | FazBrowse Home | New Git URL |
Implementation of #1450. Largely complete -- just missing the ability for non-Optional class vars to be initialized to None.
Just ignore all the crazy commits -- they're still useful history for me, so I'd prefer not to squash yet.