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 xml.sax.saxutils import quoteattr
|
||||
|
||||
|
||||
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)
|
||||
|
||||
|
||||
class Html_cleaner(HTMLParser):
|
||||
"""
|
||||
Cleans HTML of any tags not matching a whitelist.
|
||||
|
|
|
@ -103,6 +103,7 @@ class Notebooks( object ):
|
|||
@rtype: json dict
|
||||
@return: { 'notebook': notebookdict, 'note': notedict or None }
|
||||
@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 )
|
||||
if not ( yield Scheduler.SLEEP ):
|
||||
|
@ -152,6 +153,7 @@ class Notebooks( object ):
|
|||
@rtype: json dict
|
||||
@return: { 'note': notedict or None }
|
||||
@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 )
|
||||
if not ( yield Scheduler.SLEEP ):
|
||||
|
@ -197,6 +199,7 @@ class Notebooks( object ):
|
|||
@rtype: json dict
|
||||
@return: { 'note': notedict or None }
|
||||
@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 )
|
||||
if not ( yield Scheduler.SLEEP ):
|
||||
|
@ -246,6 +249,7 @@ class Notebooks( object ):
|
|||
@rtype: json dict
|
||||
@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 Validation_error: one of the arguments is invalid
|
||||
"""
|
||||
self.check_access( notebook_id, user_id, self.__scheduler.thread )
|
||||
if not ( yield Scheduler.SLEEP ):
|
||||
|
@ -305,6 +309,7 @@ class Notebooks( object ):
|
|||
@rtype: json dict
|
||||
@return: {}
|
||||
@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 )
|
||||
if not ( yield Scheduler.SLEEP ):
|
||||
|
@ -349,6 +354,7 @@ class Notebooks( object ):
|
|||
@rtype: json dict
|
||||
@return: {}
|
||||
@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 )
|
||||
if not ( yield Scheduler.SLEEP ):
|
||||
|
@ -393,6 +399,7 @@ class Notebooks( object ):
|
|||
@rtype: json dict
|
||||
@return: {}
|
||||
@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 )
|
||||
if not ( yield Scheduler.SLEEP ):
|
||||
|
@ -443,6 +450,7 @@ class Notebooks( object ):
|
|||
@rtype: json dict
|
||||
@return: {}
|
||||
@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 )
|
||||
if not ( yield Scheduler.SLEEP ):
|
||||
|
@ -481,6 +489,7 @@ class Notebooks( object ):
|
|||
@type id: id of the note
|
||||
@rtype: unicode
|
||||
@return: rendered HTML page
|
||||
@raise Validation_error: the argument is invalid
|
||||
"""
|
||||
return dict( id = id )
|
||||
|
||||
|
@ -510,6 +519,7 @@ class Notebooks( object ):
|
|||
@rtype: json dict
|
||||
@return: { 'notes': [ matching notes ] }
|
||||
@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 )
|
||||
if not ( yield Scheduler.SLEEP ):
|
||||
|
@ -561,6 +571,7 @@ class Notebooks( object ):
|
|||
@rtype: json dict
|
||||
@return: { 'notes': [ recent notes ] }
|
||||
@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 )
|
||||
if not ( yield Scheduler.SLEEP ):
|
||||
|
@ -601,6 +612,7 @@ class Notebooks( object ):
|
|||
@rtype: unicode
|
||||
@return: rendered HTML page
|
||||
@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 )
|
||||
if not ( yield Scheduler.SLEEP ):
|
||||
|
|
|
@ -84,9 +84,14 @@ class Scheduler( object ):
|
|||
self.__idle.release()
|
||||
yield None
|
||||
|
||||
# used for unit tests
|
||||
IDLE_SLEEP_SECONDS = 0.01
|
||||
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:
|
||||
sleep( self.IDLE_SLEEP_SECONDS )
|
||||
|
||||
|
@ -94,15 +99,32 @@ class Scheduler( object ):
|
|||
raise self.__last_error
|
||||
|
||||
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:
|
||||
sleep( self.IDLE_SLEEP_SECONDS )
|
||||
|
||||
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.__sleeping.append( thread )
|
||||
self.__running.remove( thread )
|
||||
|
||||
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()
|
||||
|
||||
if thread in self.__sleeping:
|
||||
|
@ -116,6 +138,9 @@ class Scheduler( object ):
|
|||
self.__messages[ thread ].append( args )
|
||||
|
||||
def shutdown( self ):
|
||||
"""
|
||||
Stop all running threads and shutdown the Scheduler.
|
||||
"""
|
||||
self.__done = True
|
||||
self.__idle.release()
|
||||
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()
|
||||
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 ):
|
||||
# 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 ):
|
||||
"""
|
||||
Controller for dealing with users, corresponding to the "/users" 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.__database = database
|
||||
self.__http_url = http_url
|
||||
|
@ -121,6 +136,25 @@ class Users( object ):
|
|||
signup_button = unicode,
|
||||
)
|
||||
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:
|
||||
raise Signup_error( u"The passwords you entered do not match. Please try again." )
|
||||
|
||||
|
@ -171,6 +205,19 @@ class Users( object ):
|
|||
login_button = unicode,
|
||||
)
|
||||
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 )
|
||||
user = ( yield Scheduler.SLEEP )
|
||||
|
||||
|
@ -194,6 +241,12 @@ class Users( object ):
|
|||
@async
|
||||
@update_client
|
||||
def logout( self ):
|
||||
"""
|
||||
Deauthenticate the user and log them out of their current session.
|
||||
|
||||
@rtype: json dict
|
||||
@return: { 'redirect': url, 'deauthenticated': True }
|
||||
"""
|
||||
yield dict(
|
||||
redirect = self.__http_url + u"/",
|
||||
deauthenticated = True,
|
||||
|
@ -209,6 +262,15 @@ class Users( object ):
|
|||
user_id = Valid_id( none_okay = True ),
|
||||
)
|
||||
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
|
||||
self.__database.load( user_id or u"User anonymous", self.__scheduler.thread )
|
||||
user = ( yield Scheduler.SLEEP )
|
||||
|
@ -217,6 +279,7 @@ class Users( object ):
|
|||
yield dict(
|
||||
user = None,
|
||||
notebooks = None,
|
||||
http_url = u"",
|
||||
)
|
||||
return
|
||||
|
||||
|
|
Reference in New Issue