Port to Python 3.

All scripts and modules have been ported to Python 3 and appear to
work.  Some changes to the lighttpd configuration were needed.
This commit is contained in:
John "Elwin" Edwards 2013-12-31 13:36:19 -05:00
parent 914f367657
commit aef04a38e1
10 changed files with 58 additions and 71 deletions

View file

@ -13,7 +13,7 @@ server.modules += ( "mod_cgi" )
## For PHP don't forget to set cgi.fix_pathinfo = 1 in the php.ini. ## For PHP don't forget to set cgi.fix_pathinfo = 1 in the php.ini.
## ##
cgi.assign = ( ".pl" => "/usr/bin/perl", cgi.assign = ( ".pl" => "/usr/bin/perl",
".cgi" => "/usr/bin/python", ".cgi" => "",
".rb" => "/usr/bin/ruby", ".rb" => "/usr/bin/ruby",
".erb" => "/usr/bin/eruby", ".erb" => "/usr/bin/eruby",
".py" => "/usr/bin/python" ) ".py" => "/usr/bin/python" )

View file

@ -451,3 +451,7 @@ server.upload-dirs = ( "/var/tmp" )
#include_shell "cat /etc/lighttpd/vhosts.d/*.conf" #include_shell "cat /etc/lighttpd/vhosts.d/*.conf"
## ##
####################################################################### #######################################################################
setenv.add-environment = (
"LC_CTYPE" => "en_US.utf8"
)

View file

@ -48,7 +48,7 @@ server.modules = (
# "mod_evasive", # "mod_evasive",
# "mod_redirect", # "mod_redirect",
# "mod_rewrite", # "mod_rewrite",
# "mod_setenv", "mod_setenv",
# "mod_usertrack", # "mod_usertrack",
) )

2
py/cleandb.py Normal file → Executable file
View file

@ -1,4 +1,4 @@
#!/usr/bin/python #!/usr/bin/python3
# cleandb.py: empty the database in an orderly fashion # cleandb.py: empty the database in an orderly fashion
import rlgalldb as rlgall import rlgalldb as rlgall

View file

@ -1,4 +1,4 @@
#!/usr/bin/python #!/usr/bin/python3
import os import os
import psycopg2 import psycopg2

View file

@ -1,10 +1,11 @@
# rlgalldb.py # rlgalldb.py
# Module for the Roguelike Gallery, using a postgres database # Module for the Roguelike Gallery, using a postgres database
# Requires Python 3.3
import os import os
import calendar
import psycopg2 import psycopg2
from datetime import datetime, tzinfo, timedelta from datetime import datetime
import pytz
# Configuration # Configuration
logdir = "/var/dgl/var/games/roguelike/" logdir = "/var/dgl/var/games/roguelike/"
@ -49,17 +50,6 @@ headerbook = {"endt":"End time", "score":"Score", "name":"Name", "xl":"XL",
offselstr = "SELECT offbytes FROM games WHERE gname = %s;" offselstr = "SELECT offbytes FROM games WHERE gname = %s;"
newoffstr = "UPDATE games SET offbytes = %s WHERE gname = %s;" newoffstr = "UPDATE games SET offbytes = %s WHERE gname = %s;"
# A representation of the UTC time zone. They say Py3k can better handle
# this madness.
class UTC(tzinfo):
def utcoffset(self, dt):
return timedelta(0)
def dst(self, dt):
return timedelta(0)
def tzname(self, dt):
return "UTC"
utc = UTC()
def getconn(): def getconn():
"Returns a database connection, or None if the connection fails." "Returns a database connection, or None if the connection fails."
try: try:
@ -71,7 +61,7 @@ def getconn():
def recnameToTS(filename): def recnameToTS(filename):
pattern = "%Y-%m-%d.%H:%M:%S.ttyrec" pattern = "%Y-%m-%d.%H:%M:%S.ttyrec"
try: try:
dt = datetime.strptime(filename, pattern).replace(tzinfo=utc) dt = datetime.strptime(filename, pattern).replace(tzinfo=pytz.utc)
return dt return dt
except ValueError: except ValueError:
return None return None
@ -90,7 +80,7 @@ def linktoArchive(entry):
"Takes an entry dict and returns a link to the ttyrec archivist." "Takes an entry dict and returns a link to the ttyrec archivist."
lstr = '<a href="/archive.cgi?name={0};game={1};time={2}">{3}</a>' lstr = '<a href="/archive.cgi?name={0};game={1};time={2}">{3}</a>'
linktext = entry["endt"].strftime("%Y/%m/%d %H:%M:%S") linktext = entry["endt"].strftime("%Y/%m/%d %H:%M:%S")
stamp = calendar.timegm(entry["endt"].utctimetuple()) stamp = int(entry["endt"].timestamp())
return lstr.format(entry["name"], entry["game"].uname, stamp, linktext) return lstr.format(entry["name"], entry["game"].uname, stamp, linktext)
def maketablerow(cells, isheader=None): def maketablerow(cells, isheader=None):
@ -156,7 +146,7 @@ class Game:
if self.sqltypes[item] == "bool": if self.sqltypes[item] == "bool":
ndict[item] = bool(int(value)) ndict[item] = bool(int(value))
elif self.sqltypes[item] == "timestamptz": elif self.sqltypes[item] == "timestamptz":
ndict[item] = datetime.fromtimestamp(int(value), utc) ndict[item] = datetime.fromtimestamp(int(value), pytz.utc)
else: else:
ndict[item] = value ndict[item] = value
return ndict return ndict
@ -181,7 +171,7 @@ class Game:
offset = cur.fetchone()[0] offset = cur.fetchone()[0]
newlist = [] newlist = []
try: try:
scr = open(self.scores) scr = open(self.scores, encoding="utf-8")
scr.seek(offset) scr.seek(offset)
self.getEntryDicts(scr, newlist) self.getEntryDicts(scr, newlist)
except IOError: except IOError:
@ -215,7 +205,7 @@ class Game:
if result: if result:
prev = result[0] prev = result[0]
else: else:
prev = datetime.fromtimestamp(0, utc); prev = datetime.fromtimestamp(0, pytz.utc);
ttyrecdir = "/var/dgl/dgldir/ttyrec/{0}/{1}/".format(nameF, self.uname) ttyrecdir = "/var/dgl/dgldir/ttyrec/{0}/{1}/".format(nameF, self.uname)
allfilekeys = [ (recnameToTS(f), f) for f in os.listdir(ttyrecdir) ] allfilekeys = [ (recnameToTS(f), f) for f in os.listdir(ttyrecdir) ]
vfilekeys = [ e for e in allfilekeys if e[0] > prev ] vfilekeys = [ e for e in allfilekeys if e[0] > prev ]
@ -242,16 +232,17 @@ class Game:
"Prints the most recent games from the logfile, NOT the database." "Prints the most recent games from the logfile, NOT the database."
newest = [] newest = []
try: try:
scr = open(self.scores) scr = open(self.scores, encoding="utf-8")
except IOError: except FileNotFoundError:
pass pass
else: else:
# Text streams don't support random seeking.
try: try:
scr.seek(self.lookback, 2) scr.buffer.seek(self.lookback, 2)
except IOError: except OSError:
scr.seek(0) # The file wasn't that long, start at the beginning scr.buffer.seek(0) # The file wasn't that long, start at the beginning
if scr.tell() != 0: if scr.buffer.tell() != 0:
scr.readline() # Throw away the incomplete line scr.buffer.readline() # Throw away the incomplete line
self.getEntryDicts(scr, newest) self.getEntryDicts(scr, newest)
newest.reverse() newest.reverse()
scr.close() scr.close()
@ -274,18 +265,11 @@ class RogueGame(Game):
"ttyrecs": "text ARRAY", "startt": "timestamptz"} "ttyrecs": "text ARRAY", "startt": "timestamptz"}
self.logdelim = " " self.logdelim = " "
self.lookback = -1500 self.lookback = -1500
colspec = "(" # Construct the insert query
valspec = "(" fields = self.sqltypes.keys()
for i, col in enumerate(self.sqltypes.keys()): colspec = ", ".join(fields)
colspec += col valspec = ", ".join([ "%({})s".format(c) for c in fields ])
valspec += "%({0})s".format(col) self.insertq = "INSERT INTO {0} ({1}) VALUES ({2});".format(self.uname,
if i == len(self.sqltypes) - 1:
colspec += ")"
valspec += ")"
else:
colspec += ", "
valspec += ", "
self.insertq = "INSERT INTO {0} {1} VALUES {2};".format(self.uname,
colspec, valspec) colspec, valspec)
# Class variables, used by some methods # Class variables, used by some methods
fields = ["name", "score", "xl", "fate", "endt"] fields = ["name", "score", "xl", "fate", "endt"]
@ -380,11 +364,12 @@ class RogueGame(Game):
def putIntoDB(self, dictlist, conn): def putIntoDB(self, dictlist, conn):
"Add the entries in dictlist to the database through connection conn, \ "Add the entries in dictlist to the database through connection conn, \
which needs the INSERT privilege." which needs the INSERT privilege."
# FIXME this monster needs to be procedurally generated fields = self.sqltypes.keys()
qstr = "INSERT INTO " + self.uname + " (endt, score, name, xl, fate, ttyrecs, startt) \ fstr = ", ".join(fields)
VALUES (%(endt)s, %(score)s, %(name)s, %(xl)s, %(fate)s, %(ttyrecs)s, %(startt)s);" vstr = ", ".join([ "%({})s".format(c) for c in fields ])
qstr = "INSERT INTO {0} ({1}) VALUES ({2});".format(self.uname, fstr, vstr);
cur = conn.cursor() cur = conn.cursor()
cur.executemany(qstr, [ d for d in dictlist if d["game"] == self ]) cur.executemany(self.insertq, [ d for d in dictlist if d["game"] == self ])
conn.commit() conn.commit()
cur.close() cur.close()
return return
@ -403,18 +388,11 @@ class ARogueGame(Game):
"fate": "text", "ttyrecs": "text ARRAY", "startt": "timestamptz"} "fate": "text", "ttyrecs": "text ARRAY", "startt": "timestamptz"}
self.logdelim = " " self.logdelim = " "
self.lookback = -1800 self.lookback = -1800
colspec = "(" # Construct the insert query
valspec = "(" fields = self.sqltypes.keys()
for i, col in enumerate(self.sqltypes.keys()): colspec = ", ".join(fields)
colspec += col valspec = ", ".join([ "%({})s".format(c) for c in fields ])
valspec += "%({0})s".format(col) self.insertq = "INSERT INTO {0} ({1}) VALUES ({2});".format(self.uname,
if i == len(self.sqltypes) - 1:
colspec += ")"
valspec += ")"
else:
colspec += ", "
valspec += ", "
self.insertq = "INSERT INTO {0} {1} VALUES {2};".format(self.uname,
colspec, valspec) colspec, valspec)
# Class variables # Class variables
fields = ["name", "score", "class", "xl", "fate", "endt"] fields = ["name", "score", "class", "xl", "fate", "endt"]
@ -492,7 +470,7 @@ gamelist = [rogue3, rogue4, rogue5, srogue, arogue5]
def playerpage(pname): def playerpage(pname):
"Generate a player's HTML page" "Generate a player's HTML page"
# Write the beginning of the page # Write the beginning of the page
ppagefi = open(ppagename.format(pname), "w") ppagefi = open(ppagename.format(pname), "w", encoding="utf-8")
ppagefi.write(phead.format(pname)) ppagefi.write(phead.format(pname))
ppagefi.write(ptop) ppagefi.write(ptop)
ppagefi.write(navplayer.format(pname)) ppagefi.write(navplayer.format(pname))
@ -509,7 +487,7 @@ def playerpage(pname):
scoresum = 0 scoresum = 0
for entry in entries: for entry in entries:
scoresum += int(entry["score"]) scoresum += int(entry["score"])
avgscr = scoresum / len(entries) avgscr = scoresum // len(entries)
ppagefi.write("<p>Average score: {0}</p>\n".format(avgscr)) ppagefi.write("<p>Average score: {0}</p>\n".format(avgscr))
# That should settle it. Print the end; then stop. # That should settle it. Print the end; then stop.
ppagefi.write(pend) ppagefi.write(pend)
@ -518,7 +496,7 @@ def playerpage(pname):
def highpage(): def highpage():
# open the page and print the beginning # open the page and print the beginning
highpfi = open(hpagename, "w") highpfi = open(hpagename, "w", encoding="utf-8")
highpfi.write(phead.format("High Scores")) highpfi.write(phead.format("High Scores"))
highpfi.write(ptop) highpfi.write(ptop)
highpfi.write(navscore.format("High Scores")) highpfi.write(navscore.format("High Scores"))

2
py/setupdb.py Normal file → Executable file
View file

@ -1,4 +1,4 @@
#!/usr/bin/python #!/usr/bin/python3
# setupdb.py: initializes the database tables used by the rlg system. # setupdb.py: initializes the database tables used by the rlg system.
import rlgalldb as rlgall import rlgalldb as rlgall

View file

@ -1,4 +1,4 @@
#!/usr/bin/python #!/usr/bin/python3
import cgi import cgi
import os import os
@ -6,6 +6,7 @@ import sys
import time import time
import calendar import calendar
from datetime import datetime from datetime import datetime
import pytz
import rlgalldb as rlgall import rlgalldb as rlgall
#import cgitb #import cgitb
@ -55,7 +56,7 @@ def input_datetime(outf, dvals=[None, None, None, None, None, None]):
emptf = '<input type="text" size="2" maxlength="2" name="{0}">' emptf = '<input type="text" size="2" maxlength="2" name="{0}">'
sstr = '<div>Date: <select name="year">\n' sstr = '<div>Date: <select name="year">\n'
# Default to today # Default to today
now = datetime.now(rlgall.utc) now = datetime.now(pytz.utc)
if dvals[0] == None: if dvals[0] == None:
dvals[0] = now.year dvals[0] = now.year
if dvals[1] == None: if dvals[1] == None:
@ -164,7 +165,7 @@ def processtime(fdata, errlist, hlist):
chtime = time.gmtime(utime) chtime = time.gmtime(utime)
for i in range(6): for i in range(6):
hlist[i] = chtime[i] hlist[i] = chtime[i]
return datetime.fromtimestamp(utime, rlgall.utc) return datetime.fromtimestamp(utime, pytz.utc)
# Now try to get a human-readable specification. # Now try to get a human-readable specification.
lerrors = [] lerrors = []
@ -261,7 +262,7 @@ def processtime(fdata, errlist, hlist):
errlist.extend(lerrors) errlist.extend(lerrors)
return None return None
#return calendar.timegm([year, month, day, hour, minute, second, 0, 0, 0]) #return calendar.timegm([year, month, day, hour, minute, second, 0, 0, 0])
return datetime(year, month, day, hour, minute, second, 0, rlgall.utc) return datetime(year, month, day, hour, minute, second, 0, pytz.utc)
# Begin processing # Begin processing
fdata = cgi.FieldStorage() fdata = cgi.FieldStorage()
@ -307,7 +308,7 @@ if dosearch:
conn.close() conn.close()
# Now we are ready to print the page. # Now we are ready to print the page.
sys.stdout.write("Content-type: text/html\r\n\r\n") sys.stdout.write("Content-Type: text/html; charset=utf-8\r\n\r\n")
sys.stdout.write(rlgall.phead.format("Archive")) sys.stdout.write(rlgall.phead.format("Archive"))
sys.stdout.write(rlgall.ptop) sys.stdout.write(rlgall.ptop)

View file

@ -1,12 +1,16 @@
#!/usr/bin/python #!/usr/bin/python3
# CGI script creating page of recent games # CGI script creating page of recent games
import sys import sys
import time import time
import rlgalldb as rlgall import rlgalldb as rlgall
# It is assumed that sys.stdout uses UTF-8 encoding. If this is not the case,
# configure the Web server to set the LC_CTYPE environment variable to a UTF-8
# locale.
# The required header # The required header
sys.stdout.write("Content-type: text/html\r\n\r\n") sys.stdout.write("Content-Type: text/html; charset=utf-8\r\n\r\n")
# The top of the page # The top of the page
sys.stdout.write(rlgall.phead.format("Recent Games")) sys.stdout.write(rlgall.phead.format("Recent Games"))
sys.stdout.write(rlgall.ptop); sys.stdout.write(rlgall.ptop);

View file

@ -1,8 +1,8 @@
#!/usr/bin/python #!/usr/bin/python3
import os import os
import sys import sys
import rlgall import rlgalldb as rlgall
playerdir = "/var/www/lighttpd/scoring/players/" playerdir = "/var/www/lighttpd/scoring/players/"
linkstr = '<li><a href="./{0}.html">{0}</a></li>\n' linkstr = '<li><a href="./{0}.html">{0}</a></li>\n'
@ -15,7 +15,7 @@ for filename in filelist:
namelist.append(filename.rsplit(".html", 1)[0]) namelist.append(filename.rsplit(".html", 1)[0])
namelist.sort() namelist.sort()
sys.stdout.write("Content-type: text/html\r\n\r\n") sys.stdout.write("Content-Type: text/html; charset=utf-8\r\n\r\n")
sys.stdout.write(rlgall.phead.format("Players")) sys.stdout.write(rlgall.phead.format("Players"))
sys.stdout.write(rlgall.ptop) sys.stdout.write(rlgall.ptop)
sys.stdout.write(rlgall.navscore.format("Players")) sys.stdout.write(rlgall.navscore.format("Players"))