Load /Users/solydzajs/Desktop/google_appengine into
[Melange.git] / thirdparty / google_appengine / google / appengine / tools / dev_appserver_main.py
blob78922f1334e5f1cd647e1a9d2efaa70044e58036
1 #!/usr/bin/env python
3 # Copyright 2007 Google Inc.
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
17 """Runs a development application server for an application.
19 %(script)s [options] <application root>
21 Application root must be the path to the application to run in this server.
22 Must contain a valid app.yaml or app.yml file.
24 Options:
25 --help, -h View this helpful message.
26 --debug, -d Use debug logging. (Default false)
27 --clear_datastore, -c Clear the Datastore on startup. (Default false)
28 --address=ADDRESS, -a ADDRESS
29 Address to which this server should bind. (Default
30 %(address)s).
31 --port=PORT, -p PORT Port for the server to run on. (Default %(port)s)
32 --datastore_path=PATH Path to use for storing Datastore file stub data.
33 (Default %(datastore_path)s)
34 --history_path=PATH Path to use for storing Datastore history.
35 (Default %(history_path)s)
36 --require_indexes Disallows queries that require composite indexes
37 not defined in index.yaml.
38 --smtp_host=HOSTNAME SMTP host to send test mail to. Leaving this
39 unset will disable SMTP mail sending.
40 (Default '%(smtp_host)s')
41 --smtp_port=PORT SMTP port to send test mail to.
42 (Default %(smtp_port)s)
43 --smtp_user=USER SMTP user to connect as. Stub will only attempt
44 to login if this field is non-empty.
45 (Default '%(smtp_user)s').
46 --smtp_password=PASSWORD Password for SMTP server.
47 (Default '%(smtp_password)s')
48 --enable_sendmail Enable sendmail when SMTP not configured.
49 (Default false)
50 --show_mail_body Log the body of emails in mail stub.
51 (Default false)
52 --auth_domain Authorization domain that this app runs in.
53 (Default gmail.com)
54 --debug_imports Enables debug logging for module imports, showing
55 search paths used for finding modules and any
56 errors encountered during the import process.
57 --allow_skipped_files Allow access to files matched by app.yaml's
58 skipped_files (default False)
59 --disable_static_caching Never allow the browser to cache static files.
60 (Default enable if expiration set in app.yaml)
61 """
65 from google.appengine.tools import os_compat
67 import getopt
68 import logging
69 import os
70 import re
71 import sys
72 import traceback
73 import tempfile
76 def SetGlobals():
77 """Set various global variables involving the 'google' package.
79 This function should not be called until sys.path has been properly set.
80 """
81 global yaml_errors, appcfg, appengine_rpc, dev_appserver, os_compat
82 from google.appengine.api import yaml_errors
83 from google.appengine.dist import py_zipimport
84 from google.appengine.tools import appcfg
85 from google.appengine.tools import appengine_rpc
86 from google.appengine.tools import dev_appserver
87 from google.appengine.tools import os_compat
91 DEFAULT_ADMIN_CONSOLE_SERVER = 'appengine.google.com'
93 ARG_ADDRESS = 'address'
94 ARG_ADMIN_CONSOLE_SERVER = 'admin_console_server'
95 ARG_ADMIN_CONSOLE_HOST = 'admin_console_host'
96 ARG_AUTH_DOMAIN = 'auth_domain'
97 ARG_CLEAR_DATASTORE = 'clear_datastore'
98 ARG_DATASTORE_PATH = 'datastore_path'
99 ARG_DEBUG_IMPORTS = 'debug_imports'
100 ARG_ENABLE_SENDMAIL = 'enable_sendmail'
101 ARG_SHOW_MAIL_BODY = 'show_mail_body'
102 ARG_HISTORY_PATH = 'history_path'
103 ARG_LOGIN_URL = 'login_url'
104 ARG_LOG_LEVEL = 'log_level'
105 ARG_PORT = 'port'
106 ARG_REQUIRE_INDEXES = 'require_indexes'
107 ARG_ALLOW_SKIPPED_FILES = 'allow_skipped_files'
108 ARG_SMTP_HOST = 'smtp_host'
109 ARG_SMTP_PASSWORD = 'smtp_password'
110 ARG_SMTP_PORT = 'smtp_port'
111 ARG_SMTP_USER = 'smtp_user'
112 ARG_STATIC_CACHING = 'static_caching'
113 ARG_TEMPLATE_DIR = 'template_dir'
114 ARG_TRUSTED = 'trusted'
116 SDK_PATH = os.path.dirname(
117 os.path.dirname(
118 os.path.dirname(
119 os.path.dirname(os_compat.__file__)
124 DEFAULT_ARGS = {
125 ARG_PORT: 8080,
126 ARG_LOG_LEVEL: logging.INFO,
127 ARG_DATASTORE_PATH: os.path.join(tempfile.gettempdir(),
128 'dev_appserver.datastore'),
129 ARG_HISTORY_PATH: os.path.join(tempfile.gettempdir(),
130 'dev_appserver.datastore.history'),
131 ARG_LOGIN_URL: '/_ah/login',
132 ARG_CLEAR_DATASTORE: False,
133 ARG_REQUIRE_INDEXES: False,
134 ARG_TEMPLATE_DIR: os.path.join(SDK_PATH, 'templates'),
135 ARG_SMTP_HOST: '',
136 ARG_SMTP_PORT: 25,
137 ARG_SMTP_USER: '',
138 ARG_SMTP_PASSWORD: '',
139 ARG_ENABLE_SENDMAIL: False,
140 ARG_SHOW_MAIL_BODY: False,
141 ARG_AUTH_DOMAIN: 'gmail.com',
142 ARG_ADDRESS: 'localhost',
143 ARG_ADMIN_CONSOLE_SERVER: DEFAULT_ADMIN_CONSOLE_SERVER,
144 ARG_ADMIN_CONSOLE_HOST: None,
145 ARG_ALLOW_SKIPPED_FILES: False,
146 ARG_STATIC_CACHING: True,
147 ARG_TRUSTED: False,
150 API_PATHS = {'1':
151 {'google': (),
152 'antlr3': ('lib', 'antlr3'),
153 'django': ('lib', 'django'),
154 'webob': ('lib', 'webob'),
155 'yaml': ('lib', 'yaml', 'lib'),
159 DEFAULT_API_VERSION = '1'
161 API_PATHS['test'] = API_PATHS[DEFAULT_API_VERSION].copy()
162 API_PATHS['test']['_test'] = ('nonexistent', 'test', 'path')
165 def SetPaths(app_config_path):
166 """Set the interpreter to use the specified API version.
168 The app.yaml file is scanned for the api_version field and the value is
169 extracted. With that information, the paths in API_PATHS are added to the
170 front of sys.paths to make sure that they take precedent over any other paths
171 to older versions of a package. All modules for each package set are cleared
172 out of sys.modules to make sure only the newest version is used.
174 Args:
175 - app_config_path: Path to the app.yaml file.
177 api_version_re = re.compile(r'api_version:\s*(?P<api_version>[\w.]{1,32})')
178 api_version = None
179 app_config_file = open(app_config_path, 'r')
180 try:
181 for line in app_config_file:
182 re_match = api_version_re.match(line)
183 if re_match:
184 api_version = re_match.group('api_version')
185 break
186 finally:
187 app_config_file.close()
189 if api_version is None:
190 logging.error("Application configuration file missing an 'api_version' "
191 "value:\n%s" % app_config_path)
192 sys.exit(1)
193 if api_version not in API_PATHS:
194 logging.error("Value of %r for 'api_version' from the application "
195 "configuration file is not valid:\n%s" %
196 (api_version, app_config_path))
197 sys.exit(1)
199 if api_version == DEFAULT_API_VERSION:
200 return DEFAULT_API_VERSION
202 sdk_path = os.path.dirname(
203 os.path.dirname(
204 os.path.dirname(
205 os.path.dirname(os_compat.__file__)
209 for pkg_name, path_parts in API_PATHS[api_version].iteritems():
210 for name in sys.modules.keys():
211 if name == pkg_name or name.startswith('%s.' % pkg_name):
212 del sys.modules[name]
213 pkg_path = os.path.join(sdk_path, *path_parts)
214 sys.path.insert(0, pkg_path)
216 return api_version
219 def PrintUsageExit(code):
220 """Prints usage information and exits with a status code.
222 Args:
223 code: Status code to pass to sys.exit() after displaying usage information.
225 render_dict = DEFAULT_ARGS.copy()
226 render_dict['script'] = os.path.basename(sys.argv[0])
227 print sys.modules['__main__'].__doc__ % render_dict
228 sys.stdout.flush()
229 sys.exit(code)
232 def ParseArguments(argv):
233 """Parses command-line arguments.
235 Args:
236 argv: Command-line arguments, including the executable name, used to
237 execute this application.
239 Returns:
240 Tuple (args, option_dict) where:
241 args: List of command-line arguments following the executable name.
242 option_dict: Dictionary of parsed flags that maps keys from DEFAULT_ARGS
243 to their values, which are either pulled from the defaults, or from
244 command-line flags.
246 option_dict = DEFAULT_ARGS.copy()
248 try:
249 opts, args = getopt.gnu_getopt(
250 argv[1:],
251 'a:cdhp:',
252 [ 'address=',
253 'admin_console_server=',
254 'admin_console_host=',
255 'allow_skipped_files',
256 'auth_domain=',
257 'clear_datastore',
258 'datastore_path=',
259 'debug',
260 'debug_imports',
261 'enable_sendmail',
262 'disable_static_caching',
263 'show_mail_body',
264 'help',
265 'history_path=',
266 'port=',
267 'require_indexes',
268 'smtp_host=',
269 'smtp_password=',
270 'smtp_port=',
271 'smtp_user=',
272 'template_dir=',
273 'trusted',
275 except getopt.GetoptError, e:
276 print >>sys.stderr, 'Error: %s' % e
277 PrintUsageExit(1)
279 for option, value in opts:
280 if option in ('-h', '--help'):
281 PrintUsageExit(0)
283 if option in ('-d', '--debug'):
284 option_dict[ARG_LOG_LEVEL] = logging.DEBUG
286 if option in ('-p', '--port'):
287 try:
288 option_dict[ARG_PORT] = int(value)
289 if not (65535 > option_dict[ARG_PORT] > 0):
290 raise ValueError
291 except ValueError:
292 print >>sys.stderr, 'Invalid value supplied for port'
293 PrintUsageExit(1)
295 if option in ('-a', '--address'):
296 option_dict[ARG_ADDRESS] = value
298 if option == '--datastore_path':
299 option_dict[ARG_DATASTORE_PATH] = os.path.abspath(value)
301 if option == '--history_path':
302 option_dict[ARG_HISTORY_PATH] = os.path.abspath(value)
304 if option in ('-c', '--clear_datastore'):
305 option_dict[ARG_CLEAR_DATASTORE] = True
307 if option == '--require_indexes':
308 option_dict[ARG_REQUIRE_INDEXES] = True
310 if option == '--smtp_host':
311 option_dict[ARG_SMTP_HOST] = value
313 if option == '--smtp_port':
314 try:
315 option_dict[ARG_SMTP_PORT] = int(value)
316 if not (65535 > option_dict[ARG_SMTP_PORT] > 0):
317 raise ValueError
318 except ValueError:
319 print >>sys.stderr, 'Invalid value supplied for SMTP port'
320 PrintUsageExit(1)
322 if option == '--smtp_user':
323 option_dict[ARG_SMTP_USER] = value
325 if option == '--smtp_password':
326 option_dict[ARG_SMTP_PASSWORD] = value
328 if option == '--enable_sendmail':
329 option_dict[ARG_ENABLE_SENDMAIL] = True
331 if option == '--show_mail_body':
332 option_dict[ARG_SHOW_MAIL_BODY] = True
334 if option == '--auth_domain':
335 option_dict['_DEFAULT_ENV_AUTH_DOMAIN'] = value
337 if option == '--debug_imports':
338 option_dict['_ENABLE_LOGGING'] = True
340 if option == '--template_dir':
341 option_dict[ARG_TEMPLATE_DIR] = value
343 if option == '--admin_console_server':
344 option_dict[ARG_ADMIN_CONSOLE_SERVER] = value.strip()
346 if option == '--admin_console_host':
347 option_dict[ARG_ADMIN_CONSOLE_HOST] = value
349 if option == '--allow_skipped_files':
350 option_dict[ARG_ALLOW_SKIPPED_FILES] = True
352 if option == '--disable_static_caching':
353 option_dict[ARG_STATIC_CACHING] = False
355 if option == '--trusted':
356 option_dict[ARG_TRUSTED] = True
358 return args, option_dict
361 def MakeRpcServer(option_dict):
362 """Create a new HttpRpcServer.
364 Creates a new HttpRpcServer to check for updates to the SDK.
366 Args:
367 option_dict: The dict of command line options.
369 Returns:
370 A HttpRpcServer.
372 server = appengine_rpc.HttpRpcServer(
373 option_dict[ARG_ADMIN_CONSOLE_SERVER],
374 lambda: ('unused_email', 'unused_password'),
375 appcfg.GetUserAgent(),
376 appcfg.GetSourceName(),
377 host_override=option_dict[ARG_ADMIN_CONSOLE_HOST])
378 server.authenticated = True
379 return server
382 def main(argv):
383 """Runs the development application server."""
384 args, option_dict = ParseArguments(argv)
386 if len(args) != 1:
387 print >>sys.stderr, 'Invalid arguments'
388 PrintUsageExit(1)
390 root_path = args[0]
391 for suffix in ('yaml', 'yml'):
392 path = os.path.join(root_path, 'app.%s' % suffix)
393 if os.path.exists(path):
394 api_version = SetPaths(path)
395 break
396 else:
397 logging.error("Application configuration file not found in %s" % root_path)
398 return 1
400 SetGlobals()
401 dev_appserver.API_VERSION = api_version
403 if '_DEFAULT_ENV_AUTH_DOMAIN' in option_dict:
404 auth_domain = option_dict['_DEFAULT_ENV_AUTH_DOMAIN']
405 dev_appserver.DEFAULT_ENV['AUTH_DOMAIN'] = auth_domain
406 if '_ENABLE_LOGGING' in option_dict:
407 enable_logging = option_dict['_ENABLE_LOGGING']
408 dev_appserver.HardenedModulesHook.ENABLE_LOGGING = enable_logging
410 log_level = option_dict[ARG_LOG_LEVEL]
411 port = option_dict[ARG_PORT]
412 datastore_path = option_dict[ARG_DATASTORE_PATH]
413 login_url = option_dict[ARG_LOGIN_URL]
414 template_dir = option_dict[ARG_TEMPLATE_DIR]
415 serve_address = option_dict[ARG_ADDRESS]
416 require_indexes = option_dict[ARG_REQUIRE_INDEXES]
417 allow_skipped_files = option_dict[ARG_ALLOW_SKIPPED_FILES]
418 static_caching = option_dict[ARG_STATIC_CACHING]
420 logging.basicConfig(
421 level=log_level,
422 format='%(levelname)-8s %(asctime)s %(filename)s:%(lineno)s] %(message)s')
424 config = None
425 try:
426 config, matcher = dev_appserver.LoadAppConfig(root_path, {})
427 except yaml_errors.EventListenerError, e:
428 logging.error('Fatal error when loading application configuration:\n' +
429 str(e))
430 return 1
431 except dev_appserver.InvalidAppConfigError, e:
432 logging.error('Application configuration file invalid:\n%s', e)
433 return 1
435 if option_dict[ARG_ADMIN_CONSOLE_SERVER] != '':
436 server = MakeRpcServer(option_dict)
437 update_check = appcfg.UpdateCheck(server, config)
438 update_check.CheckSupportedVersion()
439 if update_check.AllowedToCheckForUpdates():
440 update_check.CheckForUpdates()
442 try:
443 dev_appserver.SetupStubs(config.application, **option_dict)
444 except:
445 exc_type, exc_value, exc_traceback = sys.exc_info()
446 logging.error(str(exc_type) + ': ' + str(exc_value))
447 logging.debug(''.join(traceback.format_exception(
448 exc_type, exc_value, exc_traceback)))
449 return 1
451 http_server = dev_appserver.CreateServer(
452 root_path,
453 login_url,
454 port,
455 template_dir,
456 sdk_dir=SDK_PATH,
457 serve_address=serve_address,
458 require_indexes=require_indexes,
459 allow_skipped_files=allow_skipped_files,
460 static_caching=static_caching)
462 logging.info('Running application %s on port %d: http://%s:%d',
463 config.application, port, serve_address, port)
464 try:
465 try:
466 http_server.serve_forever()
467 except KeyboardInterrupt:
468 logging.info('Server interrupted by user, terminating')
469 except:
470 exc_info = sys.exc_info()
471 info_string = '\n'.join(traceback.format_exception(*exc_info))
472 logging.error('Error encountered:\n%s\nNow terminating.', info_string)
473 return 1
474 finally:
475 http_server.server_close()
477 return 0
480 if __name__ == '__main__':
481 sys.exit(main(sys.argv))
482 else:
483 SetGlobals()