# Buggy code
def __repr__(self):
    # Calculate a hash that represents most data about the post
    m = hashlib.md5()
    # source_path modification date (to avoid reading it)
    m.update(utils.unicode_str(os.stat(self.source_path).st_mtime).encode("utf-8"))
    clean_meta = {}
    for k, v in self.meta.items():
        sub_meta = {}
        clean_meta[k] = sub_meta
        for kk, vv in v.items():
            if vv:
                sub_meta[kk] = vv
    m.update(
        utils.unicode_str(
            json.dumps(clean_meta, cls=utils.CustomEncoder, sort_keys=True)
        ).encode("utf-8")
    )
    return "<Post: {0} {1}>".format(self.source_path, m.hexdigest())


# Traceback (UnicodeEncodeError)
$ nikola build
Scanning posts....done!
.  render_posts:timeline_changes
Traceback (most recent call last):
File "/home/kwpolska/virtualenvs/nikola-py2/lib/python2.7/site-packages/doit/doit_cmd.py", line 165, in run
return command.parse_execute(args)
File "/home/kwpolska/virtualenvs/nikola-py2/lib/python2.7/site-packages/doit/cmd_base.py", line 122, in parse_execute
return self.execute(params, args)
File "/home/kwpolska/virtualenvs/nikola-py2/lib/python2.7/site-packages/doit/cmd_base.py", line 405, in execute
return self._execute(**exec_params)
File "/home/kwpolska/virtualenvs/nikola-py2/lib/python2.7/site-packages/doit/cmd_run.py", line 239, in _execute
return runner.run_all(self.control.task_dispatcher())
File "/home/kwpolska/virtualenvs/nikola-py2/lib/python2.7/site-packages/doit/runner.py", line 238, in run_all
self.run_tasks(task_dispatcher)
File "/home/kwpolska/virtualenvs/nikola-py2/lib/python2.7/site-packages/doit/runner.py", line 205, in run_tasks
self.process_task_result(node, catched_excp)
File "/home/kwpolska/virtualenvs/nikola-py2/lib/python2.7/site-packages/doit/runner.py", line 175, in process_task_result
task.save_extra_values()
File "/home/kwpolska/virtualenvs/nikola-py2/lib/python2.7/site-packages/doit/task.py", line 354, in save_extra_values
self.values.update(value_saver())
File "/home/kwpolska/git/nikola/nikola/utils.py", line 531, in <lambda>
task.value_savers.append(lambda: {self.identifier: self._calc_digest()})
File "/home/kwpolska/git/nikola/nikola/utils.py", line 517, in _calc_digest
data = json.dumps(self.config, cls=CustomEncoder, sort_keys=True)
File "/usr/lib64/python2.7/json/__init__.py", line 250, in dumps
sort_keys=sort_keys, **kw).encode(obj)
File "/usr/lib64/python2.7/json/encoder.py", line 209, in encode
chunks = list(chunks)
File "/usr/lib64/python2.7/json/encoder.py", line 434, in _iterencode
for chunk in _iterencode_dict(o, _current_indent_level):
File "/usr/lib64/python2.7/json/encoder.py", line 408, in _iterencode_dict
for chunk in chunks:
File "/usr/lib64/python2.7/json/encoder.py", line 332, in _iterencode_list
for chunk in chunks:
File "/usr/lib64/python2.7/json/encoder.py", line 442, in _iterencode
o = _default(o)
File "/home/kwpolska/git/nikola/nikola/utils.py", line 498, in default
return super(CustomEncoder, self).default(obj)
File "/usr/lib64/python2.7/json/encoder.py", line 184, in default
raise TypeError(repr(o) + " is not JSON serializable")
UnicodeEncodeError: 'ascii' codec can't encode character u'\u0105' in position 13: ordinal not in range(128)

# Fixed code
def __repr__(self):
    # Calculate a hash that represents most data about the post
    m = hashlib.md5()
    # source_path modification date (to avoid reading it)
    m.update(utils.unicode_str(os.stat(self.source_path).st_mtime).encode("utf-8"))
    clean_meta = {}
    for k, v in self.meta.items():
        sub_meta = {}
        clean_meta[k] = sub_meta
        for kk, vv in v.items():
            if vv:
                sub_meta[kk] = vv
    m.update(
        utils.unicode_str(
            json.dumps(clean_meta, cls=utils.CustomEncoder, sort_keys=True)
        ).encode("utf-8")
    )
    return "<Post: {0!r} {1}>".format(self.source_path, m.hexdigest())