Skip to content
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

[Feat] Add type hints #251

Open
wants to merge 24 commits into
base: develop
Choose a base branch
from
Open

[Feat] Add type hints #251

wants to merge 24 commits into from

Conversation

fjosw
Copy link
Owner

@fjosw fjosw commented Dec 25, 2024

As a first step towards adding type hints, I added automatic type hints with monkeytype. Hints for the more complicated methods will need additional manual work but for the simpler methods this seems to have worked quite well.

@fjosw fjosw marked this pull request as ready for review January 3, 2025 18:12
@fjosw
Copy link
Owner Author

fjosw commented Jan 3, 2025

Not quite done but already in decent shape for a review @s-kuberski @jkuhl-uni:

  • Most of the changes are just type annotations
  • I changed a few lines to help mypy with understanding the type annotations
  • I fixed a few smaller bugs in which explicit type checks were not consistent or did not cover certain edge cases

For now I ignored the input/* modules and most of correlators.py, the remaining modules only raise a few smaller mypy errors which I wasn't able to fix right away.

@s-kuberski
Copy link
Collaborator

Great, thanks a lot for all of your work! I'll have a look in the upcoming week.

@@ -42,7 +45,7 @@ class Corr:

__slots__ = ["content", "N", "T", "tag", "prange"]

def __init__(self, data_input, padding=[0, 0], prange=None):
def __init__(self, data_input: Any, padding: list[int]=[0, 0], prange: Optional[list[int]]=None):
Copy link
Collaborator

@jkuhl-uni jkuhl-uni Jan 9, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't this be list[Obs]? Alternatively, in the list, there could be something Obs-like, I suppose, such as list[CObs]?

@jkuhl-uni
Copy link
Collaborator

Hey @fjosw,
thanks for all the work, I am looking through it right now, it looks excellent to me. I hope it was okay that I pushed a few small changes. If not, feel free to revert them, as you please :)

"""Calculates the per-timeslice trace of a correlator matrix."""
if self.N == 1:
raise ValueError("Only works for correlator matrices.")
newcontent = []
newcontent: list[Union[None, float]] = []
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Similar to Justus' question above: newcontent should be a list of NoneorObs(orCObs`?), right?

@@ -701,7 +698,7 @@ def second_deriv(self, variant="symmetric"):
else:
raise ValueError("Unknown variant.")

def m_eff(self, variant='log', guess=1.0):
def m_eff(self, variant: str='log', guess: float=1.0) -> "Corr":
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A very naive question and probably that is just something that was automatically done: When shoult it be Optional[str] (as above) and when str (as here)?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think it should be Optional[str] when the value can also be None, I think in this case one really needs to pass a string, right?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Optional[X] is equivalent to X | None (or Union[X, None]).

https://docs.python.org/3/library/typing.html#typing.Optional

@@ -785,7 +782,7 @@ def root_function(x, d):
else:
raise ValueError('Unknown variant.')

def fit(self, function, fitrange=None, silent=False, **kwargs):
def fit(self, function: Callable, fitrange: Optional[Union[str, list[int]]]=None, silent: bool=False, **kwargs) -> Fit_result:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see that fitrange can be a string.

@@ -819,7 +816,7 @@ def fit(self, function, fitrange=None, silent=False, **kwargs):
result = least_squares(xs, ys, function, silent=silent, **kwargs)
return result

def plateau(self, plateau_range=None, method="fit", auto_gamma=False):
def plateau(self, plateau_range: Optional[list[int]]=None, method: str="fit", auto_gamma: bool=False) -> Obs:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

As above, this is an optional str, but I guess this is not really important.

@@ -868,7 +865,7 @@ def set_prange(self, prange):
self.prange = prange
return

def show(self, x_range=None, comp=None, y_range=None, logscale=False, plateau=None, fit_res=None, fit_key=None, ylabel=None, save=None, auto_gamma=False, hide_sigma=None, references=None, title=None):
def show(self, x_range: Optional[list[int]]=None, comp: Optional[Corr]=None, y_range: None=None, logscale: bool=False, plateau: None=None, fit_res: Optional[Fit_result]=None, fit_key: Optional[str]=None, ylabel: None=None, save: None=None, auto_gamma: bool=False, hide_sigma: None=None, references: None=None, title: None=None):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The types of y_range, plateau, ylabel, save, hide_sigma, references, title have not been recognized.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When a type has not been recognised, this indicates that we don't have proper test coverage for this call 😬

@@ -1084,18 +1081,18 @@ def __str__(self):

__array_priority__ = 10000

def __eq__(self, y):
def __eq__(self, y: Union[Corr, Obs, int]) -> ndarray:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess y can be Any?

if isinstance(y, Corr):
comp = np.asarray(y.content, dtype=object)
else:
comp = np.asarray(y)
return np.asarray(self.content, dtype=object) == comp

def __add__(self, y):
def __add__(self, y: Any) -> "Corr":
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In contrast, here, y should be one of the types that don't raise an exception.

@@ -1119,11 +1116,11 @@ def __add__(self, y):
else:
raise TypeError("Corr + wrong type")

def __mul__(self, y):
def __mul__(self, y: Any) -> "Corr":
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

as above

@@ -1190,11 +1187,11 @@ def __rmatmul__(self, y):
else:
return NotImplemented

def __truediv__(self, y):
def __truediv__(self, y: Union[Corr, float, ndarray, int]) -> "Corr":
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Obs and CObs are missing here. BTW: does that just mean that we don't call the function with these types in our tests?

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, that's my understanding. monkeytype derived these type hints from our tests.

newcontent = [None if _check_for_none(self, item) else -1. * item for item in self.content]
return Corr(newcontent, prange=self.prange)

def __sub__(self, y):
def __sub__(self, y: Union[Corr, float, ndarray, int]) -> "Corr":
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

incomplete list for y.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Okay, I stop here for the rest of the file. There are a few more instances below, where we have to adjust `y.

@@ -1356,7 +1353,7 @@ def return_imag(obs_OR_cobs):

return self._apply_func_to_corr(return_imag)

def prune(self, Ntrunc, tproj=3, t0proj=2, basematrix=None):
def prune(self, Ntrunc: int, tproj: int=3, t0proj: int=2, basematrix: None=None) -> "Corr":
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

basematrix should be an Optional[ndarray].

@s-kuberski
Copy link
Collaborator

Thansk again for starting this. I'm still in correlators.py and have discovered quite a few places, where I think that the type hints should be changed, if one wants them to be correct. What's our strategy? Shall I continue to point them out and you change them if you agree? Or shall I directly adapt? I guess it's not breaking anything if some of the hints are incorrect, but if we introduce them in the first place, I think we should make an effort to have them correct and hopefully complete.

@fjosw
Copy link
Owner Author

fjosw commented Jan 10, 2025

Feel free to get started with fixing stuff, you should have permissions to push to the branch, I think I won't have time to work on this in the comming days. My strategy so far was to run mypy to point out typing issues and go from there. As @jkuhl-uni suggested it might be worth defining a Obs-like type which can then be used for the overloaded methods.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants