-
Notifications
You must be signed in to change notification settings - Fork 558
TYP: Enhance static typing #3771
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
base: main
Are you sure you want to change the base?
Conversation
jsiirola
left a comment
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.
Thanks for this! I have several questions (see below). Some are less about this PR specifically, and more about generally how we want to better support typing in Pyomo. It would be useful to talk through that sometime in a Tuesday Dev Call.
| if typing.TYPE_CHECKING: | ||
| from typing import overload as overload | ||
| else: |
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.
Why do you need to disable the overload?
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.
A function calling typing.overload is not recognized as an overload by the type checker.
We need to replace it with typing.overload during type checking.
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.
OK. I went digging and it turns out that logic is primarily needed by a dependent project -- but only for Python versions through 3.10. I think we should actually document that better in the code, with something like:
if sys.version_info[:2] <= (3, 10) and not TYPE_CHECKING:
def overload(func: typing.Callable):
"""Wrap typing.overload that remembers the overloaded signatures
This provides a custom implementation of typing.overload that
remembers the overloaded signatures so that they are available for
runtime inspection (backporting `get_overloads` from Python 3.11+).
"""
_overloads.setdefault(_get_fullqual_name(func), []).append(func)
return typing.overload(func)
def get_overloads_for(func: typing.Callable):
return _overloads.get(_get_fullqual_name(func), [])
else:
from typing import overload, get_overloads as get_overloads_forThere 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.
typing_extensions might be helpful.
|
|
||
|
|
||
| # NOTE: Python 3.11+ use `typing.Self` | ||
| ModelT = TypeVar("ModelT", bound="Model") |
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.
We haven't really settled on a typing convention for naming types, but appending T seems confusing.
- would standardizing on appendint
Typebe more clear? - should local TypeVar objects be private by default (i.e.,
_modelTypehere)?
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.
_ModelType is better.
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.
Agreed.
| for ver, cls in versions.items(): | ||
| if cls._cls is _environ.SolverFactory._cls: | ||
| solver_factory._active_version = ver | ||
| solver_factory._active_version = ver # type: ignore |
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.
We shouldn't need these annotations, should we?
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.
Because we cannot add attributes to FunctionType.
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.
You can't annotate the function, plus the attribute whereit is first instantiated at the bottom of the file?
def solver_factory(version: int | None = None) -> int:
# ...
solver_factory._active_version: int = solver_factory()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.
It is allowed at runtime, but it's a type violation.
ref: microsoft/pyright#8838
| @overload | ||
| def __call__(self, _name: None = None, **kwds) -> "SolverFactoryClass": ... | ||
| @overload | ||
| def __call__(self, _name, **kwds) -> "OptSolver": ... |
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 Python documentation indicates that you should only use the Ellipsis notation in .pyi files. I think this should be:
| @overload | |
| def __call__(self, _name: None = None, **kwds) -> "SolverFactoryClass": ... | |
| @overload | |
| def __call__(self, _name, **kwds) -> "OptSolver": ... | |
| @overload | |
| def __call__(self, _name: None = None, **kwds) -> "SolverFactoryClass": | |
| pass | |
| @overload | |
| def __call__(self, _name, **kwds) -> "OptSolver": | |
| 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.
I do not believe the documentation states that you must not use Ellipsis in .py files.
In fact, it is uncommon to see pass used as the body for an @overloaded function.
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #3771 +/- ##
==========================================
+ Coverage 89.23% 89.39% +0.16%
==========================================
Files 907 907
Lines 104807 104815 +8
==========================================
+ Hits 93522 93697 +175
+ Misses 11285 11118 -167
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
Fixes # NA
Summary/Motivation:
To provide better autocompletion in vscode+pylance.
Complex dynamic patterns (e.g.
Var()can returnScalarVarorIndexedVar) are out of scope as they are difficult to model accurately.Changes proposed in this PR:
typing.overloadduringTYPE_CHECKINGModel.__new__should return an instance of the subclassSolverFactorys andpyomo.future.solver_factory@overloads, overlapped overloads never be usedLegal Acknowledgement
By contributing to this software project, I have read the contribution guide and agree to the following terms and conditions for my contribution: