def get_python_version(self): """ Get Python version of the prefix (unless set_config_reference() is set to pybombs). """ return { 'pybombs': sysutils.get_interpreter_version(), 'prefix': self.get('python_ver'), }[self._config_reference]
def get_python_version(self): """ Get Python version of the prefix (unless set_config_reference() is set to pybombs). """ return { 'pybombs': sysutils.get_interpreter_version(), 'prefix': self.get('python_ver'), }[self._config_reference]
def _detect_python_version(self): """ Detect the Python version used in the prefix. Note this is *not* necessarily the version of Python used to execute this script. """ if self.get('python_ver'): self.log.debug("Python version set by config file.") return # Put all the smart stuff here, were we detect the actual Python version # used in the prefix TODO else: # If there is no indication which Python version is used in the # prefix, we'll assume it's be using the same Python version as # is currently running self.log.debug("Python version derived from current interpreter.") self.set('python_ver', sysutils.get_interpreter_version())
def _detect_python_version(self): """ Detect the Python version used in the prefix. Note this is *not* necessarily the version of Python used to execute this script. """ if self.get('python_ver'): self.log.debug("Python version set by config file.") return # Put all the smart stuff here, were we detect the actual Python version # used in the prefix TODO else: # If there is no indication which Python version is used in the # prefix, we'll assume it's be using the same Python version as # is currently running self.log.debug("Python version derived from current interpreter.") self.set('python_ver', sysutils.get_interpreter_version())
class PrefixInfo(object): """ Stores information about the current prefix being used. """ prefix_conf_dir = '.pybombs' src_dir_name = 'src' env_prefix_var = 'PYBOMBS_PREFIX' env_srcdir_var = 'PYBOMBS_PREFIX_SRC' inv_file_name = 'inventory.yml' setup_env_key = 'setup_env' default_config_info = { 'prefix_aliases': {}, 'prefix_config_dir': {}, 'env': {}, 'recipes': {}, 'packages': {'gnuradio': {'forcebuild': True}}, 'categories': {'common': {'forcebuild': True}}, 'python_ver': sysutils.get_interpreter_version(), } default_env_unix = { # These envs are non-portable 'PATH': "{prefix_dir}/bin:$PATH", 'PYTHONPATH': "{python_path}:$PYTHONPATH", 'LD_LIBRARY_PATH': "{prefix_dir}/lib:{prefix_dir}/lib64/:$LD_LIBRARY_PATH", 'LIBRARY_PATH': "{prefix_dir}/lib:{prefix_dir}/lib64/:$LIBRARY_PATH", 'PKG_CONFIG_PATH': "{prefix_dir}/lib/pkgconfig:{prefix_dir}/lib64/pkgconfig:$PKG_CONFIG_PATH", 'GRC_BLOCKS_PATH': "{prefix_dir}/share/gnuradio/grc/blocks:$GRC_BLOCKS_PATH", 'PYBOMBS_PREFIX': "{prefix_dir}", } def __init__(self, args, cfg_list, select_prefix=None): self.log = pb_logging.logger.getChild("ConfigManager.PrefixInfo") self.prefix_dir = None self.prefix_cfg_dir = None self.prefix_src = None self.alias = None self.src_dir = None self.cfg_file = None self.inv_file = None self.inventory = None self.recipe_dir = None self.target_dir = None self.env = os.environ.copy() self.is_virtualenv = False self._cfg_info = OrderedDict(self.default_config_info) if select_prefix is not None: args.prefix = select_prefix # 1) Load the config info for cfg_file in reversed(cfg_list): self._cfg_info = \ self._merge_config_info_from_file(cfg_file, self._cfg_info) # 2) Find the prefix directory self._find_prefix_dir(args) if self.prefix_dir is None: self.log.debug( "Cannot establish a prefix directory. " "This may cause issues down the line.") self._set_attrs() return assert self.prefix_dir is not None if self.alias is not None and self.alias in self._cfg_info['prefix_config_dir']: self.prefix_cfg_dir = npath(self._cfg_info['prefix_config_dir'][self.alias]) self.log.debug("Choosing prefix config dir from alias: {0}" .format(self.prefix_cfg_dir)) elif self.prefix_dir in self._cfg_info['prefix_config_dir']: self.prefix_cfg_dir = npath(self._cfg_info['prefix_config_dir'][self.prefix_dir]) self.log.debug( "Choosing prefix config dir from path lookup in prefix_config_dir: {0}" .format(self.prefix_cfg_dir)) else: self.prefix_cfg_dir = npath(os.path.join(self.prefix_dir, self.prefix_conf_dir)) self.log.debug("Choosing default prefix config dir: {0}".format(self.prefix_cfg_dir)) if not os.path.isdir(self.prefix_cfg_dir): self.log.debug("Config dir does not yet exist.") self.is_virtualenv = sysutils.is_virtualenv(self.prefix_dir) if self.is_virtualenv: self.log.info("Prefix is a Python virtualenv.") # 3) Find the config file self.cfg_file = npath(os.path.join(self.prefix_cfg_dir, ConfigManager.cfg_file_name)) config_section = {} if not os.path.isfile(self.cfg_file): self.log.debug( "Prefix configuration file not found: {0}, assuming empty." .format(self.cfg_file)) else: config_section = PBConfigFile(self.cfg_file).get('config') self._cfg_info = self._merge_config_info_from_file(self.cfg_file, self._cfg_info) # 4) Find the src dir self.src_dir = npath( config_section.get('srcdir', os.path.join(self.prefix_dir, self.src_dir_name))) self.log.debug("Prefix source dir is: {0}".format(self.src_dir)) if not os.path.isdir(self.src_dir): self.log.debug("Source dir does not exist.") # 5) Find the inventory file self.inv_file = npath(os.path.join(self.prefix_cfg_dir, self.inv_file_name)) if not os.path.isfile(self.inv_file): self.log.debug("Prefix inventory file not found: {0}".format(self.inv_file)) self.inventory = inventory.Inventory(inventory_file=self.inv_file) # 6) Prefix-specific recipes. There's two places for these: # - A 'recipes/' subdirectory # - Anything declared in the config.yml file inside the prefix self.recipe_dir = npath( config_section.get('recipes', os.path.join(self.prefix_cfg_dir, 'recipes'))) if os.path.isdir(self.recipe_dir): self.log.debug("Prefix-local recipe dir is: {0}".format(self.recipe_dir)) else: self.recipe_dir = None # 7) Detect Python version and library paths self._detect_python_version() self._detect_python_path() # 8) Load environment # If there's a setup_env option in the current config file, we use that if self.setup_env_key in config_section: self.log.debug( 'Loading environment from shell script: {0}' .format(config_section[self.setup_env_key])) self.env = self._load_environ_from_script(config_section[self.setup_env_key]) else: self.env = self._load_default_env(self.env) # Set env vars that we always need self.env[self.env_prefix_var] = self.prefix_dir self.env[self.env_srcdir_var] = self.src_dir # env: sections are always respected: old_env = os.environ # Bit of an ugly hack to allow use of os.environ = self.env # os.path.expandvars() on self.env for k, v in iteritems(self._cfg_info['env']): self.env[k.upper()] = os.path.expandvars(v.strip()) os.environ = old_env # 9) Keep relevant config sections as attributes self._set_attrs() def _set_attrs(self): """ Map the _cfg_info dict onto attributes. """ for k, v in iteritems(self._cfg_info): if k == 'env' or not k in self.default_config_info.keys(): continue setattr(self, k, v) def _merge_config_info_from_file(self, cfg_file, cfg_data): """ Load a config file, load its contents, and merge it into cfg_info. Return the result. """ try: self.log.debug('Inspecting config file: {0}'.format(cfg_file)) cfg_data_new = PBConfigFile(cfg_file).get() except Exception: self.log.debug('Well, looks like that failed.') return cfg_data return dict_merge(cfg_data, cfg_data_new) def _find_prefix_dir(self, args): """ Find the current prefix' directory. Order is: 1) From the command line (-p switch; either an alias, or a directory) 2) Environment variable (see env_prefix_var) 3) CWD (if it has a .pybombs subdir and is not the home directory) 4) The config option called 'default_prefix' If all of these fail, we have no prefix. """ if args.prefix is not None: if args.prefix in self._cfg_info['prefix_aliases']: self.log.debug("Resolving prefix alias {0}.".format(args.prefix)) self.alias = args.prefix args.prefix = self._cfg_info['prefix_aliases'][args.prefix] if not os.path.isdir(npath(args.prefix)): self.log.error("Not a prefix: {0}".format(args.prefix)) raise PBException("Can't open prefix: {0}".format(args.prefix)) self.prefix_dir = npath(args.prefix) self.prefix_src = 'cli' self.log.debug("Choosing prefix dir from command line: {0}".format(self.prefix_dir)) return if self.env_prefix_var in os.environ and os.path.isdir(os.environ[self.env_prefix_var]): self.prefix_dir = npath(os.environ[self.env_prefix_var]) self.prefix_src = 'env' self.log.debug( 'Using environment variable {0} as prefix ({1})' .format(self.env_prefix_var, self.prefix_dir)) return if os.getcwd() != os.path.expanduser('~') and \ os.path.isdir(os.path.join('.', self.prefix_conf_dir)): self.prefix_dir = os.getcwd() self.prefix_src = 'cwd' self.log.debug('Using CWD as prefix ({0})'.format(self.prefix_dir)) return if self._cfg_info.get('config', {}).get('default_prefix'): default_prefix = self._cfg_info['config']['default_prefix'] if default_prefix in self._cfg_info['prefix_aliases']: self.log.debug("Resolving prefix alias `{0}'.".format(default_prefix)) self.prefix_dir = npath(self._cfg_info['prefix_aliases'][default_prefix]) else: self.prefix_dir = npath(default_prefix) self.log.debug('Using default_prefix as prefix ({0})'.format(self.prefix_dir)) self.prefix_src = 'default' return self.prefix_src = None self.prefix_dir = None def _detect_python_version(self): """ Detect the Python version used in the prefix. Note this is *not* necessarily the version of Python used to execute this script. """ if 'python_ver' in self._cfg_info: self.log.debug("Python version set by config file.") else: # Put all the smart stuff here, were we detect the actual Python version # used in the prefix TODO # If there is no indication which Python version is used in the # prefix, we'll assume it's be using the same Python version as # is currently running self.log.debug("Python version derived from current interpreter.") self._cfg_info['python_ver'] = sysutils.get_interpreter_version() self.python_ver = self._cfg_info['python_ver'] def _detect_python_path(self): """ From the Python version, derive the Python paths within the prefix. """ py_ver_major, py_ver_minor = self._cfg_info.get('python_ver').split('.')[0:2] conservative_guess = [ '{prefix}/lib/python{major}/site-packages', '{prefix}/lib/python{major}/dist-packages', '{prefix}/lib/python{major}.{minor}/site-packages', '{prefix}/lib/python{major}.{minor}/dist-packages', '{prefix}/lib64/python{major}/site-packages', '{prefix}/lib64/python{major}/dist-packages', '{prefix}/lib64/python{major}.{minor}/site-packages', '{prefix}/lib64/python{major}.{minor}/dist-packages', ] self.python_path = ':'.join([ x.format(prefix=self.prefix_dir, major=py_ver_major, minor=py_ver_minor) for x in conservative_guess ]) def _load_environ_from_script(self, setup_env_file): """ Run setup_env_file, return the new env FIXME make this portable! """ self.log.debug('Loading environment from shell script: {0}'.format(setup_env_file)) # It would be nice if we could do os.path.expandvars() with a custom # env, wouldn't it setup_env_file = setup_env_file.replace('${0}'.format(self.env_prefix_var), self.prefix_dir) setup_env_file = setup_env_file.replace('${{{0}}}'.format(self.env_prefix_var), self.prefix_dir) # TODO add some checks this is a legit script # Damn, I hate just running stuff :/ # TODO unportable command: separator = '<<<<<<<<<<<<<<<<<<<<<<>>>>>>>>>>>>>>>>>>>>>>' get_env_cmd = "source {env_file} && echo '{sep}' && env".format(env_file=setup_env_file, sep=separator) from pybombs.utils import subproc try: script_output = subproc.check_output(get_env_cmd, shell=True) except subproc.CalledProcessError: self.log.error("Trouble sourcing file {env_file}".format(env_file=setup_env_file)) raise PBException("Could not source env file.") env_output = script_output.split(separator)[-1] # TODO assumption is that env_output now just holds the env output env_output = env_output.split('\n') env = {} for env_line in env_output: env_line = env_line.strip() if len(env_line) == 0: continue k, v = env_line.split('=', 1) env[k] = v return env def _load_default_env(self, env): """ TODO: Make this portable """ for k, v in iteritems(self.default_env_unix): env[k] = os.path.expandvars(v.strip().format( prefix_dir=self.prefix_dir, python_path=self.python_path, )) return env def get_prefix_cfg_dir_writable(self): " Returns self.prefix_cfg_dir, but if that doesn't exist, create it first. " if self.prefix_dir is None: raise PBException("Can't access prefix config dir if prefix does not exist.") sysutils.mkdir_writable(self.prefix_cfg_dir, self.log) return self.prefix_cfg_dir