-
Notifications
You must be signed in to change notification settings - Fork 17
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
refactor(chore) Add mypy static type checking #321
base: main
Are you sure you want to change the base?
Conversation
Signed-off-by: siyaka promise <siyakapromise@gmail.com>
Signed-off-by: siyaka promise <siyakapromise@gmail.com>
Hi @b4handjr , the CI type checking is failing because it seems not to be installing mypy. I added mypy to the group dev dependencies, is this appropriate? |
Signed-off-by: siyaka promise <siyakapromise@gmail.com>
Signed-off-by: siyaka promise <siyakapromise@gmail.com>
Makefile
Outdated
typecheck: | ||
poetry run mypy . |
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.
Looks like you forgot to add install_dependencies
here. Look at the lines above for a clue.
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, it now installs mypy by adding install_dependencies
, I was missing that.
Signed-off-by: siyaka promise <siyakapromise@gmail.com>
Signed-off-by: siyaka promise <siyakapromise@gmail.com>
Signed-off-by: siyaka promise <siyakapromise@gmail.com>
Signed-off-by: siyaka promise <siyakapromise@gmail.com>
Signed-off-by: siyaka promise <siyakapromise@gmail.com>
Signed-off-by: siyaka promise <siyakapromise@gmail.com>
Hi @b4handjr, please can you look into my pr to recommend necessary changes |
Looking at the build on github actions, it seems there is a failing type check in the |
Signed-off-by: siyaka promise <siyakapromise@gmail.com>
I have set mypy to ignore the doc folder, and also cleared the webserver error by removing the deprecated import. |
Great thanks, I will take a look |
Hello @cashall-0, please pull main and update accordingly, I merge the fix for the failing tests. Thanks! |
Alright, I will get to it. |
…ature/add-static-typing
Signed-off-by: siyaka promise <siyakapromise@gmail.com>
I have pulled and updated accordingly, do let me know whatever needs to change as you review. |
Thanks! I'll give it a look today |
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 working on this. I left some comments, I also noticed some of your new files don't have new lines at the end of them.
if TYPE_CHECKING: | ||
from foxpuppet.windows import BrowserWindow # Import for static typing |
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.
What's this for?
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 keeps showing errors of cyclic dependency, like two classes importing from each other. so I imported for just the typechecking.
The current import is done inside the method.
example:
ImportError: cannot import name 'BrowserWindow' from partially initialized module 'foxpuppet.windows' (most likely due to a circular import) (/root/outreachy/moxilla/FoxPuppet/foxpuppet/windows/init.py)
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 did some research on this and this pattern seems to be the accepted way
foxpuppet/windows/base.py
Outdated
@@ -26,7 +29,7 @@ def __init__(self, selenium, handle): | |||
self.wait = WebDriverWait(self.selenium, timeout=10) |
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.
Should this have a type?
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 can,I assigned type WebDriverWait
@@ -21,7 +22,7 @@ class AddOnInstallConfirmation(BaseNotification): | |||
"""Add-on install confirmation notification.""" | |||
|
|||
@property | |||
def addon_name(self): | |||
def addon_name(self) -> str | Any: |
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 is this also Any
?
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.
done, changed to str
foxpuppet/windows/manager.py
Outdated
from foxpuppet.windows import BrowserWindow | ||
|
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.
Could this import not go at the top?
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.
yes, Done
tests/conftest.py
Outdated
"""First Firefox browser window opened.""" | ||
return foxpuppet.browser | ||
|
||
|
||
@pytest.fixture | ||
def firefox_options(firefox_options): | ||
def firefox_options(firefox_options: Any) -> Any: |
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 use this, from selenium.webdriver.firefox.options import Options as FirefoxOptions
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.
Done, but I had problems with these after using the type FireFoxOptions
if os.getenv("MOZREGRESSION_BINARY"): firefox_options.binary = os.getenv("MOZREGRESSION_BINARY") # type: ignore firefox_options.log.level = "trace" # type: ignore
that's why I used type ignore. Can you please look at this. In line 54 - 56.
tests/test_notifications.py
Outdated
def test_open_close_notification( | ||
browser: BrowserWindow, blocked_notification: AddOnInstallBlocked | ||
) -> BaseNotification: |
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.
This is a test, it shouldn't return anything
@@ -32,7 +33,7 @@ def __init__(self, host="", port=8000): | |||
self.thread = threading.Thread(target=self.server.serve_forever, daemon=True) | |||
|
|||
@property | |||
def host(self): | |||
def host(self) -> Any | str: |
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.
Is this really Any or string? Why not just a string or int?
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 has the types "str | bytes | bytearray" leaving it at "str" or "str | int" would show an error. But on tracing the variables and method calls it shows type assigned as "_RetAddress" which was used as a type alias for "Any".
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.
Ah okay gotcha, thanks for that explanation.
Signed-off-by: siyaka promise <siyakapromise@gmail.com>
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.
Left some of my thoughts. There are also some files that are missing a new line
tests/conftest.py
Outdated
if os.getenv("MOZREGRESSION_BINARY"): | ||
firefox_options.binary = os.getenv("MOZREGRESSION_BINARY") | ||
firefox_options.log.level = "trace" | ||
firefox_options.binary = os.getenv("MOZREGRESSION_BINARY") # 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.
Lets get rid of this if
statement, we shouldn't need this MOZREGRESSION_BINARY
stuff
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.
Okay, I have removed the check and binary assignment.
firefox_options.binary = os.getenv("MOZREGRESSION_BINARY") | ||
firefox_options.log.level = "trace" | ||
firefox_options.binary = os.getenv("MOZREGRESSION_BINARY") # type: ignore | ||
firefox_options.log.level = "trace" # 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.
Could this be str
?
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.
log.level
is actually of the type str, but the default value assigned to the log.level
in options.py
is None, so this somehow is interfering with type check making it ask for a type None
.
tests/test_notifications.py
Outdated
def blocked_notification( | ||
addon: Any, browser: BrowserWindow, webserver: WebServer, selenium: WebDriver | ||
) -> BaseNotification: |
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.
addon
should have a type.
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.
done, unbundled the class AddOn
which was previously encapsulated in a method.
tests/test_notifications.py
Outdated
def test_confirm_addon_install( | ||
addon, browser: BrowserWindow, confirmation_notification: AddOnInstallConfirmation | ||
) -> None: |
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.
addon
should have a type.
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.
done
tests/test_notifications.py
Outdated
def test_addon_install_complete( | ||
addon, browser: BrowserWindow, complete_notification: AddOnInstallComplete | ||
) -> None: |
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.
addon
should have a type.
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.
done
I might be confused about what new files you are referring to, can you specify one of such? |
Signed-off-by: siyaka promise <siyakapromise@gmail.com>
@b4handjr I'm trying to update my branch and push but there is a conflict in poetry.lock, the hash generated is different due to changes in |
You could delete this file and merge in the new Let me know when you get that resolved and I'll take another look. It's looking good so far, thank you! |
Signed-off-by: siyaka promise <siyakapromise@gmail.com>
While trying to resolve this, I noticed poetry version changed from |
…ature/add-static-typing
Signed-off-by: siyaka promise <siyakapromise@gmail.com>
@b4handjr , I have tried to resolve the conflict and fixed from your recommendations. you can now take another look , thanks. |
Thanks, I'll take a look but I think this is looking good |
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 few spots in the main modules that are missing types. I don't think we need to be too picky in the tests. Thanks for the work so far, this is just about done!
@@ -31,15 +37,15 @@ def create(window, root): | |||
:py:class:`BaseNotification`: Firefox notification. | |||
|
|||
""" | |||
notifications = {} | |||
notifications: dict[Any, Any] = {} | |||
_id = root.get_property("id") |
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.
This could use a type
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.
Done, it is of the type str
but mypy wouldn't let my assign only that so I used suggested str | bool | WebElement | dict
Signed-off-by: siyaka promise <siyakapromise@gmail.com>
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.
Just a few final comments. I'd like to not use Any
as much as possible, but there will be some places where we need it.
"""Retrieve the notification description.""" | ||
if self.window.firefox_version >= 67: | ||
return self.root.find_element(By.CLASS_NAME, "popup-notification-description") | ||
return self.root.find_anonymous_element_by_attribute( | ||
"class", "popup-notification-description" | ||
) | ||
|
||
def find_close_button(self): | ||
def find_close_button(self) -> WebElement | Any: |
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 seems this could just a return a WebElement
no?
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.
Yes , but for this method and four others written this way
def find_close_button(self) -> WebElement | Any: """Retrieve the close button.""" if self.window.firefox_version >= 67: return self.root.find_element(By.CLASS_NAME, "popup-notification-closebutton") return self.root.find_anonymous_element_by_attribute("anonid", "closebutton")
for the second return statement here, I can't seem to find the implementation of the method find_anonymous_element_by_attribute()
hence, the any for whatever the outcome.
Signed-off-by: siyaka promise <siyakapromise@gmail.com>
Because
This commit
Fixes #314