class HTTPProvider(object): """Data provider communicating with API server.""" def __init__(self, config, verbose=10, blocking_auth=True): # TODO: implement connection self.url = config.get('serverUrl') self.verbose = verbose self.logger = logging.getLogger('HTTPProvider') self.logger.setLevel(self.verbose) self.auth = None self.app = pyrebase.initialize_app(config) guest = config.get('guest') if not guest and 'serviceAccount' not in config.keys(): self.auth = FirebaseAuth(self.app, config.get("use_email_auth"), config.get("email"), config.get("password"), blocking_auth) def add_experiment(self, experiment): headers = self._get_headers() request = requests.post(self.url + '/api/add_experiment', headers=headers, data=json.dumps( {"experiment": experiment.__dict__})) self._raise_detailed_error(request) artifacts = request.json()['artifacts'] self._update_artifacts(experiment, artifacts) def _update_artifacts(self, experiment, artifacts): for tag, art in experiment.artifacts.iteritems(): art['key'] = artifacts[tag]['key'] art['qualified'] = artifacts[tag]['qualified'] art['bucket'] = artifacts[tag]['bucket'] HTTPArtifactStore(artifacts[tag]['url'], artifacts[tag]['timestamp'], self.verbose) \ .put_artifact(art) def delete_experiment(self, experiment): if isinstance(experiment, basestring): key = experiment else: key = experiment.key headers = self._get_headers() request = requests.post(self.url + '/api/delete_experiment', headers=headers, data=json.dumps({"key": key})) self._raise_detailed_error(request) def get_experiment(self, experiment, getinfo='True'): if isinstance(experiment, basestring): key = experiment else: key = experiment.key headers = self._get_headers() request = requests.post(self.url + '/api/get_experiment', headers=headers, data=json.dumps({"key": key})) self._raise_detailed_error(request) return model.experiment_from_dict(request.json()['experiment']) def start_experiment(self, experiment): self.checkpoint_experiment(experiment) if isinstance(experiment, basestring): key = experiment else: key = experiment.key headers = self._get_headers() request = requests.post(self.url + '/api/start_experiment', headers=headers, data=json.dumps({"key": key})) self._raise_detailed_error(request) def stop_experiment(self, experiment): key = experiment.key headers = self._get_headers() request = requests.post(self.url + '/api/stop_experiment', headers=headers, data=json.dumps({"key": key})) self._raise_detailed_error(request) def finish_experiment(self, experiment): self.checkpoint_experiment(experiment) if isinstance(experiment, basestring): key = experiment else: key = experiment.key headers = self._get_headers() request = requests.post(self.url + '/api/finish_experiment', headers=headers, data=json.dumps({"key": key})) self._raise_detailed_error(request) def get_user_experiments(self, user=None, blocking=True): headers = self._get_headers() user = user if user else self._get_userid() response = requests.post(self.url + '/api/get_user_experiments', headers=headers, data=json.dumps({"user": user})) self._raise_detailed_error(response) data = response.json()['experiments'] experiments = data return experiments def get_projects(self): headers = self._get_headers() response = requests.post(self.url + '/api/get_projects', headers=headers) self._raise_detailed_error(response) projects = response.json()['projects'] return projects def get_project_experiments(self, project): headers = self._get_headers() response = requests.post(self.url + '/api/get_project_experiments', headers=headers, data=json.dumps({"project": project})) self._raise_detailed_error(response) data = response.json()['experiments'] experiments = [model.experiment_from_dict(edict) for edict in data] return experiments def get_artifacts(self): raise NotImplementedError() def get_artifact(self, artifact, only_newer='True'): return HTTPArtifactStore(artifact['url'], self.verbose) \ .get_artifact(artifact) def get_users(self): headers = self._get_headers() response = requests.post(self.url + '/api/get_users', headers=headers) self._raise_detailed_error(response) users = response.json()['users'] return users def checkpoint_experiment(self, experiment): if isinstance(experiment, basestring): key = experiment experiment = self.get_experiment(key) else: key = experiment.key headers = self._get_headers() request = requests.post(self.url + '/api/checkpoint_experiment', headers=headers, data=json.dumps({"key": key})) self._raise_detailed_error(request) artifacts = request.json()['artifacts'] self._update_artifacts(experiment, artifacts) def refresh_auth_token(self, email, refresh_token): if self.auth: self.auth.refresh_token(email, refresh_token) def _get_headers(self): headers = {"content-type": "application/json"} if self.auth: headers["Authorization"] = "Firebase " + self.auth.get_token() return headers def _get_userid(self): userid = None if self.auth: userid = self.auth.get_user_id() userid = userid if userid else 'guest' return userid def _raise_detailed_error(self, request): if request.status_code != 200: raise ValueError(request.message) data = request.json() if data['status'] == 'ok': return raise ValueError(data['status']) def __enter__(self): return self def __exit__(self, *args): pass
class FirebaseProvider(object): """Data provider for Firebase.""" def __init__(self, db_config, blocking_auth=True, verbose=10, store=None): guest = db_config.get('guest') self.app = pyrebase.initialize_app(db_config) self.logger = logging.getLogger('FirebaseProvider') self.logger.setLevel(verbose) self.auth = None if not guest and 'serviceAccount' not in db_config.keys(): self.auth = FirebaseAuth(self.app, db_config.get("use_email_auth"), db_config.get("email"), db_config.get("password"), blocking_auth) self.store = store if store else FirebaseArtifactStore( db_config, verbose=verbose, blocking_auth=blocking_auth) self._experiment_info_cache = {} self._experiment_cache = {} iothreads = 10 if ThreadPool: self.pool = ThreadPool(iothreads) else: self.pool = None if self.auth and not self.auth.expired: self.__setitem__(self._get_user_keybase() + "email", self.auth.get_user_email()) def __getitem__(self, key): try: splitKey = key.split('/') key_path = '/'.join(splitKey[:-1]) key_name = splitKey[-1] dbobj = self.app.database().child(key_path).child(key_name) return dbobj.get(self.auth.get_token()).val() if self.auth \ else dbobj.get().val() except Exception as err: self.logger.warn(("Getting key {} from a database " + "raised an exception: {}").format(key, err)) return None def __setitem__(self, key, value): try: splitKey = key.split('/') key_path = '/'.join(splitKey[:-1]) key_name = splitKey[-1] dbobj = self.app.database().child(key_path) if self.auth: dbobj.update({key_name: value}, self.auth.get_token()) else: dbobj.update({key_name: value}) except Exception as err: self.logger.warn( ("Putting key {}, value {} into a database " + "raised an exception: {}").format(key, value, err)) def _delete(self, key): dbobj = self.app.database().child(key) if self.auth: dbobj.remove(self.auth.get_token()) else: dbobj.remove() def _get_userid(self): userid = None if self.auth: userid = self.auth.get_user_id() userid = userid if userid else 'guest' return userid def _get_user_keybase(self, userid=None): if userid is None: userid = self._get_userid() return "users/" + userid + "/" def _get_experiments_keybase(self, userid=None): return "experiments/" def _get_projects_keybase(self): return "projects/" def add_experiment(self, experiment): self._delete(self._get_experiments_keybase() + experiment.key) experiment.time_added = time.time() experiment.status = 'waiting' if 'local' in experiment.artifacts['workspace'].keys() and \ os.path.exists(experiment.artifacts['workspace']['local']): experiment.git = git_util.get_git_info( experiment.artifacts['workspace']['local']) for tag, art in experiment.artifacts.iteritems(): if art['mutable']: art['key'] = self._get_experiments_keybase() + \ experiment.key + '/' + tag + '.tgz' else: if 'local' in art.keys(): # upload immutable artifacts art['key'] = self.store.put_artifact(art) if 'key' in art.keys(): art['qualified'] = self.store.get_qualified_location( art['key']) art['bucket'] = self.store.get_bucket() experiment_dict = experiment.__dict__.copy() experiment_dict['owner'] = self._get_userid() self.__setitem__(self._get_experiments_keybase() + experiment.key, experiment_dict) self.__setitem__( self._get_user_keybase() + "experiments/" + experiment.key, experiment.key) if experiment.project and self.auth: self.__setitem__( self._get_projects_keybase() + experiment.project + "/" + experiment.key + "/owner", self.auth.get_user_id()) self.checkpoint_experiment(experiment, blocking=True) self.logger.info("Added experiment " + experiment.key) def start_experiment(self, experiment): experiment.time_started = time.time() experiment.status = 'running' self.__setitem__( self._get_experiments_keybase() + experiment.key + "/status", "running") self.__setitem__( self._get_experiments_keybase() + experiment.key + "/time_started", experiment.time_started) self.checkpoint_experiment(experiment) def stop_experiment(self, key): # can be called remotely (the assumption is # that remote worker checks experiments status periodically, # and if it is 'stopped', kills the experiment. if isinstance(key, Experiment): key = key.key self.__setitem__(self._get_experiments_keybase() + key + "/status", "stopped") def finish_experiment(self, experiment): time_finished = time.time() if isinstance(experiment, basestring): key = experiment else: key = experiment.key self.checkpoint_experiment(experiment, blocking=True) experiment.status = 'finished' experiment.time_finished = time_finished self.__setitem__(self._get_experiments_keybase() + key + "/status", "finished") self.__setitem__( self._get_experiments_keybase() + key + "/time_finished", time_finished) def delete_experiment(self, experiment): if isinstance(experiment, basestring): experiment_key = experiment try: experiment = self.get_experiment(experiment) experiment_key = experiment.key except BaseException: experiment = None else: experiment_key = experiment.key self._delete(self._get_user_keybase() + 'experiments/' + experiment_key) if experiment_key in self._experiment_cache.keys(): del self._experiment_cache[experiment_key] if experiment_key in self._experiment_info_cache.keys(): del self._experiment_info_cache[experiment_key] if experiment is not None: for tag, art in experiment.artifacts.iteritems(): if art.get('key') is not None: self.logger.debug( ('Deleting artifact {} from the store, ' + 'artifact key {}').format(tag, art['key'])) self.store.delete_artifact(art) if experiment.project is not None: self._delete(self._get_projects_keybase() + experiment.project + "/" + experiment.key) self._delete(self._get_experiments_keybase() + experiment.key) def checkpoint_experiment(self, experiment, blocking=False): if isinstance(experiment, basestring): key = experiment experiment = self.get_experiment(key, getinfo=False) else: key = experiment.key checkpoint_threads = [ Thread(target=self.store.put_artifact, args=(art, )) for _, art in experiment.artifacts.iteritems() if art['mutable'] and art.get('local') ] for t in checkpoint_threads: t.start() self.__setitem__( self._get_experiments_keybase() + key + "/time_last_checkpoint", time.time()) if blocking: for t in checkpoint_threads: t.join() else: return checkpoint_threads def _get_experiment_info(self, experiment): info = {} type_found = False ''' local_modeldir = self.store.get_artifact( experiment.artifacts['modeldir']) hdf5_files = glob.glob(os.path.join(local_modeldir, '*.hdf*')) type_found = False if any(hdf5_files): info['type'] = 'keras' info['no_checkpoints'] = len(hdf5_files) type_found = True meta_files = glob.glob(os.path.join(local_modeldir, '*.meta')) if any(meta_files) and not type_found: info['type'] = 'tensorflow' global_step = checkpoint_utils.load_variable( local_modeldir, 'global_step') info['global_step'] = global_step type_found = True ''' if not type_found: info['type'] = 'unknown' info['logtail'] = self._get_experiment_logtail(experiment) if experiment.metric is not None: metric_str = experiment.metric.split(':') metric_name = metric_str[0] metric_type = metric_str[1] if len(metric_str) > 1 else None tbtar = self.store.stream_artifact(experiment.artifacts['tb']) if metric_type == 'min': def metric_accum(x, y): return min(x, y) if x else y elif metric_type == 'max': def metric_accum(x, y): return max(x, y) if x else y else: def metric_accum(x, y): return y metric_value = None for f in tbtar: if f.isreg(): for e in util.event_reader(tbtar.extractfile(f)): for v in e.summary.value: if v.tag == metric_name: metric_value = metric_accum( metric_value, v.simple_value) info['metric_value'] = metric_value return info def _get_experiment_logtail(self, experiment): try: tarf = self.store.stream_artifact(experiment.artifacts['output']) if not tarf: return None logdata = tarf.extractfile(tarf.members[0]).read() logdata = util.remove_backspaces(logdata).split('\n') return logdata except BaseException as e: self.logger.info('Getting experiment logtail raised an exception:') self.logger.info(e) return None def get_experiment(self, key, getinfo=True): data = self.__getitem__(self._get_experiments_keybase() + key) assert data, "data at path %s not found! " % ( self._get_experiments_keybase() + key) data['key'] = key experiment_stub = experiment_from_dict(data) if getinfo: self._start_info_download(experiment_stub) info = self._experiment_info_cache.get(key)[0] \ if self._experiment_info_cache.get(key) else None return experiment_from_dict(data, info) def _start_info_download(self, experiment): key = experiment.key if key not in self._experiment_info_cache.keys(): self._experiment_info_cache[key] = ({}, time.time()) def download_info(): try: self._experiment_info_cache[key] = ( self._get_experiment_info(experiment), time.time()) self.logger.debug("Finished info download for " + key) except Exception as e: self.logger.info( "Exception {} while info download for {}".format(e, key)) if not(any(self._experiment_info_cache[key][0])) or \ self._experiment_info_cache[key][1] < \ experiment.time_last_checkpoint: self.logger.debug("Starting info download for " + key) if self.pool: Thread(target=download_info).start() else: download_info() def get_user_experiments(self, userid=None, blocking=True): if userid and '@' in userid: users = self.get_users() user_ids = [u for u in users if users[u].get('email') == userid] if len(user_ids) < 1: return None else: userid = user_ids[0] experiment_keys = self.__getitem__( self._get_user_keybase(userid) + "/experiments") if not experiment_keys: experiment_keys = {} return self._get_valid_experiments(experiment_keys.keys(), getinfo=True, blocking=blocking) def get_project_experiments(self, project): experiment_keys = self.__getitem__(self._get_projects_keybase() + project) if not experiment_keys: experiment_keys = {} return self._get_valid_experiments(experiment_keys.keys(), getinfo=True) def get_artifacts(self, key): experiment = self.get_experiment(key, getinfo=False) retval = {} if experiment.artifacts is not None: for tag, art in experiment.artifacts.iteritems(): url = self.store.get_artifact_url(art) if url is not None: retval[tag] = url return retval def _get_valid_experiments(self, experiment_keys, getinfo=False, blocking=True): def cache_valid_experiment(key): try: self._experiment_cache[key] = self.get_experiment( key, getinfo=getinfo) except AssertionError: self.logger.warn( ("Experiment {} does not exist " + "or is corrupted, try to delete record").format(key)) try: self.delete_experiment(key) except BaseException: pass if self.pool: if blocking: self.pool.map(cache_valid_experiment, experiment_keys) else: self.pool.map_async(cache_valid_experiment, experiment_keys) else: for e in experiment_keys: cache_valid_experiment(e) return [ self._experiment_cache[key] for key in experiment_keys if key in self._experiment_cache.keys() ] def get_projects(self): return self.__getitem__(self._get_projects_keybase()) def get_users(self): return self.__getitem__('users/') def refresh_auth_token(self, email, refresh_token): if self.auth: self.auth.refresh_token(email, refresh_token) def get_auth_domain(self): return self.app.auth_domain def is_auth_expired(self): if self.auth: return self.auth.expired else: return False def can_write_experiment(self, key=None, user=None): assert key is not None user = user if user else self._get_userid() owner = self.__getitem__(self._get_experiments_keybase() + key + "/owner") if owner is None: return True else: return (owner == user) def __enter__(self): return self def __exit__(self, *args): if self.pool: self.pool.close() if self.app: self.app.requests.close()
class FirebaseProvider(object): """Data provider for Firebase.""" def __init__(self, db_config, blocking_auth=True, verbose=10, store=None): guest = db_config.get('guest') self.app = pyrebase.initialize_app(db_config) self.logger = logging.getLogger('FirebaseProvider') self.logger.setLevel(verbose) self.auth = None if not guest and 'serviceAccount' not in db_config.keys(): self.auth = FirebaseAuth(self.app, db_config.get("use_email_auth"), db_config.get("email"), db_config.get("password"), blocking_auth) self.store = store if store else FirebaseArtifactStore( db_config, verbose=verbose, blocking_auth=blocking_auth) if self.auth and not self.auth.expired: myemail = self._get(self._get_user_keybase() + "email") if not myemail or myemail != self.auth.get_user_email(): self.__setitem__(self._get_user_keybase() + "email", self.auth.get_user_email()) self.max_keys = db_config.get('max_keys', 100) def _get(self, key, shallow=False): try: splitKey = key.split('/') key_path = '/'.join(splitKey[:-1]) key_name = splitKey[-1] dbobj = self.app.database().child(key_path).child(key_name) return dbobj.get(self.auth.get_token(), shallow=shallow).val() \ if self.auth else dbobj.get(shallow=shallow).val() except Exception as err: self.logger.warn(("Getting key {} from a database " + "raised an exception: {}").format(key, err)) return None def __setitem__(self, key, value): try: splitKey = key.split('/') key_path = '/'.join(splitKey[:-1]) key_name = splitKey[-1] dbobj = self.app.database().child(key_path) if self.auth: dbobj.update({key_name: value}, self.auth.get_token()) else: dbobj.update({key_name: value}) except Exception as err: self.logger.warn( ("Putting key {}, value {} into a database " + "raised an exception: {}").format(key, value, err)) def _delete(self, key, token=None): dbobj = self.app.database().child(key) if self.auth: dbobj.remove(self.auth.get_token()) else: dbobj.remove() def _get_userid(self): userid = None if self.auth: userid = self.auth.get_user_id() userid = userid if userid else 'guest' return userid def _get_user_keybase(self, userid=None): if userid is None: userid = self._get_userid() return "users/" + userid + "/" def _get_experiments_keybase(self, userid=None): return "experiments/" def _get_projects_keybase(self): return "projects/" def add_experiment(self, experiment, userid=None): self._delete(self._get_experiments_keybase() + experiment.key) experiment.time_added = time.time() experiment.status = 'waiting' if 'local' in experiment.artifacts['workspace'].keys() and \ os.path.exists(experiment.artifacts['workspace']['local']): experiment.git = git_util.get_git_info( experiment.artifacts['workspace']['local']) for tag, art in experiment.artifacts.iteritems(): if art['mutable']: art['key'] = self._get_experiments_keybase() + \ experiment.key + '/' + tag + '.tgz' else: if 'local' in art.keys(): # upload immutable artifacts art['key'] = self.store.put_artifact(art) if art.get('key') is not None: art['qualified'] = self.store.get_qualified_location( art['key']) art['bucket'] = self.store.get_bucket() userid = userid if userid else self._get_userid() experiment_dict = experiment.__dict__.copy() experiment_dict['owner'] = userid self.__setitem__(self._get_experiments_keybase() + experiment.key, experiment_dict) self.__setitem__( self._get_user_keybase(userid) + "experiments/" + experiment.key, experiment.time_added) if experiment.project and self.auth: self.__setitem__( self._get_projects_keybase() + experiment.project + "/" + experiment.key + "/owner", userid) self.checkpoint_experiment(experiment, blocking=True) self.logger.info("Added experiment " + experiment.key) def start_experiment(self, experiment): experiment.time_started = time.time() experiment.status = 'running' self.__setitem__( self._get_experiments_keybase() + experiment.key + "/status", "running") self.__setitem__( self._get_experiments_keybase() + experiment.key + "/time_started", experiment.time_started) self.checkpoint_experiment(experiment) def stop_experiment(self, key): # can be called remotely (the assumption is # that remote worker checks experiments status periodically, # and if it is 'stopped', kills the experiment. if isinstance(key, Experiment): key = key.key self.__setitem__(self._get_experiments_keybase() + key + "/status", "stopped") def finish_experiment(self, experiment): time_finished = time.time() if isinstance(experiment, basestring): key = experiment else: key = experiment.key self.checkpoint_experiment(experiment, blocking=True) experiment.status = 'finished' experiment.time_finished = time_finished self.__setitem__(self._get_experiments_keybase() + key + "/status", "finished") self.__setitem__( self._get_experiments_keybase() + key + "/time_finished", time_finished) def delete_experiment(self, experiment): if isinstance(experiment, basestring): experiment_key = experiment try: experiment = self.get_experiment(experiment) experiment_key = experiment.key except BaseException: experiment = None else: experiment_key = experiment.key self._delete(self._get_user_keybase() + 'experiments/' + experiment_key) if experiment is not None: for tag, art in experiment.artifacts.iteritems(): if art.get('key') is not None: self.logger.debug( ('Deleting artifact {} from the store, ' + 'artifact key {}').format(tag, art['key'])) self.store.delete_artifact(art) if experiment.project is not None: self._delete(self._get_projects_keybase() + experiment.project + "/" + experiment_key) self._delete(self._get_experiments_keybase() + experiment_key) def checkpoint_experiment(self, experiment, blocking=True): if isinstance(experiment, basestring): key = experiment experiment = self.get_experiment(key, getinfo=False) else: key = experiment.key # self.logger.info("%s, %s: checkpointing experiment" % # (os.getpid(), key)) checkpoint_threads = [ Thread(target=self.store.put_artifact, args=(art, )) for _, art in experiment.artifacts.iteritems() if art['mutable'] and art.get('local') ] for t in checkpoint_threads: t.start() self.__setitem__( self._get_experiments_keybase() + key + "/time_last_checkpoint", time.time()) if blocking: for t in checkpoint_threads: t.join() # self.logger.info("%s, %s: finish checkpointing experiment" % # (os.getpid(), key)) else: return checkpoint_threads def _get_experiment_info(self, experiment): info = {} type_found = False if not type_found: info['type'] = 'unknown' info['logtail'] = self._get_experiment_logtail(experiment) if experiment.metric is not None: metric_str = experiment.metric.split(':') metric_name = metric_str[0] metric_type = metric_str[1] if len(metric_str) > 1 else None tbtar = self.store.stream_artifact(experiment.artifacts['tb']) if metric_type == 'min': def metric_accum(x, y): return min(x, y) if x else y elif metric_type == 'max': def metric_accum(x, y): return max(x, y) if x else y else: def metric_accum(x, y): return y metric_value = None for f in tbtar: if f.isreg(): for e in util.event_reader(tbtar.extractfile(f)): for v in e.summary.value: if v.tag == metric_name: metric_value = metric_accum( metric_value, v.simple_value) info['metric_value'] = metric_value return info def _get_experiment_logtail(self, experiment): try: tarf = self.store.stream_artifact(experiment.artifacts['output']) if not tarf: return None logdata = tarf.extractfile(tarf.members[0]).read() logdata = util.remove_backspaces(logdata).split('\n') return logdata except BaseException as e: self.logger.info('Getting experiment logtail raised an exception:') self.logger.info(e) return None finally: if tarf: tarf.close() def get_experiment(self, key, getinfo=True): data = self._get(self._get_experiments_keybase() + key) assert data, "data at path %s not found! " % ( self._get_experiments_keybase() + key) data['key'] = key experiment_stub = experiment_from_dict(data) expinfo = {} if getinfo: try: expinfo = self._get_experiment_info(experiment_stub) except Exception as e: self.logger.info( "Exception {} while info download for {}".format(e, key)) return experiment_from_dict(data, expinfo) def get_user_experiments(self, userid=None, blocking=True): if userid and '@' in userid: users = self.get_users() user_ids = [u for u in users if users[u].get('email') == userid] if len(user_ids) < 1: return None else: userid = user_ids[0] experiment_keys = self._get( self._get_user_keybase(userid) + "/experiments") if not experiment_keys: experiment_keys = {} keys = sorted(experiment_keys.keys(), key=lambda k: experiment_keys[k], reverse=True) return keys def get_project_experiments(self, project): experiment_keys = self._get(self._get_projects_keybase() + project) if not experiment_keys: experiment_keys = {} return experiment_keys def get_artifacts(self, key): experiment = self.get_experiment(key, getinfo=False) retval = {} if experiment.artifacts is not None: for tag, art in experiment.artifacts.iteritems(): url = self.store.get_artifact_url(art) if url is not None: retval[tag] = url return retval def get_artifact(self, artifact, only_newer=True): return self.store.get_artifact(artifact, only_newer=only_newer) def get_projects(self): return self._get(self._get_projects_keybase(), shallow=True) def get_users(self): user_ids = self._get('users/', shallow=True) retval = {} for user_id in user_ids.keys(): retval[user_id] = { 'email': self._get('users/' + user_id + '/email') } return retval def refresh_auth_token(self, email, refresh_token): if self.auth: self.auth.refresh_token(email, refresh_token) def is_auth_expired(self): if self.auth: return self.auth.expired else: return False def can_write_experiment(self, key=None, user=None): assert key is not None user = user if user else self._get_userid() owner = self._get(self._get_experiments_keybase() + key + "/owner") if owner is None or owner == 'guest': return True else: return (owner == user) def __enter__(self): return self def __exit__(self, *args): if self.app: self.app.requests.close() if self.store: self.store.__exit__()