diff --git a/jupyterhub/app.py b/jupyterhub/app.py index 8d073915..1fc07459 100644 --- a/jupyterhub/app.py +++ b/jupyterhub/app.py @@ -1058,11 +1058,6 @@ class JupyterHub(Application): for orm_user in db.query(orm.User): self.users[orm_user.id] = user = User(orm_user, self.tornado_settings) - if not user.state: - # without spawner state, server isn't valid - user.server = None - user_summaries.append(_user_summary(user)) - continue self.log.debug("Loading state for %s from db", user.name) spawner = user.spawner status = yield spawner.poll() diff --git a/jupyterhub/spawner.py b/jupyterhub/spawner.py index f7966376..2496c86f 100644 --- a/jupyterhub/spawner.py +++ b/jupyterhub/spawner.py @@ -191,7 +191,7 @@ class Spawner(LoggingConfigurable): def load_state(self, state): """load state from the database - This is the extensible part of state + This is the extensible part of state. Override in a subclass if there is state to load. Should call `super`. @@ -350,7 +350,27 @@ class Spawner(LoggingConfigurable): def poll(self): """Check if the single-user process is running - return None if it is, an exit status (0 if unknown) if it is not. + returns: + + None, if single-user process is running. + Exit status (0 if unknown), if it is not running. + + State transitions, behavior, and return response: + + - If the Spawner has not been initialized (neither loaded state, nor called start), + it should behave as if it is not running (status=0). + - If the Spawner has not finished starting, + it should behave as if it is running (status=None). + + Design assumptions about when `poll` may be called: + + - On Hub launch: `poll` may be called before `start` when state is loaded on Hub launch. + `poll` should return exit status 0 (unknown) if the Spawner has not been initialized via + `load_state` or `start`. + - If `.start()` is async: `poll` may be called during any yielded portions of the `start` + process. `poll` should return None when `start` is yielded, indicating that the `start` + process has not yet completed. + """ raise NotImplementedError("Override in subclass. Must be a Tornado gen.coroutine.")