63 from google.appengine.tools import os_compat |
63 from google.appengine.tools import os_compat |
64 |
64 |
65 import getopt |
65 import getopt |
66 import logging |
66 import logging |
67 import os |
67 import os |
|
68 import re |
68 import sys |
69 import sys |
69 import traceback |
70 import traceback |
70 import tempfile |
71 import tempfile |
71 |
72 |
72 from google.appengine.api import yaml_errors |
73 |
73 from google.appengine.tools import appcfg |
74 def SetGlobals(): |
74 from google.appengine.tools import appengine_rpc |
75 """Set various global variables involving the 'google' package. |
75 from google.appengine.tools import dev_appserver |
76 |
|
77 This function should not be called until sys.path has been properly set. |
|
78 """ |
|
79 global yaml_errors, appcfg, appengine_rpc, dev_appserver, os_compat |
|
80 from google.appengine.api import yaml_errors |
|
81 from google.appengine.tools import appcfg |
|
82 from google.appengine.tools import appengine_rpc |
|
83 from google.appengine.tools import dev_appserver |
|
84 from google.appengine.tools import os_compat |
|
85 |
76 |
86 |
77 |
87 |
78 DEFAULT_ADMIN_CONSOLE_SERVER = 'appengine.google.com' |
88 DEFAULT_ADMIN_CONSOLE_SERVER = 'appengine.google.com' |
79 |
89 |
80 ARG_ADDRESS = 'address' |
90 ARG_ADDRESS = 'address' |
96 ARG_SMTP_PORT = 'smtp_port' |
106 ARG_SMTP_PORT = 'smtp_port' |
97 ARG_SMTP_USER = 'smtp_user' |
107 ARG_SMTP_USER = 'smtp_user' |
98 ARG_STATIC_CACHING = 'static_caching' |
108 ARG_STATIC_CACHING = 'static_caching' |
99 ARG_TEMPLATE_DIR = 'template_dir' |
109 ARG_TEMPLATE_DIR = 'template_dir' |
100 |
110 |
101 |
111 SDK_PATH = os.path.dirname( |
102 BASE_PATH = os.path.abspath( |
112 os.path.dirname( |
103 os.path.join(os.path.dirname(dev_appserver.__file__), '../../../')) |
113 os.path.dirname( |
|
114 os.path.dirname(os_compat.__file__) |
|
115 ) |
|
116 ) |
|
117 ) |
104 |
118 |
105 DEFAULT_ARGS = { |
119 DEFAULT_ARGS = { |
106 ARG_PORT: 8080, |
120 ARG_PORT: 8080, |
107 ARG_LOG_LEVEL: logging.INFO, |
121 ARG_LOG_LEVEL: logging.INFO, |
108 ARG_DATASTORE_PATH: os.path.join(tempfile.gettempdir(), |
122 ARG_DATASTORE_PATH: os.path.join(tempfile.gettempdir(), |
110 ARG_HISTORY_PATH: os.path.join(tempfile.gettempdir(), |
124 ARG_HISTORY_PATH: os.path.join(tempfile.gettempdir(), |
111 'dev_appserver.datastore.history'), |
125 'dev_appserver.datastore.history'), |
112 ARG_LOGIN_URL: '/_ah/login', |
126 ARG_LOGIN_URL: '/_ah/login', |
113 ARG_CLEAR_DATASTORE: False, |
127 ARG_CLEAR_DATASTORE: False, |
114 ARG_REQUIRE_INDEXES: False, |
128 ARG_REQUIRE_INDEXES: False, |
115 ARG_TEMPLATE_DIR: os.path.join(BASE_PATH, 'templates'), |
129 ARG_TEMPLATE_DIR: os.path.join(SDK_PATH, 'templates'), |
116 ARG_SMTP_HOST: '', |
130 ARG_SMTP_HOST: '', |
117 ARG_SMTP_PORT: 25, |
131 ARG_SMTP_PORT: 25, |
118 ARG_SMTP_USER: '', |
132 ARG_SMTP_USER: '', |
119 ARG_SMTP_PASSWORD: '', |
133 ARG_SMTP_PASSWORD: '', |
120 ARG_ENABLE_SENDMAIL: False, |
134 ARG_ENABLE_SENDMAIL: False, |
123 ARG_ADDRESS: 'localhost', |
137 ARG_ADDRESS: 'localhost', |
124 ARG_ADMIN_CONSOLE_SERVER: DEFAULT_ADMIN_CONSOLE_SERVER, |
138 ARG_ADMIN_CONSOLE_SERVER: DEFAULT_ADMIN_CONSOLE_SERVER, |
125 ARG_ADMIN_CONSOLE_HOST: None, |
139 ARG_ADMIN_CONSOLE_HOST: None, |
126 ARG_STATIC_CACHING: True, |
140 ARG_STATIC_CACHING: True, |
127 } |
141 } |
|
142 |
|
143 API_PATHS = {'1': |
|
144 {'google': (), |
|
145 'antlr3': ('lib', 'antlr3'), |
|
146 'django': ('lib', 'django'), |
|
147 'webob': ('lib', 'webob'), |
|
148 'yaml': ('lib', 'yaml', 'lib'), |
|
149 } |
|
150 } |
|
151 |
|
152 DEFAULT_API_VERSION = '1' |
|
153 |
|
154 API_PATHS['test'] = API_PATHS[DEFAULT_API_VERSION].copy() |
|
155 API_PATHS['test']['_test'] = ('nonexistent', 'test', 'path') |
|
156 |
|
157 |
|
158 def SetPaths(app_config_path): |
|
159 """Set the interpreter to use the specified API version. |
|
160 |
|
161 The app.yaml file is scanned for the api_version field and the value is |
|
162 extracted. With that information, the paths in API_PATHS are added to the |
|
163 front of sys.paths to make sure that they take precedent over any other paths |
|
164 to older versions of a package. All modules for each package set are cleared |
|
165 out of sys.modules to make sure only the newest version is used. |
|
166 |
|
167 Args: |
|
168 - app_config_path: Path to the app.yaml file. |
|
169 """ |
|
170 api_version_re = re.compile(r'api_version:\s*(?P<api_version>[\w.]{1,32})') |
|
171 api_version = None |
|
172 app_config_file = open(app_config_path, 'r') |
|
173 try: |
|
174 for line in app_config_file: |
|
175 re_match = api_version_re.match(line) |
|
176 if re_match: |
|
177 api_version = re_match.group('api_version') |
|
178 break |
|
179 finally: |
|
180 app_config_file.close() |
|
181 |
|
182 if api_version is None: |
|
183 logging.error("Application configuration file missing an 'api_version' " |
|
184 "value:\n%s" % app_config_path) |
|
185 sys.exit(1) |
|
186 if api_version not in API_PATHS: |
|
187 logging.error("Value of %r for 'api_version' from the application " |
|
188 "configuration file is not valid:\n%s" % |
|
189 (api_version, app_config_path)) |
|
190 sys.exit(1) |
|
191 |
|
192 if api_version == DEFAULT_API_VERSION: |
|
193 return DEFAULT_API_VERSION |
|
194 |
|
195 sdk_path = os.path.dirname( |
|
196 os.path.dirname( |
|
197 os.path.dirname( |
|
198 os.path.dirname(os_compat.__file__) |
|
199 ) |
|
200 ) |
|
201 ) |
|
202 for pkg_name, path_parts in API_PATHS[api_version].iteritems(): |
|
203 for name in sys.modules.keys(): |
|
204 if name == pkg_name or name.startswith('%s.' % pkg_name): |
|
205 del sys.modules[name] |
|
206 pkg_path = os.path.join(sdk_path, *path_parts) |
|
207 sys.path.insert(0, pkg_path) |
|
208 |
|
209 return api_version |
128 |
210 |
129 |
211 |
130 def PrintUsageExit(code): |
212 def PrintUsageExit(code): |
131 """Prints usage information and exits with a status code. |
213 """Prints usage information and exits with a status code. |
132 |
214 |
203 |
285 |
204 if option in ('-a', '--address'): |
286 if option in ('-a', '--address'): |
205 option_dict[ARG_ADDRESS] = value |
287 option_dict[ARG_ADDRESS] = value |
206 |
288 |
207 if option == '--datastore_path': |
289 if option == '--datastore_path': |
208 option_dict[ARG_DATASTORE_PATH] = value |
290 option_dict[ARG_DATASTORE_PATH] = os.path.abspath(value) |
209 |
291 |
210 if option == '--history_path': |
292 if option == '--history_path': |
211 option_dict[ARG_HISTORY_PATH] = value |
293 option_dict[ARG_HISTORY_PATH] = os.path.abspath(value) |
212 |
294 |
213 if option in ('-c', '--clear_datastore'): |
295 if option in ('-c', '--clear_datastore'): |
214 option_dict[ARG_CLEAR_DATASTORE] = True |
296 option_dict[ARG_CLEAR_DATASTORE] = True |
215 |
297 |
216 if option == '--require_indexes': |
298 if option == '--require_indexes': |
239 |
321 |
240 if option == '--show_mail_body': |
322 if option == '--show_mail_body': |
241 option_dict[ARG_SHOW_MAIL_BODY] = True |
323 option_dict[ARG_SHOW_MAIL_BODY] = True |
242 |
324 |
243 if option == '--auth_domain': |
325 if option == '--auth_domain': |
244 dev_appserver.DEFAULT_ENV['AUTH_DOMAIN'] = value |
326 option_dict['_DEFAULT_ENV_AUTH_DOMAIN'] = value |
245 |
327 |
246 if option == '--debug_imports': |
328 if option == '--debug_imports': |
247 dev_appserver.HardenedModulesHook.ENABLE_LOGGING = True |
329 option_dict['_ENABLE_LOGGING'] = True |
248 |
330 |
249 if option == '--template_dir': |
331 if option == '--template_dir': |
250 option_dict[ARG_TEMPLATE_DIR] = value |
332 option_dict[ARG_TEMPLATE_DIR] = value |
251 |
333 |
252 if option == '--admin_console_server': |
334 if option == '--admin_console_server': |
289 if len(args) != 1: |
371 if len(args) != 1: |
290 print >>sys.stderr, 'Invalid arguments' |
372 print >>sys.stderr, 'Invalid arguments' |
291 PrintUsageExit(1) |
373 PrintUsageExit(1) |
292 |
374 |
293 root_path = args[0] |
375 root_path = args[0] |
|
376 for suffix in ('yaml', 'yml'): |
|
377 path = os.path.join(root_path, 'app.%s' % suffix) |
|
378 if os.path.exists(path): |
|
379 api_version = SetPaths(path) |
|
380 break |
|
381 else: |
|
382 logging.error("Application configuration file not found in %s" % root_path) |
|
383 return 1 |
|
384 |
|
385 SetGlobals() |
|
386 dev_appserver.API_VERSION = api_version |
|
387 |
|
388 if '_DEFAULT_ENV_AUTH_DOMAIN' in option_dict: |
|
389 auth_domain = option_dict['_DEFAULT_ENV_AUTH_DOMAIN'] |
|
390 dev_appserver.DEFAULT_ENV['AUTH_DOMAIN'] = auth_domain |
|
391 if '_ENABLE_LOGGING' in option_dict: |
|
392 enable_logging = option_dict['_ENABLE_LOGGING'] |
|
393 dev_appserver.HardenedModulesHook.ENABLE_LOGGING = enable_logging |
|
394 |
294 log_level = option_dict[ARG_LOG_LEVEL] |
395 log_level = option_dict[ARG_LOG_LEVEL] |
295 port = option_dict[ARG_PORT] |
396 port = option_dict[ARG_PORT] |
296 datastore_path = option_dict[ARG_DATASTORE_PATH] |
397 datastore_path = option_dict[ARG_DATASTORE_PATH] |
297 login_url = option_dict[ARG_LOGIN_URL] |
398 login_url = option_dict[ARG_LOGIN_URL] |
298 template_dir = option_dict[ARG_TEMPLATE_DIR] |
399 template_dir = option_dict[ARG_TEMPLATE_DIR] |
333 |
434 |
334 http_server = dev_appserver.CreateServer(root_path, |
435 http_server = dev_appserver.CreateServer(root_path, |
335 login_url, |
436 login_url, |
336 port, |
437 port, |
337 template_dir, |
438 template_dir, |
|
439 sdk_dir=SDK_PATH, |
338 serve_address=serve_address, |
440 serve_address=serve_address, |
339 require_indexes=require_indexes, |
441 require_indexes=require_indexes, |
340 static_caching=static_caching) |
442 static_caching=static_caching) |
341 |
443 |
342 logging.info('Running application %s on port %d: http://%s:%d', |
444 logging.info('Running application %s on port %d: http://%s:%d', |