# Buggy code
def __init__(self, config, ini_path, ini_data):  # noqa
    config.toxinipath = ini_path
    using("tox.ini: {} (pid {})".format(config.toxinipath, os.getpid()))
    config.toxinidir = config.toxinipath.dirpath()

    self._cfg = py.iniconfig.IniConfig(config.toxinipath, ini_data)
    previous_line_of = self._cfg.lineof

    self.expand_section_names(self._cfg)

    def line_of_default_to_zero(section, name=None):
        at = previous_line_of(section, name=name)
        if at is None:
            at = 0
        return at

    self._cfg.lineof = line_of_default_to_zero
    config._cfg = self._cfg
    self.config = config

    prefix = "tox" if ini_path.basename == "setup.cfg" else None
    fallbacksection = "tox:tox" if ini_path.basename == "setup.cfg" else "tox"

    context_name = getcontextname()
    if context_name == "jenkins":
        reader = SectionReader(
            "tox:jenkins",
            self._cfg,
            prefix=prefix,
            fallbacksections=[fallbacksection],
        )
        dist_share_default = "{toxworkdir}/distshare"
    elif not context_name:
        reader = SectionReader("tox", self._cfg, prefix=prefix)
        dist_share_default = "{homedir}/.tox/distshare"
    else:
        raise ValueError("invalid context")

    if config.option.hashseed is None:
        hash_seed = make_hashseed()
    elif config.option.hashseed == "noset":
        hash_seed = None
    else:
        hash_seed = config.option.hashseed
    config.hashseed = hash_seed

    reader.addsubstitutions(toxinidir=config.toxinidir, homedir=config.homedir)

    if config.option.workdir is None:
        config.toxworkdir = reader.getpath("toxworkdir", "{toxinidir}/.tox")
    else:
        config.toxworkdir = config.toxinidir.join(config.option.workdir, abs=True)

    if os.path.exists(str(config.toxworkdir)):
        config.toxworkdir = config.toxworkdir.realpath()

    reader.addsubstitutions(toxworkdir=config.toxworkdir)
    config.ignore_basepython_conflict = reader.getbool(
        "ignore_basepython_conflict", False
    )

    config.distdir = reader.getpath("distdir", "{toxworkdir}/dist")

    reader.addsubstitutions(distdir=config.distdir)
    config.distshare = reader.getpath("distshare", dist_share_default)
    config.temp_dir = reader.getpath("temp_dir", "{toxworkdir}/.tmp")
    reader.addsubstitutions(distshare=config.distshare)
    config.sdistsrc = reader.getpath("sdistsrc", None)
    config.setupdir = reader.getpath("setupdir", "{toxinidir}")
    config.logdir = config.toxworkdir.join("log")
    within_parallel = PARALLEL_ENV_VAR_KEY_PRIVATE in os.environ
    if not within_parallel and not WITHIN_PROVISION:
        ensure_empty_dir(config.logdir)

    # determine indexserver dictionary
    config.indexserver = {"default": IndexServerConfig("default")}
    prefix = "indexserver"
    for line in reader.getlist(prefix):
        name, url = map(lambda x: x.strip(), line.split("=", 1))
        config.indexserver[name] = IndexServerConfig(name, url)

    if config.option.skip_missing_interpreters == "config":
        val = reader.getbool("skip_missing_interpreters", False)
        config.option.skip_missing_interpreters = "true" if val else "false"

    override = False
    if config.option.indexurl:
        for url_def in config.option.indexurl:
            m = re.match(r"\W*(\w+)=(\S+)", url_def)
            if m is None:
                url = url_def
                name = "default"
            else:
                name, url = m.groups()
                if not url:
                    url = None
            if name != "ALL":
                config.indexserver[name].url = url
            else:
                override = url
    # let ALL override all existing entries
    if override:
        for name in config.indexserver:
            config.indexserver[name] = IndexServerConfig(name, override)

    self.handle_provision(config, reader)

    self.parse_build_isolation(config, reader)
    res = self._getenvdata(reader, config)
    config.envlist, all_envs, config.envlist_default, config.envlist_explicit = res

    # factors used in config or predefined
    known_factors = self._list_section_factors("testenv")
    known_factors.update({"py", "python"})

    # factors stated in config envlist
    stated_envlist = reader.getstring("envlist", replace=False)
    if stated_envlist:
        for env in _split_env(stated_envlist):
            known_factors.update(env.split("-"))

    # configure testenvs
    to_do = []
    failures = OrderedDict()
    results = {}
    cur_self = self

    def run(name, section, subs, config):
        try:
            results[name] = cur_self.make_envconfig(name, section, subs, config)
        except Exception as exception:
            failures[name] = (exception, traceback.format_exc())

    order = []
    for name in all_envs:
        section = "{}{}".format(testenvprefix, name)
        factors = set(name.split("-"))
        if (
            section in self._cfg
            or factors <= known_factors
            or all(
                tox.PYTHON.PY_FACTORS_RE.match(factor)
                for factor in factors - known_factors
            )
        ):
            order.append(name)
            thread = Thread(target=run, args=(name, section, reader._subs, config))
            thread.daemon = True
            thread.start()
            to_do.append(thread)
    for thread in to_do:
        while thread.is_alive():
            thread.join(timeout=20)
    if failures:
        raise tox.exception.ConfigError(
            "\n".join(
                "{} failed with {} at {}".format(key, exc, trace)
                for key, (exc, trace) in failures.items()
            ),
        )
    for name in order:
        config.envconfigs[name] = results[name]
    all_develop = all(
        name in config.envconfigs and config.envconfigs[name].usedevelop
        for name in config.envlist
    )

    config.skipsdist = reader.getbool("skipsdist", all_develop)

    if config.option.devenv is not None:
        config.option.notest = True

    if config.option.devenv is not None and len(config.envlist) != 1:
        feedback("--devenv requires only a single -e", sysexit=True)


# Traceback (tox.exception.ConfigError)
Traceback (most recent call last):
File "~/.pyenv/versions/3.8.0/lib/python3.8/runpy.py", line 192, in _
run_module_as_main
return _run_code(code, main_globals, None,
File "~/.pyenv/versions/3.8.0/lib/python3.8/runpy.py", line 85, in _run_code
exec(code, run_globals)
File ".../.tox/.tox/lib/python3.8/site-packages/tox/__main__.py", line 4, in <module>
tox.cmdline()
File ".../.tox/.tox/lib/python3.8/site-packages/tox/session/__init__.py", line 44, in cmdline
main(args)
File ".../.tox/.tox/lib/python3.8/site-packages/tox/session/__init__.py", line 64, in main
config = load_config(args)
File ".../.tox/.tox/lib/python3.8/site-packages/tox/session/__init__.py", line 80, in load_config
config = parseconfig(args)
File ".../.tox/.tox/lib/python3.8/site-packages/tox/config/__init__.py", line 274, in parseconfig
ParseIni(config, config_file, content)
File ".../.tox/.tox/lib/python3.8/site-packages/tox/config/__init__.py", line 1193, in __init__
raise tox.exception.ConfigError(
tox.exception.ConfigError: ConfigError: docs failed with ConfigError: substitution key 'temp_dir' not found at Traceback (most recent call last):
File ".../.tox/.tox/lib/python3.8/site-packages/tox/config/__init__.py", line 1169, in run
results[name] = cur_self.make_envconfig(name, section, subs, config)
File ".../.tox/.tox/lib/python3.8/site-packages/tox/config/__init__.py", line 1317, in make_envconfig
res = meth(env_attr.name, env_attr.default, replace=replace)
File ".../.tox/.tox/lib/python3.8/site-packages/tox/config/__init__.py", line 1586, in getargvlist
return _ArgvlistReader.getargvlist(self, s, replace=replace)
File ".../.tox/.tox/lib/python3.8/site-packages/tox/config/__init__.py", line 1796, in getargvlist
commands.append(cls.processcommand(reader, current_command, replace))
File ".../.tox/.tox/lib/python3.8/site-packages/tox/config/__init__.py", line 1832, in processcommand
new_word = reader._replace(word)
File ".../.tox/.tox/lib/python3.8/site-packages/tox/config/__init__.py", line 1643, in _replace
replaced = Replacer(self, crossonly=crossonly).do_replace(value)
File ".../.tox/.tox/lib/python3.8/site-packages/tox/config/__init__.py", line 1679, in do_replace
expanded = substitute_once(value)
File ".../.tox/.tox/lib/python3.8/site-packages/tox/config/__init__.py", line 1677, in substitute_once
return self.RE_ITEM_REF.sub(self._replace_match, x)
File ".../.tox/.tox/lib/python3.8/site-packages/tox/config/__init__.py", line 1724, in _replace_match
return self._replace_substitution(match)
File ".../.tox/.tox/lib/python3.8/site-packages/tox/config/__init__.py", line 1759, in _replace_substitution
val = self._substitute_from_other_section(sub_key)
File ".../.tox/.tox/lib/python3.8/site-packages/tox/config/__init__.py", line 1753, in _substitute_from_other_section
raise tox.exception.ConfigError("substitution key {!r} not found".format(key))
tox.exception.ConfigError: ConfigError: substitution key 'temp_dir' not found

# Fixed code
def __init__(self, config, ini_path, ini_data):  # noqa
    config.toxinipath = ini_path
    using("tox.ini: {} (pid {})".format(config.toxinipath, os.getpid()))
    config.toxinidir = config.toxinipath.dirpath()

    self._cfg = py.iniconfig.IniConfig(config.toxinipath, ini_data)
    previous_line_of = self._cfg.lineof

    self.expand_section_names(self._cfg)

    def line_of_default_to_zero(section, name=None):
        at = previous_line_of(section, name=name)
        if at is None:
            at = 0
        return at

    self._cfg.lineof = line_of_default_to_zero
    config._cfg = self._cfg
    self.config = config

    prefix = "tox" if ini_path.basename == "setup.cfg" else None
    fallbacksection = "tox:tox" if ini_path.basename == "setup.cfg" else "tox"

    context_name = getcontextname()
    if context_name == "jenkins":
        reader = SectionReader(
            "tox:jenkins",
            self._cfg,
            prefix=prefix,
            fallbacksections=[fallbacksection],
        )
        dist_share_default = "{toxworkdir}/distshare"
    elif not context_name:
        reader = SectionReader("tox", self._cfg, prefix=prefix)
        dist_share_default = "{homedir}/.tox/distshare"
    else:
        raise ValueError("invalid context")

    if config.option.hashseed is None:
        hash_seed = make_hashseed()
    elif config.option.hashseed == "noset":
        hash_seed = None
    else:
        hash_seed = config.option.hashseed
    config.hashseed = hash_seed

    reader.addsubstitutions(toxinidir=config.toxinidir, homedir=config.homedir)

    if config.option.workdir is None:
        config.toxworkdir = reader.getpath("toxworkdir", "{toxinidir}/.tox")
    else:
        config.toxworkdir = config.toxinidir.join(config.option.workdir, abs=True)

    if os.path.exists(str(config.toxworkdir)):
        config.toxworkdir = config.toxworkdir.realpath()

    reader.addsubstitutions(toxworkdir=config.toxworkdir)
    config.ignore_basepython_conflict = reader.getbool(
        "ignore_basepython_conflict", False
    )

    config.distdir = reader.getpath("distdir", "{toxworkdir}/dist")

    reader.addsubstitutions(distdir=config.distdir)
    config.distshare = reader.getpath("distshare", dist_share_default)
    reader.addsubstitutions(distshare=config.distshare)
    config.temp_dir = reader.getpath("temp_dir", "{toxworkdir}/.tmp")
    reader.addsubstitutions(temp_dir=config.temp_dir)
    config.sdistsrc = reader.getpath("sdistsrc", None)
    config.setupdir = reader.getpath("setupdir", "{toxinidir}")
    config.logdir = config.toxworkdir.join("log")
    within_parallel = PARALLEL_ENV_VAR_KEY_PRIVATE in os.environ
    if not within_parallel and not WITHIN_PROVISION:
        ensure_empty_dir(config.logdir)

    # determine indexserver dictionary
    config.indexserver = {"default": IndexServerConfig("default")}
    prefix = "indexserver"
    for line in reader.getlist(prefix):
        name, url = map(lambda x: x.strip(), line.split("=", 1))
        config.indexserver[name] = IndexServerConfig(name, url)

    if config.option.skip_missing_interpreters == "config":
        val = reader.getbool("skip_missing_interpreters", False)
        config.option.skip_missing_interpreters = "true" if val else "false"

    override = False
    if config.option.indexurl:
        for url_def in config.option.indexurl:
            m = re.match(r"\W*(\w+)=(\S+)", url_def)
            if m is None:
                url = url_def
                name = "default"
            else:
                name, url = m.groups()
                if not url:
                    url = None
            if name != "ALL":
                config.indexserver[name].url = url
            else:
                override = url
    # let ALL override all existing entries
    if override:
        for name in config.indexserver:
            config.indexserver[name] = IndexServerConfig(name, override)

    self.handle_provision(config, reader)

    self.parse_build_isolation(config, reader)
    res = self._getenvdata(reader, config)
    config.envlist, all_envs, config.envlist_default, config.envlist_explicit = res

    # factors used in config or predefined
    known_factors = self._list_section_factors("testenv")
    known_factors.update({"py", "python"})

    # factors stated in config envlist
    stated_envlist = reader.getstring("envlist", replace=False)
    if stated_envlist:
        for env in _split_env(stated_envlist):
            known_factors.update(env.split("-"))

    # configure testenvs
    to_do = []
    failures = OrderedDict()
    results = {}
    cur_self = self

    def run(name, section, subs, config):
        try:
            results[name] = cur_self.make_envconfig(name, section, subs, config)
        except Exception as exception:
            failures[name] = (exception, traceback.format_exc())

    order = []
    for name in all_envs:
        section = "{}{}".format(testenvprefix, name)
        factors = set(name.split("-"))
        if (
            section in self._cfg
            or factors <= known_factors
            or all(
                tox.PYTHON.PY_FACTORS_RE.match(factor)
                for factor in factors - known_factors
            )
        ):
            order.append(name)
            thread = Thread(target=run, args=(name, section, reader._subs, config))
            thread.daemon = True
            thread.start()
            to_do.append(thread)
    for thread in to_do:
        while thread.is_alive():
            thread.join(timeout=20)
    if failures:
        raise tox.exception.ConfigError(
            "\n".join(
                "{} failed with {} at {}".format(key, exc, trace)
                for key, (exc, trace) in failures.items()
            ),
        )
    for name in order:
        config.envconfigs[name] = results[name]
    all_develop = all(
        name in config.envconfigs and config.envconfigs[name].usedevelop
        for name in config.envlist
    )

    config.skipsdist = reader.getbool("skipsdist", all_develop)

    if config.option.devenv is not None:
        config.option.notest = True

    if config.option.devenv is not None and len(config.envlist) != 1:
        feedback("--devenv requires only a single -e", sysexit=True)