Skip to content

Additional type hints added to stub openpyxl#9487

Closed
ArnabRollin wants to merge 6 commits intopython:mainfrom
ArnabRollin:main
Closed

Additional type hints added to stub openpyxl#9487
ArnabRollin wants to merge 6 commits intopython:mainfrom
ArnabRollin:main

Conversation

@ArnabRollin
Copy link
Copy Markdown
Contributor

Reason for change

The VS Code Python static type-checker does not recognise

Here is an example:

from openpyxl import load_workbook

wb = load_workbook("myxl.xlsx") # Error: Function 'load_workbook' is partially unknown

Mypy doesn't seem to find the types-openpyxl package

(Working-with-Excel) -> pipenv install types-openpyxl
Installing types-openpyxl...
Pipfile.lock (43417e) out of date, updating to (83c742)...
Locking [packages] dependencies...
Locking [dev-packages] dependencies...
Updated Pipfile.lock (c868c9d2bef5b5f5f3c9b17280f5f7a15a9a9522fd50d8cb558d1eb0e083c742)!
Installing dependencies from Pipfile.lock (83c742)...
To activate this project's virtualenv, run pipenv shell.
Alternatively, run a command inside the virtualenv with pipenv run.
(Working-with-Excel) -> mypy app.py
app.py:1: error: Library stubs not installed for "openpyxl"  [import]
app.py:1: note: Hint: "python3 -m pip install types-openpyxl"
app.py:1: note: (or run "mypy --install-types" to install all missing stub packages)
app.py:1: note: See https://mypy.readthedocs.io/en/stable/running_mypy.html#missing-imports

Please point out if I am wrong anywhere.

ArnabRollin and others added 3 commits January 10, 2023 16:56
Updated classes `Workbook`, `Worksheet`, `Cell`. Type checking works in VS Code
@github-actions

This comment has been minimized.

@Avasam
Copy link
Copy Markdown
Collaborator

Avasam commented Jan 10, 2023

(I'm not a typeshed maintainer, but I contribute a lot here)
The VS Code Python static type-checker, named "Pylance", uses https://github.com/microsoft/pyright behind the scene. The reason you get partially unknown errors is because you're using some strict typing settings.

There's a big semantic difference between "Unknown" and "Any". One says to be careful because the type isn't checked. The other says you can truly use anything. Nowadays typeshed uses Any as an escape hatch to say "we can't accurately represent the type under the current type system".
mypy doesn't have an Unknown pseudo-type, it just uses Any.
That's also why, for semantic reasons, in typeshed we prefer to leave parameters and return types untyped. Or otherwise use the alias Incomplete (from _typeshed import Incomplete). See https://github.com/python/typeshed/blob/main/CONTRIBUTING.md#incomplete-stubs


To fix the issue on pyright/pylance. You could:

To resolve the issue with mypy. Since you already installed types-openpyxl, make sure that you are using mypy from your virtual environment. I am not personally familiar with pipenv, but it's possible that mypy on your path isn't pointing to the one in your project's environment. You can try running pipenv install mypy and python3 -m mypy app.py.


pyright comes with a copy of typeshed's third-party stubs. mypy doesn't and you have to install them manually.
pyright also comes with a copy of https://github.com/microsoft/python-type-stubs/

openpyxl is also very special. All those class members using descriptors are actually properties in disguise. With runtime type-checking and type coercion. I'm currently working on merging the stubs from https://github.com/microsoft/python-type-stubs/tree/main/openpyxl

@ArnabRollin
Copy link
Copy Markdown
Contributor Author

The reason you get partially unknown errors is because you're using some strict typing settings.

I usually like to put my code in strict mode. But, when I found the type stubs, I thought they were a bit incomplete... So I updated these.

@AlexWaygood
Copy link
Copy Markdown
Member

But, when I found the type stubs, I thought they were a bit incomplete... So I updated these.

They are indeed :)

And contributions are very welcome! Thank you for the PR!

@github-actions
Copy link
Copy Markdown
Contributor

Diff from mypy_primer, showing the effect of this PR on open source code:

pandas (https://github.com/pandas-dev/pandas)
+ pandas/io/excel/_openpyxl.py:73: error: Argument 1 to "load_workbook" has incompatible type "IO[bytes]"; expected "str"  [arg-type]
+ pandas/io/excel/_openpyxl.py:101: error: Argument 1 to "save" of "Workbook" has incompatible type "IO[bytes]"; expected "str"  [arg-type]
+ pandas/io/excel/_openpyxl.py:554: error: Argument 1 to "load_workbook" has incompatible type "Union[Union[str, PathLike[str]], ReadBuffer[bytes]]"; expected "str"  [arg-type]

@AlexWaygood AlexWaygood self-requested a review January 11, 2023 22:33
Copy link
Copy Markdown
Member

@AlexWaygood AlexWaygood left a comment

Choose a reason for hiding this comment

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

Thanks! There's some good stuff in here, but I'm afraid there are also quite a few errors that we need to sort out. Remarks below:


def get_type(t, value): ...
def get_time_format(t): ...
def get_type(t: Any, value: Any) -> Any: ...
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
def get_type(t: Any, value: Any) -> Any: ...
def get_type(t: type, value: object) -> str | None: ...

def get_type(t, value): ...
def get_time_format(t): ...
def get_type(t: Any, value: Any) -> Any: ...
def get_time_format(t: Any) -> Any: ...
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
def get_time_format(t: Any) -> Any: ...
def get_time_format(t: datetime) -> str: ...

worksheet: Worksheet,
row: int | None = ...,
column: int | None = ...,
value: str | float | int | datetime | None = ...,
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

PEP 484 specifies that functions annotated as accepting float should also accept int, so int is redundant in this union

Suggested change
value: str | float | int | datetime | None = ...,
value: str | float | datetime | None = ...,

def check_error(self, value: Any) -> Any | str: ...
@property
def value(self): ...
def value(self) -> str | float | int | datetime | None: ...
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
def value(self) -> str | float | int | datetime | None: ...
def value(self) -> str | float | datetime | None: ...

def value(self) -> str | float | int | datetime | None: ...
@value.setter
def value(self, value) -> None: ...
def value(self, value: str | float | int | datetime | None) -> None: ...
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
def value(self, value: str | float | int | datetime | None) -> None: ...
def value(self, value: str | float | datetime | None) -> None: ...

def delete_cols(self, idx: int, amount: int = ...) -> None: ...
def move_range(self, cell_range: CellRange, rows: int = ..., cols: int = ..., translate: bool = ...) -> None: ...
@property
def print_title_rows(self) -> Any: ...
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Please leave unannotated if you don't know the type.

def print_title_rows(self, rows: int) -> None: ...
@property
def print_title_cols(self): ...
def print_title_cols(self) -> Any: ...
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Please leave unannotated if you don't know the type.

Comment on lines +132 to +134
def print_titles(self) -> Any: ...
@property
def print_area(self): ...
def print_area(self) -> Any: ...
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Please leave unannotated if you don't know the type.

def print_area(self) -> Any: ...
@print_area.setter
def print_area(self, value) -> None: ...
def print_area(self, value: str) -> None: ...
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

def print_title_cols(self) -> Any: ...
@print_title_cols.setter
def print_title_cols(self, cols) -> None: ...
def print_title_cols(self, cols: int) -> None: ...
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

@mynhardtburger
Copy link
Copy Markdown
Contributor

@ArnabRollin are you still working on this, or can I offer my help to implement the change requests?

@AlexWaygood
Copy link
Copy Markdown
Member

AlexWaygood commented Feb 19, 2023

@mynhardtburger would you like to pick up where @ArnabRollin picked off? You'd be very welcome to! (@ArnabRollin, you'll be listed as a co-author, of course.)

@mynhardtburger
Copy link
Copy Markdown
Contributor

@AlexWaygood I would like to try...
I assume the process would be to fork ArnabRollin's work, work through your suggested changes and then submit a new PR?

This is my first time contributing to a public project.

@AlexWaygood
Copy link
Copy Markdown
Member

@AlexWaygood I would like to try... I assume the process would be to fork ArnabRollin's work, work through your suggested changes and then submit a new PR?

This is my first time contributing to a public project.

Yes, that would be the process! :)

@Avasam
Copy link
Copy Markdown
Collaborator

Avasam commented Feb 19, 2023

@AlexWaygood I would like to try... I assume the process would be to fork ArnabRollin's work, work through your suggested changes and then submit a new PR?

This is my first time contributing to a public project.

If you do, please tag me in the new PR so I can update the description in #9511

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.

4 participants