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

DEV: Unify mypy options and warn redundant workarounds #2223

Merged
merged 4 commits into from
Oct 7, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 5 additions & 13 deletions .github/workflows/github-ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,14 +22,12 @@ jobs:
runs-on: ubuntu-20.04
strategy:
matrix:
python-version: ["3.7", "3.8", "3.9", "3.10", "3.11", "3.12"]
python-version: ["3.8", "3.9", "3.10", "3.11", "3.12"]
use-crypto-lib: ["cryptography"]
include:
- python-version: "3.6"
use-crypto-lib: "pycryptodome"
- python-version: "3.9"
use-crypto-lib: "pycryptodome"
- python-version: "3.10"
- python-version: "3.7"
use-crypto-lib: ""
steps:
- name: Update APT packages
Expand Down Expand Up @@ -94,9 +92,6 @@ jobs:
- name: Test with pytest
run: |
python -m coverage run --parallel-mode -m pytest tests -vv
- name: Test with mypy
run : |
mypy pypdf --show-error-codes --disallow-untyped-defs --disallow-incomplete-defs
- name: Upload coverage data
uses: actions/upload-artifact@v3
with:
Expand All @@ -112,12 +107,6 @@ jobs:
uses: actions/checkout@v4
with:
submodules: 'recursive'
- name: Cache Downloaded Files
id: cache-downloaded-files
uses: actions/cache@v3
with:
path: '**/tests/pdf_cache/*'
key: cache-downloaded-files
- name: Setup Python 3.11
uses: actions/setup-python@v4
with:
Expand All @@ -137,6 +126,9 @@ jobs:
run: |
echo `ruff --version`
ruff .
- name: Test with mypy
run : |
mypy pypdf
Comment on lines +129 to +131
Copy link
Member

Choose a reason for hiding this comment

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

Does mypy behave the same for different Python versions?

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 simply checked py3.8 ~ py3.11, they behaved the same. but for py3.6, probably it's not.

Copy link
Member

Choose a reason for hiding this comment

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

Lets see if I get good answers for this via https://stackoverflow.com/q/77219925/562769

I suspect that it should give the same results, so I tend to agree with this change.


package:
name: Build & verify package
Expand Down
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -48,5 +48,5 @@ repos:
rev: 'v1.5.1'
hooks:
- id: mypy
additional_dependencies: [types-Pillow==10.0.0.2]
files: ^pypdf/.*
args: [--ignore-missing-imports]
10 changes: 5 additions & 5 deletions pypdf/_crypt_providers/_cryptography.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,11 +27,11 @@

import secrets

from cryptography import __version__ # type: ignore[import]
from cryptography.hazmat.primitives import padding # type: ignore[import]
from cryptography.hazmat.primitives.ciphers.algorithms import AES, ARC4 # type: ignore[import]
from cryptography.hazmat.primitives.ciphers.base import Cipher # type: ignore[import]
from cryptography.hazmat.primitives.ciphers.modes import CBC, ECB # type: ignore[import]
from cryptography import __version__
from cryptography.hazmat.primitives import padding
from cryptography.hazmat.primitives.ciphers.algorithms import AES, ARC4
from cryptography.hazmat.primitives.ciphers.base import Cipher
from cryptography.hazmat.primitives.ciphers.modes import CBC, ECB

from pypdf._crypt_providers._base import CryptBase

Expand Down
4 changes: 2 additions & 2 deletions pypdf/_crypt_providers/_fallback.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
crypt_provider = ("local_crypt_fallback", "0.0.0")


class CryptRC4(CryptBase): # type: ignore
class CryptRC4(CryptBase):
def __init__(self, key: bytes) -> None:
self.s = bytearray(range(256))
j = 0
Expand All @@ -58,7 +58,7 @@ def decrypt(self, data: bytes) -> bytes:
return self.encrypt(data)


class CryptAES(CryptBase): # type: ignore
class CryptAES(CryptBase):
def __init__(self, key: bytes) -> None:
pass

Expand Down
6 changes: 3 additions & 3 deletions pypdf/_crypt_providers/_pycryptodome.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@

import secrets

from Crypto import __version__ # type: ignore[import]
from Crypto.Cipher import AES, ARC4 # type: ignore[import]
from Crypto.Util.Padding import pad # type: ignore[import]
from Crypto import __version__
from Crypto.Cipher import AES, ARC4
from Crypto.Util.Padding import pad

from pypdf._crypt_providers._base import CryptBase

Expand Down
2 changes: 1 addition & 1 deletion pypdf/_encryption.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ def encrypt_object(self, obj: PdfObject) -> PdfObject:
obj2[key] = self.encrypt_object(value)
obj = obj2
elif isinstance(obj, ArrayObject):
obj = ArrayObject(self.encrypt_object(x) for x in obj) # type: ignore
obj = ArrayObject(self.encrypt_object(x) for x in obj)
return obj

def decrypt_object(self, obj: PdfObject) -> PdfObject:
Expand Down
2 changes: 1 addition & 1 deletion pypdf/_merger.py
Original file line number Diff line number Diff line change
Expand Up @@ -642,7 +642,7 @@ def find_outline_item(
if isinstance(oi_enum, list):
# oi_enum is still an inner node
# (OutlineType, if recursive types were supported by mypy)
res = self.find_outline_item(outline_item, oi_enum) # type: ignore
res = self.find_outline_item(outline_item, oi_enum)
if res: # deprecated
return [i] + res
elif (
Expand Down
5 changes: 2 additions & 3 deletions pypdf/_page.py
Original file line number Diff line number Diff line change
Expand Up @@ -1080,8 +1080,7 @@ def _merge_page(
if PG.ANNOTS in page:
annots = page[PG.ANNOTS]
if isinstance(annots, ArrayObject):
for ref in annots:
new_annots.append(ref)
new_annots.extend(annots)

for res in (
RES.EXT_G_STATE,
Expand Down Expand Up @@ -2323,7 +2322,7 @@ def _get_fonts(self) -> Tuple[Set[str], Set[str]]:
assert isinstance(obj, DictionaryObject)
fonts: Set[str] = set()
embedded: Set[str] = set()
fonts, embedded = _get_fonts_walk(cast(DictionaryObject, obj), fonts, embedded)
fonts, embedded = _get_fonts_walk(obj, fonts, embedded)
unembedded = fonts - embedded
return embedded, unembedded

Expand Down
4 changes: 2 additions & 2 deletions pypdf/_protocols.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@

try:
# Python 3.8+: https://peps.python.org/pep-0586
from typing import Protocol # type: ignore[attr-defined]
from typing import Protocol
except ImportError:
from typing_extensions import Protocol # type: ignore[assignment,misc]
from typing_extensions import Protocol # type: ignore[assignment]

from ._utils import StrByteType, StreamType

Expand Down
14 changes: 7 additions & 7 deletions pypdf/_reader.py
Original file line number Diff line number Diff line change
Expand Up @@ -320,7 +320,7 @@ def __init__(
self._page_id2num: Optional[
Dict[Any, Any]
] = None # map page indirect_reference number to Page Number
if hasattr(stream, "mode") and "b" not in stream.mode: # type: ignore
if hasattr(stream, "mode") and "b" not in stream.mode:
logger_warning(
"PdfReader stream/file object is not in binary mode. "
"It may not be read correctly.",
Expand Down Expand Up @@ -1035,7 +1035,7 @@ def _build_outline_item(self, node: DictionaryObject) -> Optional[Destination]:
except KeyError:
if self.strict:
raise PdfReadError(f"Outline Entry Missing /Title attribute: {node!r}")
title = "" # type: ignore
title = ""

if "/A" in node:
# Action, PDFv1.7 Section 12.6 (only type GoTo supported)
Expand Down Expand Up @@ -1074,7 +1074,7 @@ def _build_outline_item(self, node: DictionaryObject) -> Optional[Destination]:
f"Removed unexpected destination {dest!r} from destination",
__name__,
)
outline_item = self._build_destination(title, None) # type: ignore
outline_item = self._build_destination(title, None)

# if outline item created, add color, format, and child count if present
if outline_item:
Expand Down Expand Up @@ -1229,7 +1229,7 @@ def _flatten(
self.flattened_pages = []

if PA.TYPE in pages:
t = pages[PA.TYPE] # type: ignore
t = pages[PA.TYPE]
# if pdf has no type, considered as a page if /Kids is missing
elif PA.KIDS not in pages:
t = "/Page"
Expand Down Expand Up @@ -1358,7 +1358,7 @@ def get_object(
idnum, generation = self.read_object_header(self.stream)
except Exception:
if hasattr(self.stream, "getbuffer"):
buf = bytes(self.stream.getbuffer()) # type: ignore
buf = bytes(self.stream.getbuffer())
else:
p = self.stream.tell()
self.stream.seek(0, 0)
Expand Down Expand Up @@ -1412,7 +1412,7 @@ def get_object(
)
else:
if hasattr(self.stream, "getbuffer"):
buf = bytes(self.stream.getbuffer()) # type: ignore
buf = bytes(self.stream.getbuffer())
else:
p = self.stream.tell()
self.stream.seek(0, 0)
Expand Down Expand Up @@ -1705,7 +1705,7 @@ def _read_standard_xref_table(self, stream: StreamType) -> None:
except Exception:
# if something wrong occurred
if hasattr(stream, "getbuffer"):
buf = bytes(stream.getbuffer()) # type: ignore
buf = bytes(stream.getbuffer())
else:
p = stream.tell()
stream.seek(0, 0)
Expand Down
8 changes: 4 additions & 4 deletions pypdf/_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@

try:
# Python 3.10+: https://www.python.org/dev/peps/pep-0484/
from typing import TypeAlias # type: ignore[attr-defined]
from typing import TypeAlias
except ImportError:
from typing_extensions import TypeAlias

Expand Down Expand Up @@ -461,9 +461,9 @@ def deprecation_bookmark(**aliases: str) -> Callable:
outline = a collection of outline items.
"""

def decoration(func: Callable) -> Any: # type: ignore
def decoration(func: Callable) -> Any:
@functools.wraps(func)
def wrapper(*args: Any, **kwargs: Any) -> Any: # type: ignore
def wrapper(*args: Any, **kwargs: Any) -> Any:
rename_kwargs(func.__name__, kwargs, aliases, fail=True)
return func(*args, **kwargs)

Expand All @@ -472,7 +472,7 @@ def wrapper(*args: Any, **kwargs: Any) -> Any: # type: ignore
return decoration


def rename_kwargs( # type: ignore
def rename_kwargs(
func_name: str, kwargs: Dict[str, Any], aliases: Dict[str, str], fail: bool = False
) -> None:
"""
Expand Down
29 changes: 14 additions & 15 deletions pypdf/_writer.py
Original file line number Diff line number Diff line change
Expand Up @@ -278,7 +278,7 @@ def get_object(
return self._objects[indirect_reference - 1]
if indirect_reference.pdf != self:
raise ValueError("pdf must be self")
return self._objects[indirect_reference.idnum - 1] # type: ignore
return self._objects[indirect_reference.idnum - 1]

def getObject(self, ido: Union[int, IndirectObject]) -> PdfObject: # deprecated
"""
Expand All @@ -301,7 +301,7 @@ def _replace_object(
return self._objects[indirect_reference - 1]
if indirect_reference.pdf != self:
raise ValueError("pdf must be self")
return self._objects[indirect_reference.idnum - 1] # type: ignore
return self._objects[indirect_reference.idnum - 1]

def _add_page(
self,
Expand All @@ -327,8 +327,8 @@ def _add_page(
if page_org.pdf is not None:
other = page_org.pdf.pdf_header
if isinstance(other, str):
other = other.encode() # type: ignore
self.pdf_header = _get_max_pdf_version_header(self.pdf_header, other) # type: ignore
other = other.encode()
self.pdf_header = _get_max_pdf_version_header(self.pdf_header, other)
page[NameObject(PA.PARENT)] = self._pages
pages = cast(DictionaryObject, self.get_object(self._pages))
assert page.indirect_reference is not None
Expand Down Expand Up @@ -372,7 +372,7 @@ def set_need_appearances_writer(self, state: bool = True) -> None:
@property
def viewer_preferences(self) -> Optional[ViewerPreferences]:
"""Returns the existing ViewerPreferences as an overloaded dictionary."""
o = cast(DictionaryObject, self._root_object).get(CD.VIEWER_PREFERENCES, None)
o = self._root_object.get(CD.VIEWER_PREFERENCES, None)
if o is None:
return None
o = o.get_object()
Expand Down Expand Up @@ -624,7 +624,7 @@ def open_destination(
return create_string_object(str(oa))
elif isinstance(oa, ArrayObject):
try:
page, typ = oa[0:2] # type: ignore
page, typ = oa[0:2]
array = oa[2:]
fit = Fit(typ, tuple(array))
return Destination("OpenAction", page, fit)
Expand Down Expand Up @@ -1153,7 +1153,7 @@ def _flatten(
for attr in inheritable_page_attributes:
if attr in pages:
inherit[attr] = pages[attr]
for page in cast(ArrayObject, cast(DictionaryObject, pages)[PA.KIDS]):
for page in cast(ArrayObject, pages[PA.KIDS]):
addt = {}
if isinstance(page, IndirectObject):
addt["indirect_reference"] = page
Expand Down Expand Up @@ -1192,7 +1192,7 @@ def clone_document_from_reader(
if TK.INFO in reader.trailer:
self._info = reader.trailer[TK.INFO].clone(self).indirect_reference # type: ignore
try:
self._ID = cast(ArrayObject, reader.trailer[TK.ID].clone(self)) # type: ignore
self._ID = cast(ArrayObject, reader.trailer[TK.ID].clone(self))
except KeyError:
pass
if callable(after_page_append):
Expand Down Expand Up @@ -1333,7 +1333,7 @@ def encrypt(
def write_stream(self, stream: StreamType) -> None:
if hasattr(stream, "mode") and "b" not in stream.mode:
logger_warning(
f"File <{stream.name}> to write to is not in binary mode. " # type: ignore
f"File <{stream.name}> to write to is not in binary mode. "
"It may not be written to correctly.",
__name__,
)
Expand Down Expand Up @@ -2205,9 +2205,9 @@ def clean(content: ContentStream, images: List[str], forms: List[str]) -> None:
del content.operations[i]
elif operator == b"Do":
if (
cast(ObjectDeletionFlag, to_delete) & ObjectDeletionFlag.IMAGES
to_delete & ObjectDeletionFlag.IMAGES
and operands[0] in images
or cast(ObjectDeletionFlag, to_delete) & ObjectDeletionFlag.TEXT
or to_delete & ObjectDeletionFlag.TEXT
and operands[0] in forms
):
del content.operations[i]
Expand All @@ -2234,7 +2234,7 @@ def clean_forms(
try:
content: Any = None
if (
cast(ObjectDeletionFlag, to_delete) & ObjectDeletionFlag.IMAGES
to_delete & ObjectDeletionFlag.IMAGES
and o["/Subtype"] == "/Image"
):
content = NullObject()
Expand Down Expand Up @@ -2276,9 +2276,9 @@ def clean_forms(
content = ContentStream(content, page)
images, forms = clean_forms(page, [])

clean(cast(ContentStream, content), images, forms)
clean(content, images, forms)
if isinstance(page["/Contents"], ArrayObject):
for o in cast(ArrayObject, page["/Contents"]):
for o in page["/Contents"]:
self._objects[o.idnum - 1] = NullObject()
try:
self._objects[
Expand Down Expand Up @@ -3244,7 +3244,6 @@ def _insert_filtered_annotations(
p = self._get_cloned_page(d[0], pages, reader)
if p is not None:
anc = ano.clone(self, ignore_fields=("/D",))
anc = cast("DictionaryObject", anc)
cast("DictionaryObject", anc["/A"])[
NameObject("/D")
] = ArrayObject([p] + d[1:])
Expand Down
6 changes: 3 additions & 3 deletions pypdf/_xobj_image_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@
)

try:
from typing import Literal, TypeAlias # type: ignore[attr-defined]
from typing import Literal, TypeAlias
except ImportError:
# PEP 586 introduced typing.Literal with Python 3.8
# For older Python versions, the backport typing_extensions is necessary:
from typing_extensions import Literal, TypeAlias # type: ignore[misc, assignment]
from typing_extensions import Literal, TypeAlias # type: ignore[assignment]


try:
Expand Down Expand Up @@ -92,7 +92,7 @@ def _get_imagemode(
mode_map.get(color_space) # type: ignore
or list(mode_map.values())[color_components]
or prev_mode
) # type: ignore
)
return mode, mode == "CMYK"


Expand Down
4 changes: 2 additions & 2 deletions pypdf/annotations/_markup_annotations.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@
from ._base import NO_FLAGS, AnnotationDictionary

try:
from typing import TypeAlias # type: ignore[attr-defined]
from typing import TypeAlias
except ImportError:
# PEP 613 introduced typing.TypeAlias with Python 3.10
# For older Python versions, the backport typing_extensions is necessary:
from typing_extensions import TypeAlias # type: ignore[misc]
from typing_extensions import TypeAlias


Vertex: TypeAlias = Tuple[float, float]
Expand Down
Loading
Loading