More docstrings.
This commit is contained in:
parent
a866c617ec
commit
8fd463e22d
|
@ -7,10 +7,12 @@ from formatter import AbstractFormatter, NullWriter
|
||||||
from htmlentitydefs import entitydefs
|
from htmlentitydefs import entitydefs
|
||||||
from xml.sax.saxutils import quoteattr
|
from xml.sax.saxutils import quoteattr
|
||||||
|
|
||||||
|
|
||||||
def xssescape(text):
|
def xssescape(text):
|
||||||
"""Gets rid of < and > and & and, for good measure, :"""
|
"""Gets rid of < and > and & and, for good measure"""
|
||||||
return escape(text, quote=True)
|
return escape(text, quote=True)
|
||||||
|
|
||||||
|
|
||||||
class Html_cleaner(HTMLParser):
|
class Html_cleaner(HTMLParser):
|
||||||
"""
|
"""
|
||||||
Cleans HTML of any tags not matching a whitelist.
|
Cleans HTML of any tags not matching a whitelist.
|
||||||
|
|
|
@ -103,6 +103,7 @@ class Notebooks( object ):
|
||||||
@rtype: json dict
|
@rtype: json dict
|
||||||
@return: { 'notebook': notebookdict, 'note': notedict or None }
|
@return: { 'notebook': notebookdict, 'note': notedict or None }
|
||||||
@raise Access_error: the current user doesn't have access to the given notebook
|
@raise Access_error: the current user doesn't have access to the given notebook
|
||||||
|
@raise Validation_error: one of the arguments is invalid
|
||||||
"""
|
"""
|
||||||
self.check_access( notebook_id, user_id, self.__scheduler.thread )
|
self.check_access( notebook_id, user_id, self.__scheduler.thread )
|
||||||
if not ( yield Scheduler.SLEEP ):
|
if not ( yield Scheduler.SLEEP ):
|
||||||
|
@ -152,6 +153,7 @@ class Notebooks( object ):
|
||||||
@rtype: json dict
|
@rtype: json dict
|
||||||
@return: { 'note': notedict or None }
|
@return: { 'note': notedict or None }
|
||||||
@raise Access_error: the current user doesn't have access to the given notebook
|
@raise Access_error: the current user doesn't have access to the given notebook
|
||||||
|
@raise Validation_error: one of the arguments is invalid
|
||||||
"""
|
"""
|
||||||
self.check_access( notebook_id, user_id, self.__scheduler.thread )
|
self.check_access( notebook_id, user_id, self.__scheduler.thread )
|
||||||
if not ( yield Scheduler.SLEEP ):
|
if not ( yield Scheduler.SLEEP ):
|
||||||
|
@ -197,6 +199,7 @@ class Notebooks( object ):
|
||||||
@rtype: json dict
|
@rtype: json dict
|
||||||
@return: { 'note': notedict or None }
|
@return: { 'note': notedict or None }
|
||||||
@raise Access_error: the current user doesn't have access to the given notebook
|
@raise Access_error: the current user doesn't have access to the given notebook
|
||||||
|
@raise Validation_error: one of the arguments is invalid
|
||||||
"""
|
"""
|
||||||
self.check_access( notebook_id, user_id, self.__scheduler.thread )
|
self.check_access( notebook_id, user_id, self.__scheduler.thread )
|
||||||
if not ( yield Scheduler.SLEEP ):
|
if not ( yield Scheduler.SLEEP ):
|
||||||
|
@ -246,6 +249,7 @@ class Notebooks( object ):
|
||||||
@rtype: json dict
|
@rtype: json dict
|
||||||
@return: { 'new_revision': new revision of saved note, or None }
|
@return: { 'new_revision': new revision of saved note, or None }
|
||||||
@raise Access_error: the current user doesn't have access to the given notebook
|
@raise Access_error: the current user doesn't have access to the given notebook
|
||||||
|
@raise Validation_error: one of the arguments is invalid
|
||||||
"""
|
"""
|
||||||
self.check_access( notebook_id, user_id, self.__scheduler.thread )
|
self.check_access( notebook_id, user_id, self.__scheduler.thread )
|
||||||
if not ( yield Scheduler.SLEEP ):
|
if not ( yield Scheduler.SLEEP ):
|
||||||
|
@ -305,6 +309,7 @@ class Notebooks( object ):
|
||||||
@rtype: json dict
|
@rtype: json dict
|
||||||
@return: {}
|
@return: {}
|
||||||
@raise Access_error: the current user doesn't have access to the given notebook
|
@raise Access_error: the current user doesn't have access to the given notebook
|
||||||
|
@raise Validation_error: one of the arguments is invalid
|
||||||
"""
|
"""
|
||||||
self.check_access( notebook_id, user_id, self.__scheduler.thread )
|
self.check_access( notebook_id, user_id, self.__scheduler.thread )
|
||||||
if not ( yield Scheduler.SLEEP ):
|
if not ( yield Scheduler.SLEEP ):
|
||||||
|
@ -349,6 +354,7 @@ class Notebooks( object ):
|
||||||
@rtype: json dict
|
@rtype: json dict
|
||||||
@return: {}
|
@return: {}
|
||||||
@raise Access_error: the current user doesn't have access to the given notebook
|
@raise Access_error: the current user doesn't have access to the given notebook
|
||||||
|
@raise Validation_error: one of the arguments is invalid
|
||||||
"""
|
"""
|
||||||
self.check_access( notebook_id, user_id, self.__scheduler.thread )
|
self.check_access( notebook_id, user_id, self.__scheduler.thread )
|
||||||
if not ( yield Scheduler.SLEEP ):
|
if not ( yield Scheduler.SLEEP ):
|
||||||
|
@ -393,6 +399,7 @@ class Notebooks( object ):
|
||||||
@rtype: json dict
|
@rtype: json dict
|
||||||
@return: {}
|
@return: {}
|
||||||
@raise Access_error: the current user doesn't have access to the given notebook
|
@raise Access_error: the current user doesn't have access to the given notebook
|
||||||
|
@raise Validation_error: one of the arguments is invalid
|
||||||
"""
|
"""
|
||||||
self.check_access( notebook_id, user_id, self.__scheduler.thread )
|
self.check_access( notebook_id, user_id, self.__scheduler.thread )
|
||||||
if not ( yield Scheduler.SLEEP ):
|
if not ( yield Scheduler.SLEEP ):
|
||||||
|
@ -443,6 +450,7 @@ class Notebooks( object ):
|
||||||
@rtype: json dict
|
@rtype: json dict
|
||||||
@return: {}
|
@return: {}
|
||||||
@raise Access_error: the current user doesn't have access to the given notebook
|
@raise Access_error: the current user doesn't have access to the given notebook
|
||||||
|
@raise Validation_error: one of the arguments is invalid
|
||||||
"""
|
"""
|
||||||
self.check_access( notebook_id, user_id, self.__scheduler.thread )
|
self.check_access( notebook_id, user_id, self.__scheduler.thread )
|
||||||
if not ( yield Scheduler.SLEEP ):
|
if not ( yield Scheduler.SLEEP ):
|
||||||
|
@ -481,6 +489,7 @@ class Notebooks( object ):
|
||||||
@type id: id of the note
|
@type id: id of the note
|
||||||
@rtype: unicode
|
@rtype: unicode
|
||||||
@return: rendered HTML page
|
@return: rendered HTML page
|
||||||
|
@raise Validation_error: the argument is invalid
|
||||||
"""
|
"""
|
||||||
return dict( id = id )
|
return dict( id = id )
|
||||||
|
|
||||||
|
@ -510,6 +519,7 @@ class Notebooks( object ):
|
||||||
@rtype: json dict
|
@rtype: json dict
|
||||||
@return: { 'notes': [ matching notes ] }
|
@return: { 'notes': [ matching notes ] }
|
||||||
@raise Access_error: the current user doesn't have access to the given notebook
|
@raise Access_error: the current user doesn't have access to the given notebook
|
||||||
|
@raise Validation_error: one of the arguments is invalid
|
||||||
"""
|
"""
|
||||||
self.check_access( notebook_id, user_id, self.__scheduler.thread )
|
self.check_access( notebook_id, user_id, self.__scheduler.thread )
|
||||||
if not ( yield Scheduler.SLEEP ):
|
if not ( yield Scheduler.SLEEP ):
|
||||||
|
@ -561,6 +571,7 @@ class Notebooks( object ):
|
||||||
@rtype: json dict
|
@rtype: json dict
|
||||||
@return: { 'notes': [ recent notes ] }
|
@return: { 'notes': [ recent notes ] }
|
||||||
@raise Access_error: the current user doesn't have access to the given notebook
|
@raise Access_error: the current user doesn't have access to the given notebook
|
||||||
|
@raise Validation_error: one of the arguments is invalid
|
||||||
"""
|
"""
|
||||||
self.check_access( notebook_id, user_id, self.__scheduler.thread )
|
self.check_access( notebook_id, user_id, self.__scheduler.thread )
|
||||||
if not ( yield Scheduler.SLEEP ):
|
if not ( yield Scheduler.SLEEP ):
|
||||||
|
@ -601,6 +612,7 @@ class Notebooks( object ):
|
||||||
@rtype: unicode
|
@rtype: unicode
|
||||||
@return: rendered HTML page
|
@return: rendered HTML page
|
||||||
@raise Access_error: the current user doesn't have access to the given notebook
|
@raise Access_error: the current user doesn't have access to the given notebook
|
||||||
|
@raise Validation_error: one of the arguments is invalid
|
||||||
"""
|
"""
|
||||||
self.check_access( notebook_id, user_id, self.__scheduler.thread )
|
self.check_access( notebook_id, user_id, self.__scheduler.thread )
|
||||||
if not ( yield Scheduler.SLEEP ):
|
if not ( yield Scheduler.SLEEP ):
|
||||||
|
|
|
@ -84,9 +84,14 @@ class Scheduler( object ):
|
||||||
self.__idle.release()
|
self.__idle.release()
|
||||||
yield None
|
yield None
|
||||||
|
|
||||||
# used for unit tests
|
|
||||||
IDLE_SLEEP_SECONDS = 0.01
|
IDLE_SLEEP_SECONDS = 0.01
|
||||||
def wait_for( self, thread ):
|
def wait_for( self, thread ):
|
||||||
|
"""
|
||||||
|
Block until the given thread exits. Intended for use in unit tests only.
|
||||||
|
|
||||||
|
@type thread: generator
|
||||||
|
@param thread: thread to wait for
|
||||||
|
"""
|
||||||
while thread in self.__running or thread in self.__sleeping:
|
while thread in self.__running or thread in self.__sleeping:
|
||||||
sleep( self.IDLE_SLEEP_SECONDS )
|
sleep( self.IDLE_SLEEP_SECONDS )
|
||||||
|
|
||||||
|
@ -94,15 +99,32 @@ class Scheduler( object ):
|
||||||
raise self.__last_error
|
raise self.__last_error
|
||||||
|
|
||||||
def wait_until_idle( self ):
|
def wait_until_idle( self ):
|
||||||
|
"""
|
||||||
|
Block until all threads have exited. Intended for use in unit tests only.
|
||||||
|
"""
|
||||||
while len( self.__running ) > 1 or len( self.__sleeping ) > 0:
|
while len( self.__running ) > 1 or len( self.__sleeping ) > 0:
|
||||||
sleep( self.IDLE_SLEEP_SECONDS )
|
sleep( self.IDLE_SLEEP_SECONDS )
|
||||||
|
|
||||||
def sleep( self, thread ):
|
def sleep( self, thread ):
|
||||||
|
"""
|
||||||
|
Put the given thread to sleep so that is is no longer actively running.
|
||||||
|
|
||||||
|
@type thread: generator
|
||||||
|
@param thread: thread to put to sleep
|
||||||
|
"""
|
||||||
self.__idle.acquire( blocking = False )
|
self.__idle.acquire( blocking = False )
|
||||||
self.__sleeping.append( thread )
|
self.__sleeping.append( thread )
|
||||||
self.__running.remove( thread )
|
self.__running.remove( thread )
|
||||||
|
|
||||||
def add( self, thread, *args ):
|
def add( self, thread, *args ):
|
||||||
|
"""
|
||||||
|
Add the given thread to the running list for this Scheduler, and wake it up if it's asleep.
|
||||||
|
|
||||||
|
@type thread: generator
|
||||||
|
@param thread: thread to add
|
||||||
|
@type args: tuple
|
||||||
|
@param args: arguments to send() to the given thread when it is executed
|
||||||
|
"""
|
||||||
self.__idle.release()
|
self.__idle.release()
|
||||||
|
|
||||||
if thread in self.__sleeping:
|
if thread in self.__sleeping:
|
||||||
|
@ -116,6 +138,9 @@ class Scheduler( object ):
|
||||||
self.__messages[ thread ].append( args )
|
self.__messages[ thread ].append( args )
|
||||||
|
|
||||||
def shutdown( self ):
|
def shutdown( self ):
|
||||||
|
"""
|
||||||
|
Stop all running threads and shutdown the Scheduler.
|
||||||
|
"""
|
||||||
self.__done = True
|
self.__done = True
|
||||||
self.__idle.release()
|
self.__idle.release()
|
||||||
self.__scheduler_thread.join()
|
self.__scheduler_thread.join()
|
||||||
|
|
|
@ -39,7 +39,8 @@ def update_client( function ):
|
||||||
|
|
||||||
Note that this decorator itself is a generator function and works by passing along next()/send()
|
Note that this decorator itself is a generator function and works by passing along next()/send()
|
||||||
calls to its decorated generator. Only yielded values that are dictionaries are sent to the
|
calls to its decorated generator. Only yielded values that are dictionaries are sent to the
|
||||||
client. All other yielded values are in turn yielded by this decorator itself.
|
client via the provided queue. All other types of yielded values are in turn yielded by this
|
||||||
|
decorator itself.
|
||||||
"""
|
"""
|
||||||
def put_message( *args, **kwargs ):
|
def put_message( *args, **kwargs ):
|
||||||
# look in the called function's kwargs for the queue where results should be sent
|
# look in the called function's kwargs for the queue where results should be sent
|
||||||
|
|
|
@ -103,7 +103,22 @@ def update_auth( function ):
|
||||||
|
|
||||||
|
|
||||||
class Users( object ):
|
class Users( object ):
|
||||||
|
"""
|
||||||
|
Controller for dealing with users, corresponding to the "/users" URL.
|
||||||
|
"""
|
||||||
def __init__( self, scheduler, database, http_url ):
|
def __init__( self, scheduler, database, http_url ):
|
||||||
|
"""
|
||||||
|
Create a new Users object.
|
||||||
|
|
||||||
|
@type scheduler: controller.Scheduler
|
||||||
|
@param scheduler: scheduler to use for asynchronous calls
|
||||||
|
@type database: controller.Database
|
||||||
|
@param database: database that users are stored in
|
||||||
|
@type http_url: unicode
|
||||||
|
@param http_url: base URL to use for non-SSL http requests, or an empty string
|
||||||
|
@rtype: Users
|
||||||
|
@return: newly constructed Users
|
||||||
|
"""
|
||||||
self.__scheduler = scheduler
|
self.__scheduler = scheduler
|
||||||
self.__database = database
|
self.__database = database
|
||||||
self.__http_url = http_url
|
self.__http_url = http_url
|
||||||
|
@ -121,6 +136,25 @@ class Users( object ):
|
||||||
signup_button = unicode,
|
signup_button = unicode,
|
||||||
)
|
)
|
||||||
def signup( self, username, password, password_repeat, email_address, signup_button ):
|
def signup( self, username, password, password_repeat, email_address, signup_button ):
|
||||||
|
"""
|
||||||
|
Create a new User based on the given information. Start that user with their own Notebook and a
|
||||||
|
"welcome to your wiki" Note. For convenience, login the newly created user as well.
|
||||||
|
|
||||||
|
@type username: unicode (alphanumeric only)
|
||||||
|
@param username: username to use for this new user
|
||||||
|
@type password: unicode
|
||||||
|
@param password: password to use
|
||||||
|
@type password_repeat: unicode
|
||||||
|
@param password_repeat: password to use, again
|
||||||
|
@type email_address: unicode
|
||||||
|
@param email_address: user's email address
|
||||||
|
@type signup_button: unicode
|
||||||
|
@param signup_button: ignored
|
||||||
|
@rtype: json dict
|
||||||
|
@return: { 'redirect': url, 'authenticated': userdict }
|
||||||
|
@raise Signup_error: passwords don't match or the username is unavailable
|
||||||
|
@raise Validation_error: one of the arguments is invalid
|
||||||
|
"""
|
||||||
if password != password_repeat:
|
if password != password_repeat:
|
||||||
raise Signup_error( u"The passwords you entered do not match. Please try again." )
|
raise Signup_error( u"The passwords you entered do not match. Please try again." )
|
||||||
|
|
||||||
|
@ -171,6 +205,19 @@ class Users( object ):
|
||||||
login_button = unicode,
|
login_button = unicode,
|
||||||
)
|
)
|
||||||
def login( self, username, password, login_button ):
|
def login( self, username, password, login_button ):
|
||||||
|
"""
|
||||||
|
Attempt to authenticate the user. If successful, associate the given user with the current
|
||||||
|
session.
|
||||||
|
|
||||||
|
@type username: unicode (alphanumeric only)
|
||||||
|
@param username: username to login
|
||||||
|
@type password: unicode
|
||||||
|
@param password: the user's password
|
||||||
|
@rtype: json dict
|
||||||
|
@return: { 'redirect': url, 'authenticated': userdict }
|
||||||
|
@raise Authentication_error: invalid username or password
|
||||||
|
@raise Validation_error: one of the arguments is invalid
|
||||||
|
"""
|
||||||
self.__database.load( "User %s" % username, self.__scheduler.thread )
|
self.__database.load( "User %s" % username, self.__scheduler.thread )
|
||||||
user = ( yield Scheduler.SLEEP )
|
user = ( yield Scheduler.SLEEP )
|
||||||
|
|
||||||
|
@ -194,6 +241,12 @@ class Users( object ):
|
||||||
@async
|
@async
|
||||||
@update_client
|
@update_client
|
||||||
def logout( self ):
|
def logout( self ):
|
||||||
|
"""
|
||||||
|
Deauthenticate the user and log them out of their current session.
|
||||||
|
|
||||||
|
@rtype: json dict
|
||||||
|
@return: { 'redirect': url, 'deauthenticated': True }
|
||||||
|
"""
|
||||||
yield dict(
|
yield dict(
|
||||||
redirect = self.__http_url + u"/",
|
redirect = self.__http_url + u"/",
|
||||||
deauthenticated = True,
|
deauthenticated = True,
|
||||||
|
@ -209,6 +262,15 @@ class Users( object ):
|
||||||
user_id = Valid_id( none_okay = True ),
|
user_id = Valid_id( none_okay = True ),
|
||||||
)
|
)
|
||||||
def current( self, user_id ):
|
def current( self, user_id ):
|
||||||
|
"""
|
||||||
|
Return information on the currently logged-in user. If not logged in, default to the anonymous
|
||||||
|
user.
|
||||||
|
|
||||||
|
@type user_id: unicode
|
||||||
|
@param user_id: id of current logged-in user (if any), determined by @grab_user_id
|
||||||
|
@rtype: json dict
|
||||||
|
@return: { 'user': userdict or None, 'notebooks': notebooksdict, 'http_url': url }
|
||||||
|
"""
|
||||||
# if there's no logged-in user, default to the anonymous user
|
# if there's no logged-in user, default to the anonymous user
|
||||||
self.__database.load( user_id or u"User anonymous", self.__scheduler.thread )
|
self.__database.load( user_id or u"User anonymous", self.__scheduler.thread )
|
||||||
user = ( yield Scheduler.SLEEP )
|
user = ( yield Scheduler.SLEEP )
|
||||||
|
@ -217,6 +279,7 @@ class Users( object ):
|
||||||
yield dict(
|
yield dict(
|
||||||
user = None,
|
user = None,
|
||||||
notebooks = None,
|
notebooks = None,
|
||||||
|
http_url = u"",
|
||||||
)
|
)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
Reference in New Issue