Skip to content

Commit 1bdb8de

Browse files
committed
logic fix for cmdstan_version_at, get_latest_cmdstan
1 parent 4b5e308 commit 1bdb8de

1 file changed

Lines changed: 89 additions & 57 deletions

File tree

cmdstanpy/utils.py

Lines changed: 89 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -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

121126
def 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

Comments
 (0)