Source code for watson.framework.support.console.commands.project

# -*- coding: utf-8 -*-
import os
from pprint import pprint
import stat
import sys
from wsgiref import util
from string import Template
from watson.common.contextmanagers import suppress
from watson.console import ConsoleError, colors, command
from watson.console.decorators import arg
from watson.di import ContainerAware
from watson.http.messages import Request


[docs]class Project(command.Base, ContainerAware): """Creating and maintaining Watson projects. Example: .. code-block:: ./console.py dev runserver """ __ioc_definition__ = { 'property': { 'router': 'router', 'application_config': 'application.config' } }
[docs] @arg('override', action='store_const', const=1, optional=True) @arg('component_based', action='store_const', const=1, optional=True) @arg('dir', optional=True) def new(self, name, app_name, dir, override, component_based): """Creates a new project, defaults to the current working directory. Args: name: The name of the project app_name: The name of the application to create dir: The directory to create the project in override: Override any existing project in the path component_based: Create component based structure """ if dir: root = os.path.abspath(dir) if not os.path.exists(root): raise ConsoleError('Directory {} not found'.format(root)) else: root = os.getcwd() basepath = os.path.join(root, name) paths = [ basepath, os.path.join(basepath, app_name), os.path.join(basepath, 'data'), os.path.join(basepath, 'data', 'cache'), os.path.join(basepath, 'data', 'logs'), os.path.join(basepath, 'data', 'uploads'), os.path.join(basepath, 'public'), os.path.join(basepath, 'public', 'css'), os.path.join(basepath, 'public', 'img'), os.path.join(basepath, 'public', 'js'), os.path.join(basepath, 'static'), os.path.join(basepath, 'static', 'js'), os.path.join(basepath, 'static', 'scss'), os.path.join(basepath, 'tests'), ] files = [ (os.path.join(basepath, 'console.py'), CONSOLE_TEMPLATE), (os.path.join(basepath, '.editorconfig'), EDITOR_CONFIG_TEMPLATE), (os.path.join(basepath, '.gitignore'), GITIGNORE_TEMPLATE), (os.path.join(basepath, 'requirements.txt'), REQUIREMENTS_TEMPLATE), (os.path.join(basepath, 'README.md'), ''), (os.path.join(basepath, 'public', 'robots.txt'), ROBOTS_TEMPLATE), (os.path.join(basepath, app_name, '__init__.py'), BLANK_PY_TEMPLATE), (os.path.join(basepath, app_name, 'app.py'), APP_PY_TEMPLATE), (os.path.join(basepath, 'tests', '__init__.py'), BLANK_PY_TEMPLATE), (os.path.join(basepath, 'tests', app_name, '__init__.py'), BLANK_PY_TEMPLATE), ] if not component_based: paths.extend([ os.path.join(basepath, app_name, 'config'), os.path.join(basepath, app_name, 'controllers'), os.path.join(basepath, app_name, 'views'), os.path.join(basepath, app_name, 'views', 'index'), os.path.join(basepath, 'tests', app_name), os.path.join(basepath, 'tests', app_name, 'controllers'), ]) files.extend([ (os.path.join(basepath, app_name, 'config', 'base.py'), SIMPLE_CONFIG_PY_TEMPLATE), (os.path.join(basepath, app_name, 'config', 'routes.py'), SIMPLE_ROUTES_PY_TEMPLATE), (os.path.join(basepath, app_name, 'config', 'dependencies.py'), DEPENDENCIES_PY_TEMPLATE), (os.path.join(basepath, app_name, 'controllers', '__init__.py'), SIMPLE_SAMPLE_CONTROLLER_INIT_TEMPLATE), (os.path.join(basepath, app_name, 'controllers', 'index.py'), SAMPLE_CONTROLLER_TEMPLATE), (os.path.join(basepath, app_name, 'views', 'index', 'get.html'), SAMPLE_VIEW_TEMPLATE), (os.path.join(basepath, 'tests', app_name, 'controllers', '__init__.py'), BLANK_PY_TEMPLATE), (os.path.join(basepath, 'tests', app_name, 'controllers', 'test_index.py'), SIMPLE_SAMPLE_TEST_SUITE), ]) else: paths.extend([ os.path.join(basepath, app_name, 'common'), os.path.join(basepath, app_name, 'config'), os.path.join(basepath, app_name, 'common', 'controllers'), os.path.join(basepath, app_name, 'common', 'views'), os.path.join(basepath, app_name, 'common', 'views', 'index'), os.path.join(basepath, 'tests', app_name), os.path.join(basepath, 'tests', app_name, 'common'), os.path.join(basepath, 'tests', app_name, 'common', 'controllers'), ]) files.extend([ (os.path.join(basepath, app_name, 'config', '__init__.py'), BLANK_PY_TEMPLATE), (os.path.join(basepath, app_name, 'config', 'base.py'), COMPONENT_CONFIG_PY_TEMPLATE), (os.path.join(basepath, app_name, 'config', 'dependencies.py'), DEPENDENCIES_PY_TEMPLATE), (os.path.join(basepath, app_name, 'common', '__init__.py'), BLANK_PY_TEMPLATE), (os.path.join(basepath, app_name, 'common', 'routes.py'), COMPONENT_ROUTES_PY_TEMPLATE), (os.path.join(basepath, app_name, 'common', 'controllers', '__init__.py'), COMPONENT_SAMPLE_CONTROLLER_INIT_TEMPLATE), (os.path.join(basepath, app_name, 'common', 'controllers', 'index.py'), SAMPLE_CONTROLLER_TEMPLATE), (os.path.join(basepath, app_name, 'common', 'views', 'index', 'get.html'), SAMPLE_VIEW_TEMPLATE), (os.path.join(basepath, 'tests', app_name, 'common', 'controllers', '__init__.py'), BLANK_PY_TEMPLATE), (os.path.join(basepath, 'tests', app_name, 'common', 'controllers', 'test_index.py'), COMPONENT_SAMPLE_TEST_SUITE), ]) for path in paths: try: os.mkdir(path) except: if not override: raise ConsoleError( 'Project already exists at {0}'.format(basepath)) for filename, contents in files: try: with open(filename, 'w', encoding='utf-8') as file: file.write( Template( contents).safe_substitute(app_name=app_name)) except: if not override: raise ConsoleError( 'File {0} already exists.'.format(filename)) st = os.stat(files[-1][0]) os.chmod(files[0][0], st.st_mode | stat.S_IEXEC) self.write('Project {} created at {}'.format(name, root))
[docs] @arg() def config(self): """Prints out the applications configuration. """ pprint(self.application_config)
[docs] @arg() def test(self): """Runs the unit tests for the project. """ current_directory = os.getcwd() os.chdir(os.path.join(current_directory, '..')) try: app_module = os.environ['APP_MODULE'] test_runner = None cli_args = '' sys.argv = [sys.argv.pop(0)] try: import pytest test_runner = 'pytest' cli_args = '--cov {0}'.format(app_module) except: with suppress(ImportError): import nose test_runner = 'nose' cli_args = '--cover-package={0}'.format(app_module) if test_runner: sys.modules[test_runner].main(cli_args.split(' ')) else: raise ConsoleError( "You must install either 'nose' or 'py.test' to run the unit tests.") except: _no_application_error() os.chdir(current_directory)
[docs] @arg('path', optional=True) @arg('method', optional=True) @arg('format', optional=True) @arg('server', optional=True) def routes(self, path, method, format, server): """Aids in the debugging of routes associated. Args: path: Validate the specified path against the router method: The http request method format: The http request format server: The hostname of the request """ try: router = self.router if not router.routes: raise ConsoleError( 'There are no routes associated with the application.') if path: environ = {} util.setup_testing_defaults(environ) server = server or '127.0.0.1' environ.update({ 'REQUEST_METHOD': method or 'GET', 'HTTP_ACCEPT': format or 'text/html', 'PATH_INFO': path, 'SERVER_NAME': server, 'HTTP_HOST': server }) request = Request.from_environ(environ) matches = [match for match in router.matches(request)] if matches: longest_route = max([match.route.name for match in matches], key=len) self.write( colors.header('Displaying {} matching routes for {}:\n'.format( len(matches), request.url))) for match in matches: route = match.route self.write('{0}\t{1}\n'.format( route.name.rjust(len(longest_route)), route.path)) else: raise ConsoleError('There are no matching routes.') else: self.write(colors.header('Displaying {0} routes for the application:\n'.format(len(router)))) longest_route = max(router, key=len) for name, route in router: self.write('{0}\t{1}\n'.format( name.rjust(len(longest_route[0])), route.path)) except ConsoleError: raise except Exception as e: raise e _no_application_error()
def _no_application_error(): raise ConsoleError( 'No watson application can be found, are you sure you\'re in the correct directory?') EDITOR_CONFIG_TEMPLATE = """root = true [*] charset = utf-8 end_of_line = lf insert_final_newline = true trim_trailing_whitespace = true [*.{py,md,ini,rst,html}] indent_style = space indent_size = 4 [*.{css,scss,json,yml,js}] indent_style = space indent_size = 2 [*.md] trim_trailing_whitespace = false [Makefile] indent_style = tab """ GITIGNORE_TEMPLATE = """# OSX & Workspace .DS_STORE .cache/v/ ### Python ### *.py[cod] # C extensions *.so # Packages *.egg *.egg-info dist build eggs parts bin var sdist develop-eggs .installed.cfg lib lib64 __pycache__ *.pyc # Installer logs pip-log.txt # Unit test / coverage reports .coverage .tox nosetests.xml tests/_coverage # Translations *.mo # Documentation docs/_build """ ROBOTS_TEMPLATE = """User-agent: * Disallow: """ REQUIREMENTS_TEMPLATE = """# Framework requirements watson-framework # Project requirements pytest pytest-cov """ BLANK_PY_TEMPLATE = """# -*- coding: utf-8 -*- """ APP_PY_TEMPLATE = """# -*- coding: utf-8 -*- from watson.framework import applications from ${app_name}.config import base as config application = applications.Http(config) """ SIMPLE_ROUTES_PY_TEMPLATE = """# -*- coding: utf-8 -*- \"\"\"Create routes for your application here. \"\"\" routes = { 'index': { 'path': '/', 'options': {'controller': '${app_name}.controllers.Index'} } } """ COMPONENT_ROUTES_PY_TEMPLATE = """# -*- coding: utf-8 -*- \"\"\"Create routes for your application here. \"\"\" routes = { 'index': { 'path': '/', 'options': {'controller': '${app_name}.common.controllers.Index'} } } """ SIMPLE_CONFIG_PY_TEMPLATE = """# -*- coding: utf-8 -*- \"\"\"Define and extend configuration settings for your application. \"\"\" import os from ${app_name}.config.routes import routes # noqa from ${app_name}.config.dependencies import dependencies # noqa debug = { 'enabled': os.environ.get('DEV_ENV', False) } """ COMPONENT_CONFIG_PY_TEMPLATE = """# -*- coding: utf-8 -*- \"\"\"Define and extend configuration settings for your application. \"\"\" import os from ${app_name}.config.dependencies import dependencies # noqa components = [ '${app_name}.common' ] debug = { 'enabled': os.environ.get('DEV_ENV', False) } """ DEPENDENCIES_PY_TEMPLATE = """# -*- coding: utf-8 -*- \"\"\"Define container dependencies. \"\"\" dependencies = { } """ SIMPLE_SAMPLE_CONTROLLER_INIT_TEMPLATE = """# -*- coding: utf-8 -*- from ${app_name}.controllers.index import Index __all__ = ['Index'] """ COMPONENT_SAMPLE_CONTROLLER_INIT_TEMPLATE = """# -*- coding: utf-8 -*- from ${app_name}.common.controllers.index import Index __all__ = ['Index'] """ SAMPLE_CONTROLLER_TEMPLATE = """# -*- coding: utf-8 -*- from watson import framework from watson.framework import controllers class Index(controllers.Rest): def GET(self): return 'Welcome to Watson v{0}!'.format(framework.__version__) """ SAMPLE_VIEW_TEMPLATE = """<!DOCTYPE html> <html> <head> <title>Welcome to Watson!</title> </head> <body> <h1>{{ content }}</h1> <p>You are now on your way to creating your first application using Watson.</p> <p>Read more about Watson in <a href="http://watson-framework.readthedocs.org/">the documentation.</a> </body> </html> """ SIMPLE_SAMPLE_TEST_SUITE = """# -*- coding: utf-8 -*- from watson import framework from ${app_name}.controllers.index import Index class TestSuiteIndex(object): def test_get(self): index = Index() assert index.GET() == 'Welcome to Watson v{0}!'.format(framework.__version__) """ COMPONENT_SAMPLE_TEST_SUITE = """# -*- coding: utf-8 -*- from watson import framework from ${app_name}.common.controllers.index import Index class TestSuiteIndex(object): def test_get(self): index = Index() assert index.GET() == 'Welcome to Watson v{0}!'.format(framework.__version__) """ CONSOLE_TEMPLATE = """#!/usr/bin/env python import os import sys SCRIPT_DIR, SCRIPT_FILE = os.path.split(os.path.abspath(__file__)) os.environ.update({ 'APP_MODULE': '${app_name}', 'APP_SETTINGS': '${app_name}.config.base', 'APP_DIR': os.path.join(SCRIPT_DIR, '${app_name}'), 'PUBLIC_DIR': os.path.join(SCRIPT_DIR, 'public'), 'SCRIPT_DIR': SCRIPT_DIR }) os.chdir(os.environ['APP_DIR']) try: from watson.framework import applications from watson.common import imports except: sys.stdout.write( 'You must have Watson installed, please run `pip install watson-framework`\\n') sys.exit(1) if __name__ == '__main__': config = imports.import_module(os.environ['APP_SETTINGS']) application = applications.Console(config) application() """