Unnamed Fossil Project

Check-in [d3092a3ba5]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:More workup for DB design, acct/list/item org, start building out REST ops.
Downloads: Tarball | ZIP archive | SQL archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA3-256:d3092a3ba562cbfecb2629e0309029490b43427f8254a2c392aa8e702b01d651
User & Date: vandys 2019-06-20 16:23:18
Context
2019-06-20
16:37
Get some of it running for the first time. check-in: 8a1ba772ee user: vandys tags: trunk
16:23
More workup for DB design, acct/list/item org, start building out REST ops. check-in: d3092a3ba5 user: vandys tags: trunk
2019-06-19
16:01
Work up DB design for authen check-in: 68cb1d9d07 user: vandys tags: trunk
Changes
Hide Diffs Unified Diffs Ignore Whitespace Patch

Changes to get.py.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
..
37
38
39
40
41
42
43
44

45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91

92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107

108
109
110
111
112
113
114
115
116
117
118
119
120
121
122

123
124
125
126


127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156

157
#
# get.py
#	Mixin to implement HTML GET operations
#
#	One page webapp
#
import sys
import sqlite3
import items

# The GET part of our handling
class GET_mixin(object):

    def __init__(self):
................................................................................
	# (Each will be added for each item; I doubt this will
	#  ever become noticeable, but if so I'll add a distinct
	#  DB table to enumerate lists (and ownership, and members,
	#  and...)
	approot = self.server.approot
	db = sqlite3.connect(approot.dbname)
	c = db.cursor()
	c.execute("select list from items")

	lists = set()
	for tup in c:
	    lists.add(tup[0])

	# Here they are
	return True,self.send_json(lists)

    # /<listname>.json[?gen=<number>]
    #
    # Get <listname>'s contents, waiting for new contents if
    #  nothing newer than generation <number> exists yet.
    def send_list(self):
	pp = self.paths
	if (len(pp) != 3) or (pp[0] != 'l'):
	    return False,None

	# Not a known list
	nm = pp[1]
	l = ilist.ilists.get(nm)
	if l is None:
	    return False,None

	# Ok column #?
	col = l.getCol(urllib.unquote_plus(pp[2]))
	if col is None:
	    return False,None

	# Give them this static page now that we've made sure
	#  it's a reasonable list and column
	buf = self.readf("html/v_ilist.html")
	return True,self.send_result(buf, "text/html")

    # Encoded JSON list of available lists
    # /rest/l.json
    def send_json_lists(self):
	if not self.path_match("rest", "l.json"):
	    return False,None
	res = {"user": self.user}
	res["items"] = l = [ {"name": i.name, "owner": i.owner}
	    for i in ilist.ilists.itervalues() ]
	l.sort(key=lambda i: i["name"])
	return True,self.send_json(res)

    # Encoded JSON representing this column
    # /rest/l/<name>/<column# or name>.json
    def send_json_list(self):
	# Verify path, get list name

	pp = self.paths
	if (len(pp) != 4) or (pp[0] != "rest") or (pp[1] != "l"):
	    return False,None

	# Verify list name
	nm = pp[2]
	l = ilist.ilists.get(nm)
	if l is None:
	    return False,None

	# Verify colum name
	col = pp[3]
	if (not col.endswith(".json")) or (len(col) < 6):
	    return False,None
	colname = urllib.unquote_plus(col[:-5])
	col = l.getCol(colname)

	if col is None:
	    return False,None
	colidx = l.cols.index(col)

	# "l" points at our overall list, "c" at our selected column
	# Let the column encode most of the results
	res = l.asDict(colidx)
	res["user"] = self.user

	return True,self.send_json(res)

    # JSON of a new change
    # /rest/l/<name>/<column# or name>/changes.json?gen=XXX
    # We'll sleep if we don't have something new yet
    def send_json_changes(self):

	pp = self.paths
	if (len(pp) != 5) or (pp[0] != "rest") or (pp[1] != "l") or \
		(pp[4] != "changes.json") or ("gen" not in self.vals):
	    return False,None



	# List and column
	nm = pp[2]
	l = ilist.ilists.get(nm)
	if l is None:
	    return False,None
	colname = urllib.unquote_plus(pp[3])
	col = l.getCol(colname)
	if col is None:
	    return False,None
	colidx = l.cols.index(col)

	# Generation
	try:
	    gen = int(self.vals["gen"])
	except:
	    return False,None

	# Waiting for an update
	sys.stderr.write("json changes %d versus %d\n" % (gen, col.gen))
	if gen == (col.gen + 1):
	    resp = delta.await(l, colidx)
	    sys.stderr.write(" changes back %r\n" % (resp,))
	    # We blocked, here is the result

	# If they've fallen behind, tell them just to do a bulk
	#  catch-up
	else:
	    resp = [{"gen": -1}]


	return True,self.send_json(resp)




<
<
|







 







|
>







|





|


|
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|

<
<
<
<
>
|
<
<
<
<
<
<
<
<
<
<
<
<
<
|
<
>
|
<
<
<
<
<
<
<
<
|

<
<
<
<
>
|
<
<
<
>
>

<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
>
|
1
2
3
4


5
6
7
8
9
10
11
12
..
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61























62
63




64
65













66

67
68








69
70




71
72



73
74
75





























76
77
#
# get.py
#	Mixin to implement HTML GET operations
#


import sys, urllib
import sqlite3
import items

# The GET part of our handling
class GET_mixin(object):

    def __init__(self):
................................................................................
	# (Each will be added for each item; I doubt this will
	#  ever become noticeable, but if so I'll add a distinct
	#  DB table to enumerate lists (and ownership, and members,
	#  and...)
	approot = self.server.approot
	db = sqlite3.connect(approot.dbname)
	c = db.cursor()
	c.execute("select name from lists where owner=? or global=1",
	    (uid,))
	lists = set()
	for tup in c:
	    lists.add(tup[0])

	# Here they are
	return True,self.send_json(lists)

    # /l/<listname>.json[?gen=<number>]
    #
    # Get <listname>'s contents, waiting for new contents if
    #  nothing newer than generation <number> exists yet.
    def send_list(self):
	pp = self.paths
	if (len(pp) != 2) or (pp[0] != 'l') or (not pp[1].endswith(".json")):
	    return False,None

	# Logged in?  (This also verified that self.vals{} is there.)
	if not self.authenticated():























	    return True,self.send_error(403)





	# Known list?
	approot = self.server.approot













	nm = urllib.unquote_plus(pp[1][:-5])

	l = approot.get_list(nm)
	if l is None:








	    return True,self.send_error(404)





	# Wait for new revision?
	gen = self.vals.get("gen", -1)



	if l.gen <= gen:
	    l.await_gen()






























	# How the list looks now
	return True.self.send_json(l.as_json())

Changes to items.py.

1
2
3
4

5
6
7
8
9


10
11
12
13
14
15
16
..
35
36
37
38
39
40
41




42
43
44







45


















46
47
48
49
50
51
52
..
53
54
55
56
57
58
59
60
61


62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83




84
85
86
87
88
89
90
91
92
93
94
95
#
# items.py
#	Represent the items on a shopping list
#

import sqlite3

class List(object):
    def __init__(self, approot, name):
	self.approot = approot


	self.name = name
	self.items = []
	self.gen = 0

	# Load any existing state
	gen = -1
	db = self.get_db()
................................................................................
	db.close()

    # Return a DB connection
    def get_db(self):
	return sqlite3.connect(self.approot.dbname)

    # Allocate the next generational value for list state




    def next_gen(self):
	res = self.gen
	self.gen += 1







	return res



















class Item(object):
    def __init__(self, l, idx, name, gen):

	# Our parent list
	self.parent = l

................................................................................
	# Always start at first index type (usually red)
	# When -1, this is a deleted item
	self.idx = 0

	# Item's string name
	self.name = name

	# Generation of change
	self.gen = gen



    # Save ourselves to the DB
    def sync(self):
	db = self.parent.get_db()
	c = db.cursor()
	c.execute("insert or replace into items values(?,?,?,?) ",
	    (self.parent.name, self.name, self.idx, self.gen))
	db.commit()
	c.close()
	db.close()

    def assign(self, idx):
	# No change
	if self.idx == idx:
	    return

	# New value & generation
	self.idx = idx
	self.gen = self.parent.next_gen()

	# Put into DB
	self.sync()





# Shared state for any given list name
Lists = {}
def get_list(approot, name):
    global Lists

    l = Lists.get(name)
    if l is None:
	l = List(approot, name)
    else:
	assert l.approot is approot
    return l




>



|

>
>







 







>
>
>
>

|
|
>
>
>
>
>
>
>

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







 







|
|
>
>





|
|











|



>
>
>
>












1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
..
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
..
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
#
# items.py
#	Represent the items on a shopping list
#
import json
import sqlite3

class List(object):
    def __init__(self, approot, lid, isglob, name):
	self.approot = approot
	self.lid = lid
	self.isglob = isglob
	self.name = name
	self.items = []
	self.gen = 0

	# Load any existing state
	gen = -1
	db = self.get_db()
................................................................................
	db.close()

    # Return a DB connection
    def get_db(self):
	return sqlite3.connect(self.approot.dbname)

    # Allocate the next generational value for list state
    #
    # Since we release waiters on new versions of the list,
    #  be sure to put your changes in before rev'ing the
    #  generation counter.
    def next_gen(self):
	self.gen += 1
	res = self.gen

	# Release anybody waiting for new changes
	waiters = set(self.waiters)
	self.waiters.clear()
	for sema in waiters:
	    sema.release()

	return res

    # Wait for new change
    def await_gen(self):
	sema = threading.Semaphore(0)
	self.waiters.add(sema)
	sema.acquire()
	# Resume here after next_gen()

    # A JSON wrap-up of our content
    def as_json(self):
	items = []
	res = {"name": self.name, "items": items}
	for i in self.items:
	    # Don't share deleted
	    if i.deleted():
		continue
	    items.append(i.for_json())
	return json.dumps(res)

class Item(object):
    def __init__(self, l, idx, name, gen):

	# Our parent list
	self.parent = l

................................................................................
	# Always start at first index type (usually red)
	# When -1, this is a deleted item
	self.idx = 0

	# Item's string name
	self.name = name

    # JSON friendly rep of our state
    def for_json(self):
	assert self.idx != -1
	return {"name": self.name, "idx": self.idx}

    # Save ourselves to the DB
    def sync(self):
	db = self.parent.get_db()
	c = db.cursor()
	c.execute("insert or replace into items values(?,?,?) ",
	    (self.parent.name, self.name, self.idx))
	db.commit()
	c.close()
	db.close()

    def assign(self, idx):
	# No change
	if self.idx == idx:
	    return

	# New value & generation
	self.idx = idx
	self.parent.next_gen()

	# Put into DB
	self.sync()

    # Tell if we're a deleted item slot
    def deleted(self):
	return self.idx == -1

# Shared state for any given list name
Lists = {}
def get_list(approot, name):
    global Lists

    l = Lists.get(name)
    if l is None:
	l = List(approot, name)
    else:
	assert l.approot is approot
    return l

Changes to main.py.

27
28
29
30
31
32
33


34
35
36
37
38
39
40
41
42
43
44


45







46
47
48
49
50
51
52
53
54








55
56































57
58
59
60
61
62
63
    # Yes, passwords in the clear.  This is a personal service, and
    #  I need to recover passwords often, and don't really care if
    #  somebody breaks into the FS and discovers the passwords.
    def authenticated(self):
	self.user = None
	if not self.vals:
	    return False


	u = vals.get("user")
	pw = vals.get("pw")
	if (not u) or (not pw):
	    return False
	db = sqlite3.connect(self.approot.dbname)
	c = cursor()
	c.execute("select uid from users where name=? and pw=?",
	    (u, pw))
	tup = c.find_one()
	if not tup:
	    return False


	self.user = name







	return True

# Root of our app server
class App(chore.server.Server):

    def __init__(self, dbn):
	# sqlite3 database file
	self.dbname = dbn









	# Let Chore handle most things
	chore.server.Server.__init__(self, config, App_Handler);
































if __name__ == "__main__":
    if len(sys.argv) != 2:
	sys.stderr.write("Usage is: %s <file.db>\n" %
	    (sys.argv[0],))
	sys.exit(1)








>
>











>
>

>
>
>
>
>
>
>









>
>
>
>
>
>
>
>


>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
    # Yes, passwords in the clear.  This is a personal service, and
    #  I need to recover passwords often, and don't really care if
    #  somebody breaks into the FS and discovers the passwords.
    def authenticated(self):
	self.user = None
	if not self.vals:
	    return False

	# Verify user/pass
	u = vals.get("user")
	pw = vals.get("pw")
	if (not u) or (not pw):
	    return False
	db = sqlite3.connect(self.approot.dbname)
	c = cursor()
	c.execute("select uid from users where name=? and pw=?",
	    (u, pw))
	tup = c.find_one()
	if not tup:
	    return False

	# Success
	self.user = name

	# Cache resolution
	approot = self.server.approot
	self.uid = uid = int(tup[0])
	approot.uids[uid] = name
	approot.uids[name] = uid

	return True

# Root of our app server
class App(chore.server.Server):

    def __init__(self, dbn):
	# sqlite3 database file
	self.dbname = dbn

	# Cache uname <-> uid
	self.uids = {}

	# In-memory list state.
	# Keyed by (uid,name) for private lists,
	#  name for public.
	self.lists = {}

	# Let Chore handle most things
	chore.server.Server.__init__(self, config, App_Handler);

    # Get list state, pulling from DB on first reference
    def get_list(self, uid, name):
	# Already known?
	res = self.lists.get( (uid,name) )
	if res is not None:
	    return res
	res = self.lists.get(name)
	if res is not None:
	    return res

	# Try from DB, owned by us
	db = sqlite3.connect(self.dbname)
	c = db.cursor()
	c.execute("select lid,global from lists where owner=? and name=?",
	    (uid, name))
	tup = c.find_one()
	if tup is not None:
	    lid,isglob = tup
	    res = self.lists[ (uid, name) ] = List(self, lid, isglob, name)
	    return res

	# Now global DB's with this name
	c.execute("select lid from lists where name=? and global=1",
	    (name,))
	tup = c.find_one()
	if tup is None:
	    return None
	lid = tup[0]
	res = self.lists[name] = List(self, lid, True, name)
	return res

if __name__ == "__main__":
    if len(sys.argv) != 2:
	sys.stderr.write("Usage is: %s <file.db>\n" %
	    (sys.argv[0],))
	sys.exit(1)

Changes to mkdb.sql.



1
2
3
4
5
6
7

8
9
10
11


12
13
14

15
16
17
18
19
20
21


create table users(
    uid integer primary key autoincrement,
    name text,
    pw text,
    unique(name)
);


create table lists(
    lid integer primary key autoincrement,
    owner integer,
    name text,


    unique(owner,name)
);


create table items(
    list integer,
    name text,
    idx integer,
    gen integer,
    unique(list,name)
);
>
>







>




>
>



>




<


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24

25
26

/* User accounts which can access lists */
create table users(
    uid integer primary key autoincrement,
    name text,
    pw text,
    unique(name)
);

/* Each of these is a a list with items */
create table lists(
    lid integer primary key autoincrement,
    owner integer,
    name text,
    global integer,
    gen integer,
    unique(owner,name)
);

/* Any one item within a list */
create table items(
    list integer,
    name text,
    idx integer,

    unique(list,name)
);

Changes to put.py.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21


22
23
24
25
26

27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70

71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87

88
89

90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115







116
117
118
119
120
121
122
123
124
125



126
127
128
129





130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150

151
#
# put.py
#	Mixin to implement HTML PUT operations
#
# /rest/l/<listname>/<colname>?dx=<dir>
#	Move item right/left (dx = 1/-1)
#
import urllib
import ilist, delta

# The PUT part of our handling
class PUT_mixin(object):

    # Configure our WPlayer GET treatment
    def __init__(self):

	# PUT handlers
	self.dispatchers.append( ("PUT", self.put_dx) )
	self.dispatchers.append( ("PUT", self.put_colname) )

    # /l/<listname>/<colname>


    #  0     1         2
    # Change column's name
    def put_colname(self, buf):
	pp = self.paths
	if (len(pp) != 3) or (pp[0] != 'l'):

	    return False,None

	# List and column name/index
	lnm = urllib.unquote_plus(pp[1])
	l = ilist.ilists.get(lnm)
	if l is None:
	    return False,None
	colnm = urllib.unquote_plus(pp[2]);
	col = l.getCol(colnm)
	if col is None:
	    return False,None
	colidx = l.cols.index(col)

	# Ok name?
	if not ilist.ok_obname(buf):
	    res = {"code": "Illegal name"}
	    return True,self.send_json(res)

	# Change data structure
	col.name = buf
	l.dump()

	# Tell about the change
	delta.changed("change", col.gen, ["l", lnm, colidx], buf)
	if colidx > 0:
	    col2 = l.cols[colidx-1]
	    col2.gen += 1
	    delta.changed("change", col2.gen,
		["l", lnm, colidx-1, "next"], buf)
	if colidx < (len(l.cols)-1):
	    col2 = l.cols[colidx+1]
	    col2.gen += 1
	    delta.changed("change", col2.gen,
		["l", lnm, colidx+1, "prev"], buf)
	delta.commit()

	# Success
	res = {"code": "OK"}
	return True,self.send_json(res)

    # Item motion?
    # /rest/l/<listname>/<colname>/<item>?dx=<dir>
    #   0   1     2          3       4
    def put_dx(self, buf):

	pp = self.paths
	if (len(pp) != 5) or \
		(pp[0] != 'rest') or (pp[1] != 'l') or \
		(not pp[2]) or (not pp[3]) or (not pp[4]):
	    return False,None

	# Direction
	try:
	    dx = int(self.vals["dx"])
	    if dx not in (-1, 1):
		return False,None
	except:
	    return False,None

	# List name
	lnm = urllib.unquote_plus(pp[2])
	l = ilist.ilists.get(lnm)

	if l is None:
	    return False,None


	# Column name & current index
	colnm = urllib.unquote_plus(pp[3]);
	col = l.getCol(colnm)
	if col is None:
	    return False,None
	colidx = l.cols.index(col)

	# Item name
	nm = urllib.unquote_plus(pp[4])
	if (not nm) or (nm not in col.data):
	    return False,None
	nmidx = col.data.index(nm)

	# Not going to be here
	del col.data[nmidx]
	col.gen += 1
	delta.changed("del", col.gen, ["l", lnm, colidx, nm])

	# Going left?
	if dx < 0:
	    # Move to previously column (it's deleted
	    #  once you swipe it off column 0)
	    if colidx > 0:
		col2idx = colidx-1
		col2 = l.cols[col2idx]







	    else:
		col2idx = col2 = None
	else:
	    # Going right
	    col2idx = colidx+1
	    if colidx == (len(l.cols)-1):
		# Need a new column
		col2name = "%d" % (col2idx,)
		col2 = ilist.ICol(col2name)
		l.cols.append(col2)




		# The list changes with a new column
		l.gen += 1
		delta.changed("add", l.gen, ["l", lnm, col2idx])






		# And the notion of "next" becomes real for
		#  the current column
		col.gen += 1
		delta.changed("change", col.gen,
		    ["l", lnm, colidx, "next"], col2name)

	    else:
		col2 = l.cols[col2idx]

	# If our item moved to a different column, update it
	if col2 is not None:
	    col2.data.append(nm)
	    col2.data.sort()
	    col2.gen += 1
	    delta.changed("add", col2idx, ["l", lnm, col2idx, nm])

	# We're in business
	l.dump()
	delta.commit()
	resp = {"code": "OK"}

	return True,self.send_json(resp)




<
<
<
|
|








|
<

<
>
>
|
<
|

|
>


|
|
<
<
<
<
<
<
<
<
<
<
<
<
|

<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
>
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
<
>

<
>

<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
>
>
>
>
>
>
>

<
<
<
<
<
<
<
<
<
>
>
>

<
<
<
>
>
>
>
>

<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
>
|
1
2
3
4



5
6
7
8
9
10
11
12
13
14
15

16

17
18
19

20
21
22
23
24
25
26
27












28
29


























30
31














32

33
34

35
36

























37
38
39
40
41
42
43
44









45
46
47
48



49
50
51
52
53
54




















55
56
#
# put.py
#	Mixin to implement HTML PUT operations
#



import urllib, json
import items

# The PUT part of our handling
class PUT_mixin(object):

    # Configure our WPlayer GET treatment
    def __init__(self):

	# PUT handlers
	self.dispatchers.append( ("PUT", self.put_list) )



    # /l/listname.json
    #
    # Ops on the named list

    def put_list(self, buf):
	pp = self.paths
	if (len(pp) != 2) or (pp[0] != 'l') or \
		(not pp[1].endswith(".json")):
	    return False,None

	# Logged in?
	if not self.authenticated():












	    return True,self.send_error(403)



























	# Known list?
	approot = self.server.approot














	nm = urllib.unquote_plus(pp[1][:-5])

	l = approot.get_list(self.uid, nm)
	if l is None:

	    return True,self.send_error(404)


























	# Decode op
	try:
	    d = json.loads(buf)
	    op = str(d["op"])
	    if op == "idx":
		item = str(d["name"])
		val = int(d["val"])
	    else:









		return True,self.send_error(400, "No such op")
	except:
	    return True,self.send_error(400, "Malformed JSON")




	# Update item, and churn the list
	if op == "idx":
	    if l.set_idx(item, val):
		return True,self.send_json({"ok": True})
	    return True,self.send_error(400, "Bad item")





















	# "Can't Happen"
	return True,self.send_error(500)