@@ -518,6 +518,62 @@ def normalize_pep440(version):
518518 )
519519 return parser .context ()["norm" ]
520520
521+ def version (version_str , strict = False ):
522+ """Parse a PEP4408 compliant version
523+
524+ See https://packaging.python.org/en/latest/specifications/binary-distribution-format/#escaping-and-unicode
525+ and https://peps.python.org/pep-0440/
526+
527+ Args:
528+ version_str: version string to be normalized according to PEP 440.
529+ strict: fail if the version is invalid.
530+
531+ Returns:
532+ string containing the normalized version.
533+ """
534+
535+ parser = _new (version_str .strip (" " if strict else " .*" )) # PEP 440: Leading and Trailing Whitespace and .*
536+
537+ accept (parser , _is ("v" ), "" ) # PEP 440: Preceding v character
538+
539+ parts = {}
540+ fns = [
541+ ("epoch" , accept_epoch ),
542+ ("release" , accept_release ),
543+ ("pre" , accept_prerelease ),
544+ ("post" , accept_postrelease ),
545+ ("dev" , accept_devrelease ),
546+ ("local" , accept_local ),
547+ ]
548+
549+ for p , fn in fns :
550+ start = len (parser .context ()["norm" ])
551+ fn (parser )
552+ parts [p ] = parser .context ()["norm" ][start :]
553+
554+ parts ["norm" ] = parser .context ()["norm" ]
555+
556+ is_prefix = version_str .endswith (".*" )
557+ parts ["is_prefix" ] = is_prefix
558+ if is_prefix and (parts ["local" ] or parts ["post" ] or parts ["dev" ] or parts ["pre" ]):
559+ if strict :
560+ fail ("local version part has been obtained, but only public segments can have prefix matches" )
561+
562+ # https://peps.python.org/pep-0440/#public-version-identifiers
563+ return None
564+
565+ if parser .input [parser .context ()["start" ]:]:
566+ if strict :
567+ fail (
568+ "Failed to parse PEP 440 version identifier '%s'." % parser .input ,
569+ "Parse error at '%s'" % parser .input [parser .context ()["start" ]:],
570+ )
571+
572+ # If we fail to parse the version return None
573+ return None
574+
575+ return _new_version (** parts )
576+
521577def _pad_zeros (release , n ):
522578 padding = n - len (release )
523579 if padding <= 0 :
@@ -752,59 +808,3 @@ def _new_version(*, epoch = 0, release, pre = "", post = "", dev = "", local = "
752808 )
753809
754810 return self
755-
756- def version (version_str , strict = False ):
757- """Parse a PEP4408 compliant version
758-
759- See https://packaging.python.org/en/latest/specifications/binary-distribution-format/#escaping-and-unicode
760- and https://peps.python.org/pep-0440/
761-
762- Args:
763- version_str: version string to be normalized according to PEP 440.
764- strict: fail if the version is invalid.
765-
766- Returns:
767- string containing the normalized version.
768- """
769-
770- parser = _new (version_str .strip (" " if strict else " .*" )) # PEP 440: Leading and Trailing Whitespace and .*
771-
772- accept (parser , _is ("v" ), "" ) # PEP 440: Preceding v character
773-
774- parts = {}
775- fns = [
776- ("epoch" , accept_epoch ),
777- ("release" , accept_release ),
778- ("pre" , accept_prerelease ),
779- ("post" , accept_postrelease ),
780- ("dev" , accept_devrelease ),
781- ("local" , accept_local ),
782- ]
783-
784- for p , fn in fns :
785- start = len (parser .context ()["norm" ])
786- fn (parser )
787- parts [p ] = parser .context ()["norm" ][start :]
788-
789- parts ["norm" ] = parser .context ()["norm" ]
790-
791- is_prefix = version_str .endswith (".*" )
792- parts ["is_prefix" ] = is_prefix
793- if is_prefix and (parts ["local" ] or parts ["post" ] or parts ["dev" ] or parts ["pre" ]):
794- if strict :
795- fail ("local version part has been obtained, but only public segments can have prefix matches" )
796-
797- # https://peps.python.org/pep-0440/#public-version-identifiers
798- return None
799-
800- if parser .input [parser .context ()["start" ]:]:
801- if strict :
802- fail (
803- "Failed to parse PEP 440 version identifier '%s'." % parser .input ,
804- "Parse error at '%s'" % parser .input [parser .context ()["start" ]:],
805- )
806-
807- # If we fail to parse the version return None
808- return None
809-
810- return _new_version (** parts )
0 commit comments