# Buggy code
def write(self, *ignored):
    # type: (Any) -> None
    version = self.config.version
    domain = cast(ChangeSetDomain, self.env.get_domain("changeset"))
    libchanges = {}  # type: Dict[unicode, List[Tuple[unicode, unicode, int]]]
    apichanges = []  # type: List[Tuple[unicode, unicode, int]]
    otherchanges = {}  # type: Dict[Tuple[unicode, unicode], List[Tuple[unicode, unicode, int]]]  # NOQA
    if version not in self.env.versionchanges:
        logger.info(bold(__("no changes in version %s.") % version))
        return
    logger.info(bold("writing summary file..."))
    for changeset in domain.get_changesets_for(version):
        if isinstance(changeset.descname, tuple):
            descname = changeset.descname[0]
        else:
            descname = changeset.descname
        ttext = self.typemap[changeset.type]
        context = changeset.content.replace("\n", " ")
        if descname and changeset.docname.startswith("c-api"):
            if context:
                entry = "<b>%s</b>: <i>%s:</i> %s" % (descname, ttext, context)
            else:
                entry = "<b>%s</b>: <i>%s</i>." % (descname, ttext)
            apichanges.append((entry, changeset.docname, changeset.lineno))
        elif descname or changeset.module:
            if not changeset.module:
                module = _("Builtins")
            if not descname:
                descname = _("Module level")
            if context:
                entry = "<b>%s</b>: <i>%s:</i> %s" % (descname, ttext, context)
            else:
                entry = "<b>%s</b>: <i>%s</i>." % (descname, ttext)
            libchanges.setdefault(module, []).append(
                (entry, changeset.docname, changeset.lineno)
            )
        else:
            if not context:
                continue
            entry = "<i>%s:</i> %s" % (ttext.capitalize(), context)
            title = self.env.titles[changeset.docname].astext()
            otherchanges.setdefault((changeset.docname, title), []).append(
                (entry, changeset.docname, changeset.lineno)
            )

    ctx = {
        "project": self.config.project,
        "version": version,
        "docstitle": self.config.html_title,
        "shorttitle": self.config.html_short_title,
        "libchanges": sorted(iteritems(libchanges)),
        "apichanges": sorted(apichanges),
        "otherchanges": sorted(iteritems(otherchanges)),
        "show_copyright": self.config.html_show_copyright,
        "show_sphinx": self.config.html_show_sphinx,
    }
    with codecs.open(path.join(self.outdir, "index.html"), "w", "utf8") as f:  # type: ignore  # NOQA
        f.write(self.templates.render("changes/frameset.html", ctx))
    with codecs.open(path.join(self.outdir, "changes.html"), "w", "utf8") as f:  # type: ignore  # NOQA
        f.write(self.templates.render("changes/versionchanges.html", ctx))

    hltext = [
        ".. versionadded:: %s" % version,
        ".. versionchanged:: %s" % version,
        ".. deprecated:: %s" % version,
    ]

    def hl(no, line):
        # type: (int, unicode) -> unicode
        line = '<a name="L%s"> </a>' % no + htmlescape(line)
        for x in hltext:
            if x in line:
                line = '<span class="hl">%s</span>' % line
                break
        return line

    logger.info(bold(__("copying source files...")))
    for docname in self.env.all_docs:
        with codecs.open(
            self.env.doc2path(docname),
            "r",  # type: ignore
            self.env.config.source_encoding,
        ) as f:
            try:
                lines = f.readlines()
            except UnicodeDecodeError:
                logger.warning(__("could not read %r for changelog creation"), docname)
                continue
        targetfn = path.join(self.outdir, "rst", os_path(docname)) + ".html"
        ensuredir(path.dirname(targetfn))
        with codecs.open(targetfn, "w", "utf-8") as f:  # type: ignore
            text = "".join(hl(i + 1, line) for (i, line) in enumerate(lines))
            ctx = {"filename": self.env.doc2path(docname, None), "text": text}
            f.write(self.templates.render("changes/rstsource.html", ctx))
    themectx = dict(
        ("theme_" + key, val) for (key, val) in iteritems(self.theme.get_options({}))
    )
    copy_asset_file(
        path.join(package_dir, "themes", "default", "static", "default.css_t"),
        self.outdir,
        context=themectx,
        renderer=self.templates,
    )
    copy_asset_file(
        path.join(package_dir, "themes", "basic", "static", "basic.css"), self.outdir
    )


# Traceback (UnboundLocalError)
# Python version: 3.6.7 (CPython)
# Docutils version: 0.14
# Jinja2 version: 2.10
# Last messages:
#   pickling environment...
#
#   done
#
#   checking consistency...
#
#   done
#
#   writing summary file...
#
# Loaded extensions:
#   sphinx.ext.mathjax (1.8.4) from /home/martin/branches/cpython/Doc/venv/lib/python3.6/site-packages/sphinx/ext/mathjax.py
#   alabaster (0.7.12) from /home/martin/branches/cpython/Doc/venv/lib/python3.6/site-packages/alabaster/__init__.py
#   sphinx.ext.coverage (1.8.4) from /home/martin/branches/cpython/Doc/venv/lib/python3.6/site-packages/sphinx/ext/coverage.py
#   sphinx.ext.doctest (1.8.4) from /home/martin/branches/cpython/Doc/venv/lib/python3.6/site-packages/sphinx/ext/doctest.py
#   pyspecific (1.0) from /home/martin/branches/cpython/Doc/tools/extensions/pyspecific.py
#   c_annotations (1.0) from /home/martin/branches/cpython/Doc/tools/extensions/c_annotations.py
#   escape4chm (1.0) from /home/martin/branches/cpython/Doc/tools/extensions/escape4chm.py
Traceback (most recent call last):
File "/home/martin/branches/cpython/Doc/venv/lib/python3.6/site-packages/sphinx/cmd/build.py", line 304, in build_main
app.build(args.force_all, filenames)
File "/home/martin/branches/cpython/Doc/venv/lib/python3.6/site-packages/sphinx/application.py", line 341, in build
self.builder.build_update()
File "/home/martin/branches/cpython/Doc/venv/lib/python3.6/site-packages/sphinx/builders/__init__.py", line 342, in build_update
self.build(['__all__'], to_build)
File "/home/martin/branches/cpython/Doc/venv/lib/python3.6/site-packages/sphinx/builders/__init__.py", line 412, in build
self.write(docnames, list(updated_docnames), method)
File "/home/martin/branches/cpython/Doc/venv/lib/python3.6/site-packages/sphinx/builders/changes.py", line 97, in write
libchanges.setdefault(module, []).append((entry, changeset.docname,
UnboundLocalError: local variable 'module' referenced before assignment

# Fixed code
def write(self, *ignored):
    # type: (Any) -> None
    version = self.config.version
    domain = cast(ChangeSetDomain, self.env.get_domain("changeset"))
    libchanges = {}  # type: Dict[unicode, List[Tuple[unicode, unicode, int]]]
    apichanges = []  # type: List[Tuple[unicode, unicode, int]]
    otherchanges = {}  # type: Dict[Tuple[unicode, unicode], List[Tuple[unicode, unicode, int]]]  # NOQA
    if version not in self.env.versionchanges:
        logger.info(bold(__("no changes in version %s.") % version))
        return
    logger.info(bold("writing summary file..."))
    for changeset in domain.get_changesets_for(version):
        if isinstance(changeset.descname, tuple):
            descname = changeset.descname[0]
        else:
            descname = changeset.descname
        ttext = self.typemap[changeset.type]
        context = changeset.content.replace("\n", " ")
        if descname and changeset.docname.startswith("c-api"):
            if context:
                entry = "<b>%s</b>: <i>%s:</i> %s" % (descname, ttext, context)
            else:
                entry = "<b>%s</b>: <i>%s</i>." % (descname, ttext)
            apichanges.append((entry, changeset.docname, changeset.lineno))
        elif descname or changeset.module:
            module = changeset.module or _("Builtins")
            if not descname:
                descname = _("Module level")
            if context:
                entry = "<b>%s</b>: <i>%s:</i> %s" % (descname, ttext, context)
            else:
                entry = "<b>%s</b>: <i>%s</i>." % (descname, ttext)
            libchanges.setdefault(module, []).append(
                (entry, changeset.docname, changeset.lineno)
            )
        else:
            if not context:
                continue
            entry = "<i>%s:</i> %s" % (ttext.capitalize(), context)
            title = self.env.titles[changeset.docname].astext()
            otherchanges.setdefault((changeset.docname, title), []).append(
                (entry, changeset.docname, changeset.lineno)
            )

    ctx = {
        "project": self.config.project,
        "version": version,
        "docstitle": self.config.html_title,
        "shorttitle": self.config.html_short_title,
        "libchanges": sorted(iteritems(libchanges)),
        "apichanges": sorted(apichanges),
        "otherchanges": sorted(iteritems(otherchanges)),
        "show_copyright": self.config.html_show_copyright,
        "show_sphinx": self.config.html_show_sphinx,
    }
    with codecs.open(path.join(self.outdir, "index.html"), "w", "utf8") as f:  # type: ignore  # NOQA
        f.write(self.templates.render("changes/frameset.html", ctx))
    with codecs.open(path.join(self.outdir, "changes.html"), "w", "utf8") as f:  # type: ignore  # NOQA
        f.write(self.templates.render("changes/versionchanges.html", ctx))

    hltext = [
        ".. versionadded:: %s" % version,
        ".. versionchanged:: %s" % version,
        ".. deprecated:: %s" % version,
    ]

    def hl(no, line):
        # type: (int, unicode) -> unicode
        line = '<a name="L%s"> </a>' % no + htmlescape(line)
        for x in hltext:
            if x in line:
                line = '<span class="hl">%s</span>' % line
                break
        return line

    logger.info(bold(__("copying source files...")))
    for docname in self.env.all_docs:
        with codecs.open(
            self.env.doc2path(docname),
            "r",  # type: ignore
            self.env.config.source_encoding,
        ) as f:
            try:
                lines = f.readlines()
            except UnicodeDecodeError:
                logger.warning(__("could not read %r for changelog creation"), docname)
                continue
        targetfn = path.join(self.outdir, "rst", os_path(docname)) + ".html"
        ensuredir(path.dirname(targetfn))
        with codecs.open(targetfn, "w", "utf-8") as f:  # type: ignore
            text = "".join(hl(i + 1, line) for (i, line) in enumerate(lines))
            ctx = {"filename": self.env.doc2path(docname, None), "text": text}
            f.write(self.templates.render("changes/rstsource.html", ctx))
    themectx = dict(
        ("theme_" + key, val) for (key, val) in iteritems(self.theme.get_options({}))
    )
    copy_asset_file(
        path.join(package_dir, "themes", "default", "static", "default.css_t"),
        self.outdir,
        context=themectx,
        renderer=self.templates,
    )
    copy_asset_file(
        path.join(package_dir, "themes", "basic", "static", "basic.css"), self.outdir
    )