@@ -75,50 +75,6 @@ def _validate_output_prefix(value: str | None) -> str | None:
7575 return value
7676
7777
78- def _require_positive_page (
79- text : str , * , description : str , require_page_word : bool = False
80- ) -> str :
81- if not text .isdigit () or int (text ) < 1 :
82- message = (
83- f"{ description } must be a page number greater than or equal to 1."
84- if require_page_word
85- else f"{ description } must be greater than or equal to 1."
86- )
87- raise ValueError (message )
88- return text
89-
90-
91- def _validate_page_range_entry (value : str ) -> str :
92- """Normalize and validate a single page range entry."""
93- if not isinstance (value , str ):
94- msg = "Each page range entry must be a string."
95- raise TypeError (msg )
96- entry = value .strip ()
97- if entry == "" :
98- msg = "Each page range entry must be a non-empty string."
99- raise ValueError (msg )
100- if entry == "last" :
101- return entry
102- if entry .isdigit ():
103- return _require_positive_page (entry , description = "Page numbers" )
104- if "-" in entry :
105- start_raw , end_raw = (part .strip () for part in entry .split ("-" , maxsplit = 1 ))
106- start = _require_positive_page (
107- start_raw , description = "Page range start" , require_page_word = True
108- )
109- if end_raw == "last" :
110- return f"{ start } -last"
111- end = _require_positive_page (
112- end_raw , description = "Page range end" , require_page_word = True
113- )
114- if int (end ) < int (start ):
115- msg = "Page range end must be greater than or equal to the start."
116- raise ValueError (msg )
117- return f"{ start } -{ end } "
118- msg = "Page range entries must be positive integers, 'last', or a range like '1-3' or '6-last'."
119- raise ValueError (msg )
120-
121-
12278def _split_comma_list (value : Any ) -> Any :
12379 if isinstance (value , str ):
12480 return value .split ("," )
@@ -157,7 +113,13 @@ def _serialize_as_comma_separated_string(value: list[Any] | None) -> str | None:
157113 return "," .join (str (element ) for element in value )
158114
159115
160- PageRangeEntry = Annotated [str , AfterValidator (_validate_page_range_entry )]
116+ def _serialize_page_ranges (value : list [str | int | tuple [str | int , ...]]) -> str :
117+ def join_tuple (value : str | int | tuple [str | int , ...]) -> str :
118+ if isinstance (value , tuple ):
119+ return "-" .join (str (e ) for e in value )
120+ return str (value )
121+
122+ return "," .join (join_tuple (v ) for v in value )
161123
162124
163125def _serialize_redactions (value : list [_PdfRedactionVariant ]) -> str :
@@ -204,6 +166,32 @@ class UploadURLs(BaseModel):
204166
205167ColorModelT = TypeVar ("ColorModelT" , bound = str )
206168
169+ PageNumber = Annotated [int , Field (ge = 1 ), PlainSerializer (lambda x : str (x ))]
170+
171+
172+ def _split_page_range_tuple (x : str ) -> tuple [str , str ]:
173+ start , end = x .split ("-" , maxsplit = 1 )
174+ return start , end
175+
176+
177+ def _ascending_page_range (
178+ range : tuple [int , int | Literal ["last" ]],
179+ ) -> tuple [int , int | Literal ["last" ]]:
180+ start , end = range
181+ if end != "last" and int (start ) > int (end ):
182+ msg = "The start page must be less than or equal to the end page."
183+ raise ValueError (msg )
184+ return range
185+
186+
187+ _AscendingPageRangeTuple = Annotated [
188+ tuple [PageNumber , PageNumber ] | tuple [PageNumber , Literal ["last" ]],
189+ BeforeValidator (_split_page_range_tuple ),
190+ AfterValidator (_ascending_page_range ),
191+ ]
192+
193+ AscendingPageRange = PageNumber | Literal ["last" ] | _AscendingPageRangeTuple
194+
207195
208196class BasePdfRestGraphicPayload (BaseModel , Generic [ColorModelT ]):
209197 files : Annotated [
@@ -226,12 +214,12 @@ class BasePdfRestGraphicPayload(BaseModel, Generic[ColorModelT]):
226214 AfterValidator (_validate_output_prefix ),
227215 ]
228216 page_range : Annotated [
229- list [PageRangeEntry ] | None ,
217+ list [AscendingPageRange ] | None ,
230218 Field (serialization_alias = "pages" , min_length = 1 , default = None ),
231219 BeforeValidator (_ensure_list ),
232220 BeforeValidator (_split_comma_list ),
233221 BeforeValidator (_int_to_string ),
234- PlainSerializer (_serialize_as_comma_separated_string ),
222+ PlainSerializer (_serialize_page_ranges ),
235223 ]
236224 resolution : Annotated [int , Field (ge = 12 , le = 2400 , default = 300 )]
237225 color_model : Annotated [ColorModelT , Field (default = ...)]
0 commit comments