Commit aef7d7d2 authored by cduvall@chromium.org's avatar cduvall@chromium.org

Extensions Docs Server: Integration testing

A test that makes sure all the pages in the templates/public directory render
without erroring.

BUG=131095


Review URL: https://chromiumcodereview.appspot.com/10828042

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@149101 0039d316-1c4b-4281-b951-d872f2087c98
parent 69e1c460
...@@ -80,6 +80,7 @@ ...@@ -80,6 +80,7 @@
"entry": { "entry": {
"type": "object", "type": "object",
"constructor": "Entry", "constructor": "Entry",
"additionalProperties": { "type": "any" },
"optional": true, "optional": true,
"description": "Selected file entry. It will be null if a file hasn't been selected." "description": "Selected file entry. It will be null if a file hasn't been selected."
} }
......
...@@ -435,7 +435,7 @@ ...@@ -435,7 +435,7 @@
"description": "This event is sent when focus enters a text box. It is sent to all extensions that are listening to this event, and enabled by the user.", "description": "This event is sent when focus enters a text box. It is sent to all extensions that are listening to this event, and enabled by the user.",
"parameters": [ "parameters": [
{ {
"type": "InputContext", "$ref": "InputContext",
"name": "context", "name": "context",
"description": "Describes the text field that has acquired focus." "description": "Describes the text field that has acquired focus."
} }
...@@ -459,7 +459,7 @@ ...@@ -459,7 +459,7 @@
"description": "This event is sent when the properties of the current InputContext change, such as the the type. It is sent to all extensions that are listening to this event, and enabled by the user.", "description": "This event is sent when the properties of the current InputContext change, such as the the type. It is sent to all extensions that are listening to this event, and enabled by the user.",
"parameters": [ "parameters": [
{ {
"type": "InputContext", "$ref": "InputContext",
"name": "context", "name": "context",
"description": "An InputContext object describing the text field that has changed." "description": "An InputContext object describing the text field that has changed."
} }
...@@ -482,7 +482,7 @@ ...@@ -482,7 +482,7 @@
"description": "ID of the engine receiving the event" "description": "ID of the engine receiving the event"
}, },
{ {
"type": "KeyboardEvent", "$ref": "KeyboardEvent",
"name": "keyData", "name": "keyData",
"description": "Data on the key event" "description": "Data on the key event"
} }
......
...@@ -2488,7 +2488,7 @@ ...@@ -2488,7 +2488,7 @@
<h4>onFocus</h4> <h4>onFocus</h4>
<div class="summary"> <div class="summary">
<!-- Note: intentionally longer 80 columns --> <!-- Note: intentionally longer 80 columns -->
<span class="subdued">chrome.input.ime.</span><span>onFocus</span><span class="subdued">.addListener</span>(function(<span>InputContext context</span>) <span class="subdued">{...}</span><span></span>); <span class="subdued">chrome.input.ime.</span><span>onFocus</span><span class="subdued">.addListener</span>(function(<span>input.ime.InputContext context</span>) <span class="subdued">{...}</span><span></span>);
</div> </div>
<div class="description"> <div class="description">
<p>This event is sent when focus enters a text box. It is sent to all extensions that are listening to this event, and enabled by the user.</p> <p>This event is sent when focus enters a text box. It is sent to all extensions that are listening to this event, and enabled by the user.</p>
...@@ -2506,7 +2506,7 @@ ...@@ -2506,7 +2506,7 @@
( (
<span id="typeTemplate"> <span id="typeTemplate">
<span> <span>
<span>InputContext</span> <a href="input.ime.html#type-input.ime.InputContext">input.ime.InputContext</a>
</span> </span>
</span> </span>
) )
...@@ -2533,7 +2533,7 @@ ...@@ -2533,7 +2533,7 @@
<h4>onInputContextUpdate</h4> <h4>onInputContextUpdate</h4>
<div class="summary"> <div class="summary">
<!-- Note: intentionally longer 80 columns --> <!-- Note: intentionally longer 80 columns -->
<span class="subdued">chrome.input.ime.</span><span>onInputContextUpdate</span><span class="subdued">.addListener</span>(function(<span>InputContext context</span>) <span class="subdued">{...}</span><span></span>); <span class="subdued">chrome.input.ime.</span><span>onInputContextUpdate</span><span class="subdued">.addListener</span>(function(<span>input.ime.InputContext context</span>) <span class="subdued">{...}</span><span></span>);
</div> </div>
<div class="description"> <div class="description">
<p>This event is sent when the properties of the current InputContext change, such as the the type. It is sent to all extensions that are listening to this event, and enabled by the user.</p> <p>This event is sent when the properties of the current InputContext change, such as the the type. It is sent to all extensions that are listening to this event, and enabled by the user.</p>
...@@ -2551,7 +2551,7 @@ ...@@ -2551,7 +2551,7 @@
( (
<span id="typeTemplate"> <span id="typeTemplate">
<span> <span>
<span>InputContext</span> <a href="input.ime.html#type-input.ime.InputContext">input.ime.InputContext</a>
</span> </span>
</span> </span>
) )
...@@ -2578,7 +2578,7 @@ ...@@ -2578,7 +2578,7 @@
<h4>onKeyEvent</h4> <h4>onKeyEvent</h4>
<div class="summary"> <div class="summary">
<!-- Note: intentionally longer 80 columns --> <!-- Note: intentionally longer 80 columns -->
<span class="subdued">chrome.input.ime.</span><span>onKeyEvent</span><span class="subdued">.addListener</span>(function(<span>string engineID, KeyboardEvent keyData</span>) <span class="subdued">{...}</span><span></span>); <span class="subdued">chrome.input.ime.</span><span>onKeyEvent</span><span class="subdued">.addListener</span>(function(<span>string engineID, input.ime.KeyboardEvent keyData</span>) <span class="subdued">{...}</span><span></span>);
</div> </div>
<div class="description"> <div class="description">
<p>This event is sent if this extension owns the active IME.</p> <p>This event is sent if this extension owns the active IME.</p>
...@@ -2619,7 +2619,7 @@ ...@@ -2619,7 +2619,7 @@
( (
<span id="typeTemplate"> <span id="typeTemplate">
<span> <span>
<span>KeyboardEvent</span> <a href="input.ime.html#type-input.ime.KeyboardEvent">input.ime.KeyboardEvent</a>
</span> </span>
</span> </span>
) )
......
...@@ -71,4 +71,4 @@ class APIDataSource(object): ...@@ -71,4 +71,4 @@ class APIDataSource(object):
self._base_path + '/' + idl_path), path) self._base_path + '/' + idl_path), path)
except Exception as e: except Exception as e:
logging.warn(e) logging.warn(e)
return None raise
...@@ -29,8 +29,7 @@ class APIDataSourceTest(unittest.TestCase): ...@@ -29,8 +29,7 @@ class APIDataSourceTest(unittest.TestCase):
self.assertEqual(expected, data_source['test_file']) self.assertEqual(expected, data_source['test_file'])
self.assertEqual(expected, data_source['testFile']) self.assertEqual(expected, data_source['testFile'])
self.assertEqual(expected, data_source['testFile.html']) self.assertEqual(expected, data_source['testFile.html'])
self.assertRaises(OSError, data_source.get, 'junk')
self.assertEqual(None, data_source['junk'])
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()
...@@ -6,7 +6,7 @@ threadsafe: false ...@@ -6,7 +6,7 @@ threadsafe: false
handlers: handlers:
- url: /.* - url: /.*
script: echo_handler.py script: appengine_main.py
inbound_services: inbound_services:
- warmup - warmup
#!/usr/bin/env python
# Copyright (c) 2012 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import os
import sys
# Add the original server location to sys.path so we are able to import
# modules from there.
SERVER_PATH = 'chrome/common/extensions/docs/server2'
if os.path.abspath(SERVER_PATH) not in sys.path:
sys.path.append(os.path.abspath(SERVER_PATH))
from google.appengine.ext import webapp
from google.appengine.ext.webapp.util import run_wsgi_app
from handler import Handler
def main():
run_wsgi_app(webapp.WSGIApplication([('/.*', Handler)], debug=False))
if __name__ == '__main__':
main()
...@@ -2,6 +2,8 @@ ...@@ -2,6 +2,8 @@
# Use of this source code is governed by a BSD-style license that can be # Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file. # found in the LICENSE file.
from appengine_wrappers import memcache
MEMCACHE_FILE_SYSTEM_READ = 'MemcacheFileSystem.Get' MEMCACHE_FILE_SYSTEM_READ = 'MemcacheFileSystem.Get'
MEMCACHE_FILE_SYSTEM_STAT = 'MemcacheFileSystem.Stat' MEMCACHE_FILE_SYSTEM_STAT = 'MemcacheFileSystem.Stat'
MEMCACHE_BRANCH_UTILITY = 'BranchUtility' MEMCACHE_BRANCH_UTILITY = 'BranchUtility'
...@@ -11,18 +13,17 @@ class AppEngineMemcache(object): ...@@ -11,18 +13,17 @@ class AppEngineMemcache(object):
use. Uses a branch to make sure there are no key collisions if separate use. Uses a branch to make sure there are no key collisions if separate
branches cache the same file. branches cache the same file.
""" """
def __init__(self, branch, memcache): def __init__(self, branch):
self._branch = branch self._branch = branch
self._memcache = memcache
def Set(self, key, value, namespace, time=60): def Set(self, key, value, namespace, time=60):
return self._memcache.set(key, return memcache.set(key,
value, value,
namespace=self._branch + '.' + namespace, namespace=self._branch + '.' + namespace,
time=time) time=time)
def Get(self, key, namespace): def Get(self, key, namespace):
return self._memcache.get(key, namespace=self._branch + '.' + namespace) return memcache.get(key, namespace=self._branch + '.' + namespace)
def Delete(self, key, namespace): def Delete(self, key, namespace):
return self._memcache.delete(key, namespace=self._branch + '.' + namespace) return memcache.delete(key, namespace=self._branch + '.' + namespace)
...@@ -2,8 +2,7 @@ ...@@ -2,8 +2,7 @@
# Use of this source code is governed by a BSD-style license that can be # Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file. # found in the LICENSE file.
from google.appengine.api import urlfetch from appengine_wrappers import urlfetch
from future import Future from future import Future
class _AsyncFetchDelegate(object): class _AsyncFetchDelegate(object):
......
# Copyright (c) 2012 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
from in_memory_memcache import InMemoryMemcache
# This will attempt to import the actual Appengine modules, and if it fails,
# they will be replaced with fake modules. This is useful during testing.
try:
import google.appengine.ext.webapp as webapp
import google.appengine.api.memcache as memcache
import google.appengine.api.urlfetch as urlfetch
except ImportError:
class FakeUrlFetch(object):
def fetch(self, url):
raise NotImplementedError()
def create_rpc(self):
raise NotImplementedError()
def make_fetch_call(self):
raise NotImplementedError()
urlfetch = FakeUrlFetch()
# Use an in-memory memcache if Appengine memcache isn't available.
memcache = InMemoryMemcache()
# A fake webapp.RequestHandler class for Handler to extend.
class webapp(object):
class RequestHandler(object):
def __init__(self, request, response):
self.request = request
self.response = response
...@@ -3,9 +3,9 @@ ...@@ -3,9 +3,9 @@
# Use of this source code is governed by a BSD-style license that can be # Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file. # found in the LICENSE file.
from appengine_memcache import AppEngineMemcache
from branch_utility import BranchUtility from branch_utility import BranchUtility
from fake_url_fetcher import FakeUrlFetcher from fake_url_fetcher import FakeUrlFetcher
from in_memory_memcache import InMemoryMemcache
import unittest import unittest
class BranchUtilityTest(unittest.TestCase): class BranchUtilityTest(unittest.TestCase):
...@@ -13,7 +13,7 @@ class BranchUtilityTest(unittest.TestCase): ...@@ -13,7 +13,7 @@ class BranchUtilityTest(unittest.TestCase):
self._branch_util = BranchUtility('branch_utility/first.json', self._branch_util = BranchUtility('branch_utility/first.json',
'stable', 'stable',
FakeUrlFetcher('test_data'), FakeUrlFetcher('test_data'),
InMemoryMemcache()) AppEngineMemcache(''))
def testSplitChannelNameFromPath(self): def testSplitChannelNameFromPath(self):
self.assertEquals(('dev', 'hello/stuff.html'), self.assertEquals(('dev', 'hello/stuff.html'),
......
...@@ -46,7 +46,8 @@ class HandlebarDictGenerator(object): ...@@ -46,7 +46,8 @@ class HandlebarDictGenerator(object):
try: try:
self._namespace = model.Namespace(clean_json, clean_json['namespace']) self._namespace = model.Namespace(clean_json, clean_json['namespace'])
except Exception as e: except Exception as e:
logging.info(e) logging.error(e)
raise
def _StripPrefix(self, name): def _StripPrefix(self, name):
if name.startswith(self._namespace.name + '.'): if name.startswith(self._namespace.name + '.'):
...@@ -84,7 +85,8 @@ class HandlebarDictGenerator(object): ...@@ -84,7 +85,8 @@ class HandlebarDictGenerator(object):
'properties': self._GenerateProperties(self._namespace.properties) 'properties': self._GenerateProperties(self._namespace.properties)
} }
except Exception as e: except Exception as e:
logging.info(e) logging.error(e)
raise
def _GenerateType(self, type_): def _GenerateType(self, type_):
type_dict = { type_dict = {
......
#!/usr/bin/env python
# Copyright (c) 2012 The Chromium Authors. All rights reserved. # Copyright (c) 2012 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be # Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file. # found in the LICENSE file.
...@@ -7,19 +6,14 @@ import logging ...@@ -7,19 +6,14 @@ import logging
import os import os
import sys import sys
# Add the original server location to sys.path so we are able to import from appengine_wrappers import webapp
# modules from there. from appengine_wrappers import memcache
SERVER_PATH = 'chrome/common/extensions/docs/server2' from appengine_wrappers import urlfetch
if os.path.abspath(SERVER_PATH) not in sys.path:
sys.path.append(os.path.abspath(SERVER_PATH))
from google.appengine.ext import webapp
from google.appengine.api import memcache
from google.appengine.ext.webapp.util import run_wsgi_app
from api_data_source import APIDataSource from api_data_source import APIDataSource
from api_list_data_source import APIListDataSource from api_list_data_source import APIListDataSource
from appengine_memcache import AppEngineMemcache from appengine_memcache import AppEngineMemcache
from appengine_url_fetcher import AppEngineUrlFetcher
from branch_utility import BranchUtility from branch_utility import BranchUtility
from example_zipper import ExampleZipper from example_zipper import ExampleZipper
from file_system_cache import FileSystemCache from file_system_cache import FileSystemCache
...@@ -32,10 +26,22 @@ from subversion_file_system import SubversionFileSystem ...@@ -32,10 +26,22 @@ from subversion_file_system import SubversionFileSystem
from template_data_source import TemplateDataSource from template_data_source import TemplateDataSource
from appengine_url_fetcher import AppEngineUrlFetcher from appengine_url_fetcher import AppEngineUrlFetcher
# The branch that the server will default to when no branch is specified in the
# URL. This is necessary because it is not possible to pass flags to the script
# handler.
DEFAULT_BRANCH = 'local'
SVN_URL = 'http://src.chromium.org/chrome' SVN_URL = 'http://src.chromium.org/chrome'
TRUNK_URL = SVN_URL + '/trunk' TRUNK_URL = SVN_URL + '/trunk'
BRANCH_URL = SVN_URL + '/branches' BRANCH_URL = SVN_URL + '/branches'
OMAHA_PROXY_URL = 'http://omahaproxy.appspot.com/json'
BRANCH_UTILITY = BranchUtility(OMAHA_PROXY_URL,
DEFAULT_BRANCH,
AppEngineUrlFetcher(''),
AppEngineMemcache('branch_utility'))
STATIC_DIR_PREFIX = 'docs/server2'
EXTENSIONS_PATH = 'chrome/common/extensions' EXTENSIONS_PATH = 'chrome/common/extensions'
DOCS_PATH = 'docs' DOCS_PATH = 'docs'
API_PATH = 'api' API_PATH = 'api'
...@@ -46,36 +52,19 @@ PRIVATE_TEMPLATE_PATH = DOCS_PATH + '/server2/templates/private' ...@@ -46,36 +52,19 @@ PRIVATE_TEMPLATE_PATH = DOCS_PATH + '/server2/templates/private'
EXAMPLES_PATH = DOCS_PATH + '/examples' EXAMPLES_PATH = DOCS_PATH + '/examples'
FULL_EXAMPLES_PATH = DOCS_PATH + '/' + EXAMPLES_PATH FULL_EXAMPLES_PATH = DOCS_PATH + '/' + EXAMPLES_PATH
# The branch that the server will default to when no branch is specified in the
# URL. This is necessary because it is not possible to pass flags to the script
# handler.
DEFAULT_BRANCH = 'local'
# Global cache of instances because Handler is recreated for every request. # Global cache of instances because Handler is recreated for every request.
SERVER_INSTANCES = {} SERVER_INSTANCES = {}
OMAHA_PROXY_URL = 'http://omahaproxy.appspot.com/json' def _GetInstanceForBranch(branch, local_path):
BRANCH_UTILITY = BranchUtility(OMAHA_PROXY_URL,
DEFAULT_BRANCH,
AppEngineUrlFetcher(''),
AppEngineMemcache('branch_utility', memcache))
def _GetURLFromBranch(branch):
if branch == 'trunk':
return TRUNK_URL + '/src'
return BRANCH_URL + '/' + branch + '/src'
class Handler(webapp.RequestHandler):
def _GetInstanceForBranch(self, branch):
if branch in SERVER_INSTANCES: if branch in SERVER_INSTANCES:
return SERVER_INSTANCES[branch] return SERVER_INSTANCES[branch]
if branch == 'local': if branch == 'local':
file_system = LocalFileSystem(EXTENSIONS_PATH) file_system = LocalFileSystem(local_path)
else: else:
fetcher = AppEngineUrlFetcher( fetcher = AppEngineUrlFetcher(
_GetURLFromBranch(branch) + '/' + EXTENSIONS_PATH) _GetURLFromBranch(branch) + '/' + EXTENSIONS_PATH)
file_system = MemcacheFileSystem(SubversionFileSystem(fetcher), file_system = MemcacheFileSystem(SubversionFileSystem(fetcher),
AppEngineMemcache(branch, memcache)) AppEngineMemcache(branch))
cache_builder = FileSystemCache.Builder(file_system) cache_builder = FileSystemCache.Builder(file_system)
api_data_source = APIDataSource(cache_builder, API_PATH) api_data_source = APIDataSource(cache_builder, API_PATH)
...@@ -108,14 +97,34 @@ class Handler(webapp.RequestHandler): ...@@ -108,14 +97,34 @@ class Handler(webapp.RequestHandler):
cache_builder) cache_builder)
return SERVER_INSTANCES[branch] return SERVER_INSTANCES[branch]
def _GetURLFromBranch(branch):
if branch == 'trunk':
return TRUNK_URL + '/src'
return BRANCH_URL + '/' + branch + '/src'
class Handler(webapp.RequestHandler):
def __init__(self, request, response, local_path=EXTENSIONS_PATH):
self._local_path = local_path
super(Handler, self).__init__(request, response)
def _NavigateToPath(self, path):
channel_name, real_path = BRANCH_UTILITY.SplitChannelNameFromPath(path)
branch = BRANCH_UTILITY.GetBranchNumberForChannelName(channel_name)
if real_path == '':
real_path = 'index.html'
# TODO: This leaks Server instances when branch bumps.
_GetInstanceForBranch(branch, self._local_path).Get(real_path,
self.request,
self.response)
def get(self): def get(self):
path = self.request.path path = self.request.path
if '_ah/warmup' in path: if '_ah/warmup' in path:
logging.info('Warmup request.') logging.info('Warmup request.')
self.get('/chrome/extensions/trunk/samples.html') self._NavigateToPath('trunk/samples.html')
self.get('/chrome/extensions/dev/samples.html') self._NavigateToPath('dev/samples.html')
self.get('/chrome/extensions/beta/samples.html') self._NavigateToPath('beta/samples.html')
self.get('/chrome/extensions/stable/samples.html') self._NavigateToPath('stable/samples.html')
return return
# Redirect paths like "directory" to "directory/". This is so relative file # Redirect paths like "directory" to "directory/". This is so relative file
...@@ -124,18 +133,4 @@ class Handler(webapp.RequestHandler): ...@@ -124,18 +133,4 @@ class Handler(webapp.RequestHandler):
self.redirect(path + '/') self.redirect(path + '/')
path = path.replace('/chrome/extensions/', '') path = path.replace('/chrome/extensions/', '')
path = path.strip('/') path = path.strip('/')
self._NavigateToPath(path)
channel_name, real_path = BRANCH_UTILITY.SplitChannelNameFromPath(path)
branch = BRANCH_UTILITY.GetBranchNumberForChannelName(channel_name)
if real_path == '':
real_path = 'index.html'
# TODO: This leaks Server instances when branch bumps.
self._GetInstanceForBranch(branch).Get(real_path,
self.request,
self.response)
def main():
run_wsgi_app(webapp.WSGIApplication([('/.*', Handler)], debug=False))
if __name__ == '__main__':
main()
...@@ -9,16 +9,16 @@ class InMemoryMemcache(object): ...@@ -9,16 +9,16 @@ class InMemoryMemcache(object):
def __init__(self): def __init__(self):
self._cache = {} self._cache = {}
def Set(self, key, value, namespace, time=60): def set(self, key, value, namespace, time=60):
if namespace not in self._cache: if namespace not in self._cache:
self._cache[namespace] = {} self._cache[namespace] = {}
self._cache[namespace][key] = value self._cache[namespace][key] = value
def Get(self, key, namespace): def get(self, key, namespace):
if namespace not in self._cache: if namespace not in self._cache:
return None return None
return self._cache[namespace].get(key, None) return self._cache[namespace].get(key, None)
def Delete(self, key, namespace): def delete(self, key, namespace):
if namespace in self._cache: if namespace in self._cache:
self._cache[namespace].pop(key) self._cache[namespace].pop(key)
#!/usr/bin/env python
# Copyright (c) 2012 The Chromium Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
import os
from StringIO import StringIO
import unittest
from handler import Handler
KNOWN_FAILURES = [
'webstore.html',
]
class _MockResponse:
def __init__(self):
self.status = 200
self.out = StringIO()
def set_status(self, status):
self.status = status
class _MockRequest:
def __init__(self, path):
self.headers = {}
self.path = path
class IntegrationTest(unittest.TestCase):
def testAll(self):
for filename in os.listdir(os.path.join('templates', 'public')):
if filename in KNOWN_FAILURES or filename.startswith('.'):
continue
request = _MockRequest(filename)
response = _MockResponse()
Handler(request, response, local_path='../..').get()
self.assertEqual(200, response.status)
self.assertTrue(response.out.getvalue())
def test404(self):
request = _MockRequest('junk.html')
bad_response = _MockResponse()
Handler(request, bad_response, local_path='../..').get()
self.assertEqual(404, bad_response.status)
self.assertTrue(bad_response.out.getvalue())
if __name__ == '__main__':
unittest.main()
...@@ -7,13 +7,13 @@ import os ...@@ -7,13 +7,13 @@ import os
import unittest import unittest
import appengine_memcache as memcache import appengine_memcache as memcache
from in_memory_memcache import InMemoryMemcache from appengine_memcache import AppEngineMemcache
from local_file_system import LocalFileSystem from local_file_system import LocalFileSystem
from memcache_file_system import MemcacheFileSystem from memcache_file_system import MemcacheFileSystem
class LocalFileSystemTest(unittest.TestCase): class LocalFileSystemTest(unittest.TestCase):
def setUp(self): def setUp(self):
self._memcache = InMemoryMemcache() self._memcache = AppEngineMemcache('')
self._file_system = MemcacheFileSystem( self._file_system = MemcacheFileSystem(
LocalFileSystem(os.path.join('test_data', 'file_system')), LocalFileSystem(os.path.join('test_data', 'file_system')),
self._memcache) self._memcache)
......
...@@ -7,7 +7,7 @@ import mimetypes ...@@ -7,7 +7,7 @@ import mimetypes
import os import os
STATIC_DIR_PREFIX = 'docs/server2' STATIC_DIR_PREFIX = 'docs/server2'
DOCS_PREFIX = 'docs' DOCS_PATH = 'docs'
class ServerInstance(object): class ServerInstance(object):
"""This class is used to hold a data source and fetcher for an instance of a """This class is used to hold a data source and fetcher for an instance of a
...@@ -40,7 +40,7 @@ class ServerInstance(object): ...@@ -40,7 +40,7 @@ class ServerInstance(object):
content = self._example_zipper.Create(path[:-len('.zip')]) content = self._example_zipper.Create(path[:-len('.zip')])
response.headers['content-type'] = mimetypes.types_map['.zip'] response.headers['content-type'] = mimetypes.types_map['.zip']
elif path.startswith('examples/'): elif path.startswith('examples/'):
content = self._cache.GetFromFile(DOCS_PREFIX + '/' + path) content = self._cache.GetFromFile(DOCS_PATH + '/' + path)
response.headers['content-type'] = 'text/plain' response.headers['content-type'] = 'text/plain'
elif path.startswith('static/'): elif path.startswith('static/'):
content = self._FetchStaticResource(path, response) content = self._FetchStaticResource(path, response)
......
...@@ -14,7 +14,7 @@ import build_server ...@@ -14,7 +14,7 @@ import build_server
SERVER_PATH = sys.path[0] SERVER_PATH = sys.path[0]
SRC_PATH = os.path.join(SERVER_PATH, os.pardir, os.pardir, os.pardir, os.pardir, SRC_PATH = os.path.join(SERVER_PATH, os.pardir, os.pardir, os.pardir, os.pardir,
os.pardir) os.pardir)
FILENAMES = ['app.yaml', 'echo_handler.py'] FILENAMES = ['app.yaml', 'appengine_main.py']
def CleanUp(signal, frame): def CleanUp(signal, frame):
for filename in FILENAMES: for filename in FILENAMES:
......
...@@ -123,5 +123,5 @@ class TemplateDataSource(object): ...@@ -123,5 +123,5 @@ class TemplateDataSource(object):
try: try:
return self._cache.GetFromFile(base_path + '/' + real_path) return self._cache.GetFromFile(base_path + '/' + real_path)
except Exception as e: except Exception as e:
logging.warn(e) logging.error(e)
return None return None
{{+partials.standard_api api:apis.experimental_inputUI intro:intros.experimental_inputUI}}
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment