55# pylint: disable=C0103,C0114,C0115,C0116,C0301
66# pylint: disable=R0902,R0904,R0912,R0915,R1705,R1710,E1121
77
8+ # Note: this script requires at least Python 3.6 to run.
9+ # Don't add changes not compatible with it, it is meant to report
10+ # incompatible python versions.
811
912import argparse
1013import os
1619
1720def parse_version (version ):
1821 """Convert a major.minor.patch version into a tuple"""
19- #
2022 return tuple (int (x ) for x in version .split ("." ))
2123
2224
@@ -27,6 +29,7 @@ def ver_str(version):
2729
2830
2931RECOMMENDED_VERSION = parse_version ("3.4.3" )
32+ MIN_PYTHON_VERSION = parse_version ("3.7" )
3033
3134
3235class SphinxDependencyChecker :
@@ -132,6 +135,65 @@ def find_python_no_venv():
132135 # Python not found at the PATH
133136 return python_names [- 1 ]
134137
138+ @staticmethod
139+ def get_python_version (cmd ):
140+
141+ result = SphinxDependencyChecker .run ([cmd , "--version" ],
142+ capture_output = True , text = True )
143+ version = result .stdout .strip ()
144+
145+ match = re .search (r"(\d+\.\d+\.\d+)" , version )
146+ if match :
147+ return parse_version (match .group (1 ))
148+
149+ print (f"Can't parse version { version } " )
150+ return (0 , 0 , 0 )
151+
152+ @staticmethod
153+ def find_python ():
154+
155+ patterns = [
156+ "python3.[0-9]" ,
157+ "python3.[0-9][0-9]" ,
158+ ]
159+
160+ new_python_cmd = None
161+
162+ # Seek for a python binary newer than MIN_PYTHON_VERSION
163+ for path in os .getenv ("PATH" , "" ).split (":" ):
164+ for pattern in patterns :
165+ for cmd in glob (os .path .join (path , pattern )):
166+ if os .path .isfile (cmd ) and os .access (cmd , os .X_OK ):
167+ version = SphinxDependencyChecker .get_python_version (cmd )
168+ if version >= MIN_PYTHON_VERSION :
169+ return (cmd )
170+
171+ @staticmethod
172+ def check_python ():
173+
174+ cur_ver = sys .version_info [:3 ]
175+ if cur_ver >= MIN_PYTHON_VERSION :
176+ return
177+
178+ python_ver = ver_str (cur_ver )
179+
180+ new_python_cmd = SphinxDependencyChecker .find_python ()
181+ if not new_python_cmd :
182+ print (f"ERROR: Python version { python_ver } is not spported anymore" )
183+ print (f" Can't find a new version. This script may fail" )
184+ return
185+
186+ # Restart script using the newer version
187+ script_path = os .path .abspath (sys .argv [0 ])
188+ args = [new_python_cmd , script_path ] + sys .argv [1 :]
189+
190+ print (f"Python { python_ver } not supported. Changing to { new_python_cmd } " )
191+
192+ try :
193+ os .execv (new_python_cmd , args )
194+ except OSError as e :
195+ sys .exit (f"Failed to restart with { new_python_cmd } : { e } " )
196+
135197 @staticmethod
136198 def run (* args , ** kwargs ):
137199 """Excecute a command, hiding its output by default"""
@@ -1107,6 +1169,7 @@ def main():
11071169
11081170 checker = SphinxDependencyChecker (args )
11091171
1172+ checker .check_python ()
11101173 checker .check_needs ()
11111174
11121175
0 commit comments