Created the pecan project for containers for API
Co-Authored-By: Digambar Patil <digambarpat@gmail.com> Co-Authored-By: Steven Dake <sdake@redhat.com> Change-Id: I886a26ded99708f39bba22d0ebd08ce99cd26020
This commit is contained in:
parent
84d943ec2d
commit
3d15bd1ba8
1
containers/MANIFEST.in
Normal file
1
containers/MANIFEST.in
Normal file
@ -0,0 +1 @@
|
|||||||
|
recursive-include public *
|
0
containers/api/__init__.py
Normal file
0
containers/api/__init__.py
Normal file
14
containers/api/app.py
Normal file
14
containers/api/app.py
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
from pecan import make_app
|
||||||
|
from api import model
|
||||||
|
|
||||||
|
|
||||||
|
def setup_app(config):
|
||||||
|
|
||||||
|
model.init_model()
|
||||||
|
app_conf = dict(config.app)
|
||||||
|
|
||||||
|
return make_app(
|
||||||
|
app_conf.pop('root'),
|
||||||
|
logging=getattr(config, 'logging', {}),
|
||||||
|
**app_conf
|
||||||
|
)
|
0
containers/api/controllers/__init__.py
Normal file
0
containers/api/controllers/__init__.py
Normal file
22
containers/api/controllers/root.py
Normal file
22
containers/api/controllers/root.py
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
from pecan import expose, redirect
|
||||||
|
from webob.exc import status_map
|
||||||
|
|
||||||
|
|
||||||
|
class RootController(object):
|
||||||
|
|
||||||
|
@expose(generic=True, template='index.html')
|
||||||
|
def index(self):
|
||||||
|
return dict()
|
||||||
|
|
||||||
|
@index.when(method='POST')
|
||||||
|
def index_post(self, q):
|
||||||
|
redirect('http://pecan.readthedocs.org/en/latest/search.html?q=%s' % q)
|
||||||
|
|
||||||
|
@expose('error.html')
|
||||||
|
def error(self, status):
|
||||||
|
try:
|
||||||
|
status = int(status)
|
||||||
|
except ValueError: # pragma: no cover
|
||||||
|
status = 500
|
||||||
|
message = getattr(status_map.get(status), 'explanation', '')
|
||||||
|
return dict(status=status, message=message)
|
15
containers/api/model/__init__.py
Normal file
15
containers/api/model/__init__.py
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
from pecan import conf # noqa
|
||||||
|
|
||||||
|
|
||||||
|
def init_model():
|
||||||
|
"""
|
||||||
|
This is a stub method which is called at application startup time.
|
||||||
|
|
||||||
|
If you need to bind to a parsed database configuration, set up tables or
|
||||||
|
ORM classes, or perform any database initialization, this is the
|
||||||
|
recommended place to do it.
|
||||||
|
|
||||||
|
For more information working with databases, and some common recipes,
|
||||||
|
see http://pecan.readthedocs.org/en/latest/databases.html
|
||||||
|
"""
|
||||||
|
pass
|
12
containers/api/templates/error.html
Normal file
12
containers/api/templates/error.html
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<%inherit file="layout.html" />
|
||||||
|
|
||||||
|
## provide definitions for blocks we want to redefine
|
||||||
|
<%def name="title()">
|
||||||
|
Server Error ${status}
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
## now define the body of the template
|
||||||
|
<header>
|
||||||
|
<h1>Server Error ${status}</h1>
|
||||||
|
</header>
|
||||||
|
<p>${message}</p>
|
34
containers/api/templates/index.html
Normal file
34
containers/api/templates/index.html
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
<%inherit file="layout.html" />
|
||||||
|
|
||||||
|
## provide definitions for blocks we want to redefine
|
||||||
|
<%def name="title()">
|
||||||
|
Welcome to Pecan!
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
## now define the body of the template
|
||||||
|
<header>
|
||||||
|
<h1><img src="/images/logo.png" /></h1>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div id="content">
|
||||||
|
|
||||||
|
<p>This is a sample Pecan project.</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
Instructions for getting started can be found online at <a
|
||||||
|
href="http://pecanpy.org" target="window">pecanpy.org</a>
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<p>
|
||||||
|
...or you can search the documentation here:
|
||||||
|
</p>
|
||||||
|
|
||||||
|
<form method="POST" action="/">
|
||||||
|
<fieldset>
|
||||||
|
<input name="q" />
|
||||||
|
<input type="submit" value="Search" />
|
||||||
|
</fieldset>
|
||||||
|
<small>Enter search terms or a module, class or function name.</small>
|
||||||
|
</form>
|
||||||
|
|
||||||
|
</div>
|
22
containers/api/templates/layout.html
Normal file
22
containers/api/templates/layout.html
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>${self.title()}</title>
|
||||||
|
${self.style()}
|
||||||
|
${self.javascript()}
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
${self.body()}
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
|
|
||||||
|
<%def name="title()">
|
||||||
|
Default Title
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
<%def name="style()">
|
||||||
|
<link rel="stylesheet" type="text/css" media="screen" href="/css/style.css" />
|
||||||
|
</%def>
|
||||||
|
|
||||||
|
<%def name="javascript()">
|
||||||
|
<script language="text/javascript" src="/javascript/shared.js"></script>
|
||||||
|
</%def>
|
22
containers/api/tests/__init__.py
Normal file
22
containers/api/tests/__init__.py
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
import os
|
||||||
|
from unittest import TestCase
|
||||||
|
from pecan import set_config
|
||||||
|
from pecan.testing import load_test_app
|
||||||
|
|
||||||
|
__all__ = ['FunctionalTest']
|
||||||
|
|
||||||
|
|
||||||
|
class FunctionalTest(TestCase):
|
||||||
|
"""
|
||||||
|
Used for functional tests where you need to test your
|
||||||
|
literal application and its integration with the framework.
|
||||||
|
"""
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
self.app = load_test_app(os.path.join(
|
||||||
|
os.path.dirname(__file__),
|
||||||
|
'config.py'
|
||||||
|
))
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
set_config({}, overwrite=True)
|
25
containers/api/tests/config.py
Normal file
25
containers/api/tests/config.py
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
# Server Specific Configurations
|
||||||
|
server = {
|
||||||
|
'port': '8080',
|
||||||
|
'host': '0.0.0.0'
|
||||||
|
}
|
||||||
|
|
||||||
|
# Pecan Application Configurations
|
||||||
|
app = {
|
||||||
|
'root': 'api.controllers.root.RootController',
|
||||||
|
'modules': ['api'],
|
||||||
|
'static_root': '%(confdir)s/../../public',
|
||||||
|
'template_path': '%(confdir)s/../templates',
|
||||||
|
'debug': True,
|
||||||
|
'errors': {
|
||||||
|
'404': '/error/404',
|
||||||
|
'__force_dict__': True
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Custom Configurations must be in Python dictionary format::
|
||||||
|
#
|
||||||
|
# foo = {'bar':'baz'}
|
||||||
|
#
|
||||||
|
# All configurations are accessible at::
|
||||||
|
# pecan.conf
|
22
containers/api/tests/test_functional.py
Normal file
22
containers/api/tests/test_functional.py
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
from unittest import TestCase
|
||||||
|
from webtest import TestApp
|
||||||
|
from api.tests import FunctionalTest
|
||||||
|
|
||||||
|
|
||||||
|
class TestRootController(FunctionalTest):
|
||||||
|
|
||||||
|
def test_get(self):
|
||||||
|
response = self.app.get('/')
|
||||||
|
assert response.status_int == 200
|
||||||
|
|
||||||
|
def test_search(self):
|
||||||
|
response = self.app.post('/', params={'q': 'RestController'})
|
||||||
|
assert response.status_int == 302
|
||||||
|
assert response.headers['Location'] == (
|
||||||
|
'http://pecan.readthedocs.org/en/latest/search.html'
|
||||||
|
'?q=RestController'
|
||||||
|
)
|
||||||
|
|
||||||
|
def test_get_not_found(self):
|
||||||
|
response = self.app.get('/a/bogus/url', expect_errors=True)
|
||||||
|
assert response.status_int == 404
|
7
containers/api/tests/test_units.py
Normal file
7
containers/api/tests/test_units.py
Normal file
@ -0,0 +1,7 @@
|
|||||||
|
from unittest import TestCase
|
||||||
|
|
||||||
|
|
||||||
|
class TestUnits(TestCase):
|
||||||
|
|
||||||
|
def test_units(self):
|
||||||
|
assert 5 * 5 == 25
|
54
containers/config.py
Normal file
54
containers/config.py
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
# Server Specific Configurations
|
||||||
|
server = {
|
||||||
|
'port': '8080',
|
||||||
|
'host': '0.0.0.0'
|
||||||
|
}
|
||||||
|
|
||||||
|
# Pecan Application Configurations
|
||||||
|
app = {
|
||||||
|
'root': 'api.controllers.root.RootController',
|
||||||
|
'modules': ['api'],
|
||||||
|
'static_root': '%(confdir)s/public',
|
||||||
|
'template_path': '%(confdir)s/api/templates',
|
||||||
|
'debug': True,
|
||||||
|
'errors': {
|
||||||
|
404: '/error/404',
|
||||||
|
'__force_dict__': True
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
logging = {
|
||||||
|
'root': {'level': 'INFO', 'handlers': ['console']},
|
||||||
|
'loggers': {
|
||||||
|
'api': {'level': 'DEBUG', 'handlers': ['console']},
|
||||||
|
'pecan.commands.serve': {'level': 'DEBUG', 'handlers': ['console']},
|
||||||
|
'py.warnings': {'handlers': ['console']},
|
||||||
|
'__force_dict__': True
|
||||||
|
},
|
||||||
|
'handlers': {
|
||||||
|
'console': {
|
||||||
|
'level': 'DEBUG',
|
||||||
|
'class': 'logging.StreamHandler',
|
||||||
|
'formatter': 'color'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
'formatters': {
|
||||||
|
'simple': {
|
||||||
|
'format': ('%(asctime)s %(levelname)-5.5s [%(name)s]'
|
||||||
|
'[%(threadName)s] %(message)s')
|
||||||
|
},
|
||||||
|
'color': {
|
||||||
|
'()': 'pecan.log.ColorFormatter',
|
||||||
|
'format': ('%(asctime)s [%(padded_color_levelname)s] [%(name)s]'
|
||||||
|
'[%(threadName)s] %(message)s'),
|
||||||
|
'__force_dict__': True
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
# Custom Configurations must be in Python dictionary format::
|
||||||
|
#
|
||||||
|
# foo = {'bar':'baz'}
|
||||||
|
#
|
||||||
|
# All configurations are accessible at::
|
||||||
|
# pecan.conf
|
43
containers/public/css/style.css
Normal file
43
containers/public/css/style.css
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
body {
|
||||||
|
background: #311F00;
|
||||||
|
color: white;
|
||||||
|
font-family: 'Helvetica Neue', 'Helvetica', 'Verdana', sans-serif;
|
||||||
|
padding: 1em 2em;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: #FAFF78;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
|
||||||
|
div#content {
|
||||||
|
width: 800px;
|
||||||
|
margin: 0 auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
form {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
fieldset {
|
||||||
|
border: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
input.error {
|
||||||
|
background: #FAFF78;
|
||||||
|
}
|
||||||
|
|
||||||
|
header {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1, h2, h3, h4, h5, h6 {
|
||||||
|
font-family: 'Futura-CondensedExtraBold', 'Futura', 'Helvetica', sans-serif;
|
||||||
|
text-transform: uppercase;
|
||||||
|
}
|
BIN
containers/public/images/logo.png
Normal file
BIN
containers/public/images/logo.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 20 KiB |
6
containers/setup.cfg
Normal file
6
containers/setup.cfg
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
[nosetests]
|
||||||
|
match=^test
|
||||||
|
where=api
|
||||||
|
nocapture=1
|
||||||
|
cover-package=api
|
||||||
|
cover-erase=1
|
22
containers/setup.py
Normal file
22
containers/setup.py
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
try:
|
||||||
|
from setuptools import setup, find_packages
|
||||||
|
except ImportError:
|
||||||
|
from ez_setup import use_setuptools
|
||||||
|
use_setuptools()
|
||||||
|
from setuptools import setup, find_packages
|
||||||
|
|
||||||
|
setup(
|
||||||
|
name='api',
|
||||||
|
version='0.1',
|
||||||
|
description='',
|
||||||
|
author='',
|
||||||
|
author_email='',
|
||||||
|
install_requires=[
|
||||||
|
"pecan",
|
||||||
|
],
|
||||||
|
test_suite='api',
|
||||||
|
zip_safe=False,
|
||||||
|
include_package_data=True,
|
||||||
|
packages=find_packages(exclude=['ez_setup'])
|
||||||
|
)
|
Loading…
Reference in New Issue
Block a user