Last active
December 23, 2015 08:29
-
-
Save techiev2/6607997 to your computer and use it in GitHub Desktop.
Bootstrapper for Tornadoweb with Object wrapper support
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
""" | |
Tornadoweb bootstrapper. | |
""" | |
import sys | |
sys.dont_write_bytecode = True | |
import os | |
from datetime import datetime | |
import re | |
ROOT = os.path.abspath(os.path.dirname(__file__)) | |
USER = os.getlogin() | |
REQ = 'requires' | |
REQ = os.path.join(ROOT, REQ) | |
REQ_INIT = os.path.join(REQ, '__init__.py') | |
UTILS = 'utils' | |
UTILS = os.path.join(ROOT, UTILS) | |
UTILS_INIT = os.path.join(UTILS, '__init__.py') | |
UTILS_SERV = os.path.join(UTILS, 'server.py') | |
UTILS_DECOR = os.path.join(UTILS, 'decorators.py') | |
UTILS_OBJ = os.path.join(UTILS, 'object.py') | |
UTILS_MONGO = os.path.join(UTILS, 'mongo.py') | |
SETTINGS = os.path.join(REQ, 'settings.py') | |
CORE = 'core' | |
CORE = os.path.join(ROOT, CORE) | |
CORE_INIT = os.path.join(CORE, '__init__.py') | |
CORE_URLS = os.path.join(CORE, 'urls.py') | |
CORE_HANDLERS = os.path.join(CORE, 'handlers.py') | |
README = os.path.join(ROOT, 'readme.md') | |
HAS_REQ = 'requires' in os.listdir(ROOT) | |
HAS_CORE = 'core' in os.listdir(ROOT) | |
HAS_UTILS = 'utils' in os.listdir(ROOT) | |
HAS_APP = lambda app: app in os.listdir(ROOT) | |
APP_INIT = lambda app: os.path.join(os.path.join(ROOT, app), '__init__.py') | |
APP_HANDLER = lambda app: os.path.join(os.path.join(ROOT, app), 'handlers.py') | |
APP_URLS = lambda app: os.path.join(os.path.join(ROOT, app), 'urls.py') | |
try: | |
HAS_SETTINGS = 'settings.py' in os.listdir(REQ) | |
except OSError: | |
HAS_SETTINGS = False | |
def gen_docstring(open_string=False): | |
""" | |
Generate docstring. | |
""" | |
doc = '''""" | |
Created on {0} | |
@author: {1} | |
'''.format(datetime.now().strftime('%B %d %Y'), USER) | |
if not open_string: | |
doc += '"""' | |
return doc | |
def gen_base_imports(): | |
""" | |
Generate basic imports. | |
""" | |
doc = ''' | |
import sys | |
sys.dont_write_bytecode = True | |
from tornado.web import Application, os, StaticFileHandler | |
from tornado.httpserver import HTTPServer | |
from tornado.ioloop import IOLoop | |
''' | |
return doc | |
def gen_settings(): | |
""" | |
Generate settings. | |
""" | |
doc = ''' | |
# Use this lambda to generate absolute path for template/static. | |
GEN_PATH = lambda path: os.path.join(os.getcwd(), path) | |
SETTINGS = { | |
'APPS': ['core'], # Add your apps to this list | |
'cookie': '', # Specify the cookie variable name | |
'login_url': '', # Login path for the application | |
'template_path': '', # Absolute template path for the application. | |
'static_path': '', # Absolute static file path for the application. | |
'debug': True # Retain debug True for development. | |
} | |
URLS = [('/src/(.*?)$', StaticFileHandler, | |
{'path': SETTINGS['static_path']})] | |
''' | |
return doc | |
def gen_app_loader(): | |
""" | |
Generate app loader. | |
""" | |
doc = ''' | |
if SETTINGS['APPS']: | |
for app in SETTINGS['APPS']: | |
sys.path.append(os.path.join(os.getcwd(), app)) | |
_urls = __import__(app) | |
URLS.extend(_urls.URLS) | |
''' | |
return doc | |
def gen_app(): | |
""" | |
Generate application object invocation. | |
""" | |
doc = ''' | |
APP = Application(URLS, **SETTINGS) | |
SERVER = HTTPServer(APP) | |
LOOP = IOLoop.instance() | |
PORT = 8888 # Default port. main.py picks the default from here. | |
if __name__ == '__main__': | |
pass | |
''' | |
return doc | |
def gen_settings_str(): | |
""" | |
Generate settings module for application. | |
""" | |
settings_str = '' | |
settings_str += gen_docstring() | |
settings_str += ''' | |
# pylint: disable=W0142 | |
''' | |
settings_str += gen_base_imports() | |
settings_str += gen_settings() | |
settings_str += gen_app_loader() | |
settings_str += gen_app() | |
return settings_str | |
def gen_req_package(): | |
""" | |
Generate package structure for requires. | |
""" | |
doc = ''' | |
""" | |
Requires for app | |
""" | |
import sys | |
sys.dont_write_bytecode = True | |
import requires.settings | |
from requires.settings import SERVER, LOOP, PORT | |
__all__ = ['SERVER', 'LOOP', 'PORT'] | |
if __name__ == '__main__': | |
pass | |
''' | |
return doc | |
def gen_core_app(): | |
""" | |
Generate package structure for core app. | |
""" | |
doc = ''' | |
""" | |
Core app. | |
""" | |
import sys | |
sys.dont_write_bytecode = True | |
import core.urls | |
from core.urls import URLS | |
__all__ = ['URLS'] | |
if __name__ == '__main__': | |
pass | |
''' | |
return doc | |
def gen_user_app(user_app_name): | |
""" | |
Generate package structure for user app. | |
:param: user_app_name: Provides the application name to bootstrap | |
""" | |
doc = ''' | |
""" | |
%s app. | |
""" | |
import sys | |
sys.dont_write_bytecode = True | |
import %s.urls | |
from %s.urls import URLS | |
__all__ = ['URLS'] | |
if __name__ == '__main__': | |
pass | |
''' % (user_app_name, user_app_name, user_app_name) | |
return doc | |
def gen_utils_init(): | |
""" | |
Generate package structure for requires. | |
""" | |
doc = ''' | |
""" | |
Utils package | |
""" | |
import sys | |
sys.dont_write_bytecode = True | |
from .server import Handler | |
from .mongo import db_conn | |
from .object import QueryObject, CreateObject | |
__all__ = ['Handler', 'db_conn', 'QueryObject', 'CreateObject'] | |
if __name__ == '__main__': | |
pass | |
''' | |
return doc | |
def gen_utils_mongo(): | |
""" | |
Generte mongo module | |
""" | |
doc = ''' | |
#!/usr/bin/env python | |
""" | |
Mongo module | |
""" | |
# coding=utf-8 | |
__author__ = 'Sriram Velamur' | |
import sys | |
sys.dont_write_bytecode = True | |
import mongoengine as me | |
from pymongo.errors import AutoReconnect | |
from requires.settings import DB | |
from datetime import datetime as dt | |
import json | |
def db_conn(): | |
"""Mongo connection wrapper""" | |
try: | |
return me.connect(**DB) | |
except AutoReconnect: | |
raise Exception("DB Connection error") | |
except me.ConnectionError: | |
raise Exception("DB Connection error") | |
class BaseDocument(me.Document): | |
""" | |
Base document definition. | |
Defines the following attributes common to all derived models. | |
:attr created_at datetime: Creation time defaulting to UTC | |
:attr updated_at datetime: Updation time defaulting to UTC | |
Defines the following metadata for the derived models' support. | |
:attr allow_inheritance True: Required by Mongoengine. | |
Allows inheritance | |
:attr abstract True: Created derived class instances would use | |
a collection of <DerivedClass> | |
instead of BaseDocument.<DerivedClass> | |
""" | |
created_at = me.DateTimeField(default=dt.utcnow()) | |
updated_at = me.DateTimeField(default=dt.utcnow()) | |
deleted_at = me.DateTimeField() | |
obj_id = me.IntField() | |
meta = { | |
'indexes': [], | |
'sparse': True, | |
'allow_inheritance': True, | |
'date_str': '%d-%m-%Y', | |
} | |
def save(self, *args, **kwargs): | |
self.obj_id = self._get_collection().find({}).count() + 1 | |
super(BaseDocument, self).save(*args, **kwargs) | |
def update(self, *args, **kwargs): | |
super(BaseDocument, self).update(*args, **kwargs) | |
self.reload() | |
def json(self, fields=None, excludes=None, as_string=False): | |
# Pick up a list of fields for the JSON construct from the | |
# fields param. If fields is not list/tuple, initialize an | |
# empty list and get export fields from inherited model meta. | |
# If neither is True, fetch a list of all keys from _fields | |
# attribute for instance. | |
# Inherited model meta declaration to get metadata for operation | |
obj_meta = getattr(self, '_meta', {}) | |
fields = [] if not isinstance(fields, (list, tuple)) else fields | |
if not fields: | |
fields.extend(obj_meta.get('export_fields', [])) | |
fields = fields or getattr(self, '_fields').iterkeys() | |
# Pick up a list of fields for the JSON construct from the | |
# excludes param. If fields is not list/tuple, initialize an | |
# empty list and get exclude fields from inherited model meta. | |
excludes = [] if not isinstance( | |
excludes, (list, tuple)) else excludes | |
excludes = excludes or obj_meta.get('exclude_fields', []) or [] | |
# Dumper considerations | |
# If it is a reference field, and thus has a to_mongo attr | |
# do a reload before attempting a json dump of the object | |
# instance. Alternatively, could consider | |
# QueryObject('<Model>', {'pk':<pk>}) to update. | |
# A Spec test should decide the better alternative. | |
ret_val = {x: getattr(self, x).reload().json() if hasattr( | |
getattr(self, x), 'to_mongo') else [ | |
y.reload().json() if hasattr(y, 'to_mongo') else unicode(y) for y | |
in getattr(self, x)] if isinstance( | |
getattr(self, x), me.base.datastructures.BaseList) | |
else getattr(self, x) if isinstance(getattr(self, x), | |
me.base.datastructures.BaseDict) else unicode( | |
dt.strftime(getattr(self, x), obj_meta.get('date_str', | |
'%d-%m-%Y'))) if isinstance(getattr(self, x), dt) else \ | |
getattr(self, x) if isinstance(getattr(self, x), | |
(me.IntField, int)) else unicode(getattr(self, x)) if \ | |
getattr(self, x) else None for x in fields if hasattr( | |
self, x) and not x in excludes} | |
return ret_val if not as_string else json.dumps(ret_val) | |
__all__ = ['db_conn', 'BaseDocument'] | |
if __name__ == '__main__': | |
pass | |
''' | |
return doc | |
def gen_utils_object(): | |
""" | |
Generate object module | |
""" | |
doc = ''' | |
#!/usr/bin/env python | |
"""Object utils""" | |
# coding=utf-8 | |
__author__ = 'Sriram Velamur' | |
import sys | |
sys.dont_write_bytecode = True | |
from mongoengine import (ValidationError, NotUniqueError, | |
LookUpError, InvalidQueryError) | |
from re import findall | |
import logging | |
class QueryObject(object): | |
""" | |
Query object wrapper. Wraps around a mongoengine query and provides | |
convenience wrapper methods for API reponses/exceptions. | |
:Todo: Normalize class to be usable as an ORM agnostic wrapper. | |
""" | |
def __init__(self, controller=None, model=None, query=None, meta=None): | |
""" | |
Object creation wrapper init | |
:param model:str Model to create an object instance of. Required | |
:param query: Query data for object instance. Optional | |
:param meta:dict Meta data for filtering query. Optional | |
""" | |
if not model: | |
raise Exception("No model provided to query object") | |
self.result, self.exception, self.model = None, None, None | |
self.query = query or {} | |
self.meta = meta or {} | |
self.controller = controller | |
try: | |
if controller and hasattr(controller, 'settings'): | |
apps = controller.settings.get('APPS', []) | |
for app in apps: | |
if 'models' in __import__(app).__dict__.keys(): | |
app = __import__("%s.models" % app) | |
app_model = getattr(app, 'models') | |
if app_model: | |
self.model_name = model | |
self.model = \ | |
app_model.__dict__.get(self.model_name) | |
else: | |
self.models = __import__('models') | |
self.model_name = model | |
self.model = getattr(self.models, model) | |
self.result = self.model.objects.filter( | |
**self.query) if self.model else [] | |
self.result = self.result.order_by('obj_id') if self.model._fields.get('obj_id') else self.result | |
for (key, val) in self.meta.iteritems(): | |
if hasattr(self.result, key) and \ | |
hasattr(getattr(self.result, key), '__call__'): | |
self.result = getattr(self.result, key).__call__(val) | |
if len(self.result) == 0: | |
self.result = None | |
self.exception = { | |
'status_code': 404 | |
} | |
except ImportError, ie: | |
logging.log(9001, "Import error: %s" % ie.message) | |
self.result = None | |
self.exception = { | |
'status_code': 500, | |
'custom_msg': "Unable to import specified model" | |
} | |
except LookupError, le: | |
logging.log(9001, "Lookup error in model: %s" % le.message) | |
self.result = None | |
self.exception = { | |
'status_code': 400, | |
'custom_msg': le.message | |
} | |
except Exception, b_e: | |
logging.log(9001, "Base Exception: %s" % b_e.message) | |
self.result = None | |
if 'duplicate' in b_e.message: | |
self.exception = { | |
'status_code': 500, | |
'custom_msg': 'Duplicate entity' | |
} | |
elif 'Cannot resolve' in b_e.message: | |
self.exception = { | |
'status_code': 500, | |
'custom_msg': 'Invalid query' | |
} | |
else: | |
self.exception = { | |
'status_code': 500, | |
'custom_msg': str(b_e.message) | |
} | |
if reduce(lambda x, y: x and y, [ | |
hasattr(self.controller, x) for x in ['ui', 'request']]): | |
setattr(controller, 'response', self.json()) | |
# if self.controller: | |
# setattr(self.controller, 'response', | |
# self.exception if self.exception else self.json()) | |
def json(self, fields=None, exclude=None, as_string=False): | |
"""JSON dumper wrapper""" | |
req_args = getattr(self.controller, 'req_args', {}) | |
fields = req_args.get('fields', []) or fields | |
exclude = req_args.get('exclude', []) or exclude | |
start = req_args.get('start', None) | |
end = req_args.get('end', None) | |
try: | |
start = int(start[0]) if start and len(start) == 1 and\ | |
int(end[0]) < self.count else 0 | |
except ValueError: | |
start = 0 | |
try: | |
end = int(end[0]) if end and len(end) == 1 and \ | |
int(end[0]) < self.count else self.count | |
except ValueError: | |
end = self.count | |
result = self.result[start:end] if self.result else self.result | |
return {x:y.json(fields, exclude, as_string) for x, y in | |
enumerate(result) if hasattr(y, 'json')} \ | |
if self.count > 1 else result[0].json(fields, | |
exclude, as_string) if self.count == 1 else \ | |
self.exception if self.exception else [] | |
@property | |
def count(self): | |
"""Object instance counter""" | |
count = 0 | |
if self.result: | |
count = self.result.count() | |
return count | |
def delete(self, **kwargs): | |
"""Object instance delete wrapper""" | |
if self.result and hasattr(self.result, 'delete') \ | |
and hasattr(self.result.delete, '__call__'): | |
try: | |
if self.controller: | |
user = getattr(self.controller, 'user') | |
# if not user: | |
# return { | |
# 'status_code': 401 | |
# } | |
self.result.delete() | |
return_val = { | |
'status_code': 204 | |
} | |
else: | |
self.result.delete() | |
return_val = { | |
'status_code': 204 | |
} | |
except Exception, e: | |
return_val = { | |
'status_code': 202, | |
'message': '{0} object deletion failed'.format( | |
self.model_name) | |
} | |
else: | |
return_val = { | |
'status_code': 404 | |
} | |
if reduce(lambda x, y: x and y, [ | |
hasattr(self.controller, x) for x in ['ui', 'request']]): | |
setattr(controller, 'response', return_val) | |
return_val = None | |
return return_val | |
def update(self, update_data): | |
"""Update object instance wrapper for QueryObject""" | |
if update_data and self.result: | |
# if not getattr(self.controller, 'user'): | |
# self.response = {'status_code': 401} | |
# return | |
try: | |
query_data = update_data | |
update_kwargs = {'set__%s' % key: val for \ | |
key, val in update_data.iteritems()} | |
self.result.update(**update_kwargs) | |
self.result = QueryObject(self.controller, self.model_name, | |
query=query_data).result | |
except LookUpError, l_e: | |
self.result = None | |
self.exception = { | |
'status_code': 500, | |
'custom_msg': l_e.message | |
} | |
except InvalidQueryError, iq_e: | |
self.result = None | |
self.exception = { | |
'status_code': 422, | |
'custom_msg': iq_e.message[0].replace( | |
"Cannot resolve field", | |
"Invalid field").replace('"', '') | |
} | |
except Exception, exc: | |
self.result = None | |
self.exception = { | |
'status_code': 500, | |
'custom_msg': exc.message | |
} | |
if reduce(lambda x, y: x and y, [ | |
hasattr(self.controller, x) for x in ['ui', 'request']]): | |
setattr(self.controller, 'response', self.json()) | |
class CreateObject(object): | |
def __init__(self, controller=None, model=None, data=None): | |
""" | |
Object creation wrapper init | |
:param model:str Model to create an object instance of | |
:param data: Data for object instance creation | |
""" | |
self.object, self.exception = None, None | |
if not model: | |
raise Exception("No model provided for object creation") | |
if not data: | |
raise Exception("No data provided for object creation") | |
try: | |
if controller and hasattr(controller, 'settings'): | |
apps = controller.settings.get('APPS', []) | |
for app in apps: | |
if 'models' in __import__(app).__dict__.keys(): | |
app = __import__("%s.models" % app) | |
app_model = getattr(app, 'models') | |
if app_model: | |
self.model_name = model | |
self.model= app_model.__dict__.get(self.model_name) | |
else: | |
self.models = __import__('models') | |
self.model_name = model | |
self.model = getattr(self.models, model) | |
self.data = data | |
self.object = self.model(**self.data) | |
self.object.save() | |
except ImportError, ie: | |
raise Exception("Unable to import specified models pack") | |
except ValidationError, v_e: | |
self.object = None | |
self.exception = { | |
'status_code': 500, | |
'custom_msg': '' | |
} | |
message = v_e.message | |
self.exception['custom_msg'] = message.split( | |
') (')[1].rstrip(')') | |
except NotUniqueError, nu_err: | |
dup_msg = 'Duplicate value for {0} field' | |
field = findall(r'.*?\$(\w+)\_.*?', nu_err.message) | |
field = field[0] if field else None | |
self.object = None | |
self.exception = { | |
'status_code': 500, | |
'custom_msg': '' | |
} | |
if field: | |
self.exception['custom_msg'] = dup_msg.format(field) | |
if controller: | |
setattr(controller, 'response', | |
self.exception if self.exception else self.json()) | |
def json(self): | |
return self.object.json() if hasattr( | |
self.object, 'json') else self.exception if \ | |
self.exception else {'status_code': 500} | |
__all__ = ['QueryObject', 'CreateObject'] | |
if __name__ == '__main__': | |
pass | |
''' | |
return doc | |
def gen_utils_server(): | |
""" | |
Generate package structure for utils. | |
""" | |
doc = ''' | |
# pylint: disable=R0904 | |
""" | |
Utils. | |
""" | |
import sys | |
sys.dont_write_bytecode = True | |
from tornado.web import RequestHandler | |
import json | |
from re import sub | |
from urllib2 import unquote | |
from utils.object import QueryObject | |
def serialize(data, pattern=None, keysplit=None): | |
""" | |
Serializes a request string from get URI format to a dictionary. | |
Requires the pattern for splitting key/value pairs as keysplit | |
and pattern for splitting params as pattern. | |
""" | |
ret_data = None | |
try: | |
ret_data = json.loads(data.replace("'", '"')) | |
except (ValueError, TypeError): | |
if not pattern: | |
pattern = "&" | |
if not keysplit: | |
keysplit = "=" | |
if not isinstance(data, str): | |
try: | |
data = sub(r'\+', ' ', unquote(data.request.body)) | |
except KeyError: | |
pass | |
if data: | |
data = data.split(pattern) | |
ret_data = dict(zip([x.split(keysplit)[0] for x in data], | |
[sub(r'\+', ' ', unquote( | |
x.split(keysplit)[1])) for x in data])) | |
return ret_data | |
VIEW_ERRORS = { | |
'405': { | |
'status': 405, | |
'message': 'End point does not accept the method' | |
}, | |
'404': { | |
'status': 404, | |
'message': '{0} not found' | |
}, | |
'403': { | |
'status': 403, | |
'message': 'Not authorized to access end point' | |
}, | |
'401': { | |
'status': 401, | |
'message': 'No authorization found' | |
}, | |
'400': { | |
'status': 400, | |
'message': 'Missing keys/Invalid data' | |
}, | |
'412': { | |
'status': 412, | |
'message': 'Missing keys/Invalid data' | |
}, | |
'500': { | |
'status': 500, | |
'message': 'Server error' | |
} | |
} | |
VIEW_MESSAGES = { | |
'delete_succeed': { | |
'status': 204, | |
}, | |
'delete_fail': { | |
'status': 202, | |
'message': '{0} object deletion failed' | |
} | |
} | |
class Handler(RequestHandler, object): | |
""" | |
Base request handler overridden with required decorators and data | |
members. | |
""" | |
def set_default_headers(self, *args, **kwargs): | |
"""Set API handler default headers""" | |
super(Handler, self).set_default_headers(*args, **kwargs) | |
self.set_header("Access-Control-Allow-Origin", "*") | |
self.set_header('Access-Control-Allow-Methods', | |
'GET, POST, PUT, DELETE, OPTIONS') | |
def __init__(self, *args, **kwargs): | |
""" | |
Handler init. | |
""" | |
super(Handler, self).__init__(*args, **kwargs) | |
self.req_args = {x:y[0].split(',') if len(y) == 1 else [] if | |
len(y) == 0 else y for x, y in | |
self.request.arguments.iteritems()} | |
self.view = BaseView(self) | |
self.data, self.user, self.response, self.query_data = \ | |
None, None, None, {} | |
if self.request.method in ['GET', 'PUT', 'POST']: | |
self.data = serialize(self.request.body) | |
# if self.request.uri not in ['/', '/register/']: | |
# self.token = self.request.headers.get('X-Signature') | |
# if self.token: | |
# self.algo, self.token = self.token.split('=') | |
# self.user = QueryObject(model='User', | |
# query={'token': str(self.token)}) | |
# self.user = self.user.result[0] if \ | |
# self.user.count == 1 else None | |
# if not self.user: | |
# self._transforms = [] | |
# self.view.raise_error(401) | |
if not self.request.method in self.SUPPORTED_METHODS: | |
self._transforms = [] | |
self.view.raise_error(405) | |
# Add decorators here | |
def get(self, *args, **kwargs): | |
""" | |
HTTP GET Request handler method. | |
""" | |
pass | |
# Add decorators here | |
def post(self, *args, **kwargs): | |
""" | |
HTTP POST Request handler method. | |
""" | |
pass | |
class BaseView(object): | |
""" | |
Base view class | |
""" | |
def __init__(self, controller): | |
""" | |
Base view init | |
:param controller: Instance of Request handler from controller | |
""" | |
if not isinstance(controller, RequestHandler): | |
raise Exception("Invalid construct") | |
self.data, self.status_code, self.response, self.controller = \ | |
None, None, None, controller | |
def as_json(self): | |
""" | |
Respond with a JSON response from View. | |
""" | |
self.response = self.data or self.controller.response | |
if not self.response and self.status_code not in (204,): | |
raise Exception("No controller response") | |
headers_written = getattr(self.controller, '_headers_written') | |
finished = getattr(self.controller, '_finished') | |
if not (headers_written or finished): | |
self.controller.set_header("Content-Type", "application/json") | |
if self.response.get('status_code') and \ | |
self.response.get('status_code') != 200: | |
self.raise_error(**self.response) | |
return | |
self.controller.write(self.response) | |
self.controller.finish() | |
def raise_standard(self, key): | |
""" | |
Raise standard message. | |
""" | |
key = VIEW_MESSAGES.get(key) or '' | |
if not key: | |
return VIEW_ERRORS['500'] | |
def raise_error(self, status_code, custom_msg=None): | |
""" | |
Custom raise error method for BaseView | |
:param status_code: HTTP error status code to raise. | |
:param custom_msg: Custom error message | |
""" | |
self.status_code = status_code or 500 | |
self.status_message = VIEW_ERRORS.get(str(self.status_code)) | |
if self.status_code == 404: | |
model = self.controller.model if \ | |
hasattr(self.controller, 'model') else 'Object' | |
self.status_message['message'] = \ | |
self.status_message['message'].format(model) | |
if custom_msg: | |
self.status_message['message'] = custom_msg | |
self.controller.set_status(self.status_code) | |
if self.status_code not in (204,): | |
self.controller.write(self.status_message) | |
self.controller.finish() | |
def render(self): | |
"""Template render method for BaseView""" | |
self.controller._transforms = [] if not \ | |
self.controller._transforms \ | |
else self.controller._transforms | |
self.controller._headers_written = False if \ | |
self.controller._headers_written else \ | |
self.controller._headers_written | |
self.controller._finished = False if \ | |
self.controller._finished else\ | |
self.controller._finished | |
self.response = self.controller.response or {} | |
if getattr(self.controller, 'as_json', False): | |
self.as_json() | |
return | |
self.controller.render(self.controller.template_file, | |
data=self.response) | |
__all__ = ['Handler'] | |
if __name__ == '__main__': | |
pass | |
''' | |
return doc | |
def gen_utils_decorators(): | |
""" | |
Generate decorator module structure for utils. | |
""" | |
doc = ''' | |
# pylint: disable=R0904 | |
""" | |
Utils. | |
""" | |
import sys | |
sys.dont_write_bytecode = True | |
from functools import wraps | |
def is_authenticated(method): | |
""" | |
Basic authenticated check decorator. | |
""" | |
@wraps(method) | |
def wrapper(self, *args, **kwargs): | |
""" | |
Wrapper method for is_authenticated decorator. | |
""" | |
# Add decorator flow. | |
return method(self, *args, **kwargs) | |
return wrapper | |
__all__ = ['is_authenticated'] | |
if __name__ == '__main__': | |
pass | |
''' | |
return doc | |
def gen_core_urls(): | |
""" | |
Generate urlmap for core app. | |
""" | |
doc = gen_docstring(open_string=True) | |
doc += '''URL map for core app. | |
""" | |
from core.handlers import Main | |
''' | |
doc += ''' | |
URLS = [('/$', Main)] | |
__all__ = ['URLS'] | |
if __name__ == '__main__': | |
pass | |
''' | |
return doc | |
def gen_app_urls(user_app_name): | |
""" | |
Generate urlmap for core app. | |
""" | |
doc = gen_docstring(open_string=True) | |
doc += '''URL map for %s app. | |
""" | |
# import application specific handlers | |
''' % user_app_name | |
doc += ''' | |
URLS = [] # Fill up app specific urlmap | |
__all__ = ['URLS'] | |
if __name__ == '__main__': | |
pass | |
''' | |
return doc | |
def gen_core_handlers(): | |
""" | |
Generate base handler for core app. | |
""" | |
doc = ''' | |
# pylint: disable=R0904 | |
""" | |
Core handlers | |
""" | |
import sys | |
sys.dont_write_bytecode = True | |
from utils import Handler | |
from utils.object import QueryObject, CreateObject | |
class Main(Handler): | |
""" | |
Main request handler for core app. | |
""" | |
def __init__(self, *args, **kwargs): | |
""" | |
Main request handler init. | |
""" | |
super(Main, self).__init__(*args, **kwargs) | |
self.template_file = 'index.html' | |
self.as_json = True | |
def get(self, *args, **kwargs): | |
""" | |
HTTP GET Request handler method for Main handler. | |
""" | |
super(Main, self).get(*args, **kwargs) | |
self.response = {'status': 200} | |
self.view.render() | |
def post(self, *args, **kwargs): | |
""" | |
HTTP POST Request handler method for Main handler. | |
""" | |
pass | |
__all__ = ['Main'] | |
if __name__ == '__main__': | |
pass | |
''' | |
return doc | |
def gen_app_handlers(user_app_name): | |
""" | |
Generate base handler for user app. | |
""" | |
doc = ''' | |
# pylint: disable=R0904 | |
""" | |
%s app handlers | |
""" | |
import sys | |
sys.dont_write_bytecode = True | |
from utils import Handler | |
from utils.object import QueryObject, CreateObject | |
class %sController(Handler, object): | |
"""%s controller""" | |
def __init__(self, *args, **kwargs): | |
"""%ss controller init""" | |
super(%sController, self).__init__(*args, **kwargs) | |
__all__ = ["%sController"] | |
if __name__ == '__main__': | |
pass | |
''' % (user_app_name, | |
user_app_name.capitalize(), | |
user_app_name.capitalize(), | |
user_app_name.capitalize(), | |
user_app_name.capitalize(), | |
user_app_name.capitalize()) | |
return doc | |
def gen_readme(): | |
""" | |
Generate a readme file for Bootstrapped setup. | |
""" | |
doc = ''' | |
=== README for Tornastrap === | |
Applications are self contained as packages and are added to the | |
SETTINGS which would take care of adding apps to the path at runtime. | |
''' | |
return doc | |
def gen_main(): | |
""" | |
Generate main.py for app. | |
""" | |
doc = gen_docstring() | |
doc += ''' | |
import sys | |
sys.dont_write_bytecode = True | |
from requires import LOOP, SERVER, PORT | |
from socket import error as SockErr | |
from utils import db_conn | |
if __name__ == '__main__': | |
try: | |
if len(sys.argv) == 2: | |
try: | |
S_PORT = int(sys.argv[1]) | |
except TypeError: | |
S_PORT = PORT | |
print "Non numeric port. Starting on {0}".format(PORT) | |
else: | |
S_PORT = PORT | |
db_conn() | |
SERVER.bind(S_PORT) | |
SERVER.start() | |
print "Started on http://0.0.0.0:{0}".format(S_PORT) | |
LOOP.start() | |
except KeyboardInterrupt: | |
pass | |
except SockErr: | |
sys.exit("Another program using the port. Please try again") | |
''' | |
return doc | |
def update_settings(user_app_name): | |
""" | |
Update settings file with generated user app. | |
""" | |
base_apps = None | |
settings = open(SETTINGS, "r").readlines() | |
settings_app = [x for x in settings if "'APPS':" in x][0] | |
settings_at = settings.index(settings_app) | |
apps = re.findall('.*?\[(.*?)\].*?', settings_app.strip()) | |
if apps and len(apps) == 1: | |
apps = apps[0] | |
if apps: | |
base_apps = apps | |
apps = [x.strip() for x in apps.split(',')] | |
apps.extend(["'%s'" % user_app_name]) | |
apps = ', '.join(apps) | |
settings_app = re.sub(base_apps, apps, settings_app) | |
settings[settings_at] = settings_app | |
settings = reduce(lambda a, b: a + b, settings) | |
with open(SETTINGS, "w") as sfile: | |
sfile.write(settings) | |
# def make_stack(stack_folder): | |
# """Make stack skeleton""" | |
# if not stack_folder: | |
# raise Exception("No relative stack location provided") | |
# if not os.listdir(os.getcwd()) | |
if __name__ == '__main__': | |
# is_stack = has_stack_dir = False | |
# if len(sys.argv) >= 2: | |
# is_stack = '--stack' in sys.argv | |
# if len(sys.argv) < 3: | |
# has_stack_dir = False | |
# else: | |
# has_stack_dir = True | |
# if is_stack: | |
# if not has_stack_dir: | |
# stack_dir = raw_input("Stack folder name : ") | |
# else: | |
# stack_dir = sys.argv[2] | |
# make_stack() | |
app_name = None | |
if '--app' in sys.argv and len(sys.argv) >= 3 and sys.argv[1] == '--app': | |
app_name = sys.argv[2] | |
# App structure generation | |
if app_name: | |
if not 'main.py' in os.listdir(ROOT): | |
sys.exit('App generation works only inside bootstrapped env.') | |
else: | |
if not HAS_APP(app_name): | |
os.mkdir(app_name) | |
print "Generating application ~ %s..." % app_name | |
with open(APP_INIT(app_name), 'w') as aifile: | |
aifile.write(gen_user_app(app_name)) | |
print "Generting url map for app ~ %s..." % app_name | |
with open(APP_URLS(app_name), 'w') as aufile: | |
aufile.write(gen_app_urls(app_name)) | |
print "Generating handlers for app ~ %s..." % app_name | |
with open(APP_HANDLER(app_name), 'w') as ahfile: | |
ahfile.write(gen_app_handlers(app_name)) | |
print "Updating settings file with app ~ %s" % app_name | |
update_settings(app_name) | |
print "Completed generating app ~ %s" % app_name | |
# Bootstrap stack generation flow | |
elif '--stack' in sys.argv and len( | |
sys.argv) >= 2 and sys.argv[1] == '--stack': | |
if not HAS_REQ: | |
os.mkdir('requires') | |
else: | |
pass | |
if not HAS_CORE: | |
os.mkdir('core') | |
else: | |
pass | |
if not HAS_UTILS: | |
os.mkdir('utils') | |
else: | |
pass | |
print "Generating requirements package..." | |
with open(REQ_INIT, 'w') as rifile: | |
rifile.write(gen_req_package()) | |
print "Generating settings module..." | |
with open(SETTINGS, 'w') as sfile: | |
sfile.write(gen_settings_str()) | |
print "Completed generating requirements package" | |
print "Generating utils package..." | |
with open(UTILS_INIT, 'w') as uifile: | |
uifile.write(gen_utils_init()) | |
print "Generating server module..." | |
with open(UTILS_SERV, 'w') as usfile: | |
usfile.write(gen_utils_server()) | |
print "Generating decorators module..." | |
with open(UTILS_DECOR, 'w') as udfile: | |
udfile.write(gen_utils_decorators()) | |
print "Generating object module" | |
with open(UTILS_OBJ, 'w') as uofile: | |
uofile.write(gen_utils_object()) | |
print "Generating mongo module" | |
with open(UTILS_MONGO, 'w') as umfile: | |
umfile.write(gen_utils_mongo()) | |
print "Completed generating utils package" | |
print "Generating core app package..." | |
with open(CORE_INIT, 'w') as cifile: | |
cifile.write(gen_core_app()) | |
print "Generting url map for core package..." | |
with open(CORE_URLS, 'w') as cufile: | |
cufile.write(gen_core_urls()) | |
print "Generating handlers for core app..." | |
with open(CORE_HANDLERS, 'w') as chfile: | |
chfile.write(gen_core_handlers()) | |
print "Completed generating core app package" | |
print "Generating main.py for bootstrap..." | |
with open('main.py', 'w') as mfile: | |
mfile.write(gen_main()) | |
print "Generating readme markdown..." | |
with open(README, 'w') as rfile: | |
rfile.write(gen_readme()) | |
print "Completed bootstrapping. Start the server with main.py" | |
else: | |
sys.exit('Invalid parameters') |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment