# Buggy code
def mod_repo(repo, basedir=None, **kwargs):
    """
    Modify one or more values for a repo. If the repo does not exist, it will
    be created, so long as the following values are specified:

    repo
        name by which the yum refers to the repo
    name
        a human-readable name for the repo
    baseurl
        the URL for yum to reference
    mirrorlist
        the URL for yum to reference

    Key/Value pairs may also be removed from a repo's configuration by setting
    a key to a blank value. Bear in mind that a name cannot be deleted, and a
    baseurl can only be deleted if a mirrorlist is specified (or vice versa).

    CLI Examples:

    .. code-block:: bash

        salt '*' pkg.mod_repo reponame enabled=1 gpgcheck=1
        salt '*' pkg.mod_repo reponame basedir=/path/to/dir enabled=1
        salt '*' pkg.mod_repo reponame baseurl= mirrorlist=http://host.com/
    """
    # Filter out '__pub' arguments, as well as saltenv
    repo_opts = dict(
        (x, kwargs[x])
        for x in kwargs
        if not x.startswith("__") and x not in ("saltenv",)
    )

    if all(x in repo_opts for x in ("mirrorlist", "baseurl")):
        raise SaltInvocationError(
            "Only one of 'mirrorlist' and 'baseurl' can be specified"
        )

    # Build a list of keys to be deleted
    todelete = []
    for key in repo_opts:
        if repo_opts[key] != 0 and not repo_opts[key]:
            del repo_opts[key]
            todelete.append(key)

    # Add baseurl or mirrorlist to the 'todelete' list if the other was
    # specified in the repo_opts
    if "mirrorlist" in repo_opts:
        todelete.append("baseurl")
    elif "baseurl" in repo_opts:
        todelete.append("mirrorlist")

    # Fail if the user tried to delete the name
    if "name" in todelete:
        raise SaltInvocationError("The repo name cannot be deleted")

    # Give the user the ability to change the basedir
    repos = {}
    basedirs = _normalize_basedir(basedir)
    repos = list_repos(basedirs)

    repofile = ""
    header = ""
    filerepos = {}
    if repo not in repos:
        # If the repo doesn't exist, create it in a new file in the first
        # repo directory that exists
        newdir = None
        for d in basedirs:
            if os.path.exists(d):
                newdir = d
                break
        if not newdir:
            raise SaltInvocationError(
                "The repo does not exist and needs to be created, but none "
                "of the following basedir directories exist: {0}".format(basedirs)
            )

        repofile = "{0}/{1}.repo".format(newdir, repo)

        if "name" not in repo_opts:
            raise SaltInvocationError(
                "The repo does not exist and needs to be created, but a name "
                "was not given"
            )

        if "baseurl" not in repo_opts and "mirrorlist" not in repo_opts:
            raise SaltInvocationError(
                "The repo does not exist and needs to be created, but either "
                "a baseurl or a mirrorlist needs to be given"
            )
        filerepos[repo] = {}
    else:
        # The repo does exist, open its file
        repofile = repos[repo]["file"]
        header, filerepos = _parse_repo_file(repofile)

    # Error out if they tried to delete baseurl or mirrorlist improperly
    if "baseurl" in todelete:
        if "mirrorlist" not in repo_opts and "mirrorlist" not in filerepos[repo]:
            raise SaltInvocationError(
                "Cannot delete baseurl without specifying mirrorlist"
            )
    if "mirrorlist" in todelete:
        if "baseurl" not in repo_opts and "baseurl" not in filerepos[repo]:
            raise SaltInvocationError(
                "Cannot delete mirrorlist without specifying baseurl"
            )

    # Delete anything in the todelete list
    for key in todelete:
        if key in six.iterkeys(filerepos[repo].copy()):
            del filerepos[repo][key]

    _bool_to_str = lambda x: "1" if x else "0"
    # Old file or new, write out the repos(s)
    filerepos[repo].update(repo_opts)
    content = header
    for stanza in six.iterkeys(filerepos):
        comments = salt.utils.pkg.rpm.combine_comments(
            filerepos[stanza].pop("comments", [])
        )
        content += "[{0}]\n".format(stanza)
        for line in six.iterkeys(filerepos[stanza]):
            content += "{0}={1}\n".format(
                line,
                filerepos[stanza][line]
                if not isinstance(filerepos[stanza][line], bool)
                else _bool_to_str(filerepos[stanza][line]),
            )
        content += comments + "\n"

    with salt.utils.fopen(repofile, "w") as fileout:
        fileout.write(content)

    return {repofile: filerepos}


# Traceback (RuntimeError)
[ERROR   ] An un-handled exception was caught by salt's global exception handler:
RuntimeError: dictionary changed size during iteration
Traceback (most recent call last):
File "/bin/salt-call", line 11, in <module>
salt_call()
File "/usr/lib/python2.7/site-packages/salt/scripts.py", line 391, in salt_call
client.run()
File "/usr/lib/python2.7/site-packages/salt/cli/call.py", line 58, in run
caller.run()
File "/usr/lib/python2.7/site-packages/salt/cli/caller.py", line 134, in run
ret = self.call()
File "/usr/lib/python2.7/site-packages/salt/cli/caller.py", line 204, in call
ret['return'] = func(*args, **kwargs)
File "/usr/lib/python2.7/site-packages/salt/modules/yumpkg.py", line 2607, in mod_repo
for key in repo_opts:
RuntimeError: dictionary changed size during iteration
Traceback (most recent call last):
File "/bin/salt-call", line 11, in <module>
salt_call()
File "/usr/lib/python2.7/site-packages/salt/scripts.py", line 391, in salt_call
client.run()
File "/usr/lib/python2.7/site-packages/salt/cli/call.py", line 58, in run
caller.run()
File "/usr/lib/python2.7/site-packages/salt/cli/caller.py", line 134, in run
ret = self.call()
File "/usr/lib/python2.7/site-packages/salt/cli/caller.py", line 204, in call
ret['return'] = func(*args, **kwargs)
File "/usr/lib/python2.7/site-packages/salt/modules/yumpkg.py", line 2607, in mod_repo
for key in repo_opts:
RuntimeError: dictionary changed size during iteration

# Fixed code
def mod_repo(repo, basedir=None, **kwargs):
    """
    Modify one or more values for a repo. If the repo does not exist, it will
    be created, so long as the following values are specified:

    repo
        name by which the yum refers to the repo
    name
        a human-readable name for the repo
    baseurl
        the URL for yum to reference
    mirrorlist
        the URL for yum to reference

    Key/Value pairs may also be removed from a repo's configuration by setting
    a key to a blank value. Bear in mind that a name cannot be deleted, and a
    baseurl can only be deleted if a mirrorlist is specified (or vice versa).

    CLI Examples:

    .. code-block:: bash

        salt '*' pkg.mod_repo reponame enabled=1 gpgcheck=1
        salt '*' pkg.mod_repo reponame basedir=/path/to/dir enabled=1
        salt '*' pkg.mod_repo reponame baseurl= mirrorlist=http://host.com/
    """
    # Filter out '__pub' arguments, as well as saltenv
    repo_opts = dict(
        (x, kwargs[x])
        for x in kwargs
        if not x.startswith("__") and x not in ("saltenv",)
    )

    if all(x in repo_opts for x in ("mirrorlist", "baseurl")):
        raise SaltInvocationError(
            "Only one of 'mirrorlist' and 'baseurl' can be specified"
        )

    # Build a list of keys to be deleted
    todelete = []
    # list() of keys because the dict could be shrinking in the for loop.
    for key in list(repo_opts):
        if repo_opts[key] != 0 and not repo_opts[key]:
            del repo_opts[key]
            todelete.append(key)

    # Add baseurl or mirrorlist to the 'todelete' list if the other was
    # specified in the repo_opts
    if "mirrorlist" in repo_opts:
        todelete.append("baseurl")
    elif "baseurl" in repo_opts:
        todelete.append("mirrorlist")

    # Fail if the user tried to delete the name
    if "name" in todelete:
        raise SaltInvocationError("The repo name cannot be deleted")

    # Give the user the ability to change the basedir
    repos = {}
    basedirs = _normalize_basedir(basedir)
    repos = list_repos(basedirs)

    repofile = ""
    header = ""
    filerepos = {}
    if repo not in repos:
        # If the repo doesn't exist, create it in a new file in the first
        # repo directory that exists
        newdir = None
        for d in basedirs:
            if os.path.exists(d):
                newdir = d
                break
        if not newdir:
            raise SaltInvocationError(
                "The repo does not exist and needs to be created, but none "
                "of the following basedir directories exist: {0}".format(basedirs)
            )

        repofile = "{0}/{1}.repo".format(newdir, repo)

        if "name" not in repo_opts:
            raise SaltInvocationError(
                "The repo does not exist and needs to be created, but a name "
                "was not given"
            )

        if "baseurl" not in repo_opts and "mirrorlist" not in repo_opts:
            raise SaltInvocationError(
                "The repo does not exist and needs to be created, but either "
                "a baseurl or a mirrorlist needs to be given"
            )
        filerepos[repo] = {}
    else:
        # The repo does exist, open its file
        repofile = repos[repo]["file"]
        header, filerepos = _parse_repo_file(repofile)

    # Error out if they tried to delete baseurl or mirrorlist improperly
    if "baseurl" in todelete:
        if "mirrorlist" not in repo_opts and "mirrorlist" not in filerepos[repo]:
            raise SaltInvocationError(
                "Cannot delete baseurl without specifying mirrorlist"
            )
    if "mirrorlist" in todelete:
        if "baseurl" not in repo_opts and "baseurl" not in filerepos[repo]:
            raise SaltInvocationError(
                "Cannot delete mirrorlist without specifying baseurl"
            )

    # Delete anything in the todelete list
    for key in todelete:
        if key in six.iterkeys(filerepos[repo].copy()):
            del filerepos[repo][key]

    _bool_to_str = lambda x: "1" if x else "0"
    # Old file or new, write out the repos(s)
    filerepos[repo].update(repo_opts)
    content = header
    for stanza in six.iterkeys(filerepos):
        comments = salt.utils.pkg.rpm.combine_comments(
            filerepos[stanza].pop("comments", [])
        )
        content += "[{0}]\n".format(stanza)
        for line in six.iterkeys(filerepos[stanza]):
            content += "{0}={1}\n".format(
                line,
                filerepos[stanza][line]
                if not isinstance(filerepos[stanza][line], bool)
                else _bool_to_str(filerepos[stanza][line]),
            )
        content += comments + "\n"

    with salt.utils.fopen(repofile, "w") as fileout:
        fileout.write(content)

    return {repofile: filerepos}