From b3e3f041fe92f9a1ac8123df088e05ea5c714bab Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Sun, 19 Aug 2012 22:55:50 +0300 Subject: [PATCH 1/3] Remove trailing spaces and replace tabs with spaces --- share/tools/web_config/webconfig.py | 116 ++++++++++++++-------------- 1 file changed, 58 insertions(+), 58 deletions(-) diff --git a/share/tools/web_config/webconfig.py b/share/tools/web_config/webconfig.py index 7ed6d519d..ce44b26c8 100755 --- a/share/tools/web_config/webconfig.py +++ b/share/tools/web_config/webconfig.py @@ -18,17 +18,17 @@ def run_fish_cmd(text): from subprocess import PIPE p = subprocess.Popen(["fish"], stdin=PIPE, stdout=PIPE, stderr=PIPE) if IS_PY2: - out, err = p.communicate(text) + out, err = p.communicate(text) else: - out, err = p.communicate(bytes(text, 'utf-8')) + out, err = p.communicate(bytes(text, 'utf-8')) out = str(out, 'utf-8') err = str(err, 'utf-8') return(out, err) - + def escape_fish_cmd(text): - # Replace one backslash with two, and single quotes with backslash-quote - escaped = text.replace('\\', '\\\\').replace("'", "\\'") - return "'" + escaped + "'" + # Replace one backslash with two, and single quotes with backslash-quote + escaped = text.replace('\\', '\\\\').replace("'", "\\'") + return "'" + escaped + "'" named_colors = { 'black' : '000000', @@ -56,15 +56,15 @@ def parse_one_color(comp): return '' def better_color(c1, c2): - """ Indicate which color is "better", i.e. prefer term256 colors """ - if not c2: return c1 - if not c1: return c2 - if c1 == 'normal': return c2 - if c2 == 'normal': return c1 - if c2 in named_colors: return c1 - if c1 in named_colors: return c2 - return c1 - + """ Indicate which color is "better", i.e. prefer term256 colors """ + if not c2: return c1 + if not c1: return c2 + if c1 == 'normal': return c2 + if c2 == 'normal': return c1 + if c2 in named_colors: return c1 + if c1 in named_colors: return c2 + return c1 + def parse_color(color_str): """ A basic function to parse a color string, for example, 'red' '--bold' """ @@ -85,10 +85,10 @@ def parse_color(color_str): else: # Regular color color = better_color(color, parse_one_color(comp)) - + return [color, background_color, bold, underline] - - + + def parse_bool(val): val = val.lower() if val.startswith('f') or val.startswith('0'): return False @@ -102,7 +102,7 @@ class FishVar: self.value = value self.universal = False self.exported = False - + def get_json_obj(self): # Return an array(3): name, value, flags flags = [] @@ -111,19 +111,19 @@ class FishVar: return [self.name, self.value, ', '.join(flags)] class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): - + def write_to_wfile(self, txt): if IS_PY2: - self.wfile.write(txt) + self.wfile.write(txt) else: # Python 3 - self.wfile.write(bytes(txt, 'utf-8')) + self.wfile.write(bytes(txt, 'utf-8')) + - def do_get_colors(self): # Looks for fish_color_*. # Returns an array of lists [color_name, color_description, color_value] result = [] - + # Make sure we return at least these remaining = set(['normal', 'error', @@ -140,7 +140,7 @@ class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): 'valid_path', 'autosuggestion' ]) - + # Here are our color descriptions descriptions = { 'normal': 'Default text', @@ -157,11 +157,11 @@ class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): 'operator': 'Like * and ~', 'escape': 'Escapes like \\n', 'cwd': 'Current directory', - 'cwd_root': 'cwd for root user', + 'cwd_root': 'cwd for root user', 'valid_path': 'Valid paths', 'autosuggestion': 'Suggested completion' } - + out, err = run_fish_cmd('set -L') for line in out.split('\n'): @@ -170,36 +170,36 @@ class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): color_desc = descriptions.get(color_name, '') result.append([color_name, color_desc, parse_color(color_value)]) remaining.discard(color_name) - + # Ensure that we have all the color names we know about, so that if the # user deletes one he can still set it again via the web interface for color_name in remaining: color_desc = descriptions.get(color_name, '') result.append([color_name, color_desc, parse_color('')]) - + # Sort our result (by their keys) result.sort() - + return result - + def do_get_functions(self): out, err = run_fish_cmd('functions') out = out.strip() - + # Not sure why fish sometimes returns this with newlines if "\n" in out: return out.split('\n') else: return out.strip().split(', ') - + def do_get_variable_names(self, cmd): " Given a command like 'set -U' return all the variable names " out, err = run_fish_cmd(cmd) return out.split('\n') - + def do_get_variables(self): out, err = run_fish_cmd('set -L') - + # Put all the variables into a dictionary vars = {} for line in out.split('\n'): @@ -207,16 +207,16 @@ class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): if len(comps) < 2: continue fish_var = FishVar(comps[0], comps[1]) vars[fish_var.name] = fish_var - + # Mark universal variables. L means don't abbreviate. for name in self.do_get_variable_names('set -nUL'): if name in vars: vars[name].universal = True # Mark exported variables. L means don't abbreviate. for name in self.do_get_variable_names('set -nxL'): if name in vars: vars[name].exported = True - + return [vars[key].get_json_obj() for key in sorted(vars.keys(), key=str.lower)] - + def do_get_history(self): # Use \x1e ("record separator") to distinguish between history items. The first # backslash is so Python passes one backslash to fish @@ -224,13 +224,13 @@ class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): result = out.split(' \x1e') if result: result.pop() # Trim off the trailing element return result - + def do_get_color_for_variable(self, name): "Return the color with the given name, or the empty string if there is none" out, err = run_fish_cmd("echo -n $" + name) return out - + def do_set_color_for_variable(self, name, color, background_color, bold, underline): if not color: color = 'normal' "Sets a color for a fish color name, like 'autosuggestion'" @@ -239,19 +239,19 @@ class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): if background_color: command += ' --background=' + background_color if bold: command += ' --bold' if underline: command += ' --underline' - + out, err = run_fish_cmd(command) return out - + def do_get_function(self, func_name): out, err = run_fish_cmd('functions ' + func_name) return out - + def do_delete_history_item(self, history_item_text): - # It's really lame that we always return success here - out, err = run_fish_cmd('builtin history --save --delete -- ' + escape_fish_cmd(history_item_text)) - return True - + # It's really lame that we always return success here + out, err = run_fish_cmd('builtin history --save --delete -- ' + escape_fish_cmd(history_item_text)) + return True + def do_GET(self): p = self.path if p == '/colors/': @@ -270,15 +270,15 @@ class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): output = self.do_get_color_for_variable(name) else: return SimpleHTTPServer.SimpleHTTPRequestHandler.do_GET(self) - + # Return valid output self.send_response(200) self.send_header('Content-type','text/html') self.write_to_wfile('\n') - + # Output JSON self.write_to_wfile(json.dumps(output)) - + def do_POST(self): p = self.path if IS_PY2: @@ -332,17 +332,17 @@ class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): what = postvars.get(b'what') what[0] = str(what[0]).lstrip("b'").rstrip("'") if self.do_delete_history_item(what[0]): - output = ["OK"] + output = ["OK"] else: - output = ["Unable to delete history item"] + output = ["Unable to delete history item"] else: return SimpleHTTPServer.SimpleHTTPRequestHandler.do_POST(self) - + # Return valid output self.send_response(200) self.send_header('Content-type','text/html') self.write_to_wfile('\n') - + # Output JSON self.write_to_wfile(json.dumps(output)) @@ -379,10 +379,10 @@ if PORT > 9000: # Just look at the first letter initial_tab = '' if len(sys.argv) > 1: - for tab in ['functions', 'colors', 'variables', 'history']: - if tab.startswith(sys.argv[1]): - initial_tab = '#' + tab - break + for tab in ['functions', 'colors', 'variables', 'history']: + if tab.startswith(sys.argv[1]): + initial_tab = '#' + tab + break url = 'http://localhost:{0}/{1}'.format(PORT, initial_tab) print("Web config started at '{0}'. Hit enter to stop.".format(url)) From 10dfca1a753f8a7a00c813b194589313cac1ddd4 Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Sun, 19 Aug 2012 23:19:07 +0300 Subject: [PATCH 2/3] =?UTF-8?q?Decode=20data=20in=20python3=20=E2=80=93=20?= =?UTF-8?q?Fixes=20#265.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In both in python2 and python3 parse_qs expects str object. In python2 it worked ok, because self.rfile was open in binary mode and str in python2 is actually a string of bytes. However in python3 str is actually string of unicode literals, not bytes and file was still open in binary mode. Thus, deleting any file with non-ascii byte inside filename failed in python3. Also, cgi.parse_qs is deprecated and shouldn't be used. --- share/tools/web_config/webconfig.py | 27 ++++++++------------------- 1 file changed, 8 insertions(+), 19 deletions(-) diff --git a/share/tools/web_config/webconfig.py b/share/tools/web_config/webconfig.py index ce44b26c8..c90c58686 100755 --- a/share/tools/web_config/webconfig.py +++ b/share/tools/web_config/webconfig.py @@ -7,9 +7,11 @@ IS_PY2 = sys.version_info[0] == 2 if IS_PY2: import SimpleHTTPServer import SocketServer + from urlparse import parse_qs as parse_qs else: import http.server as SimpleHTTPServer import socketserver as SocketServer + from urllib.parse import parse_qs as parse_qs import webbrowser import subprocess import re, json, socket, os, sys, cgi, select, time @@ -285,6 +287,7 @@ class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): ctype, pdict = cgi.parse_header(self.headers.getheader('content-type')) else: # Python 3 ctype, pdict = cgi.parse_header(self.headers['content-type']) + if ctype == 'multipart/form-data': postvars = cgi.parse_multipart(self.rfile, pdict) elif ctype == 'application/x-www-form-urlencoded': @@ -292,7 +295,11 @@ class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): length = int(self.headers.getheader('content-length')) except AttributeError: length = int(self.headers['content-length']) - postvars = cgi.parse_qs(self.rfile.read(length), keep_blank_values=1) + + data = self.rfile.read(length) + if not IS_PY2: + data = data.decode('utf-8') + postvars = parse_qs(data, keep_blank_values=1) else: postvars = {} @@ -303,18 +310,6 @@ class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): bold = postvars.get('bold') underline = postvars.get('underline') - if what == None: #Will be None for python3 - what = postvars.get(b'what') - what[0] = str(what[0]).lstrip("b'").rstrip("'") - color = postvars.get(b'color') - color[0] = str(color[0]).lstrip("b'").rstrip("'") - background_color = postvars.get(b'background_color') - background_color[0] = str(background_color[0]).lstrip("b'").rstrip("'") - bold = postvars.get(b'bold') - bold[0] = str(bold[0]).lstrip("b'").rstrip("'") - underline = postvars.get(b'underline') - underline[0] = str(underline[0]).lstrip("b'").rstrip("'") - if what: # Not sure why we get lists here? output = self.do_set_color_for_variable(what[0], color[0], background_color[0], parse_bool(bold[0]), parse_bool(underline[0])) @@ -322,15 +317,9 @@ class FishConfigHTTPRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler): output = 'Bad request' elif p == '/get_function/': what = postvars.get('what') - if what == None: #Will be None for python3 - what = postvars.get(b'what') - what[0] = str(what[0]).lstrip("b'").rstrip("'") output = [self.do_get_function(what[0])] elif p == '/delete_history_item/': what = postvars.get('what') - if what == None: #Will be None for python3 - what = postvars.get(b'what') - what[0] = str(what[0]).lstrip("b'").rstrip("'") if self.do_delete_history_item(what[0]): output = ["OK"] else: From ef566836c45b2bfeca9f56c45d8d922fb6b5f99b Mon Sep 17 00:00:00 2001 From: Simonas Kazlauskas Date: Sun, 19 Aug 2012 23:26:15 +0300 Subject: [PATCH 3/3] Wipe unnecessary `as` statement --- share/tools/web_config/webconfig.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/share/tools/web_config/webconfig.py b/share/tools/web_config/webconfig.py index c90c58686..c59985fbc 100755 --- a/share/tools/web_config/webconfig.py +++ b/share/tools/web_config/webconfig.py @@ -7,11 +7,11 @@ IS_PY2 = sys.version_info[0] == 2 if IS_PY2: import SimpleHTTPServer import SocketServer - from urlparse import parse_qs as parse_qs + from urlparse import parse_qs else: import http.server as SimpleHTTPServer import socketserver as SocketServer - from urllib.parse import parse_qs as parse_qs + from urllib.parse import parse_qs import webbrowser import subprocess import re, json, socket, os, sys, cgi, select, time