thirdparty/google_appengine/google/appengine/tools/dev_appserver_main.py
changeset 109 620f9b141567
child 149 f2e327a7c5de
equal deleted inserted replaced
108:261778de26ff 109:620f9b141567
       
     1 #!/usr/bin/env python
       
     2 #
       
     3 # Copyright 2007 Google Inc.
       
     4 #
       
     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
       
     8 #
       
     9 #     http://www.apache.org/licenses/LICENSE-2.0
       
    10 #
       
    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.
       
    16 #
       
    17 """Runs a development application server for an application.
       
    18 
       
    19 %(script)s [options] <application root>
       
    20 
       
    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.
       
    23 
       
    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 """
       
    58 
       
    59 
       
    60 
       
    61 import os
       
    62 os.environ['TZ'] = 'UTC'
       
    63 import time
       
    64 if hasattr(time, 'tzset'):
       
    65   time.tzset()
       
    66 
       
    67 import getopt
       
    68 import logging
       
    69 import sys
       
    70 import traceback
       
    71 import tempfile
       
    72 
       
    73 from google.appengine.api import yaml_errors
       
    74 from google.appengine.tools import appcfg
       
    75 from google.appengine.tools import dev_appserver
       
    76 
       
    77 
       
    78 DEFAULT_ADMIN_CONSOLE_SERVER = 'appengine.google.com'
       
    79 
       
    80 ARG_ADDRESS = 'address'
       
    81 ARG_ADMIN_CONSOLE_SERVER = 'admin_console_server'
       
    82 ARG_ADMIN_CONSOLE_HOST = 'admin_console_host'
       
    83 ARG_AUTH_DOMAIN = 'auth_domain'
       
    84 ARG_CLEAR_DATASTORE = 'clear_datastore'
       
    85 ARG_DATASTORE_PATH = 'datastore_path'
       
    86 ARG_DEBUG_IMPORTS = 'debug_imports'
       
    87 ARG_ENABLE_SENDMAIL = 'enable_sendmail'
       
    88 ARG_SHOW_MAIL_BODY = 'show_mail_body'
       
    89 ARG_HISTORY_PATH = 'history_path'
       
    90 ARG_LOGIN_URL = 'login_url'
       
    91 ARG_LOG_LEVEL = 'log_level'
       
    92 ARG_PORT = 'port'
       
    93 ARG_REQUIRE_INDEXES = 'require_indexes'
       
    94 ARG_SMTP_HOST = 'smtp_host'
       
    95 ARG_SMTP_PASSWORD = 'smtp_password'
       
    96 ARG_SMTP_PORT = 'smtp_port'
       
    97 ARG_SMTP_USER = 'smtp_user'
       
    98 ARG_TEMPLATE_DIR = 'template_dir'
       
    99 
       
   100 
       
   101 BASE_PATH = os.path.abspath(
       
   102   os.path.join(os.path.dirname(dev_appserver.__file__), '../../../'))
       
   103 
       
   104 DEFAULT_ARGS = {
       
   105   ARG_PORT: 8080,
       
   106   ARG_LOG_LEVEL: logging.INFO,
       
   107   ARG_DATASTORE_PATH: os.path.join(tempfile.gettempdir(),
       
   108                                    'dev_appserver.datastore'),
       
   109   ARG_HISTORY_PATH: os.path.join(tempfile.gettempdir(),
       
   110                                  'dev_appserver.datastore.history'),
       
   111   ARG_LOGIN_URL: '/_ah/login',
       
   112   ARG_CLEAR_DATASTORE: False,
       
   113   ARG_REQUIRE_INDEXES: False,
       
   114   ARG_TEMPLATE_DIR: os.path.join(BASE_PATH, 'templates'),
       
   115   ARG_SMTP_HOST: '',
       
   116   ARG_SMTP_PORT: 25,
       
   117   ARG_SMTP_USER: '',
       
   118   ARG_SMTP_PASSWORD: '',
       
   119   ARG_ENABLE_SENDMAIL: False,
       
   120   ARG_SHOW_MAIL_BODY: False,
       
   121   ARG_AUTH_DOMAIN: 'gmail.com',
       
   122   ARG_ADDRESS: 'localhost',
       
   123   ARG_ADMIN_CONSOLE_SERVER: DEFAULT_ADMIN_CONSOLE_SERVER,
       
   124   ARG_ADMIN_CONSOLE_HOST: None,
       
   125 }
       
   126 
       
   127 
       
   128 def PrintUsageExit(code):
       
   129   """Prints usage information and exits with a status code.
       
   130 
       
   131   Args:
       
   132     code: Status code to pass to sys.exit() after displaying usage information.
       
   133   """
       
   134   render_dict = DEFAULT_ARGS.copy()
       
   135   render_dict['script'] = os.path.basename(sys.argv[0])
       
   136   print sys.modules['__main__'].__doc__ % render_dict
       
   137   sys.stdout.flush()
       
   138   sys.exit(code)
       
   139 
       
   140 
       
   141 def ParseArguments(argv):
       
   142   """Parses command-line arguments.
       
   143 
       
   144   Args:
       
   145     argv: Command-line arguments, including the executable name, used to
       
   146       execute this application.
       
   147 
       
   148   Returns:
       
   149     Tuple (args, option_dict) where:
       
   150       args: List of command-line arguments following the executable name.
       
   151       option_dict: Dictionary of parsed flags that maps keys from DEFAULT_ARGS
       
   152         to their values, which are either pulled from the defaults, or from
       
   153         command-line flags.
       
   154   """
       
   155   option_dict = DEFAULT_ARGS.copy()
       
   156 
       
   157   try:
       
   158     opts, args = getopt.gnu_getopt(
       
   159       argv[1:],
       
   160       'a:cdhp:',
       
   161       [ 'address=',
       
   162         'admin_console_server=',
       
   163         'admin_console_host=',
       
   164         'auth_domain=',
       
   165         'clear_datastore',
       
   166         'datastore_path=',
       
   167         'debug',
       
   168         'debug_imports',
       
   169         'enable_sendmail',
       
   170         'show_mail_body',
       
   171         'help',
       
   172         'history_path=',
       
   173         'port=',
       
   174         'require_indexes',
       
   175         'smtp_host=',
       
   176         'smtp_password=',
       
   177         'smtp_port=',
       
   178         'smtp_user=',
       
   179         'template_dir=',
       
   180       ])
       
   181   except getopt.GetoptError, e:
       
   182     print >>sys.stderr, 'Error: %s' % e
       
   183     PrintUsageExit(1)
       
   184 
       
   185   for option, value in opts:
       
   186     if option in ('-h', '--help'):
       
   187       PrintUsageExit(0)
       
   188 
       
   189     if option in ('-d', '--debug'):
       
   190       option_dict[ARG_LOG_LEVEL] = logging.DEBUG
       
   191 
       
   192     if option in ('-p', '--port'):
       
   193       try:
       
   194         option_dict[ARG_PORT] = int(value)
       
   195         if not (65535 > option_dict[ARG_PORT] > 0):
       
   196           raise ValueError
       
   197       except ValueError:
       
   198         print >>sys.stderr, 'Invalid value supplied for port'
       
   199         PrintUsageExit(1)
       
   200 
       
   201     if option in ('-a', '--address'):
       
   202       option_dict[ARG_ADDRESS] = value
       
   203 
       
   204     if option == '--datastore_path':
       
   205       option_dict[ARG_DATASTORE_PATH] = value
       
   206 
       
   207     if option == '--history_path':
       
   208       option_dict[ARG_HISTORY_PATH] = value
       
   209 
       
   210     if option in ('-c', '--clear_datastore'):
       
   211       option_dict[ARG_CLEAR_DATASTORE] = True
       
   212 
       
   213     if option == '--require_indexes':
       
   214       option_dict[ARG_REQUIRE_INDEXES] = True
       
   215 
       
   216     if option == '--smtp_host':
       
   217       option_dict[ARG_SMTP_HOST] = value
       
   218 
       
   219     if option == '--smtp_port':
       
   220       try:
       
   221         option_dict[ARG_SMTP_PORT] = int(value)
       
   222         if not (65535 > option_dict[ARG_SMTP_PORT] > 0):
       
   223           raise ValueError
       
   224       except ValueError:
       
   225         print >>sys.stderr, 'Invalid value supplied for SMTP port'
       
   226         PrintUsageExit(1)
       
   227 
       
   228     if option == '--smtp_user':
       
   229       option_dict[ARG_SMTP_USER] = value
       
   230 
       
   231     if option == '--smtp_password':
       
   232       option_dict[ARG_SMTP_PASSWORD] = value
       
   233 
       
   234     if option == '--enable_sendmail':
       
   235       option_dict[ARG_ENABLE_SENDMAIL] = True
       
   236 
       
   237     if option == '--show_mail_body':
       
   238       option_dict[ARG_SHOW_MAIL_BODY] = True
       
   239 
       
   240     if option == '--auth_domain':
       
   241       dev_appserver.DEFAULT_ENV['AUTH_DOMAIN'] = value
       
   242 
       
   243     if option == '--debug_imports':
       
   244       dev_appserver.HardenedModulesHook.ENABLE_LOGGING = True
       
   245 
       
   246     if option == '--template_dir':
       
   247       option_dict[ARG_TEMPLATE_DIR] = value
       
   248 
       
   249     if option == '--admin_console_server':
       
   250       option_dict[ARG_ADMIN_CONSOLE_SERVER] = value.strip()
       
   251 
       
   252     if option == '--admin_console_host':
       
   253       option_dict[ARG_ADMIN_CONSOLE_HOST] = value
       
   254 
       
   255   return args, option_dict
       
   256 
       
   257 
       
   258 def MakeRpcServer(option_dict):
       
   259   """Create a new HttpRpcServer.
       
   260 
       
   261   Creates a new HttpRpcServer to check for updates to the SDK.
       
   262 
       
   263   Args:
       
   264     option_dict: The dict of command line options.
       
   265 
       
   266   Returns:
       
   267     A HttpRpcServer.
       
   268   """
       
   269   server = appcfg.HttpRpcServer(
       
   270       option_dict[ARG_ADMIN_CONSOLE_SERVER],
       
   271       lambda: ('unused_email', 'unused_password'),
       
   272       host_override=option_dict[ARG_ADMIN_CONSOLE_HOST])
       
   273   server.authenticated = True
       
   274   return server
       
   275 
       
   276 
       
   277 def main(argv):
       
   278   """Runs the development application server."""
       
   279   args, option_dict = ParseArguments(argv)
       
   280 
       
   281   if len(args) != 1:
       
   282     print >>sys.stderr, 'Invalid arguments'
       
   283     PrintUsageExit(1)
       
   284 
       
   285   root_path = args[0]
       
   286   log_level = option_dict[ARG_LOG_LEVEL]
       
   287   port = option_dict[ARG_PORT]
       
   288   datastore_path = option_dict[ARG_DATASTORE_PATH]
       
   289   login_url = option_dict[ARG_LOGIN_URL]
       
   290   template_dir = option_dict[ARG_TEMPLATE_DIR]
       
   291   serve_address = option_dict[ARG_ADDRESS]
       
   292   require_indexes = option_dict[ARG_REQUIRE_INDEXES]
       
   293 
       
   294   logging.basicConfig(
       
   295     level=log_level,
       
   296     format='%(levelname)-8s %(asctime)s %(filename)s] %(message)s')
       
   297 
       
   298   config = None
       
   299   try:
       
   300     config, matcher = dev_appserver.LoadAppConfig(root_path, {})
       
   301   except yaml_errors.EventListenerError, e:
       
   302     logging.error('Fatal error when loading application configuration:\n' +
       
   303                   str(e))
       
   304     return 1
       
   305   except dev_appserver.InvalidAppConfigError, e:
       
   306     logging.error('Application configuration file invalid:\n%s', e)
       
   307     return 1
       
   308 
       
   309   if option_dict[ARG_ADMIN_CONSOLE_SERVER] != '':
       
   310     server = MakeRpcServer(option_dict)
       
   311     update_check = appcfg.UpdateCheck(server, config)
       
   312     update_check.CheckSupportedVersion()
       
   313     if update_check.AllowedToCheckForUpdates():
       
   314       update_check.CheckForUpdates()
       
   315 
       
   316   try:
       
   317     dev_appserver.SetupStubs(config.application, **option_dict)
       
   318   except:
       
   319     exc_type, exc_value, exc_traceback = sys.exc_info()
       
   320     logging.error(str(exc_type) + ': ' + str(exc_value))
       
   321     logging.debug(''.join(traceback.format_exception(
       
   322           exc_type, exc_value, exc_traceback)))
       
   323     return 1
       
   324 
       
   325   http_server = dev_appserver.CreateServer(root_path,
       
   326                                            login_url,
       
   327                                            port,
       
   328                                            template_dir,
       
   329                                            serve_address=serve_address,
       
   330                                            require_indexes=require_indexes)
       
   331 
       
   332   logging.info('Running application %s on port %d: http://%s:%d',
       
   333                config.application, port, serve_address, port)
       
   334   try:
       
   335     try:
       
   336       http_server.serve_forever()
       
   337     except KeyboardInterrupt:
       
   338       logging.info('Server interrupted by user, terminating')
       
   339     except:
       
   340       exc_info = sys.exc_info()
       
   341       info_string = '\n'.join(traceback.format_exception(*exc_info))
       
   342       logging.error('Error encountered:\n%s\nNow terminating.', info_string)
       
   343       return 1
       
   344   finally:
       
   345     http_server.server_close()
       
   346 
       
   347   return 0
       
   348 
       
   349 
       
   350 if __name__ == '__main__':
       
   351   sys.exit(main(sys.argv))