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

MAINT: Add initial type support #853

Merged
merged 1 commit into from
May 2, 2022
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
3 changes: 3 additions & 0 deletions .github/workflows/github-ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ jobs:
run: |
python -OO -m coverage run --parallel-mode -m pytest tests -vv
if: matrix.python-version == '3.10.1'
- name: Test with mypy
run : |
mypy PyPDF2 --show-error-codes
- name: Upload coverage data
uses: actions/upload-artifact@v3
with:
Expand Down
11 changes: 8 additions & 3 deletions PyPDF2/filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,11 @@
import struct
from io import StringIO

try:
from typing import Literal # type: ignore[attr-defined]
except ImportError:
from typing_extensions import Literal # type: ignore[misc]

from PyPDF2.constants import CcittFaxDecodeParameters as CCITT
from PyPDF2.constants import ColorSpaces
from PyPDF2.constants import FilterTypeAbbreviations as FTA
Expand Down Expand Up @@ -66,8 +71,8 @@ def compress(data):
except ImportError: # pragma: no cover
# Unable to import zlib. Attempt to use the System.IO.Compression
# library from the .NET framework. (IronPython only)
import System
from System import IO, Array
import System # type: ignore[import]
from System import IO, Array # type: ignore[import]

def _string_to_bytearr(buf):
retval = Array.CreateInstance(System.Byte, len(buf))
Expand Down Expand Up @@ -536,7 +541,7 @@ def _xobj_to_image(x_object_obj):
size = (x_object_obj[IA.WIDTH], x_object_obj[IA.HEIGHT])
data = x_object_obj.getData()
if x_object_obj[IA.COLOR_SPACE] == ColorSpaces.DEVICE_RGB:
mode = "RGB"
mode: Literal["RGB", "P"] = "RGB"
else:
mode = "P"
extension = None
Expand Down
39 changes: 24 additions & 15 deletions PyPDF2/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@
import re
import warnings
from io import BytesIO
from typing import Dict, Optional

from PyPDF2.constants import FilterTypes as FT
from PyPDF2.constants import StreamAttributes as SA
Expand All @@ -57,7 +58,13 @@
IndirectPattern = re.compile(b_(r"[+-]?(\d+)\s+(\d+)\s+R[^a-zA-Z]"))


def readObject(stream, pdf):
class PdfObject:
def getObject(self):
"""Resolve indirect references."""
return self


def readObject(stream, pdf) -> PdfObject:
tok = stream.read(1)
stream.seek(-1, 1) # reset to start
idx = ObjectPrefix.find(tok)
Expand Down Expand Up @@ -101,12 +108,6 @@ def readObject(stream, pdf):
return NumberObject.readFromStream(stream)


class PdfObject:
def getObject(self):
"""Resolve indirect references."""
return self


class NullObject(PdfObject):
def writeToStream(self, stream, encryption_key):
stream.write(b_("null"))
Expand Down Expand Up @@ -508,7 +509,7 @@ def readFromStream(stream, pdf):
# Name objects should represent irregular characters
# with a '#' followed by the symbol's hex number
if not pdf.strict:
warnings.warn("Illegal character in Name Object", utils.PdfReadWarning)
warnings.warn("Illegal character in Name Object", PdfReadWarning)
return NameObject(name)
else:
raise PdfReadError("Illegal character in Name Object")
Expand Down Expand Up @@ -805,10 +806,18 @@ def emptyTree(self):

class StreamObject(DictionaryObject):
def __init__(self):
self._data = None
self.__data: Optional[str] = None
self.decodedSelf = None

def writeToStream(self, stream, encryption_key):
@property
def _data(self):
return self.__data

@_data.setter
def _data(self, value):
self.__data = value

def writeToStream(self, stream, encryption_key) -> None:
self[NameObject(SA.LENGTH)] = NumberObject(len(self._data))
DictionaryObject.writeToStream(self, stream, encryption_key)
del self[SA.LENGTH]
Expand Down Expand Up @@ -989,7 +998,8 @@ def _readInlineImage(self, stream):
data.write(tok)
return {"settings": settings, "data": data.getvalue()}

def _getData(self):
@property
def _data(self):
newdata = BytesIO()
for operands, operator in self.operations:
if operator == b_("INLINE IMAGE"):
Expand All @@ -1008,11 +1018,10 @@ def _getData(self):
newdata.write(b_("\n"))
return newdata.getvalue()

def _setData(self, value):
@_data.setter
def _data(self, value):
self.__parseContentStream(BytesIO(b_(value)))

_data = property(_getData, _setData)


class RectangleObject(ArrayObject):
"""
Expand Down Expand Up @@ -1686,7 +1695,7 @@ def decode_pdfdocencoding(byte_array):

assert len(_pdfDocEncoding) == 256

_pdfDocEncoding_rev = {}
_pdfDocEncoding_rev: Dict[str, int] = {}
for i in range(256):
char = _pdfDocEncoding[i]
if char == "\u0000":
Expand Down
21 changes: 19 additions & 2 deletions PyPDF2/merger.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@

from io import BytesIO
from io import FileIO as file
from typing import List, Optional, Union

from PyPDF2._reader import PdfFileReader
from PyPDF2._writer import PdfFileWriter
Expand All @@ -37,6 +38,8 @@

StreamIO = BytesIO

ERR_CLOSED_WRITER = "close() was called and thus the writer cannot be used anymore"


class _MergedPage:
"""
Expand Down Expand Up @@ -68,7 +71,7 @@ class PdfFileMerger:
def __init__(self, strict=False):
self.inputs = []
self.pages = []
self.output = PdfFileWriter()
self.output: Optional[PdfFileWriter] = PdfFileWriter()
self.bookmarks = []
self.named_dests = []
self.id_count = 0
Expand Down Expand Up @@ -220,6 +223,8 @@ def write(self, fileobj):
:param fileobj: Output file. Can be a filename or any kind of
file-like object.
"""
if self.output is None:
raise RuntimeError(ERR_CLOSED_WRITER)
my_file = False
if isinstance(fileobj, str):
fileobj = file(fileobj, "wb")
Expand Down Expand Up @@ -267,6 +272,8 @@ def addMetadata(self, infos):
and each value is your new metadata.
Example: ``{u'/Title': u'My title'}``
"""
if self.output is None:
raise RuntimeError(ERR_CLOSED_WRITER)
self.output.addMetadata(infos)

def setPageLayout(self, layout):
Expand All @@ -293,6 +300,8 @@ def setPageLayout(self, layout):
* - /TwoPageRight
- Show two pages at a time, odd-numbered pages on the right
"""
if self.output is None:
raise RuntimeError(ERR_CLOSED_WRITER)
self.output.setPageLayout(layout)

def setPageMode(self, mode):
Expand All @@ -317,6 +326,8 @@ def setPageMode(self, mode):
* - /UseAttachments
- Show attachments panel
"""
if self.output is None:
raise RuntimeError(ERR_CLOSED_WRITER)
self.output.setPageMode(mode)

def _trim_dests(self, pdf, dests, pages):
Expand Down Expand Up @@ -359,6 +370,8 @@ def _trim_outline(self, pdf, outline, pages):
return new_outline

def _write_dests(self):
if self.output is None:
raise RuntimeError(ERR_CLOSED_WRITER)
for named_dest in self.named_dests:
pageno = None
if "/Page" in named_dest:
Expand All @@ -371,6 +384,8 @@ def _write_dests(self):
self.output.addNamedDestinationObject(named_dest)

def _write_bookmarks(self, bookmarks=None, parent=None):
if self.output is None:
raise RuntimeError(ERR_CLOSED_WRITER)
if bookmarks is None:
bookmarks = self.bookmarks

Expand Down Expand Up @@ -533,13 +548,15 @@ def addBookmark(
:param str fit: The fit of the destination page. See
:meth:`addLink()<addLin>` for details.
"""
if self.output is None:
raise RuntimeError(ERR_CLOSED_WRITER)
if len(self.output.getObject(self.output._pages)["/Kids"]) > 0:
page_ref = self.output.getObject(self.output._pages)["/Kids"][pagenum]
else:
page_ref = self.output.getObject(self.output._pages)

action = DictionaryObject()
zoom_args = []
zoom_args: List[Union[NumberObject, NullObject]] = []
for a in args:
if a is not None:
zoom_args.append(NumberObject(a))
Expand Down
4 changes: 3 additions & 1 deletion PyPDF2/pagerange.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
"""

import re
from typing import List, Tuple, Union

from PyPDF2.errors import ParseError

Expand Down Expand Up @@ -96,6 +97,7 @@ def to_slice(self):
def __str__(self):
"""A string like "1:2:3"."""
s = self._slice
indices: Union[Tuple[int, int], Tuple[int, int, int]]
if s.step is None:
if s.start is not None and s.stop == s.start + 1:
return str(s.start)
Expand Down Expand Up @@ -133,7 +135,7 @@ def parse_filename_page_ranges(args):
expressions, slice objects, or PageRange objects.
A filename not followed by a page range indicates all pages of the file.
"""
pairs = []
pairs: List[Tuple[str, PageRange]] = []
pdf_filename = None
did_page_range = False
for arg in args + [None]:
Expand Down
Empty file added PyPDF2/py.typed
Empty file.
Loading