Repository: getnikola/nikola

Issue:
"nikola auto" breaks on Python 3 in 7.5.1 (was good in 7.5.0)
To reproduce, run nikola using Python 3:

```
nikola auto
```

Visit http://localhost:8000, "A server error occurred.  Please contact the administrator." is displayed in browser, and the following message is displayed in the terminal:

```
Traceback (most recent call last):
  File "/usr/lib/python3.4/wsgiref/handlers.py", line 137, in run
    self.result = application(self.environ, self.start_response)
  File "/home/user/.local/lib/python3.4/site-packages/nikola/plugins/command/auto/__init__.py", line 188, in __call__
    return parent.serve_static(environ, start_response)
  File "/home/user/.local/lib/python3.4/site-packages/nikola/plugins/command/auto/__init__.py", line 243, in serve_static
    start_response(b'200 OK', [(b'Content-type', mimetype)])
  File "/usr/lib/python3.4/wsgiref/handlers.py", line 226, in start_response
    self.headers = self.headers_class(headers)
  File "/usr/lib/python3.4/wsgiref/headers.py", line 38, in __init__
    self._convert_string_type(k)
  File "/usr/lib/python3.4/wsgiref/headers.py", line 46, in _convert_string_type
    " of type str (got {0})".format(repr(value)))
AssertionError: Header names/values must be of type str (got b'Content-type')
```

"auto" command works well in Python 2.

I've bisected the regression. The culprit is commit aec80b4522a33c63f58946a78692a022e60e8b22



Discussion:
This looks like a bug in ws4py, I may have a workaround later today when I can spend some time debugging it.

any good news?

@xuhdev not yet. This is a complicated bug, and I am having a rough month at work (as in: lots and lots of work). I am going to be at a sprint next week, so I _could_ be antisocial and do some late night coding. We'll see :-)

No problem, no need to hurry :)


Resolving patch:
diff --git a/nikola/plugins/command/auto/__init__.py b/nikola/plugins/command/auto/__init__.py
--- a/nikola/plugins/command/auto/__init__.py
+++ b/nikola/plugins/command/auto/__init__.py
@@ -237,29 +237,30 @@ def serve_static(self, environ, start_response):
         uri = wsgiref.util.request_uri(environ)
         p_uri = urlparse(uri)
         f_path = os.path.join(self.site.config['OUTPUT_FOLDER'], *p_uri.path.split('/'))
-        mimetype = mimetypes.guess_type(uri)[0] or b'text/html'
+        mimetype = mimetypes.guess_type(uri)[0] or 'text/html'
 
         if os.path.isdir(f_path):
             f_path = os.path.join(f_path, self.site.config['INDEX_FILE'])
 
         if p_uri.path == '/robots.txt':
-            start_response(b'200 OK', [(b'Content-type', 'txt/plain')])
-            return '''User-Agent: *\nDisallow: /\n'''
+            start_response('200 OK', [('Content-type', 'text/plain')])
+            return ['User-Agent: *\nDisallow: /\n']
         elif os.path.isfile(f_path):
-            with open(f_path) as fd:
-                start_response(b'200 OK', [(b'Content-type', mimetype)])
-                return self.inject_js(mimetype, fd.read())
+            with open(f_path, 'rb') as fd:
+                start_response('200 OK', [('Content-type', mimetype)])
+                return [self.inject_js(mimetype, fd.read())]
         elif p_uri.path == '/livereload.js':
-            with open(LRJS_PATH) as fd:
-                start_response(b'200 OK', [(b'Content-type', mimetype)])
-                return self.inject_js(mimetype, fd.read())
-        start_response(b'404 ERR', [])
-        return self.inject_js('text/html', ERROR_N.format(404).format(uri))
+            with open(LRJS_PATH, 'rb') as fd:
+                start_response('200 OK', [('Content-type', mimetype)])
+                return [self.inject_js(mimetype, fd.read())]
+        start_response('404 ERR', [])
+        return [self.inject_js('text/html', ERROR_N.format(404).format(uri))]
 
     def inject_js(self, mimetype, data):
         """Inject livereload.js in HTML files."""
         if mimetype == 'text/html':
-            data = re.sub('</head>', self.snippet, data, 1, re.IGNORECASE)
+            data = re.sub('</head>', self.snippet, data.decode('utf8'), 1, re.IGNORECASE)
+            data = data.encode('utf8')
         return data
 
 
@@ -275,7 +276,7 @@ def __init__(self, *a, **kw):
         super(LRSocket, self).__init__(*a, **kw)
 
     def received_message(self, message):
-        message = json.loads(message.data)
+        message = json.loads(message.data.decode('utf8'))
         self.logger.info('<--- {0}'.format(message))
         response = None
         if message['command'] == 'hello':  # Handshake


Test patch: