import os, signal, subprocess
from itertools import chain

class ServerError(Exception):
    pass

def safedir(path):
    if not os.path.exists(path):
        os.makedirs(path)

class Server(object):
    """
    Server is an abstract base class.

    Concrete implementations must provide `host`, `port`, `log_dir`, `pid_dir`.
    Some subclasses may also need to provide `env` if the default is not satisfactory.
    """
    def __init__(self, settings):
        self.settings = settings

    @property
    def env(self):
        return self.settings.env

    @property
    def id(self):
        return self.__class__.__name__, self.host, self.port

    @property
    def log_file(self):
        return os.path.join(self.log_dir, '%s-%s_%s.log' % self.id)

    @property
    def pid(self):
        return int(open(self.pid_file).readline().strip())

    @property
    def pid_file(self):
        return os.path.join(self.pid_dir, '%s-%s_%s.pid' % self.id)

    def restart(self):
        return chain(self.stop(), self.start())

    def start(self, **kwargs):
        if self._status == 'running':
            raise ServerError("%s already started" % self)
        process = subprocess.Popen(self.args, env=self.env, **kwargs)
        if process.wait():
            raise ServerError("Failed to start %s" % self)
        yield '%s started' % self

    @property
    def _status(self):
        try:
            os.getpgid(self.pid)
            return 'running'
        except Exception:
            return 'stopped'

    def status(self):
        yield '%s %s' % (self, self._status)

    def stop(self):
        try:
            os.kill(self.pid, signal.SIGTERM)
            while self._status == 'running':
                pass
        except Exception:
            pass
        return chain(self.status())

    def __str__(self):
        return '%s %s:%s' % self.id

import os, signal, subprocess
from itertools import chain

class ServerError(Exception):
    pass

def safedir(path):
    if not os.path.exists(path):
        os.makedirs(path)

class Server(object):
    """
    Server is an abstract base class.

    Concrete implementations must provide `host`, `port`, `log_dir`, `pid_dir`.
    Some subclasses may also need to provide `env` if the default is not satisfactory.
    """
    def __init__(self, settings):
        self.settings = settings

    @property
    def env(self):
        return self.settings.env

    @property
    def id(self):
        return self.__class__.__name__, self.host, self.port

    @property
    def log_file(self):
        return os.path.join(self.log_dir, '%s-%s_%s.log' % self.id)

    @property
    def pid(self):
        return int(open(self.pid_file).readline().strip())

    @property
    def pid_file(self):
        return os.path.join(self.pid_dir, '%s-%s_%s.pid' % self.id)

    def restart(self):
        return chain(self.stop(), self.start())

    def start(self, *args, **kwargs):
        if self._status == 'running':
            raise ServerError("%s already started" % self)
        process = subprocess.Popen(args or self.args, env=self.env, **kwargs)
        if process.wait():
            raise ServerError("Failed to start %s" % self)
        yield '%s started' % self

    @property
    def _status(self):
        try:
            os.getpgid(self.pid)
            return 'running'
        except Exception:
            return 'stopped'

    def status(self):
        yield '%s %s' % (self, self._status)

    def stop(self):
        try:
            os.kill(self.pid, signal.SIGTERM)
            while self._status == 'running':
                pass
        except Exception:
            pass
        return chain(self.status())

    def __str__(self):
        return '%s %s:%s' % self.id

allow overriding of args to start server