Skip to content

Commit 0bd0a28

Browse files
STY: Update to mypy 1.16.0 (#3300)
Closes #3299.
1 parent fdae3cc commit 0bd0a28

File tree

12 files changed

+49
-32
lines changed

12 files changed

+49
-32
lines changed

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ repos:
3131
args: [--py38-plus]
3232

3333
- repo: https://github.com/pre-commit/mirrors-mypy
34-
rev: 'v1.15.0'
34+
rev: 'v1.16.0'
3535
hooks:
3636
- id: mypy
3737
additional_dependencies: [types-Pillow==10.2.0.20240822]

pypdf/_cmap.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
from ._codecs import adobe_glyphs, charset_encoding
77
from ._utils import logger_error, logger_warning
88
from .generic import (
9+
ArrayObject,
910
DecodedStreamObject,
1011
DictionaryObject,
1112
StreamObject,
@@ -443,7 +444,7 @@ def build_font_width_map(
443444
)
444445
break
445446
elif "/Widths" in ft:
446-
w = ft["/Widths"].get_object()
447+
w = cast(ArrayObject, ft["/Widths"].get_object())
447448
if "/FontDescriptor" in ft and "/MissingWidth" in cast(
448449
DictionaryObject, ft["/FontDescriptor"]
449450
):

pypdf/_doc_common.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -329,7 +329,7 @@ def viewer_preferences(self) -> Optional[ViewerPreferences]:
329329
o = o.get_object()
330330
if not isinstance(o, ViewerPreferences):
331331
o = ViewerPreferences(o)
332-
if hasattr(o, "indirect_reference"):
332+
if hasattr(o, "indirect_reference") and o.indirect_reference is not None:
333333
self._replace_object(o.indirect_reference, o)
334334
else:
335335
self.root_object[NameObject(CD.VIEWER_PREFERENCES)] = o

pypdf/_page.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1430,7 +1430,7 @@ def merge_transformed_page(
14301430
self._merge_page(
14311431
page2,
14321432
lambda page2Content: PageObject._add_transformation_matrix(
1433-
page2Content, page2.pdf, cast(CompressedTransformationMatrix, ctm)
1433+
page2Content, page2.pdf, ctm
14341434
),
14351435
ctm,
14361436
over,

pypdf/_reader.py

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,7 @@ def _info(self) -> Optional[DictionaryObject]:
254254
info = self.trailer.get(TK.INFO, None)
255255
if is_null_or_none(info):
256256
return None
257+
assert info is not None, "mypy"
257258
info = info.get_object()
258259
if not isinstance(info, DictionaryObject):
259260
raise PdfReadError(
@@ -271,7 +272,10 @@ def _ID(self) -> Optional[ArrayObject]:
271272
272273
"""
273274
id = self.trailer.get(TK.ID, None)
274-
return None if is_null_or_none(id) else cast(ArrayObject, id.get_object())
275+
if is_null_or_none(id):
276+
return None
277+
assert id is not None, "mypy"
278+
return cast(ArrayObject, id.get_object())
275279

276280
@property
277281
def pdf_header(self) -> str:

pypdf/_writer.py

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -501,7 +501,7 @@ def _add_page(
501501
while not is_null_or_none(node):
502502
node = cast(DictionaryObject, node.get_object())
503503
node[NameObject(PA.COUNT)] = NumberObject(cast(int, node[PA.COUNT]) + 1)
504-
node = node.get(PA.PARENT, None)
504+
node = node.get(PA.PARENT, None) # type: ignore[assignment] # TODO: Fix.
505505
recurse += 1
506506
if recurse > 1000:
507507
raise PyPdfError("Too many recursive calls!")
@@ -1103,10 +1103,11 @@ def update_page_form_field_values(
11031103
)
11041104
else:
11051105
parent_annotation[NameObject(FA.V)] = TextStringObject(value)
1106-
if parent_annotation.get(FA.FT) in ("/Btn"):
1106+
if parent_annotation.get(FA.FT) == "/Btn":
11071107
# Checkbox button (no /FT found in Radio widgets)
11081108
v = NameObject(value)
1109-
if v not in annotation[NameObject(AA.AP)][NameObject("/N")]:
1109+
ap = cast(DictionaryObject, annotation[NameObject(AA.AP)])
1110+
if v not in cast(ArrayObject, ap[NameObject("/N")]):
11101111
v = NameObject("/Off")
11111112
# other cases will be updated through the for loop
11121113
annotation[NameObject(AA.AS)] = v

pypdf/_xobj_image_helpers.py

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@
1313
ArrayObject,
1414
DecodedStreamObject,
1515
EncodedStreamObject,
16-
IndirectObject,
1716
NullObject,
1817
TextStringObject,
1918
)
@@ -57,49 +56,46 @@ def _get_imagemode(
5756
)
5857
if isinstance(color_space, NullObject):
5958
return "", False
59+
color_space_str: str = ""
6060
if isinstance(color_space, str):
61-
pass
61+
color_space_str = color_space
6262
elif not isinstance(color_space, list):
6363
raise PdfReadError(
6464
"Cannot interpret color space", color_space
6565
) # pragma: no cover
6666
elif color_space[0].startswith("/Cal"): # /CalRGB and /CalGray
67-
color_space = "/Device" + color_space[0][4:]
67+
color_space_str = "/Device" + color_space[0][4:]
6868
elif color_space[0] == "/ICCBased":
6969
icc_profile = color_space[1].get_object()
7070
color_components = cast(int, icc_profile["/N"])
71-
color_space = icc_profile.get("/Alternate", "")
71+
color_space_str = icc_profile.get("/Alternate", "")
7272
elif color_space[0] == "/Indexed":
73-
color_space = color_space[1].get_object()
73+
color_space_str = color_space[1].get_object()
7474
mode, invert_color = _get_imagemode(
75-
color_space, color_components, prev_mode, depth + 1
75+
color_space_str, color_components, prev_mode, depth + 1
7676
)
7777
if mode in ("RGB", "CMYK"):
7878
mode = "P"
7979
return mode, invert_color
8080
elif color_space[0] == "/Separation":
81-
color_space = color_space[2]
82-
if isinstance(color_space, IndirectObject):
83-
color_space = color_space.get_object()
81+
color_space_str = color_space[2].get_object()
8482
mode, invert_color = _get_imagemode(
85-
color_space, color_components, prev_mode, depth + 1
83+
color_space_str, color_components, prev_mode, depth + 1
8684
)
8785
return mode, True
8886
elif color_space[0] == "/DeviceN":
8987
original_color_space = color_space
9088
color_components = len(color_space[1])
91-
color_space = color_space[2]
92-
if isinstance(color_space, IndirectObject): # pragma: no cover
93-
color_space = color_space.get_object()
94-
if color_space == "/DeviceCMYK" and color_components == 1:
89+
color_space_str = color_space[2].get_object()
90+
if color_space_str == "/DeviceCMYK" and color_components == 1:
9591
if original_color_space[1][0] != "/Black":
9692
logger_warning(
9793
f"Color {original_color_space[1][0]} converted to Gray. Please share PDF with pypdf dev team",
9894
__name__,
9995
)
10096
return "L", True
10197
mode, invert_color = _get_imagemode(
102-
color_space, color_components, prev_mode, depth + 1
98+
color_space_str, color_components, prev_mode, depth + 1
10399
)
104100
return mode, invert_color
105101

@@ -114,7 +110,7 @@ def _get_imagemode(
114110
}
115111

116112
mode = (
117-
mode_map.get(color_space)
113+
mode_map.get(color_space_str)
118114
or list(mode_map.values())[color_components]
119115
or prev_mode
120116
)

pypdf/generic/_data_structures.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -411,7 +411,7 @@ def _clone(
411411
v.indirect_reference = None
412412
vv = v.clone(pdf_dest, force_duplicate, ignore_fields)
413413
assert vv.indirect_reference is not None
414-
self[k.clone(pdf_dest)] = vv.indirect_reference # type: ignore[attr-defined]
414+
self[k.clone(pdf_dest)] = vv.indirect_reference
415415
elif k not in self:
416416
self[NameObject(k)] = (
417417
v.clone(pdf_dest, force_duplicate, ignore_fields)
@@ -495,6 +495,7 @@ def xmp_metadata(self) -> Optional[XmpInformationProtocol]:
495495
metadata = self.get("/Metadata", None)
496496
if is_null_or_none(metadata):
497497
return None
498+
assert metadata is not None, "mypy"
498499
metadata = metadata.get_object()
499500

500501
if not isinstance(metadata, XmpInformation):

pypdf/generic/_viewerpref.py

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ def __init__(self, obj: Optional[DictionaryObject] = None) -> None:
4848
except AttributeError:
4949
pass
5050

51-
def _get_bool(self, key: str, default: Optional[BooleanObject]) -> BooleanObject:
51+
def _get_bool(self, key: str, default: Optional[BooleanObject]) -> Optional[BooleanObject]:
5252
return self.get(key, default)
5353

5454
def _set_bool(self, key: str, v: bool) -> None:
@@ -64,7 +64,7 @@ def _set_name(self, key: str, lst: List[str], v: NameObject) -> None:
6464
raise ValueError(f"{v} is an unacceptable value")
6565
self[NameObject(key)] = NameObject(v)
6666

67-
def _get_arr(self, key: str, default: Optional[List[Any]]) -> NumberObject:
67+
def _get_arr(self, key: str, default: Optional[List[Any]]) -> Optional[ArrayObject]:
6868
return self.get(key, None if default is None else ArrayObject(default))
6969

7070
def _set_arr(self, key: str, v: Optional[ArrayObject]) -> None:
@@ -78,7 +78,7 @@ def _set_arr(self, key: str, v: Optional[ArrayObject]) -> None:
7878
raise ValueError("ArrayObject is expected")
7979
self[NameObject(key)] = v
8080

81-
def _get_int(self, key: str, default: Optional[NumberObject]) -> NumberObject:
81+
def _get_int(self, key: str, default: Optional[NumberObject]) -> Optional[NumberObject]:
8282
return self.get(key, default)
8383

8484
def _set_int(self, key: str, v: int) -> None:

pypdf/xmp.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
from xml.dom.minidom import Element as XmlElement
2222
from xml.parsers.expat import ExpatError
2323

24+
from ._protocols import XmpInformationProtocol
2425
from ._utils import StreamType, deprecate_no_replacement
2526
from .errors import PdfReadError
2627
from .generic import ContentStream, PdfObject
@@ -200,7 +201,7 @@ def get(self: "XmpInformation") -> Optional[Any]:
200201
return get
201202

202203

203-
class XmpInformation(PdfObject):
204+
class XmpInformation(XmpInformationProtocol, PdfObject):
204205
"""
205206
An object that represents Extensible Metadata Platform (XMP) metadata.
206207
Usually accessed by :py:attr:`xmp_metadata()<pypdf.PdfReader.xmp_metadata>`.
@@ -244,7 +245,7 @@ def get_nodes_in_namespace(self, about_uri: str, namespace: str) -> Iterator[Any
244245
if desc.getAttributeNS(RDF_NAMESPACE, "about") == about_uri:
245246
for i in range(desc.attributes.length):
246247
attr = desc.attributes.item(i)
247-
if attr.namespaceURI == namespace:
248+
if attr and attr.namespaceURI == namespace:
248249
yield attr
249250
for child in desc.childNodes:
250251
if child.namespaceURI == namespace:

requirements/ci-3.11.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ fpdf2==2.8.1
2424
# via -r requirements/ci.in
2525
iniconfig==2.0.0
2626
# via pytest
27-
mypy==1.15.0
27+
mypy==1.16.0
2828
# via -r requirements/ci.in
2929
mypy-extensions==1.0.0
3030
# via mypy

tests/test_doc_common.py

Lines changed: 14 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,13 @@
99
import pytest
1010

1111
from pypdf import PdfReader, PdfWriter
12-
from pypdf.generic import EmbeddedFile, NullObject
12+
from pypdf.generic import EmbeddedFile, NullObject, ViewerPreferences
1313
from tests import get_data_from_url
1414

1515
TESTS_ROOT = Path(__file__).parent.resolve()
1616
PROJECT_ROOT = TESTS_ROOT.parent
1717
SAMPLE_ROOT = PROJECT_ROOT / "sample-files"
18+
RESOURCES_ROOT = PROJECT_ROOT / "resources"
1819

1920
PDFATTACH_BINARY = shutil.which("pdfattach")
2021

@@ -155,3 +156,15 @@ def test_byte_encoded_named_destinations():
155156
"/Zoom": NullObject()
156157
}
157158
}
159+
160+
161+
def test_viewer_preferences__indirect_reference():
162+
input_path = RESOURCES_ROOT / "git.pdf"
163+
reader = PdfReader(input_path)
164+
assert (0, 24) not in reader.resolved_objects
165+
viewer_preferences = reader.viewer_preferences
166+
assert isinstance(viewer_preferences, ViewerPreferences)
167+
assert viewer_preferences == {"/DisplayDocTitle": True}
168+
assert (0, 24) in reader.resolved_objects
169+
assert id(viewer_preferences) == id(reader.viewer_preferences)
170+
assert id(viewer_preferences) == id(reader.resolved_objects[(0, 24)])

0 commit comments

Comments
 (0)