From 910515b2e70b77e7ea87ccfa7a35a56188c157fd Mon Sep 17 00:00:00 2001 From: Davide Alberani Date: Sun, 22 Mar 2015 08:58:25 +0100 Subject: [PATCH] docstring in Python code --- backend.py | 77 ++++++++++++++++++++++++++++++++++++++++++++-- eventman_server.py | 61 ++++++++++++++++++++++++++---------- 2 files changed, 119 insertions(+), 19 deletions(-) diff --git a/backend.py b/backend.py index 54457a8..e6dcf2e 100644 --- a/backend.py +++ b/backend.py @@ -1,6 +1,6 @@ -"""Event Man(ager) backend +"""Event Man(ager) database backend -Classes and functions used to manage events and attendants. +Classes and functions used to manage events and attendants database. """ import pymongo @@ -8,15 +8,29 @@ from bson.objectid import ObjectId class EventManDB(object): + """MongoDB connector.""" db = None connection = None def __init__(self, url=None, dbName='eventman'): + """Initialize the instance, connecting to the database. + + :param url: URL of the database + :type url: str (or None to connect to localhost) + """ self._url = url self._dbName = dbName self.connect(url) def connect(self, url=None, dbName=None): + """Connect to the database. + + :param url: URL of the database + :type url: str (or None to connect to localhost) + + :return: the database we're connected to + :rtype: :class:`~pymongo.database.Database` + """ if self.db is not None: return self.db if url: @@ -28,12 +42,32 @@ class EventManDB(object): return self.db def get(self, collection, _id): + """Get a single document with the specified `_id`. + + :param collection: search the document in this collection + :type collection: str + :param _id: unique ID of the document + :type _id: str or :class:`~bson.objectid.ObjectId` + + :return: the document with the given `_id` + :rtype: dict + """ if not isinstance(_id, ObjectId): _id = ObjectId(_id) results = self.query(collection, {'_id': _id}) return results and results[0] or {} def query(self, collection, query=None): + """Get multiple documents matching a query. + + :param collection: search for documents in this collection + :type collection: str + :param query: search for documents with those attributes + :type query: dict or None + + :return: list of matching documents + :rtype: list + """ db = self.connect() query = query or {} if'_id' in query and not isinstance(query['_id'], ObjectId): @@ -44,11 +78,33 @@ class EventManDB(object): return results def add(self, collection, data): + """Insert a new document. + + :param collection: insert the document in this collection + :type collection: str + :param data: the document to store + :type data: dict + + :return: the document, as created in the database + :rtype: dict + """ db = self.connect() _id = db[collection].insert(data) return self.get(collection, _id) def update(self, collection, _id, data): + """Update an existing document. + + :param collection: update a document in this collection + :type collection: str + :param _id: unique ID of the document to be updatd + :type _id: str or :class:`~bson.objectid.ObjectId` + :param data: the updated information to store + :type data: dict + + :return: the document, after the update + :rtype: dict + """ db = self.connect() data = data or {} if '_id' in data: @@ -56,3 +112,20 @@ class EventManDB(object): db[collection].update({'_id': ObjectId(_id)}, {'$set': data}) return self.get(collection, _id) + def delete(self, collection, _id_or_query=None, force=False): + """Remove one or more documents from a collection. + + :param collection: search the documents in this collection + :type collection: str + :param _id_or_query: unique ID of the document or query to match multiple documents + :type _id_or_query: str or :class:`~bson.objectid.ObjectId` or dict + :param force: force the deletion of all documents, when `_id_or_query` is empty + :type force: bool + """ + if not _id_or_query and not force: + return + db = self.connect() + if not isinstance(_id_or_query, (ObjectId, dict)): + _id_or_query = ObjectId(_id_or_query) + db[collection].remove(_id_or_query) + diff --git a/eventman_server.py b/eventman_server.py index 6e656b0..8fd05ea 100755 --- a/eventman_server.py +++ b/eventman_server.py @@ -18,58 +18,84 @@ from tornado import gen, escape import backend -class BaseHandler(tornado.web.RequestHandler): - def initialize(self, **kwargs): - for key, value in kwargs.iteritems(): - setattr(self, key, value) - - -class RootHandler(BaseHandler): - angular_app_path = os.path.join(os.path.dirname(__file__), "angular_app") - @gen.coroutine - def get(self): - with open(self.angular_app_path + "/index.html", 'r') as fd: - self.write(fd.read()) - - class ImprovedEncoder(json.JSONEncoder): + """Enhance the default JSON encoder to serialize datetime objects.""" def default(self, o): - if isinstance(o, datetime.datetime): + if isinstance(o, (datetime.datetime, datetime.date, + datetime.time, datetime.timedelta)): return str(o) return json.JSONEncoder.default(self, o) json._default_encoder = ImprovedEncoder() +class BaseHandler(tornado.web.RequestHandler): + """Base class for request handlers.""" + def initialize(self, **kwargs): + """Add every passed (key, value) as attributes of the instance.""" + for key, value in kwargs.iteritems(): + setattr(self, key, value) + + +class RootHandler(BaseHandler): + """Handler for the / path.""" + angular_app_path = os.path.join(os.path.dirname(__file__), "angular_app") + + @gen.coroutine + def get(self): + # serve the ./angular_app/index.html file + with open(self.angular_app_path + "/index.html", 'r') as fd: + self.write(fd.read()) + + class CollectionHandler(BaseHandler): + """Base class for handlers that need to interact with the database backend. + + Introduce basic CRUD operations.""" + # set of documents we're managing (a collection in MongoDB or a table in a SQL database) collection = None @gen.coroutine def get(self, id_=None): if id_ is not None: + # read a single document self.write(self.db.get(self.collection, id_)) else: + # return an object containing the list of all objects in the collection; + # e.g.: {'events': [{'_id': 'obj1-id, ...}, {'_id': 'obj2-id, ...}, ...]} + # Please, never return JSON lists that are not encapsulated in an object, + # to avoid XSS vulnerabilities. self.write({self.collection: self.db.query(self.collection)}) @gen.coroutine def post(self, id_=None, **kwargs): data = escape.json_decode(self.request.body or {}) if id_ is None: + # insert a new document newData = self.db.add(self.collection, data) else: + # update an existing document newData = self.db.update(self.collection, id_, data) self.write(newData) + # PUT is handled by the POST method put = post + class PersonsHandler(CollectionHandler): + """Handle requests for Persons.""" collection = 'persons' + class EventsHandler(CollectionHandler): + """Handle requests for Events.""" collection = 'events' -def main(): +def run(): + """Run the Tornado web application.""" + # command line arguments; can also be written in a configuration file, + # specified with the --config argument. define("port", default=5242, help="run on the given port", type=int) define("data", default=os.path.join(os.path.dirname(__file__), "data"), help="specify the directory used to store the data") @@ -82,6 +108,7 @@ def main(): callback=lambda path: tornado.options.parse_config_file(path, final=False)) tornado.options.parse_command_line() + # database backend connector db_connector = backend.EventManDB(url=options.mongodbURL, dbName=options.dbName) init_params = dict(db=db_connector) @@ -100,5 +127,5 @@ def main(): if __name__ == '__main__': - main() + run()