more python3

This commit is contained in:
Ville Rantanen
2020-10-12 11:41:17 +03:00
parent 52db6b9bf4
commit dfd5111bea
3 changed files with 220 additions and 171 deletions

View File

@@ -1,7 +1,7 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
# coding=utf-8 # coding=utf-8
''' A script that creates an index for a folder. """ A script that creates an index for a folder.
''' """
import os import os
import sys import sys
@@ -13,45 +13,89 @@ import base64
import random import random
VERSION = "20200619" VERSION = "20200619"
IMAGE_EXTENSIONS = ['png', 'gif', 'jpg', 'jpeg', 'tif', 'tiff'] IMAGE_EXTENSIONS = ["png", "gif", "jpg", "jpeg", "tif", "tiff"]
def setup(): def setup():
''' Setup the command line options ''' """ Setup the command line options """
from argparse import ArgumentParser from argparse import ArgumentParser
parser = ArgumentParser( parser = ArgumentParser(
epilog = "Recursively generate indexes: \n# find . -type d -not -path '*/.*' -exec SimpleWebPage -f \{\} \;" epilog="Recursively generate indexes: \n# find . -type d -not -path '*/.*' -exec SimpleWebPage -f \{\} \;"
) )
parser.add_argument("-f",action="store_true",dest="overwrite",default=False, parser.add_argument(
help="Overwrite existing index file, even if it's not generated with SimpleWebPage. By default, if file is generated with SimpleWebPage it will be overwritten!") "-f",
parser.add_argument("-H",action="store_true",dest="hidden",default=False, action="store_true",
help="Show hidden files") dest="overwrite",
parser.add_argument("-t",type=str,dest="title",default=None, default=False,
help="Name for the title (Default: Folder name)") help="Overwrite existing index file, even if it's not generated with SimpleWebPage. By default, if file is generated with SimpleWebPage it will be overwritten!",
parser.add_argument("-o",type=str,dest="filename",default="index.html", )
help="Output filename (Default: index.html)") parser.add_argument(
parser.add_argument("-p",action="store_false",dest="parent",default=True, "-H",
help="Do no print .. link for parent folder.") action="store_true",
parser.add_argument("--password",type=str,dest="password",default=None, dest="hidden",
help="Set a password to view page. The file list will be written to a randomly generated filename. Note: this is not secure, as the target link will still be unprotected if known.") default=False,
parser.add_argument("-r",action="store_true",dest="recursive",default=False, help="Show hidden files",
help="Include all files recursively in the list. Do not include any folders.") )
parser.add_argument("--images",action="store_true",dest="images",default=False, parser.add_argument(
help="Show images with <img> tags") "-t",
parser.add_argument("--include","-i", type=str,
dest="title",
default=None,
help="Name for the title (Default: Folder name)",
)
parser.add_argument(
"-o",
type=str,
dest="filename",
default="index.html",
help="Output filename (Default: index.html)",
)
parser.add_argument(
"-p",
action="store_false",
dest="parent",
default=True,
help="Do no print .. link for parent folder.",
)
parser.add_argument(
"--password",
type=str,
dest="password",
default=None,
help="Set a password to view page. The file list will be written to a randomly generated filename. Note: this is not secure, as the target link will still be unprotected if known.",
)
parser.add_argument(
"-r",
action="store_true",
dest="recursive",
default=False,
help="Include all files recursively in the list. Do not include any folders.",
)
parser.add_argument(
"--images",
action="store_true",
dest="images",
default=False,
help="Show images with <img> tags",
)
parser.add_argument(
"--include",
"-i",
action="store", action="store",
dest="includes", dest="includes",
default=['*'], default=["*"],
help="Glob match for files to be included in the table. ex. *.jpg. You can pass several includes.", help="Glob match for files to be included in the table. ex. *.jpg. You can pass several includes.",
nargs = '*' nargs="*",
) )
parser.add_argument("--version",action='version', version=VERSION) parser.add_argument("--version", action="version", version=VERSION)
parser.add_argument( parser.add_argument(
"path", "path",
type=str, type=str,
action="store", action="store",
default=os.path.abspath('.'), default=os.path.abspath("."),
nargs='?', nargs="?",
help="Root path of the index" help="Root path of the index",
) )
options = parser.parse_args() options = parser.parse_args()
@@ -60,28 +104,36 @@ def setup():
options.title = os.path.basename(options.path) options.title = os.path.basename(options.path)
return options return options
def setup2HTML(opts): def setup2HTML(opts):
return '<meta name="SimpleWebPageSetup" content="%s"/>'%";".join([ return '<meta name="SimpleWebPageSetup" content="%s"/>' % ";".join(
'hidden=%s'%opts.hidden, [
'parent=%s'%opts.parent, "hidden=%s" % opts.hidden,
'title=%s'%urllib.parse.quote(opts.title), "parent=%s" % opts.parent,
'images=%s'%opts.images "title=%s" % urllib.parse.quote(opts.title),
]) "images=%s" % opts.images,
]
)
def HTML2setup(opts): def HTML2setup(opts):
""" returns new opts and was it able to read HTML """ """ returns new opts and was it able to read HTML """
try: try:
read_config = False read_config = False
with open(os.path.join(opts.path,opts.filename), 'rt') as f: with open(os.path.join(opts.path, opts.filename), "rt") as f:
for l in f.readlines(): for l in f.readlines():
if l.find('<meta name="SimpleWebPageSetup"') > -1: if l.find('<meta name="SimpleWebPageSetup"') > -1:
content = l[l.find('name="SimpleWebPageSetup"'):] content = l[l.find('name="SimpleWebPageSetup"') :]
for s in content.split('"')[3].split(";"): for s in content.split('"')[3].split(";"):
(k,v) = s.split('=',1) (k, v) = s.split("=", 1)
if k == 'hidden': opts.hidden = v == "True" if k == "hidden":
if k == 'parent': opts.parent = v == "True" opts.hidden = v == "True"
if k == 'title': opts.title = urllib.parse.unquote(v) if k == "parent":
if k == 'images': opts.images = v == "True" opts.parent = v == "True"
if k == "title":
opts.title = urllib.parse.unquote(v)
if k == "images":
opts.images = v == "True"
read_config = True read_config = True
print("Reading options from existing " + opts.filename) print("Reading options from existing " + opts.filename)
break break
@@ -89,6 +141,7 @@ def HTML2setup(opts):
except: except:
return (opts, False) return (opts, False)
def get_files_and_folders(opts): def get_files_and_folders(opts):
if opts.recursive: if opts.recursive:
@@ -99,10 +152,10 @@ def get_files_and_folders(opts):
if rpath == None: if rpath == None:
rpath = path rpath = path
if not opts.hidden: if not opts.hidden:
files = [ f for f in files if not f.startswith(".")] files = [f for f in files if not f.startswith(".")]
dirs[:] = [ d for d in dirs if not d.startswith(".")] dirs[:] = [d for d in dirs if not d.startswith(".")]
files = [os.path.join(os.path.relpath(path, opts.path), f) for f in files] files = [os.path.join(os.path.relpath(path, opts.path), f) for f in files]
files = [f[2:] if f.startswith("./") else f for f in files ] files = [f[2:] if f.startswith("./") else f for f in files]
rfiles.extend(files) rfiles.extend(files)
return rdirs, rfiles, rpath return rdirs, rfiles, rpath
@@ -110,8 +163,8 @@ def get_files_and_folders(opts):
else: else:
for path, dirs, files in os.walk(opts.path): for path, dirs, files in os.walk(opts.path):
if not opts.hidden: if not opts.hidden:
files = [ f for f in files if not f.startswith(".")] files = [f for f in files if not f.startswith(".")]
dirs = [ d for d in dirs if not d.startswith(".")] dirs = [d for d in dirs if not d.startswith(".")]
return dirs, files, path return dirs, files, path
@@ -125,18 +178,21 @@ def generate_index(opts):
if opts.filename in files: if opts.filename in files:
opts, existing_config = HTML2setup(opts) opts, existing_config = HTML2setup(opts)
if not existing_config and not opts.overwrite: if not existing_config and not opts.overwrite:
print(opts.filename + " exists, and not generated with SimpleWebPage. Exiting.") print(
opts.filename
+ " exists, and not generated with SimpleWebPage. Exiting."
)
sys.exit(1) sys.exit(1)
files = [ f for f in files if f != opts.filename] files = [f for f in files if f != opts.filename]
files = [ f for f in files if f != opts.password_filename] files = [f for f in files if f != opts.password_filename]
files = match_files(files, opts.includes) files = match_files(files, opts.includes)
dirs.sort() dirs.sort()
files.sort() files.sort()
files.sort(key = lambda x: x.find("/") > 0) files.sort(key=lambda x: x.find("/") > 0)
with open(os.path.join(path,opts.filename), 'wt') as f: with open(os.path.join(path, opts.filename), "wt") as f:
f.write(get_header(opts)) f.write(get_header(opts))
if opts.parent: if opts.parent:
f.write(get_pathlink(path, '..')) f.write(get_pathlink(path, ".."))
for di in dirs: for di in dirs:
f.write(get_pathlink(path, di)) f.write(get_pathlink(path, di))
for fi in files: for fi in files:
@@ -151,55 +207,42 @@ def generate_password_page(path, password_file, password):
def ha(p): def ha(p):
h = 0 h = 0
for c in p: for c in p:
h = ((h<<5) - h) + ord(c) h = ((h << 5) - h) + ord(c)
h = h & 0xffffffff h = h & 0xFFFFFFFF
if h > (1<<31) - 1: if h > (1 << 31) - 1:
h -= (1<<32) h -= 1 << 32
return h return h
def scramble(p, t):
def scramble(p,t):
p += t p += t
s = '' s = ""
for i in range(len(t)): for i in range(len(t)):
s += chr(ord(p[i]) + ord(t[i])) s += chr(ord(p[i]) + ord(t[i]))
return s return s
def enc(s): def enc(s):
return base64.b64encode(s.encode('latin1')).decode('ascii') return base64.b64encode(s.encode("latin1")).decode("ascii")
def random_string(stringLength=16): def random_string(stringLength=16):
letters = string.ascii_lowercase + string.digits letters = string.ascii_lowercase + string.digits
return ''.join(random.choice(letters) for i in range(stringLength)) return "".join(random.choice(letters) for i in range(stringLength))
def get_target(filename): def get_target(filename):
splitted = os.path.splitext(filename) splitted = os.path.splitext(filename)
return ( return (splitted[0], random_string(), splitted[1])
splitted[0],
random_string(),
splitted[1]
)
target_base, target_middle, target_ext = get_target(password_file) target_base, target_middle, target_ext = get_target(password_file)
secret = "{}:{}".format( secret = "{}:{}".format(ha(password), enc(scramble(password, target_middle)))
ha(password), with open(os.path.join(path, password_file), "wt") as f:
enc(scramble(password, target_middle))
)
with open(os.path.join(path, password_file), 'wt') as f:
f.write(get_password_page(secret, target_base, target_ext)) f.write(get_password_page(secret, target_base, target_ext))
return "{}.{}{}".format( return "{}.{}{}".format(target_base, target_middle, target_ext)
target_base,
target_middle,
target_ext
)
def get_filelink(path,fname,images=False): def get_filelink(path, fname, images=False):
if os.path.islink(os.path.join(path, fname)) and not os.path.exists(os.path.join(path, fname)): if os.path.islink(os.path.join(path, fname)) and not os.path.exists(
(fsize, fsstr, fsstrb, fdstr)=(0, "NA", "NA", "NA") os.path.join(path, fname)
):
(fsize, fsstr, fsstrb, fdstr) = (0, "NA", "NA", "NA")
else: else:
fsize = os.path.getsize(os.path.join(path, fname)) fsize = os.path.getsize(os.path.join(path, fname))
fsstr = sizeof(fsize) fsstr = sizeof(fsize)
@@ -210,42 +253,37 @@ def get_filelink(path,fname,images=False):
fname_str = get_imagestr(fname) fname_str = get_imagestr(fname)
else: else:
fname_str = fname fname_str = fname
return '<tr><td><a class="link_file" href="%s">%s</a><td class="right">%s</td><td class="right bytes">%s</td><td class="right">%s</td></tr>\n'%( return (
urllib.parse.quote(fname), '<tr><td><a class="link_file" href="%s">%s</a><td class="right">%s</td><td class="right bytes">%s</td><td class="right">%s</td></tr>\n'
fname_str, % (urllib.parse.quote(fname), fname_str, fsstr, fsstrb, fdstr)
fsstr,
fsstrb,
fdstr
) )
def get_wget_lines(files): def get_wget_lines(files):
wget = "\n<!--\n" wget = "\n<!--\n"
for f in files: for f in files:
wget += "#FILE %s\n"%(urllib.parse.quote(f),) wget += "#FILE %s\n" % (urllib.parse.quote(f),)
wget += "#WGET URL=[insert URL] && curl $URL | grep '^#FILE ' | cut -c7- | sed \"s,^,$URL,\" | xargs -n1 wget\n" wget += "#WGET URL=[insert URL] && curl $URL | grep '^#FILE ' | cut -c7- | sed \"s,^,$URL,\" | xargs -n1 wget\n"
wget += "-->\n" wget += "-->\n"
return wget return wget
def get_imagestr(fname): def get_imagestr(fname):
return '<img src="%s" title="%s"/>'%( return '<img src="%s" title="%s"/>' % (urllib.parse.quote(fname), fname)
urllib.parse.quote(fname),
fname
)
def get_pathlink(path,dname):
def get_pathlink(path, dname):
fdate = time.localtime(os.path.getmtime(os.path.join(path, dname))) fdate = time.localtime(os.path.getmtime(os.path.join(path, dname)))
fdstr = time.strftime("%Y/%m/%d %H:%M:%S", fdate) fdstr = time.strftime("%Y/%m/%d %H:%M:%S", fdate)
return '<tr><td><a class="link_dir" href="%s">&#8627;&nbsp;%s/</a><td class="right">[DIR]</td><td class="right bytes">0</td><td class="right">%s</td></tr>\n'%( return (
urllib.parse.quote(dname), '<tr><td><a class="link_dir" href="%s">&#8627;&nbsp;%s/</a><td class="right">[DIR]</td><td class="right bytes">0</td><td class="right">%s</td></tr>\n'
dname, % (urllib.parse.quote(dname), dname, fdstr)
fdstr
) )
def get_password_page(secret, target_base, target_ext): def get_password_page(secret, target_base, target_ext):
return """<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> return (
"""<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<html> <html>
<head> <head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
@@ -334,27 +372,28 @@ function newPass(password, target) {
</div> </div>
</body> </body>
</html>""".replace( </html>""".replace(
"TARGET_EXT", "TARGET_EXT", target_ext, 1
target_ext, )
1 .replace("TARGET_BASE", target_base, 1)
).replace( .replace("SECRET", secret, 1)
"TARGET_BASE", )
target_base,
1
).replace(
"SECRET",
secret,
1
)
def get_header(opts): def get_header(opts):
header='''<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN"> header = (
"""<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 3.2 Final//EN">
<html> <html>
<head> <head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8"> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta name="generator" content="SimpleWebPage ''' + VERSION + '''" /> <meta name="generator" content="SimpleWebPage """
''' + setup2HTML(opts) + ''' + VERSION
<title>''' + opts.title + '''</title> + """" />
"""
+ setup2HTML(opts)
+ """
<title>"""
+ opts.title
+ """</title>
<style> <style>
/* Style modified from: https://css-tricks.com/snippets/php/display-styled-directory-contents/ */ /* Style modified from: https://css-tricks.com/snippets/php/display-styled-directory-contents/ */
* { * {
@@ -754,14 +793,19 @@ function alternate(table) {
</head> </head>
<body> <body>
<div class="container"> <div class="container">
<h1>''' + opts.title + '''</h1> <h1>"""
+ opts.title
+ """</h1>
<table class="sortable" id="fileList"><thead><tr><th>Name</th><th class="right">Size</th><th class="right">Size B</th><th class="right">Modified</th></tr></thead><tbody> <table class="sortable" id="fileList"><thead><tr><th>Name</th><th class="right">Size</th><th class="right">Size B</th><th class="right">Modified</th></tr></thead><tbody>
''' """
)
return header return header
def get_footer(): def get_footer():
return '''</tbody></table></div> return """</tbody></table></div>
</body></html>''' </body></html>"""
def is_imagefile(fname): def is_imagefile(fname):
for ext in IMAGE_EXTENSIONS: for ext in IMAGE_EXTENSIONS:
@@ -774,20 +818,21 @@ def match_files(files, glob_list):
matched = [] matched = []
for f in files: for f in files:
for g in glob_list: for g in glob_list:
if fnmatch.fnmatch(f,g): if fnmatch.fnmatch(f, g):
matched.append(f) matched.append(f)
break break
return matched return matched
def sizeof(num): def sizeof(num):
for x in ['&nbsp;B','KB','MB','GB','TB']: for x in ["&nbsp;B", "KB", "MB", "GB", "TB"]:
if num < 1024.0: if num < 1024.0:
if x=='&nbsp;B': if x == "&nbsp;B":
return "%d&nbsp;%s" % (num, x) return "%d&nbsp;%s" % (num, x)
return "%3.1f&nbsp;%s" % (num, x) return "%3.1f&nbsp;%s" % (num, x)
num /= 1024.0 num /= 1024.0
if __name__ == "__main__": if __name__ == "__main__":
opts = setup() opts = setup()
generate_index(opts) generate_index(opts)

View File

@@ -41,9 +41,9 @@ def get_files_size(path):
def get_numerical(num_string): def get_numerical(num_string):
num_string = num_string.lower() num_string = num_string.lower()
numerical_part = '' numerical_part = ""
last_digit = 0 last_digit = 0
for i,c in enumerate(num_string): for i, c in enumerate(num_string):
if is_digit(c): if is_digit(c):
last_digit = i + 1 last_digit = i + 1
numerical_part = num_string[0:last_digit] numerical_part = num_string[0:last_digit]
@@ -56,18 +56,23 @@ def get_numerical(num_string):
def parse_options(): def parse_options():
parser = argparse.ArgumentParser( parser = argparse.ArgumentParser(
description = 'Transfer time calculator', description="Transfer time calculator",
epilog = "You may omit the 'b' to time events. ex. '20 minutes / task, 7 tasks to do': %(prog)s 3/h 7" epilog="You may omit the 'b' to time events. ex. '20 minutes / task, 7 tasks to do': %(prog)s 3/h 7",
) )
parser.add_argument( parser.add_argument(
'-r', "-r",
dest = 'rate', dest="rate",
help = "Rate: inverse of speed. Instead of 10b/s, write 0.1s/b", help="Rate: inverse of speed. Instead of 10b/s, write 0.1s/b",
action = "store_true", action="store_true",
default = False default=False,
)
parser.add_argument(
"speed", help="Speed of transfer (ex. 3.2Mb/s). Time units: s, m, h, d, w"
)
parser.add_argument(
"size",
help="Data size (ex. 4.55GB), or folder/file path. Units: b, kb, mb, gb, tb, pb or kib, mib ... ",
) )
parser.add_argument('speed', help = "Speed of transfer (ex. 3.2Mb/s). Time units: s, m, h, d, w")
parser.add_argument('size', help = "Data size (ex. 4.55GB), or folder/file path. Units: b, kb, mb, gb, tb, pb or kib, mib ... ")
return parser.parse_args() return parser.parse_args()
@@ -86,7 +91,7 @@ def parse_rate(rate_string):
numerical_part = float(numerical_part) numerical_part = float(numerical_part)
multiplier = divisor_string[last_digit:] multiplier = divisor_string[last_digit:]
divider = parse_size("1"+divider_string) divider = parse_size("1" + divider_string)
if divider == None: if divider == None:
# Cannot parse # Cannot parse
return divider return divider
@@ -119,24 +124,24 @@ def parse_size(size_string):
if multiplier_part == "kb": if multiplier_part == "kb":
return 1024 * numerical_part return 1024 * numerical_part
if multiplier_part == "mb": if multiplier_part == "mb":
return 1024**2 * numerical_part return 1024 ** 2 * numerical_part
if multiplier_part == "gb": if multiplier_part == "gb":
return 1024**3 * numerical_part return 1024 ** 3 * numerical_part
if multiplier_part == "tb": if multiplier_part == "tb":
return 1024**4 * numerical_part return 1024 ** 4 * numerical_part
if multiplier_part == "pb": if multiplier_part == "pb":
return 1024**5 * numerical_part return 1024 ** 5 * numerical_part
if multiplier_part == "kib": if multiplier_part == "kib":
return 1000 * numerical_part return 1000 * numerical_part
if multiplier_part == "mib": if multiplier_part == "mib":
return 1000**2 * numerical_part return 1000 ** 2 * numerical_part
if multiplier_part == "gib": if multiplier_part == "gib":
return 1000**3 * numerical_part return 1000 ** 3 * numerical_part
if multiplier_part == "tib": if multiplier_part == "tib":
return 1000**4 * numerical_part return 1000 ** 4 * numerical_part
if multiplier_part == "pib": if multiplier_part == "pib":
return 1000**5 * numerical_part return 1000 ** 5 * numerical_part
return None return None
@@ -167,12 +172,12 @@ def parse_speed(speed_string):
def print_err(s): def print_err(s):
sys.stderr.write(str(s)+"\n") sys.stderr.write(str(s) + "\n")
sys.stderr.flush() sys.stderr.flush()
def time_human(num): def time_human(num):
return str(datetime.timedelta(seconds = num)) return str(datetime.timedelta(seconds=num))
if __name__ == "__main__": if __name__ == "__main__":
@@ -182,13 +187,13 @@ if __name__ == "__main__":
speed = parse_rate(opts.speed.lower()) speed = parse_rate(opts.speed.lower())
if speed == None: if speed == None:
print_err("Cannot parse rate ( ex. 3.5s/kb )") print_err("Cannot parse rate ( ex. 3.5s/kb )")
print_err("Rate: %s"%( opts.speed, )) print_err("Rate: %s" % (opts.speed,))
sys.exit(1) sys.exit(1)
else: else:
speed = parse_speed(opts.speed.lower()) speed = parse_speed(opts.speed.lower())
if speed == None: if speed == None:
print_err("Cannot parse speed ( ex. 3.5Mb/s )") print_err("Cannot parse speed ( ex. 3.5Mb/s )")
print_err("Speed: %s"%( opts.speed, )) print_err("Speed: %s" % (opts.speed,))
sys.exit(1) sys.exit(1)
if os.path.isfile(opts.size): if os.path.isfile(opts.size):
@@ -197,11 +202,10 @@ if __name__ == "__main__":
size = parse_size(opts.size.lower()) size = parse_size(opts.size.lower())
if size == None: if size == None:
print_err("Cannot parse size, and it's not a path either ( ex. 11Gb / file.name )") print_err(
print_err("Size: %s"%( opts.size, )) "Cannot parse size, and it's not a path either ( ex. 11Gb / file.name )"
)
print_err("Size: %s" % (opts.size,))
sys.exit(1) sys.exit(1)
print(time_human(round(size/speed))) print(time_human(round(size / speed)))

View File

@@ -1,8 +1,8 @@
#!/usr/bin/env python #!/usr/bin/env python3
import sys, os import sys, os
import BaseHTTPServer import http.server
from SimpleHTTPServer import SimpleHTTPRequestHandler from http.server import SimpleHTTPRequestHandler
def setup_options(): def setup_options():
''' Create command line options ''' ''' Create command line options '''
@@ -25,7 +25,7 @@ def setup_options():
def serve(options): def serve(options):
""" Run the web server """ """ Run the web server """
HandlerClass = SimpleHTTPRequestHandler HandlerClass = SimpleHTTPRequestHandler
ServerClass = BaseHTTPServer.HTTPServer ServerClass = http.server.HTTPServer
Protocol = "HTTP/1.0" Protocol = "HTTP/1.0"
server_address = (options.address, options.port) server_address = (options.address, options.port)
@@ -36,7 +36,7 @@ def serve(options):
httpd = ServerClass(server_address, HandlerClass) httpd = ServerClass(server_address, HandlerClass)
sa = httpd.socket.getsockname() sa = httpd.socket.getsockname()
print "Serving http://"+ sa[0]+ ":"+ str(sa[1])+ "/" print(("Serving http://"+ sa[0]+ ":"+ str(sa[1])+ "/"))
try: try:
httpd.serve_forever() httpd.serve_forever()
except KeyboardInterrupt: except KeyboardInterrupt: