Improved Features types#725
Conversation
jduerholt
left a comment
There was a problem hiding this comment.
This looks super interesting, I personally like the overload approach, despite not really understanding it :D
@bertiqwerty what do you think? I personally think that implementing the overload would be quite nice.
| ) | ||
| return clean_exp | ||
|
|
||
| @overload |
There was a problem hiding this comment.
Why do we need the overload two times?
There was a problem hiding this comment.
Unfortunately that's part of the specification for overloads (link). I think the idea is that the actual implementation of get should have no type hints, and all of the type hints go into the different overloads.
So the second overload is a fallback if the argument types don't match the first overload, which will happen if excludes is provided, since you can't express Intersection[GetIncludesT, ~GetExcludesT] in Python's type system (yet) so we need the fallback to be more generic!
|
If you wanted to take this even further, you can also use class Feature: ...
class ContinuousFeature(Feature): ...
class CategoricalFeature(Feature): ...
features: dict[str, Feature]
def get_continuous_key() -> Annotated[str, ContinuousFeature]:
# Dummy function to get a key with the correct annotation.
# One could imagine Inputs.get_keys(includes=ContinuousInput) returns a similarly
# annotated feature key
return "x1"
@overload
def get_feature_from_key(key: Annotated[str, ContinuousFeature]) -> ContinuousFeature:
...
@overload
def get_feature_from_key(key: Annotated[str, CategoricalFeature]) -> CategoricalFeature:
...
def get_feature_from_key(key: str) -> Feature:
return features[key]
key = get_continuous_key()
feature = get_feature_from_key(key)
reveal_type(feature) # ContinuousFeatureWhat do you think? It may be a bit overkill, but really we should only need to touch the |
|
I like it and you are right we are only using it for the feature containers. It will definitely add some boiler plate code but will make typehints etc. much nicer. @bertiqwerty: should Toby go ahead with this? What do you think? |
|
I was digging into this a bit further, and it turns out the Edit: The latest commit shows an example how custom key types might work. Unfortunately, I don't think it's really viable. If you're curious why I say that, it's because in my opinion two key things are missing from the Python Type system:
My suggestion going forward would be to write code like |
|
Hi @TobyBoyne, thanks for your efforts here, until this is ready, I will pin the ty version. To see if we get degradation with respect to this one. |
|
| """ | ||
| constraints = [] | ||
| inputs = domain.inputs.get([ContinuousInput, DiscreteInput]) | ||
| for c in domain.constraints.get(constraint): |
There was a problem hiding this comment.
ty infers the type of c to be Constraint, whereas pyright correctly infers it to by LinearEqualityConstraint | LinearInequalityConstraint. Adding an extra .get([LinearEqualityConstraint, LinearInequalityConstraint]) here reveals @Todo with ty.
Motivation
With the latest release of
ty==0.0.17, the type checker now errors for attribute access on union where some elements lack the attribute (as mentioned in #719). This means that code like below now raises a typing error:A short term fix is just to pin the version of ty. This PR presents two different possible approaches to fix this in the long term. Let me know what you think of them @jduerholt, and whether you would like me to go ahead with either/both of them :)
1. TypeGuards
We can use TypeGuard to filter down the possible features contained within a
Featuresobject. For example, this PR currently includes one for checking if all the features in anInputsobject are continuous. This can be used as below (also see the change inbenchmarks.pyfor an example).2. Overloads
We can also overload the
getmethod onFeaturesto specify the types of features in the containers. This approach should be a bit more seamless, since you won't need to call an extra function compared to approach (1). I've currently added an example implementing this forOutputs, with an example innaming_conventions.py.Have you read the Contributing Guidelines on pull requests?
Yes
Have you updated
CHANGELOG.md?Not yet
Test Plan
The number ty errors that are currently raised on the CI should go down.