Source code for gdxpds.tools

import logging
import os
import subprocess as subp
import re

logger = logging.getLogger(__name__)


[docs]class Error(Exception): """ Base class for all Exceptions raised by this package. """
[docs]class GamsDirFinder(object): """ Class for finding and accessing the system's GAMS directory. The find function first looks for the 'GAMS_DIR' environment variable. If that is unsuccessful, it next uses 'which gams' for POSIX systems, and the default install location, 'C:/GAMS', for Windows systems. In the latter case it prefers the largest version number. You can always specify the GAMS directory directly, and this class will attempt to clean up your input. (Even on Windows, the GAMS path must use '/' rather than '\'.) """ gams_dir_cache = None def __init__(self,gams_dir=None): self.gams_dir = gams_dir @property def gams_dir(self): """The GAMS directory on this system.""" if self.__gams_dir is None: raise RuntimeError("Unable to locate your GAMS directory.") return self.__gams_dir @gams_dir.setter def gams_dir(self, value): self.__gams_dir = None if isinstance(value, str): self.__gams_dir = self.__clean_gams_dir(value) elif value is not None: logger.warning(f"Unexpected gams_dir type {type(value)}. Ignoring " f"input {value!r} because it is not a str.") if self.__gams_dir is None: self.__gams_dir = self.__find_gams() def __clean_gams_dir(self,value): """ Cleans up the path string. """ if value is None: return None assert(isinstance(value, str)) ret = os.path.realpath(value) if not os.path.exists(ret): return None ret = re.sub('\\\\','/',ret) return ret def __find_gams(self): """ For all systems, the first place we examine is the GAMS_DIR environment variable, and the second is GAMSDIR. For Windows, the next step is to try 'where gams'. Then we look in the default install location (C:/GAMS), preferring win64 to win32 and the most recent version. For all others, the next step is 'which gams'. Returns ------- str or None If not None, the return value is the found gams_dir """ # check for environment variable ret = os.environ.get('GAMS_DIR') ret = self.__clean_gams_dir(ret) if ret is None: ret = os.environ.get('GAMSDIR') ret = self.__clean_gams_dir(ret) if ret is None and os.name == 'nt': # windows systems try: ret = os.path.dirname(subp.check_output(['where', 'gams']).decode().split("\n")[0]) except: ret = None ret = self.__clean_gams_dir(ret) if ret is None and os.name == 'nt': # search in default installation location cur_dir = r'C:\GAMS' if os.path.exists(cur_dir): # level 1 - prefer win64 to win32 for _p, dirs, _files in os.walk(cur_dir): if 'win64' in dirs: cur_dir = os.path.join(cur_dir, 'win64') elif len(dirs) > 0: cur_dir = os.path.join(cur_dir, dirs[0]) else: return ret break if os.path.exists(cur_dir): # level 2 - prefer biggest number (most recent version) for _p, dirs, _files in os.walk(cur_dir): if len(dirs) > 1: try: versions = [float(x) for x in dirs] ret = os.path.join(cur_dir, "{}".format(max(versions))) except: ret = os.path.join(cur_dir, dirs[0]) elif len(dirs) > 0: ret = os.path.join(cur_dir, dirs[0]) break ret = self.__clean_gams_dir(ret) if ret is None and os.name != 'nt': # posix systems try: ret = os.path.dirname(subp.check_output(['which', 'gams'])).decode() except: ret = None ret = self.__clean_gams_dir(ret) if ret is not None: GamsDirFinder.gams_dir_cache = ret if ret is None: logger.debug(f"Did not find GAMS directory. Using cached value {self.gams_dir_cache}.") ret = GamsDirFinder.gams_dir_cache return ret
[docs]class NeedsGamsDir(object): """Mix-in class that asserts that a GAMS directory is needed and provides the methods required to find and access it.""" def __init__(self,gams_dir=None): self.gams_dir = gams_dir @property def gams_dir(self): """ The GAMS directory whose value has either been directly set or has been found using the GamsDirFinder class. Returns ------- str """ return self.__gams_dir @gams_dir.setter def gams_dir(self, value): self.__gams_dir = GamsDirFinder(value).gams_dir