11"""
2- _version.py v1.5
2+ _version.py v1.6
33
44Simple version string management, using a hard-coded version string
55for simplicity and compatibility, while adding git info at runtime.
3838
3939def get_version () -> str :
4040 """Get the version string."""
41- if repo_dir :
42- return get_extended_version ()
43- return __version__
41+ try :
42+ if repo_dir :
43+ release , post , tag , dirty = get_version_info_from_git ()
44+ result = get_extended_version (release , post , tag , dirty )
4445
46+ # Warn if release does not match base_version.
47+ # Can happen between bumping and tagging. And also when merging a
48+ # version bump into a working branch, because we use --first-parent.
49+ if release and release != base_version :
50+ release2 , _post , _tag , _dirty = get_version_info_from_git (
51+ first_parent = False
52+ )
53+ if release2 != base_version :
54+ warning (
55+ f"{ project_name } version from git ({ release } )"
56+ f" and __version__ ({ base_version } ) don't match."
57+ )
4558
46- def get_extended_version () -> str :
47- """Get an extended version string with information from git."""
48- release , post , labels = get_version_info_from_git ()
59+ return result
60+
61+ except Exception as err :
62+ # Failsafe.
63+ warning (f"Error getting refined version: { err } " )
4964
50- # Start version string (__version__ string is leading)
65+ return base_version
66+
67+
68+ def get_extended_version (release : str , post : str , tag : str , dirty : str ) -> str :
69+ """Get an extended version string with information from git."""
70+ # Start version string (__version__ string is leading).
5171 version = base_version
52- tag_prefix = "#"
72+ labels = []
5373
5474 if release and release != base_version :
55- # Can happen between bumping and tagging. And also when merging a
56- # version bump into a working branch, because we use --first-parent.
57- release2 , _post , _labels = get_version_info_from_git (first_parent = False )
58- if release2 != base_version :
59- warning (
60- f"{ project_name } version from git ({ release } )"
61- f" and __version__ ({ base_version } ) don't match."
62- )
63- version += "+from_tag_" + release .replace ("." , "_" )
64- tag_prefix = "."
65-
66- # Add git info
67- if post and post != "0" :
75+ pre_label = "from_tag_" + release .replace ("." , "_" )
76+ labels = [pre_label , f"post{ post } " , tag , dirty ]
77+ elif post and post != "0" :
6878 version += f".post{ post } "
69- if labels :
70- version += tag_prefix + "." .join (labels )
71- elif labels and labels [- 1 ] == "dirty" :
72- version += tag_prefix + "." .join (labels )
73-
79+ labels = [tag , dirty ]
80+ elif dirty :
81+ labels = [tag , dirty ]
82+ else :
83+ # If not post and not dirty, show 'clean' version without git tag.
84+ pass
85+
86+ # Compose final version (remove empty labels, e.g. when not dirty).
87+ # Everything after the '+' is not sortable (does not get in version_info).
88+ label_str = "." .join (label for label in labels if label )
89+ if label_str :
90+ version += "+" + label_str
7491 return version
7592
7693
77- def get_version_info_from_git (* , first_parent : bool = True ) -> str :
94+ def get_version_info_from_git (
95+ * , first_parent : bool = True
96+ ) -> tuple [str , str , str , str ]:
7897 """
79- Get (release, post, labels ) from Git.
98+ Get (release, post, tag, dirty ) from Git.
8099
81100 With `release` the version number from the latest tag, `post` the
82- number of commits since that tag, and `labels ` a tuple with the
83- git-hash and optionally a dirty flag .
101+ number of commits since that tag, `tag` the git hash, and `dirty ` a string
102+ that is either empty or says 'dirty' .
84103 """
85- # Call out to Git
104+ # Call out to Git.
86105 command = ["git" , "describe" , "--long" , "--always" , "--tags" , "--dirty" ]
87106 if first_parent :
88107 command .append ("--first-parent" )
@@ -92,9 +111,9 @@ def get_version_info_from_git(*, first_parent: bool = True) -> str:
92111 warning (f"Could not get { project_name } version: { e } " )
93112 p = None
94113
95- # Parse the result into parts
114+ # Parse the result into parts.
96115 if p is None :
97- parts = (None , None , "unknown" )
116+ parts = ("" , "" , "unknown" )
98117 else :
99118 output = p .stdout .decode (errors = "ignore" )
100119 if p .returncode :
@@ -105,40 +124,50 @@ def get_version_info_from_git(*, first_parent: bool = True) -> str:
105124 + "\n \n stderr: "
106125 + stderr
107126 )
108- parts = (None , None , "unknown" )
127+ parts = ("" , "" , "unknown" )
109128 else :
110129 parts = output .strip ().lstrip ("v" ).split ("-" )
111130 if len (parts ) <= 2 :
112131 # No tags (and thus no post). Only git hash and maybe 'dirty'.
113- parts = (None , None , * parts )
132+ parts = ("" , "" , * parts )
114133
115- # Return unpacked parts
116- release , post , * labels = parts
117- return release , post , labels
134+ # Return unpacked parts.
135+ release = parts [0 ]
136+ post = parts [1 ]
137+ tag = parts [2 ]
138+ dirty = "dirty" if len (parts ) > 3 else ""
139+ return release , post , tag , dirty
118140
119141
120142def version_to_tuple (v : str ) -> tuple :
121143 parts = []
122- for part in v .split ("." ):
123- p , _ , h = part .partition ("#" )
124- if p :
125- parts .append (p )
126- if h :
127- parts .append ("#" + h )
128- return tuple (int (i ) if i .isnumeric () else i for i in parts )
144+ for part in v .split ("+" , maxsplit = 1 )[0 ].split ("." ):
145+ if not part :
146+ pass
147+ elif part .startswith ("post" ):
148+ try :
149+ parts .extend (["post" , int (part [4 :])])
150+ except ValueError :
151+ parts .append (part )
152+ else :
153+ try :
154+ parts .append (int (part ))
155+ except ValueError :
156+ parts .append (part )
157+ return tuple (parts )
129158
130159
131160def warning (m : str ) -> None :
132161 logger .warning (m )
133162
134163
135- # Apply the versioning
164+ # Apply the versioning.
136165base_version = __version__
137166__version__ = get_version ()
138167version_info = version_to_tuple (__version__ )
139168
140169
141- # The CLI part
170+ # The CLI part.
142171
143172CLI_USAGE = """
144173_version.py
0 commit comments