@@ -181,6 +181,68 @@ def __ne__(self, other: object) -> bool:
181181_LOCAL_PATTERN = re .compile (r"[a-z0-9]+(?:[._-][a-z0-9]+)*" , re .IGNORECASE )
182182
183183
184+ def _validate_epoch (value : object , / ) -> int :
185+ epoch = value or 0
186+ if isinstance (epoch , int ) and epoch >= 0 :
187+ return epoch
188+ msg = f"epoch must be non-negative integer, got { epoch } "
189+ raise InvalidVersion (msg )
190+
191+
192+ def _validate_release (value : object , / ) -> tuple [int , ...]:
193+ release = (0 ,) if value is None else value
194+ if (
195+ isinstance (release , tuple )
196+ and len (release ) > 0
197+ and all (isinstance (i , int ) and i >= 0 for i in release )
198+ ):
199+ return release
200+ msg = f"release must be a non-empty tuple of non-negative integers, got { release } "
201+ raise InvalidVersion (msg )
202+
203+
204+ def _validate_pre (value : object , / ) -> tuple [Literal ["a" , "b" , "rc" ], int ] | None :
205+ if value is None :
206+ return value
207+ if (
208+ isinstance (value , tuple )
209+ and len (value ) == 2
210+ and value [0 ] in ("a" , "b" , "rc" )
211+ and isinstance (value [1 ], int )
212+ and value [1 ] >= 0
213+ ):
214+ return value
215+ msg = f"pre must be a tuple of ('a'|'b'|'rc', non-negative int), got { value } "
216+ raise InvalidVersion (msg )
217+
218+
219+ def _validate_post (value : object , / ) -> tuple [Literal ["post" ], int ] | None :
220+ if value is None :
221+ return value
222+ if isinstance (value , int ) and value >= 0 :
223+ return ("post" , value )
224+ msg = f"post must be non-negative integer, got { value } "
225+ raise InvalidVersion (msg )
226+
227+
228+ def _validate_dev (value : object , / ) -> tuple [Literal ["dev" ], int ] | None :
229+ if value is None :
230+ return value
231+ if isinstance (value , int ) and value >= 0 :
232+ return ("dev" , value )
233+ msg = f"dev must be non-negative integer, got { value } "
234+ raise InvalidVersion (msg )
235+
236+
237+ def _validate_local (value : object , / ) -> LocalType | None :
238+ if value is None :
239+ return value
240+ if isinstance (value , str ) and _LOCAL_PATTERN .fullmatch (value ):
241+ return _parse_local_version (value )
242+ msg = f"local must be a valid version string, got { value !r} "
243+ raise InvalidVersion (msg )
244+
245+
184246class Version (_BaseVersion ):
185247 """This class abstracts handling of a project's versions.
186248
@@ -245,91 +307,35 @@ def __init__(self, version: str) -> None:
245307 self ._key_cache = None
246308
247309 def __replace__ (self , ** kwargs : Unpack [_VersionReplace ]) -> Self :
310+ epoch = _validate_epoch (kwargs ["epoch" ]) if "epoch" in kwargs else self ._epoch
311+ release = (
312+ _validate_release (kwargs ["release" ])
313+ if "release" in kwargs
314+ else self ._release
315+ )
316+ pre = _validate_pre (kwargs ["pre" ]) if "pre" in kwargs else self ._pre
317+ post = _validate_post (kwargs ["post" ]) if "post" in kwargs else self ._post
318+ dev = _validate_dev (kwargs ["dev" ]) if "dev" in kwargs else self ._dev
319+ local = _validate_local (kwargs ["local" ]) if "local" in kwargs else self ._local
320+
321+ if (
322+ epoch == self ._epoch
323+ and release == self ._release
324+ and pre == self ._pre
325+ and post == self ._post
326+ and dev == self ._dev
327+ and local == self ._local
328+ ):
329+ return self
330+
248331 new_version = self .__class__ .__new__ (self .__class__ )
249332 new_version ._key_cache = None
250- if "epoch" in kwargs :
251- epoch = kwargs ["epoch" ] or 0
252- if isinstance (epoch , int ) and epoch >= 0 : # type: ignore[redundant-expr]
253- new_version ._epoch = epoch
254- else :
255- msg = f"epoch must be non-negative integer, got { epoch } "
256- raise InvalidVersion (msg )
257- else :
258- new_version ._epoch = self ._epoch
259-
260- if "release" in kwargs :
261- release = (0 ,) if kwargs ["release" ] is None else kwargs ["release" ]
262- if (
263- isinstance (release , tuple ) # type: ignore[redundant-expr]
264- and len (release ) > 0
265- and all (isinstance (i , int ) and i >= 0 for i in release ) # type: ignore[redundant-expr]
266- ):
267- new_version ._release = release
268- else :
269- msg = (
270- "release must be a non-empty tuple of non-negative integers,"
271- f" got { release } "
272- )
273- raise InvalidVersion (msg )
274- else :
275- new_version ._release = self ._release
276-
277- if "pre" in kwargs :
278- pre = kwargs ["pre" ]
279- if pre is None or (
280- (
281- isinstance (pre , tuple ) # type: ignore[redundant-expr]
282- and len (pre ) == 2 # type: ignore[redundant-expr]
283- and pre [0 ] in ("a" , "b" , "rc" )
284- and isinstance (pre [1 ], int )
285- )
286- and pre [1 ] >= 0
287- ):
288- new_version ._pre = pre
289- else :
290- msg = (
291- "pre must be a tuple of ('a'|'b'|'rc', non-negative int),"
292- f" got { pre } "
293- )
294- raise InvalidVersion (msg )
295- else :
296- new_version ._pre = self ._pre
297-
298- if "post" in kwargs :
299- post = kwargs ["post" ]
300- if post is None :
301- new_version ._post = None
302- elif isinstance (post , int ) and post >= 0 : # type: ignore[redundant-expr]
303- new_version ._post = ("post" , post )
304- else :
305- msg = f"post must be non-negative integer, got { post } "
306- raise InvalidVersion (msg )
307- else :
308- new_version ._post = self ._post
309-
310- if "dev" in kwargs :
311- dev = kwargs ["dev" ]
312- if dev is None :
313- new_version ._dev = None
314- elif isinstance (dev , int ) and dev >= 0 : # type: ignore[redundant-expr]
315- new_version ._dev = ("dev" , dev )
316- else :
317- msg = f"dev must be non-negative integer, got { dev } "
318- raise InvalidVersion (msg )
319- else :
320- new_version ._dev = self ._dev
321-
322- if "local" in kwargs :
323- local = kwargs ["local" ]
324- if local is None :
325- new_version ._local = None
326- elif isinstance (local , str ) and _LOCAL_PATTERN .fullmatch (local ): # type: ignore[redundant-expr]
327- new_version ._local = _parse_local_version (local )
328- else :
329- msg = f"local must be a valid version string, got { local !r} "
330- raise InvalidVersion (msg )
331- else :
332- new_version ._local = self ._local
333+ new_version ._epoch = epoch
334+ new_version ._release = release
335+ new_version ._pre = pre
336+ new_version ._post = post
337+ new_version ._dev = dev
338+ new_version ._local = local
333339
334340 return new_version
335341
0 commit comments