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

NAS-129505 / 24.10 / Beginning of type hinting for middleware #13864

Merged
merged 10 commits into from
Jun 12, 2024

Conversation

creatorcary
Copy link
Contributor

Type hinting is an internal documentation technique that does not affect the execution of code during runtime; type hinting is not actually enforced by the interpreter. Instead, it integrates with most IDE's and linters to make the development process cleaner by improving readability and allowing certain bugs to be noticed earlier.

@bugclerk bugclerk changed the title Beginning of type hinting for middleware NAS-129505 / 24.10 / Beginning of type hinting for middleware Jun 10, 2024
@bugclerk
Copy link
Contributor

@creatorcary creatorcary requested a review from a team June 10, 2024 19:27
from middlewared.schema import Any, clean_and_validate_arg, ValidationErrors
from middlewared.settings import conf


class Events:
def __init__(self, role_manager):
_events: dict[str, dict[str]]
Copy link
Member

Choose a reason for hiding this comment

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

Why move them to class variables ? We can specify their types in __init__ as well ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

You're right, fixed it

self.role_manager = role_manager
self._events = {}
self.__events_private = set()

def register(self, name, description, private, returns, no_auth_required, no_authz_required, roles):
def register(self, name:str, description:str, private:bool, returns:bool, no_auth_required, no_authz_required, roles:Iterable[str]):
Copy link
Member

@sonicaj sonicaj Jun 10, 2024

Choose a reason for hiding this comment

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

There should be a space after : i.e name: str (in PEP this is E231)

@@ -28,7 +33,7 @@ def register(self, name, description, private, returns, no_auth_required, no_aut
if private:
self.__events_private.add(name)

def get_event(self, name):
def get_event(self, name:str) -> dict[str]:
Copy link
Member

Choose a reason for hiding this comment

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

This can be null as well so we should mark it as such i.e typing.Optional

Copy link
Member

Choose a reason for hiding this comment

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

Also this would be typing.Dict[key_type, value_type]

Copy link
Member

Choose a reason for hiding this comment

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

Please see different modules in middleware which already have type hinting in place, that would be a nice point to see how we have been doing it so far

@@ -80,7 +80,7 @@ def to_json_schema(self, parent=None):
"""
raise NotImplementedError("Attribute must implement to_json_schema method")

def _to_json_schema_common(self, parent):
def _to_json_schema_common(self, parent) -> dict[str]:
Copy link
Member

Choose a reason for hiding this comment

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

typing.Dict[type_here, value_type_here]

Copy link
Contributor Author

Choose a reason for hiding this comment

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

As of Python 3.9 you can use just regular list and dict, but I'll change to the preferred List and Dict

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'm also learning the repo as I go, so Dict[str] evaluates to Dict[str, Any] since I wasn't sure

@@ -12,7 +12,7 @@ class Attribute:

def __init__(
self, name='', title=None, description=None, required=False, null=False, empty=True, private=False,
validators=None, register=False, hidden=False, editable=True, example=None, **kwargs
validators:list[function]=None, register=False, hidden=False, editable=True, example=None, **kwargs
Copy link
Member

Choose a reason for hiding this comment

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

This would be syntax error i.e function is not defined anywhere so this won't execute and the correct way to do this is with typing.List

Copy link
Member

Choose a reason for hiding this comment

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

Also i think we should have a base validators class with __call__ method raising NotImplementedError and all validators inheriting from it and we can specify that validator here like this
typing.List[Validator]

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I wasn't able to find a general Validator type, so I left it as List[Callable] for now.

Copy link
Member

Choose a reason for hiding this comment

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

Okay so to clarify, can we add something like this

class ValidatorBase:
    def __call__(self, *args, **kwargs):
        raise NotImplementedError()


class Exact(ValidatorBase):
    def __init__(self, value):
        self.value = value

    def __call__(self, value):
        if value != self.value:
            raise ValueError(f"Should be {self.value!r}")

We can import the validator base here and then use it in type hints

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for the thorough review, I've updated the PR with everything you pointed out.

@creatorcary creatorcary requested a review from sonicaj June 11, 2024 16:28
@creatorcary creatorcary removed the WIP label Jun 11, 2024
@aiden3c aiden3c self-requested a review June 12, 2024 13:01
Copy link
Contributor

Choose a reason for hiding this comment

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

One of your merges into this branch looks like it's overwriting/undoing this commit. Doing a merge doesn't seem to fix it because (I think) you explicitly reverted the changes in your commit history on this branch.

Copy link
Contributor

Choose a reason for hiding this comment

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

You'll want to run git revert 31c6c2994cd343638a45a020f1da8b6c75a785c1 to revert the revert

@creatorcary creatorcary merged commit 5556b0f into master Jun 12, 2024
3 checks passed
@bugclerk
Copy link
Contributor

This PR has been merged and conversations have been locked.
If you would like to discuss more about this issue please use our forums or raise a Jira ticket.

@truenas truenas locked as resolved and limited conversation to collaborators Jun 12, 2024

def register(self, name, description, private, returns, no_auth_required, no_authz_required, roles):
def register(self, name: str, description: str, private: bool, returns, no_auth_required, no_authz_required, roles: typing.Iterable[str]):
Copy link
Member

Choose a reason for hiding this comment

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

no_auth_required/no_authz_required have been left out ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

If I came across anything that I wasn't sure about, I just skipped it. Figured it's better to be less specific than wrong, plus I plan on spending the whole week working on type hints/docstrings so I can go back to it.

@@ -1645,7 +1648,7 @@ def event_register(self, name, description, *, private=False, returns=None, no_a
roles = roles or []
self.events.register(name, description, private, returns, no_auth_required, no_authz_required, roles)

def send_event(self, name, event_type, **kwargs):
def send_event(self, name, event_type: str, **kwargs):
Copy link
Member

Choose a reason for hiding this comment

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

Missing type hint for name ?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I'll get back to this one

@@ -1,8 +1,10 @@
import copy
import json
import textwrap
import typing
Copy link
Member

Choose a reason for hiding this comment

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

To be sure, we are not right now completing type hints here and would be done at a later stage ?

@sonicaj
Copy link
Member

sonicaj commented Jun 12, 2024

Oops, got a bit late :D

@creatorcary creatorcary deleted the NAS-129505 branch June 12, 2024 13:14
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants