# Buggy code
def addChange(self, who=None, files=None, comments=None, **kwargs):
    # deprecated in 0.9.0; will be removed in 1.0.0
    log.msg(
        "WARNING: change source is using deprecated "
        "self.master.addChange method; this method will disappear in "
        "Buildbot-1.0.0"
    )
    # handle positional arguments
    kwargs["who"] = who
    kwargs["files"] = files
    kwargs["comments"] = comments

    def handle_deprec(oldname, newname):
        if oldname in kwargs:
            old = kwargs.pop(oldname)
            if old is not None:
                if kwargs.get(newname) is None:
                    log.msg(
                        "WARNING: change source is using deprecated "
                        "addChange parameter '%s'" % oldname
                    )
                    return old
                raise TypeError(
                    "Cannot provide '%s' and '%s' to addChange" % (oldname, newname)
                )
        return kwargs.get(newname)

    kwargs["author"] = handle_deprec("who", "author")
    kwargs["when_timestamp"] = handle_deprec("when", "when_timestamp")

    # timestamp must be an epoch timestamp now
    if isinstance(kwargs.get("when_timestamp"), datetime.datetime):
        kwargs["when_timestamp"] = datetime2epoch(kwargs["when_timestamp"])

    # unicodify stuff
    for k in (
        "comments",
        "author",
        "revision",
        "branch",
        "category",
        "revlink",
        "repository",
        "codebase",
        "project",
    ):
        if k in kwargs:
            kwargs[k] = ascii2unicode(kwargs[k])
    if kwargs.get("files"):
        kwargs["files"] = [ascii2unicode(f) for f in kwargs["files"]]
    if kwargs.get("properties"):
        kwargs["properties"] = dict(
            (ascii2unicode(k), v) for k, v in iteritems(kwargs["properties"])
        )

    # pass the converted call on to the data API
    changeid = yield self.data.updates.addChange(**kwargs)

    # and turn that changeid into a change object, since that's what
    # callers expected (and why this method was deprecated)
    chdict = yield self.db.changes.getChange(changeid)
    change = yield changes.Change.fromChdict(self, chdict)
    defer.returnValue(change)


# Traceback (exceptions.UnicodeDecodeError)
2017-06-07 14:28:38+0300 [_GenericHTTPChannelProtocol,10,127.0.0.1] Attempting to load module buildbot.www.hooks.base
2017-06-07 14:28:38+0300 [_GenericHTTPChannelProtocol,10,127.0.0.1] Got the following changes [{'category': None, 'codebase': None, 'repository': 'kernel', 'author': '\xd0\xbf\xd1\x80\xd0\xb8\xd0\xb2\xd0\xb5\xd1\x82 \xd0\xbf\xd1\x80\xd0\xb8\xd0\xb2\xd0\xb5\xd1\x82 \xd0\xbd\xd0\xb5 \xd1\x80\xd0\xb0\xd0\xb1\xd0\xbe\xd1\x82\xd0\xb0\xd0\xb5\xd1\x82', 'when': None, 'comments': u'\u043d\u0435 asci \u043a\u043e\u043c\u043c\u0435\u043d\u0442\u0430\u0440\u0438\u0439', 'project': 'kernel', 'branch': None, 'revlink': None, 'properties': {}, 'files': [], 'revision': 'test'}]
2017-06-07 14:28:38+0300 [_GenericHTTPChannelProtocol,10,127.0.0.1] Payload: {'project': ['kernel'], 'revision': ['test'], 'repository': ['kernel'], 'comments': ['\xd0\xbd\xd0\xb5 asci \xd0\xba\xd0\xbe\xd0\xbc\xd0\xbc\xd0\xb5\xd0\xbd\xd1\x82\xd0\xb0\xd1\x80\xd0\xb8\xd0\xb9'], 'author': ['\xd0\xbf\xd1\x80\xd0\xb8\xd0\xb2\xd0\xb5\xd1\x82 \xd0\xbf\xd1\x80\xd0\xb8\xd0\xb2\xd0\xb5\xd1\x82 \xd0\xbd\xd0\xb5 \xd1\x80\xd0\xb0\xd0\xb1\xd0\xbe\xd1\x82\xd0\xb0\xd0\xb5\xd1\x82']}
2017-06-07 14:28:38+0300 [_GenericHTTPChannelProtocol,10,127.0.0.1] WARNING: change source is using deprecated self.master.addChange method; this method will disappear in Buildbot-1.0.0
2017-06-07 14:28:38+0300 [_GenericHTTPChannelProtocol,10,127.0.0.1] adding changes from web hook
Traceback (most recent call last):
File "/home/builder/buildbot/sandbox/lib/python2.7/site-packages/twisted/internet/defer.py", line 1447, in unwindGenerator
return _inlineCallbacks(None, gen, Deferred())
File "/home/builder/buildbot/sandbox/lib/python2.7/site-packages/twisted/internet/defer.py", line 1301, in _inlineCallbacks
result = g.send(result)
File "/home/builder/buildbot/sandbox/lib/python2.7/site-packages/buildbot/www/change_hook.py", line 158, in submitChanges
change = yield self.master.addChange(src=src, **chdict)
File "/home/builder/buildbot/sandbox/lib/python2.7/site-packages/twisted/internet/defer.py", line 1447, in unwindGenerator
return _inlineCallbacks(None, gen, Deferred())
--- <exception caught here> ---
File "/home/builder/buildbot/sandbox/lib/python2.7/site-packages/twisted/internet/defer.py", line 1301, in _inlineCallbacks
result = g.send(result)
File "/home/builder/buildbot/sandbox/lib/python2.7/site-packages/buildbot/master.py", line 485, in addChange
kwargs[k] = ascii2unicode(kwargs[k])
File "/home/builder/buildbot/sandbox/lib/python2.7/site-packages/buildbot/util/__init__.py", line 239, in ascii2unicode
return bytes2unicode(x, encoding='ascii', errors=errors)
File "/home/builder/buildbot/sandbox/lib/python2.7/site-packages/buildbot/util/__init__.py", line 235, in bytes2unicode
return text_type(x, encoding, errors)
exceptions.UnicodeDecodeError: 'ascii' codec can't decode byte 0xd0 in position 0: ordinal not in range(128)

# Fixed code
def addChange(self, who=None, files=None, comments=None, **kwargs):
    # deprecated in 0.9.0; will be removed in 1.0.0
    log.msg(
        "WARNING: change source is using deprecated "
        "self.master.addChange method; this method will disappear in "
        "Buildbot-1.0.0"
    )
    # handle positional arguments
    kwargs["who"] = who
    kwargs["files"] = files
    kwargs["comments"] = comments

    def handle_deprec(oldname, newname):
        if oldname in kwargs:
            old = kwargs.pop(oldname)
            if old is not None:
                if kwargs.get(newname) is None:
                    log.msg(
                        "WARNING: change source is using deprecated "
                        "addChange parameter '%s'" % oldname
                    )
                    return old
                raise TypeError(
                    "Cannot provide '%s' and '%s' to addChange" % (oldname, newname)
                )
        return kwargs.get(newname)

    kwargs["author"] = handle_deprec("who", "author")
    kwargs["when_timestamp"] = handle_deprec("when", "when_timestamp")

    # timestamp must be an epoch timestamp now
    if isinstance(kwargs.get("when_timestamp"), datetime.datetime):
        kwargs["when_timestamp"] = datetime2epoch(kwargs["when_timestamp"])

    # unicodify stuff
    for k in (
        "comments",
        "author",
        "revision",
        "branch",
        "category",
        "revlink",
        "repository",
        "codebase",
        "project",
    ):
        if k in kwargs:
            kwargs[k] = bytes2unicode(kwargs[k])
    if kwargs.get("files"):
        kwargs["files"] = [bytes2unicode(f) for f in kwargs["files"]]
    if kwargs.get("properties"):
        kwargs["properties"] = dict(
            (bytes2unicode(k), v) for k, v in iteritems(kwargs["properties"])
        )

    # pass the converted call on to the data API
    changeid = yield self.data.updates.addChange(**kwargs)

    # and turn that changeid into a change object, since that's what
    # callers expected (and why this method was deprecated)
    chdict = yield self.db.changes.getChange(changeid)
    change = yield changes.Change.fromChdict(self, chdict)
    defer.returnValue(change)