@@ -84,38 +84,43 @@ def get_latest_cmdstan(cmdstan_dir: str) -> Optional[str]:
8484 """
8585 Given a valid directory path, find all installed CmdStan versions
8686 and return highest (i.e., latest) version number.
87- Assumes directory populated via `install_cmdstan`.
87+
88+ Assumes directory consists of CmdStan releases, created by
89+ function `install_cmdstan`, and therefore dirnames have format
90+ "cmdstan-<maj>.<min>.<patch>" or "cmdstan-<maj>.<min>.<patch>-rc<num>",
91+ which is CmdStan release practice as of v 2.24.
8892 """
8993 versions = [
90- '' . join ( name . split ( '-' )[ 1 :]) # name may contain '-rc'
94+ name [ 8 :]
9195 for name in os .listdir (cmdstan_dir )
9296 if os .path .isdir (os .path .join (cmdstan_dir , name ))
9397 and name .startswith ('cmdstan-' )
9498 and name [8 ].isdigit ()
99+ and len (name [8 :].split ('.' )) == 3
95100 ]
96- # munge rc for sort, e.g. 2.25.0-rc1 -> 2.25.0.-99
101+ if len (versions ) == 0 :
102+ return None
103+ # munge rc for sort, e.g. 2.25.0-rc1 -> 2.25.-99
97104 for i in range (len (versions )): # # pylint: disable=C0200
98- tmp = versions [i ]. split ( 'rc' )
99- if len ( tmp ) == 1 :
100- versions [ i ] = '.' . join ([ tmp [ 0 ], '0' ] )
101- else :
102- rc_sortable = str (int (tmp [ 1 ] ) - 100 )
103- versions [i ] = '.' .join ([tmp [0 ], rc_sortable ])
105+ if '-rc' in versions [i ]:
106+ comps = versions [ i ]. split ( '-rc' )
107+ mmp = comps [ 0 ]. split ( '.' )
108+ rc_num = comps [ 1 ]
109+ patch = str (int (rc_num ) - 100 )
110+ versions [i ] = '.' .join ([mmp [0 ], mmp [ 1 ], patch ])
104111
105112 versions .sort (key = lambda s : list (map (int , s .split ('.' ))))
106- if len (versions ) == 0 :
107- return None
108- latest = 'cmdstan-{}' .format (versions [len (versions ) - 1 ])
113+ latest = versions [len (versions ) - 1 ]
109114
110- # unmunge
111- tmp = latest .split ('.' )
112- prefix = '.' . join ( tmp [ 0 : 3 ])
113- if int ( tmp [ 3 ]) == 0 :
114- latest = prefix
115- else :
116- tmp [ 3 ] = 'rc' + str ( int ( tmp [ 3 ]) + 100 )
117- latest = '-' . join ([ prefix , tmp [ 3 ]])
118- return latest
115+ # unmunge as needed
116+ mmp = latest .split ('.' )
117+ if int ( mmp [ 2 ]) < 0 :
118+ print ( "here" )
119+ rc_num = str ( int ( mmp [ 2 ]) + 100 )
120+ mmp [ 2 ] = "0-rc" + rc_num
121+ latest = '.' . join ( mmp )
122+
123+ return 'cmdstan-' + latest
119124
120125
121126def validate_cmdstan_path (path : str ) -> None :
@@ -177,48 +182,75 @@ def cmdstan_path() -> str:
177182 return cmdstan
178183
179184
180- def cmdstan_version_at (maj : int , min : int ) -> bool :
185+ def cmdstan_version_at (major : int , minor : int ) -> bool :
181186 """
182- Check that CmdStan version is at or above Maj.min version.
183- Parses version string out of CmdStan makefile in CmdStan path dir.
187+ Check that CmdStan version is at or above Major.minor version.
188+ Parses version string out of CmdStan makefile variable CMDSTAN_VERSION.
189+
190+ If CmdStan installation is found but cannot parse version from makefile
191+ logs warning and returns False. Lenient behavoir required for CI tests,
192+ per comment here:
193+ https://github.com/stan-dev/cmdstanpy/pull/321#issuecomment-733817554
184194
185- :param maj : Major version number
186- :param min : Minor version number
195+ :param major : Major version number
196+ :param minor : Minor version number
187197
188- :return: True if version at or above, else False
198+ :return: True if version at or above major.minor , else False.
189199 """
190- # pylint:disable=bare-except
191200 try :
192- path = cmdstan_path ()
193- makefile = os .path .join (path , 'makefile' )
194- if not os .path .exists (makefile ):
195- raise ValueError (
196- 'CmdStan installation {}: missing makefile' .format (path )
197- )
198- version = None
199- with open (makefile , 'r' ) as fd :
200- contents = fd .read ()
201- start_idx = contents .find ('CMDSTAN_VERSION := ' ) + len (
202- 'CMDSTAN_VERSION := '
203- )
204- end_idx = contents .find ('\n ' , start_idx )
205- version = contents [start_idx :end_idx ]
206- if version is None :
207- raise ValueError (
208- 'Cannot parse version from makefile: {}' .format (makefile )
209- )
210- splits = version .split ('.' )
211- if len (splits ) < 2 :
212- raise ValueError (
213- 'Cannot parse version from makefile: {}' .format (makefile )
214- )
215- cur_maj = int (splits [0 ])
216- cur_min = int (splits [1 ])
201+ makefile = os .path .join (cmdstan_path (), 'makefile' )
202+ except ValueError :
203+ get_logger .warning (
204+ 'No CmdStan installation found, '
205+ 'cannot check that Cmdstan version is at or above %d.%d.' ,
206+ major ,
207+ minor ,
208+ )
209+ return False
217210
218- if cur_maj > maj or (cur_maj == maj and cur_min >= min ):
219- return True
220- except : # noqa
221- pass
211+ if not os .path .exists (makefile ):
212+ get_logger .warning (
213+ 'CmdStan installation %s missing makefile, '
214+ 'cannot check that Cmdstan version is at or above %d.%d.' ,
215+ cmdstan_path (),
216+ major ,
217+ minor ,
218+ )
219+ return False
220+
221+ with open (makefile , 'r' ) as fd :
222+ contents = fd .read ()
223+
224+ start_idx = contents .find ('CMDSTAN_VERSION := ' )
225+ if start_idx < 0 :
226+ get_logger .warmning (
227+ 'Cannot parse version from makefile: %s,'
228+ 'cannot check that Cmdstan version is at or above %d.%d.' ,
229+ makefile ,
230+ major ,
231+ minor ,
232+ )
233+ return False
234+
235+ start_idx += len ('CMDSTAN_VERSION := ' )
236+ end_idx = contents .find ('\n ' , start_idx )
237+
238+ version = contents [start_idx :end_idx ]
239+ if version is None or len (version ) < 3 or len (version .split ('.' )) < 2 :
240+ get_logger .warmning (
241+ 'Cannot parse version from makefile: %s,'
242+ 'cannot check that Cmdstan version is at or above %d.%d.' ,
243+ makefile ,
244+ major ,
245+ minor ,
246+ )
247+ return False
248+
249+ splits = version .split ('.' )
250+ cur_major = int (splits [0 ])
251+ cur_minor = int (splits [1 ])
252+ if cur_major > major or (cur_major == major and cur_minor >= minor ):
253+ return True
222254 return False
223255
224256
0 commit comments