def getversioncfg(): if sys.version_info[0] >= 3: from configparser import RawConfigParser else: from ConfigParser import RawConfigParser vd0 = dict(version=FALLBACK_VERSION, commit='', date='', timestamp=0) # first fetch data from gitarchivecfgfile, ignore if it is unexpanded g = vd0.copy() cp0 = RawConfigParser(vd0) cp0.read(gitarchivecfgfile) if len(cp0.get('DEFAULT', 'commit')) > 20: g = cp0.defaults() mx = re.search(r'\btag: v(\d[^,]*)', g.pop('refnames')) if mx: g['version'] = mx.group(1) # then try to obtain version data from git. gitdir = os.path.join(MYDIR, '.git') if os.path.exists(gitdir) or 'GIT_DIR' in os.environ: try: g = gitinfo() except OSError: pass # finally, check and update the active version file cp = RawConfigParser() cp.read(versioncfgfile) d = cp.defaults() rewrite = not d or (g['commit'] and (g['version'] != d.get('version') or g['commit'] != d.get('commit'))) if rewrite: cp.set('DEFAULT', 'version', g['version']) cp.set('DEFAULT', 'commit', g['commit']) cp.set('DEFAULT', 'date', g['date']) cp.set('DEFAULT', 'timestamp', g['timestamp']) cp.write(open(versioncfgfile, 'w')) return cp
def populate_config(): config_path = os.path.expanduser('~/.pseudomyth') if os.path.exists(config_path): parser = RawConfigParser(dict_type=dict) parser.read(config_path) CONFIG.update(parser.defaults())
def getversioncfg(): if PY3: from configparser import RawConfigParser else: from ConfigParser import RawConfigParser vd0 = dict(version=FALLBACK_VERSION, commit='', date='', timestamp=0) # first fetch data from gitarchivecfgfile, ignore if it is unexpanded g = vd0.copy() cp0 = RawConfigParser(vd0) cp0.read(gitarchivecfgfile) if len(cp0.get('DEFAULT', 'commit')) > 20: g = cp0.defaults() mx = re.search(r'\btag: v(\d[^,]*)', g.pop('refnames')) if mx: g['version'] = mx.group(1) # then try to obtain version data from git. gitdir = os.path.join(MYDIR, '.git') if os.path.exists(gitdir) or 'GIT_DIR' in os.environ: try: g = gitinfo() except OSError: pass # finally, check and update the active version file cp = RawConfigParser() cp.read(versioncfgfile) d = cp.defaults() rewrite = not d or (g['commit'] and ( g['version'] != d.get('version') or g['commit'] != d.get('commit'))) if rewrite: cp.set('DEFAULT', 'version', g['version']) cp.set('DEFAULT', 'commit', g['commit']) cp.set('DEFAULT', 'date', g['date']) cp.set('DEFAULT', 'timestamp', g['timestamp']) with open(versioncfgfile, 'w') as fp: cp.write(fp) return cp
class Config(object): """ Manages Bonsai configuration environments. Configuration information is pulled from different locations. This class helps keep it organized. Configuration information comes from environment variables, the user `~./.bonsai` file, a local `./.bonsai` file, the `./.brains` file, command line arguments, and finally, parameters overridden in code. An optional `profile` key can be used to switch between different profiles stored in the `~/.bonsai` configuration file. The users active profile is selected if none is specified. Attributes: accesskey: Users access key from the web. (Example: 00000000-1111-2222-3333-000000000001) workspace_id: Users login name. url: URL of the server to connect to. (Example: "https://api.bons.ai") brain: Name of the BRAIN to use. predict: True is predicting against a BRAIN, False for training. brain_version: Version number of the brain to use for prediction. proxy: Server name and port number of proxy to connect through. (Example: "localhost:9000") Example Usage: import sys, bonsai_ai config = bonsai_ai.Config(sys.argv) print(config) if config.predict: ... """ def __init__(self, argv: List[str] = sys.argv, profile: Any = None, use_aad: bool = False, require_workspace: bool = True): """ Construct Config object with program arguments. Pass in sys.argv for command-line arguments and an optional profile name to select a specific profile. Arguments: argv: A list of argument strings. profile: The name of a profile to select. (optional) control_plane_auth: Instance will be used on control plane use_aad: Use AAD authentication """ self.accesskey = None self.workspace_id = None self.tenant_id = None self.url = None self.gateway_url = None self.use_color = True self.use_aad = use_aad self.sdk3 = False self.brain = None self.predict = False self.brain_version = 0 self._proxy = None self._retry_timeout_seconds = 300 self._network_timeout_seconds = 60 self.verbose = False self.record_file = None self.record_enabled = False self.file_paths = set() self._config_parser = RawConfigParser(allow_no_value=True) self._read_config() self.profile = profile self._parse_env() self._parse_config(_DEFAULT) self._parse_config(profile) self._parse_brains() self._parse_args(argv) # parse args works differently in 2.7 if sys.version_info >= (3, 0): self._parse_legacy(argv) self.aad_client = AADClient(self.tenant_id) self.accesskey = self.aad_client.get_access_token() def __repr__(self): """ Prints out a JSON formatted string of the Config state. """ return '{{'\ '\"profile\": \"{self.profile!r}\", ' \ '\"accesskey\": \"{self.accesskey!r}\", ' \ '\"workspace_id\": \"{self.workspace_id!r}\", ' \ '\"brain\": \"{self.brain!r}\", ' \ '\"url\": \"{self.url!r}\", ' \ '\"use_color\": \"{self.use_color!r}\", ' \ '\"predict\": \"{self.predict!r}\", ' \ '\"brain_version\": \"{self.brain_version!r}\", ' \ '\"proxy\": \"{self.proxy!r}\", ' \ '\"retry_timeout\": \"{self.retry_timeout!r}\", ' \ '\"network_timeout\": \"{self.network_timeout!r}\" ' \ '}}'.format(self=self) @property def proxy(self): # shell-local environment vars get top precedence, falling back to # OS-specific registry/configuration values if self._proxy is not None: return self._proxy proxy_dict = getproxies() proxy = proxy_dict.get(_ALL_PROXY, None) http_proxy = proxy_dict.get(_HTTP_PROXY, None) if http_proxy is not None: proxy = http_proxy if self.url is not None: uri = urlparse(self.url) if uri.scheme == 'https': https_proxy = proxy_dict.get(_HTTPS_PROXY, None) if https_proxy is not None: proxy = https_proxy return proxy @proxy.setter def proxy(self, proxy: str): uri = urlparse(proxy) uri.port self._proxy = proxy @property def record_format(self): """ The log record format, as inferred from the extension of the log filename""" if self.record_file: _, fmt = splitext(self.record_file) return fmt else: return None @property def retry_timeout(self): return self._retry_timeout_seconds @retry_timeout.setter def retry_timeout(self, value: int): if value < -1: raise ValueError( 'Retry timeout must be a positive integer, 0, or -1.') self._retry_timeout_seconds = value @property def network_timeout(self): return self._network_timeout_seconds @network_timeout.setter def network_timeout(self, value: int): if value < 1: raise ValueError('Network timeout must be a positive integer.') self._network_timeout_seconds = value def refresh_access_token(self): if self.aad_client: self.accesskey = self.aad_client.get_access_token() if not self.accesskey: raise AuthenticationError( 'Could not refresh AAD bearer token.') def _parse_env(self): ''' parse out environment variables used in hosted containers ''' self.brain = environ.get(_BONSAI_TRAIN_BRAIN, None) headless = environ.get(_BONSAI_HEADLESS, None) if headless == 'True': self.headless = True def _parse_config(self, profile: Optional[str]): ''' parse both the '~/.bonsai' and './.bonsai' config files. ''' # read the values def assign_key(key: str): if self._config_parser.has_option(section, key): if key.lower() == _USE_COLOR.lower(): self.__dict__[key] = self._config_parser.getboolean( section, key) else: self.__dict__[key] = self._config_parser.get(section, key) # get the profile section = _DEFAULT if profile is None: if self._config_parser.has_option(_DEFAULT, _PROFILE): section = self._config_parser.get(_DEFAULT, _PROFILE) self.profile = section else: section = profile assign_key(_ACCESSKEY) assign_key(_WORKSPACEID) assign_key(_TENANTID) assign_key(_SUBSCRIPTION) assign_key(_RESOURCEGROUP) assign_key(_URL) assign_key(_GATEWAYURL) assign_key(_PROXY) assign_key(_USE_COLOR) # if url is none set it to default bonsai api url if self.url is None: self.url = _DEFAULT_URL elif not urlparse(self.url).scheme: # if no url scheme is supplied, assume https self.url = 'https://{}'.format(self.url) def _parse_brains(self): ''' parse the './.brains' config file Example: {"brains": [{"default": true, "name": "test"}]} ''' data = {} try: with open(_DOT_BRAINS) as file: data = json.load(file) # parse file now for brain in data['brains']: if brain['default'] is True: self.brain = brain['name'] return # except FileNotFoundError: python3 except IOError: return def _parse_legacy(self, argv: List[str]): ''' print support for legacy CLI arguments ''' if sys.version_info >= (3, 0): optional = ArgumentParser(description="", allow_abbrev=False, add_help=False) else: optional = ArgumentParser(description="", add_help=False) optional.add_argument('--legacy', action='store_true', help='Legacy command line options') optional.add_argument('--train-brain', help=_TRAIN_BRAIN_HELP) optional.add_argument('--predict-brain', help=_PREDICT_BRAIN_HELP) optional.add_argument('--predict-version', help=_PREDICT_VERSION_HELP) optional.add_argument('--recording-file', help=_RECORDING_FILE_HELP) args, remainder = optional.parse_known_args(argv) if args.train_brain is not None: self.brain = args.train_brain self.predict = False if args.predict_version is not None: self.predict = True if args.predict_version == "latest": self.brain_version = 0 else: self.brain_version = int(args.predict_version) if remainder is not None: pass def _parse_args(self, argv: List[str]): ''' parser command line arguments ''' if sys.version_info >= (3, 0): parser = ArgumentParser(allow_abbrev=False) else: parser = ArgumentParser() parser.add_argument('--accesskey', '--access-key', help=_ACCESS_KEY_HELP) parser.add_argument('--workspace_id', help=_WORKSPACE_ID_HELP) parser.add_argument('--tenant_id', help=_TENANT_ID_HELP) parser.add_argument('--subscription', help=_SUBSCRIPTION_HELP) parser.add_argument('--resource_group', help=_RESOURCE_GROUP_HELP) parser.add_argument('--url', help=_URL_HELP) parser.add_argument('--gateway_url', help=_GATEWAY_URL_HELP) parser.add_argument('--proxy', help=_PROXY_HELP) parser.add_argument('--brain', help=_BRAIN_HELP) parser.add_argument('--predict', help=_PREDICT_HELP, nargs='?', const='latest', default=None) parser.add_argument('--aad', action='store_true', help=_AAD_HELP) parser.add_argument('--verbose', action='store_true', help=_VERBOSE_HELP) parser.add_argument('--performance', action='store_true', help=_PERFORMANCE_HELP) parser.add_argument('--log', nargs='+', help=_LOG_HELP) parser.add_argument('--record', nargs=1, default=None, help=_RECORD_HELP) parser.add_argument('--retry-timeout', type=int, help=_RETRY_TIMEOUT_HELP) parser.add_argument('--network-timeout', type=int, help=_NETWORK_TIMEOUT_HELP) parser.add_argument('--sdk3', action='store_true', help=_SDK3_HELP) args, remainder = parser.parse_known_args(argv[1:]) if args.aad: self.use_aad = args.aad if args.accesskey is not None: self.accesskey = args.accesskey if args.workspace_id is not None: self.workspace_id = args.workspace_id if args.tenant_id is not None: self.tenant_id = args.tenant_id if args.subscription is not None: self.subscription = args.subscription if args.resource_group is not None: self.resource_group = args.resource_group if args.url is not None: self.url = args.url if args.gateway_url is not None: self.gateway_url = args.url if args.proxy is not None: self.proxy = args.proxy if args.brain is not None: self.brain = args.brain if args.verbose: self.verbose = args.verbose log.set_enable_all(args.verbose) if args.performance: # logging::log().set_enabled(true); # logging::log().set_enable_all_perf(true); pass if args.log is not None: for domain in args.log: log.set_enabled(domain) if args.record: self.record_file = args.record[0] self.record_enabled = True if args.retry_timeout is not None: self.retry_timeout = args.retry_timeout if args.network_timeout is not None: self.network_timeout = args.network_timeout if args.sdk3: self.sdk3 = True if sys.version_info < (3, 6): raise RuntimeError('Use of the --sdk3 flag requires ' 'Python 3.6 or greater') brain_version = None if args.predict is not None: if args.predict == "latest": brain_version = 0 else: brain_version = args.predict self.predict = True # update brain_version after all args have been processed if brain_version is not None: brain_version = int(brain_version) if brain_version < 0: raise ValueError('BRAIN version number must be' 'positive integer or "latest".') self.brain_version = brain_version if remainder is not None: pass def _config_files(self): return [join(expanduser('~'), _DOT_BONSAI), join('.', _DOT_BONSAI)] def _read_config(self): # verify that at least one of the config files exists # as RawConfigParser ignores missing files found = False config_files = self._config_files() for path in config_files: if os.access(path, os.R_OK): found = True break if not found: # Write empty .bonsai to disk if no file is found self._write_dot_bonsai() self._config_parser.read(config_files) for path in config_files: if os.path.exists(path): self.file_paths.add(path) def _set_profile(self, section: Any): # Create section if it does not exist if not self._config_parser.has_section( section) and section != _DEFAULT: self._config_parser.add_section(section) # Set profile in class and config self.profile = section if section == _DEFAULT: self._config_parser.set(_DEFAULT, _PROFILE, 'DEFAULT') else: self._config_parser.set(_DEFAULT, _PROFILE, str(section)) def _write_dot_bonsai(self): """ Writes to .bonsai in users home directory """ config_path = join(expanduser('~'), _DOT_BONSAI) try: with open(config_path, 'w') as f: self._config_parser.write(f) except (FileNotFoundError, PermissionError): log.info( 'WARNING: Unable to write .bonsai to {}'.format(config_path)) def websocket_url(self): """ Converts api url to websocket url """ api_url = self.url or '' parsed_api_url = urlparse(api_url) if parsed_api_url.scheme == 'http': parsed_ws_url = parsed_api_url._replace(scheme='ws') elif parsed_api_url.scheme == 'https': parsed_ws_url = parsed_api_url._replace(scheme='wss') else: return None ws_url = urlunparse(parsed_ws_url) return ws_url def has_section(self, section: str): """Checks the configuration to see if section exists.""" if section == _DEFAULT: return True return self._config_parser.has_section(section) def section_list(self): """ Returns a list of sections in config """ return self._config_parser.sections() def section_items(self, section: str): """ Returns a dictionary of items in a section """ return self._config_parser.items(section) def defaults(self): """ Returns an ordered dict of items in the DEFAULT section """ return self._config_parser.defaults() def update(self, **kwargs: Any): """ Updates the configuration with the Key/value pairs in kwargs and writes to the .bonsai file in the users home directory. """ if not kwargs: return for key, value in kwargs.items(): if key.lower() == _PROFILE.lower(): self._set_profile(value) else: try: self._config_parser.set(self.profile, key, str(value)) except NoSectionError: # Create and set default profile if it does not exist in .bonsai self._set_profile(self.profile) self._config_parser.set(self.profile, key, str(value)) self._write_dot_bonsai() self._parse_config(self.profile)