Get the final merged configuration for supvervisord, as a string.

    This is the top-level function exported by this module.  It combines
    the config file from the main project with default settings and those
    specified in the command-line, processes various special section names,
    and returns the resulting configuration as a string.

def get_merged_config(**options):
    """Get the final merged configuration for supvervisord, as a string.

    This is the top-level function exported by this module.  It combines
    the config file from the main project with default settings and those
    specified in the command-line, processes various special section names,
    and returns the resulting configuration as a string.
    """
    #  Find and load the containing project module.
    #  This can be specified explicity using the --project-dir option.
    #  Otherwise, we attempt to guess by looking for the manage.py file.
    project_dir = options.get("project_dir")
    if project_dir is None:
        project_dir = guess_project_dir()
    # Find the config file to load.
    # Default to <project-dir>/supervisord.conf.
    config_file = options.get("config_file")
    if config_file is None:
        config_file = os.path.join(project_dir,CONFIG_FILE)
    #  Build the default template context variables.
    #  This is mostly useful information about the project and environment.
    ctx = {
        "PROJECT_DIR": project_dir,
        "PYTHON": os.path.realpath(os.path.abspath(sys.executable)),
        "SUPERVISOR_OPTIONS": rerender_options(options),
        "settings": settings,
        "environ": os.environ,
    }
    #  Initialise the ConfigParser.
    #  Fortunately for us, ConfigParser has merge-multiple-config-files
    #  functionality built into it.  You just read each file in turn, and
    #  values from later files overwrite values from former.
    cfg = RawConfigParser()
    #  Start from the default configuration options.
    data = render_config(DEFAULT_CONFIG,ctx)
    cfg.readfp(StringIO(data))
    #  Add in the project-specific config file.
    with open(config_file,"r") as f:
        data = render_config(f.read(),ctx)
    cfg.readfp(StringIO(data))
    #  Add in the options specified on the command-line.
    cfg.readfp(StringIO(get_config_from_options(**options)))
    #  Add options from [program:__defaults__] to each program section
    #  if it happens to be missing that option.
    PROG_DEFAULTS = "program:__defaults__"
    if cfg.has_section(PROG_DEFAULTS):
        for option in cfg.options(PROG_DEFAULTS):
            default = cfg.get(PROG_DEFAULTS,option)
            for section in cfg.sections():
                if section.startswith("program:"):
                    if not cfg.has_option(section,option):
                        cfg.set(section,option,default)
        cfg.remove_section(PROG_DEFAULTS)
    #  Add options from [program:__overrides__] to each program section
    #  regardless of whether they already have that option.
    PROG_OVERRIDES = "program:__overrides__"
    if cfg.has_section(PROG_OVERRIDES):
        for option in cfg.options(PROG_OVERRIDES):
            override = cfg.get(PROG_OVERRIDES,option)
            for section in cfg.sections():
                if section.startswith("program:"):
                    cfg.set(section,option,override)
        cfg.remove_section(PROG_OVERRIDES)
    #  Make sure we've got a port configured for supervisorctl to
    #  talk to supervisord.  It's passworded based on secret key.
    #  If they have configured a unix socket then use that, otherwise
    #  use an inet server on localhost at fixed-but-randomish port.
    username = hashlib.md5(settings.SECRET_KEY).hexdigest()[:7]
    password = hashlib.md5(username).hexdigest()
    if cfg.has_section("unix_http_server"):
        set_if_missing(cfg,"unix_http_server","username",username)
        set_if_missing(cfg,"unix_http_server","password",password)
        serverurl = "unix://" + cfg.get("unix_http_server","file")
    else:
        #  This picks a "random" port in the 9000 range to listen on.
        #  It's derived from the secret key, so it's stable for a given
        #  project but multiple projects are unlikely to collide.
        port = int(hashlib.md5(password).hexdigest()[:3],16) % 1000
        addr = "127.0.0.1:9%03d" % (port,)
        set_if_missing(cfg,"inet_http_server","port",addr)
        set_if_missing(cfg,"inet_http_server","username",username)
        set_if_missing(cfg,"inet_http_server","password",password)
        serverurl = "http://" + cfg.get("inet_http_server","port")
    set_if_missing(cfg,"supervisorctl","serverurl",serverurl)
    set_if_missing(cfg,"supervisorctl","username",username)
    set_if_missing(cfg,"supervisorctl","password",password)
    set_if_missing(cfg,"rpcinterface:supervisor",
                       "supervisor.rpcinterface_factory",
                       "supervisor.rpcinterface:make_main_rpcinterface")
    #  Remove any [program:] sections with exclude=true
    for section in cfg.sections():
        try:
            if cfg.getboolean(section,"exclude"):
                cfg.remove_section(section)
        except NoOptionError:
            pass
    #  Sanity-check to give better error messages.
    for section in cfg.sections():
        if section.startswith("program:"):
            if not cfg.has_option(section,"command"):
                msg = "Process name '%s' has no command configured"
                raise ValueError(msg % (section.split(":",1)[-1]))
    #  Write it out to a StringIO and return the data
    s = StringIO()
    cfg.write(s)
    return s.getvalue()