-
Notifications
You must be signed in to change notification settings - Fork 1.5k
BUG: fix for merge page with annotation #3467 #3532
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
base: main
Are you sure you want to change the base?
Changes from all commits
f7c5507
01a52a2
f82d39d
b47c25e
4817e85
e82dd43
cbd58d9
2560d16
2a7e5b3
5f2e9a6
8d82c2d
1899aa6
f5f5604
81e38cd
0ae775b
a5d4b6c
817e981
0e410ba
330f253
bcf9d2c
bb710e1
5266c84
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -52,6 +52,41 @@ def __init__(self, *, title_bar: Optional[str] = None) -> None: | |
| self[NameObject("/T")] = TextStringObject(title_bar) | ||
|
|
||
|
|
||
| class AbstractPolyLine(MarkupAnnotation, ABC): | ||
| """ | ||
| Base class for Polygon and PolyLine | ||
|
|
||
| Args: | ||
| vertices: List of coordinates of each vertex; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is there something missing in the parameter description? |
||
|
|
||
| """ | ||
| def __init__( | ||
| self, | ||
| vertices: Union[list[Vertex], ArrayObject], | ||
| **kwargs: Any | ||
| ) -> None: | ||
| super().__init__(**kwargs) | ||
| if len(vertices) == 0: | ||
| raise ValueError(f"A {type(self).__name__.lower()} needs at least 1 vertex with two coordinates") | ||
|
|
||
| @staticmethod | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
i would need to store the
do you mean using
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Is garbage collection really relevant here? If yes, I vote to get rid of the base class completely and just make this method a helper function directly. Regarding the stdlib part: You initially had
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
we store alternatively, i could move call of
i thought one solution cover all python version was better. less code branch to maintain. |
||
| def _determine_vertices( | ||
| vertices: Union[list[Vertex], ArrayObject] | ||
| ) -> tuple[list[Vertex], list[NumberObject]]: | ||
| coord_list: ArrayObject = ArrayObject() | ||
| if isinstance(vertices, ArrayObject): | ||
| coord_list = vertices | ||
| args = [iter(vertices)] * 2 # Adapted def grouper() | ||
| vertices = list(zip(*args)) # from https://docs.python.org/3.9/library/itertools.html#itertools-recipes | ||
|
|
||
| else: | ||
| for x, y in vertices: | ||
| coord_list.append(NumberObject(x)) | ||
| coord_list.append(NumberObject(y)) | ||
|
|
||
| return vertices, coord_list | ||
|
|
||
|
|
||
| class Text(MarkupAnnotation): | ||
| """ | ||
| A text annotation. | ||
|
|
@@ -186,19 +221,15 @@ def __init__( | |
| ) | ||
|
|
||
|
|
||
| class PolyLine(MarkupAnnotation): | ||
| class PolyLine(AbstractPolyLine): | ||
| def __init__( | ||
| self, | ||
| vertices: list[Vertex], | ||
| vertices: Union[list[Vertex], ArrayObject], | ||
| **kwargs: Any, | ||
| ) -> None: | ||
| super().__init__(**kwargs) | ||
| if len(vertices) == 0: | ||
| raise ValueError("A polyline needs at least 1 vertex with two coordinates") | ||
| coord_list = [] | ||
| for x, y in vertices: | ||
| coord_list.append(NumberObject(x)) | ||
| coord_list.append(NumberObject(y)) | ||
| super().__init__(vertices=vertices, **kwargs) | ||
|
|
||
| vertices, coord_list = self._determine_vertices(vertices) | ||
| self.update( | ||
| { | ||
| NameObject("/Subtype"): NameObject("/PolyLine"), | ||
|
|
@@ -280,20 +311,14 @@ def __init__( | |
| ) | ||
|
|
||
|
|
||
| class Polygon(MarkupAnnotation): | ||
| class Polygon(AbstractPolyLine): | ||
| def __init__( | ||
| self, | ||
| vertices: list[tuple[float, float]], | ||
| vertices: Union[list[Vertex], ArrayObject], | ||
| **kwargs: Any, | ||
| ) -> None: | ||
| super().__init__(**kwargs) | ||
| if len(vertices) == 0: | ||
| raise ValueError("A polygon needs at least 1 vertex with two coordinates") | ||
|
|
||
| coord_list = [] | ||
| for x, y in vertices: | ||
| coord_list.append(NumberObject(x)) | ||
| coord_list.append(NumberObject(y)) | ||
| super().__init__(vertices=vertices, **kwargs) | ||
| vertices, coord_list = self._determine_vertices(vertices) | ||
| self.update( | ||
| { | ||
| NameObject("/Type"): NameObject("/Annot"), | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -29,6 +29,7 @@ | |||||
| __author__ = "Mathieu Fenniak" | ||||||
| __author_email__ = "[email protected]" | ||||||
|
|
||||||
| import inspect | ||||||
| import logging | ||||||
| import re | ||||||
| import sys | ||||||
|
|
@@ -263,6 +264,40 @@ def read_from_stream( | |||||
|
|
||||||
|
|
||||||
| class DictionaryObject(dict[Any, Any], PdfObject): | ||||||
|
|
||||||
| _init_alt_arg_names_: Optional[dict[str, str]] = None | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||
|
|
||||||
|
|
||||||
| """ | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The documentation seems to be somehow located at the wrong place. It should go into the methods, not before them. |
||||||
| Used to map DictionaryObject keyword to __init__ arg names | ||||||
| when cloning. key -> arg_name. | ||||||
|
|
||||||
| Args: | ||||||
| key: string identifying DictionaryObject keyword | ||||||
| arg_name: string identifying argument name (value) | ||||||
| """ | ||||||
| def set_alt_arg_name(self, key: str, arg_name: str) -> None: | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What benefit does this have? Couldn't we just provide the mapping directly in the corresponding subclass? |
||||||
| if self._init_alt_arg_names_ is None: | ||||||
| self._init_alt_arg_names_ = {} | ||||||
|
|
||||||
| if not isinstance(key, str) or not isinstance(arg_name, str): | ||||||
| raise TypeError | ||||||
|
|
||||||
| self._init_alt_arg_names_[key] = arg_name | ||||||
|
|
||||||
| """ | ||||||
| Used to return __init__ arg names when cloning | ||||||
| Args: | ||||||
| key: string identifying DictionaryObject keyword | ||||||
| Returns: | ||||||
| Returns None if not found, else the string representing the | ||||||
| argument name | ||||||
| """ | ||||||
| def get_alt_arg_name(self, key: str) -> Optional[str]: | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||||||
| if self._init_alt_arg_names_ is None or not isinstance(key, str): | ||||||
| return None | ||||||
| return self._init_alt_arg_names_.get(key, None) | ||||||
|
|
||||||
| def replicate( | ||||||
| self, | ||||||
| pdf_dest: PdfWriterProtocol, | ||||||
|
|
@@ -291,9 +326,22 @@ def clone( | |||||
| pass | ||||||
|
|
||||||
| visited: set[tuple[int, int]] = set() # (idnum, generation) | ||||||
|
|
||||||
| kwargs = {} | ||||||
| inspector = inspect.getfullargspec(self.__class__.__init__) | ||||||
|
|
||||||
| for key, val in self.items(): | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| alt_arg_name = self.get_alt_arg_name(key) | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It seems like this code could be simplified by passing the stripped key to the alternative name retrieval method and let it return the original code for unmapped entries. You would not need the first check condition anymore. |
||||||
| key_stripped = key.removeprefix("/").lower() | ||||||
|
|
||||||
| if alt_arg_name: | ||||||
| kwargs[alt_arg_name] = val | ||||||
| elif key_stripped in inspector.args or key_stripped in inspector.kwonlyargs: | ||||||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Previously, unknown keys would raise a proper error. With this change, these issues will get obfuscated completely. This does not sound correct. |
||||||
| kwargs[key_stripped] = val | ||||||
|
|
||||||
| d__ = cast( | ||||||
| "DictionaryObject", | ||||||
| self._reference_clone(self.__class__(), pdf_dest, force_duplicate), | ||||||
| self._reference_clone(self.__class__(**kwargs), pdf_dest, force_duplicate), | ||||||
| ) | ||||||
| if ignore_fields is None: | ||||||
| ignore_fields = [] | ||||||
|
|
||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -54,6 +54,18 @@ | |
| RESOURCE_ROOT = PROJECT_ROOT / "resources" | ||
|
|
||
|
|
||
| class DummyDictObject(DictionaryObject): | ||
| def __init__(self, test_name: str) -> None: | ||
| super().__init__() | ||
| self.set_alt_arg_name("/TestName", "test_name") | ||
|
|
||
| self.update( | ||
| { | ||
| NameObject("/TestName"): test_name | ||
| } | ||
| ) | ||
|
|
||
|
|
||
| class ChildDummy(DictionaryObject): | ||
| @property | ||
| def indirect_reference(self): | ||
|
|
@@ -1282,6 +1294,22 @@ def test_coverage_arrayobject(): | |
| assert isinstance(k, int) | ||
| assert isinstance(v, PdfObject) | ||
|
|
||
| def test_alt_keyword_when_cloning(): | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please put exactly two empty lines between top-level functions. |
||
| obj = DummyDictObject(test_name="testval") | ||
| obj2: DummyDictObject = None | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This type hint is wrong. |
||
|
|
||
| clone_obj = obj.clone(obj2) | ||
|
|
||
| assert clone_obj.get("/TestName") == "testval" | ||
|
|
||
| def test_type_error_when_using_alt_arg_keys(): | ||
| dict_obj = DictionaryObject() | ||
| with pytest.raises(TypeError): | ||
| dict_obj.set_alt_arg_name(1, "test_key") | ||
|
|
||
| with pytest.raises(TypeError): | ||
| dict_obj.set_alt_arg_name("test_key", 1) | ||
|
|
||
|
|
||
| def test_coverage_streamobject(): | ||
| writer = PdfWriter() | ||
|
|
||
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.
Which user-facing functionality needs this class to be public API?
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 had trouble with sphinx documentation build as it could not see
AbstractPolyLine.MarkupAnnotationis also marked in__all__, but only reference in_markup_annotations.py, no other file. because the two class are similar, i thought this correct approachThere 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.
Please provide the exact error you are getting for the documentation. I do not want to make this base class a public interface, as this introduces unnecessary maintenance burdens.