Skip to content

Instantly share code, notes, and snippets.

@techiev2
Last active December 23, 2015 08:29
Show Gist options
  • Save techiev2/6607997 to your computer and use it in GitHub Desktop.
Save techiev2/6607997 to your computer and use it in GitHub Desktop.
Bootstrapper for Tornadoweb with Object wrapper support
"""
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