class IO(object): """ """ def __init__(self): """ """ self.buffer = StringIO() def write(self, data): """ """ sys.__stdout__.write(data) if hasattr(self, 'callback'): self.callback(data) else: self.buffer.write(data)
def render(self, *a, **b): from gluon.compileapp import run_view_in if len(a) > 2: raise SyntaxError( 'Response.render can be called with two arguments, at most') elif len(a) == 2: (view, self._vars) = (a[0], a[1]) elif len(a) == 1 and isinstance(a[0], str): (view, self._vars) = (a[0], {}) elif len(a) == 1 and hasattr(a[0], 'read') and callable(a[0].read): (view, self._vars) = (a[0], {}) elif len(a) == 1 and isinstance(a[0], dict): (view, self._vars) = (None, a[0]) else: (view, self._vars) = (None, {}) self._vars.update(b) self._view_environment.update(self._vars) if view: from gluon._compat import StringIO (obody, oview) = (self.body, self.view) (self.body, self.view) = (StringIO(), view) run_view_in(self._view_environment) page = self.body.getvalue() self.body.close() (self.body, self.view) = (obody, oview) else: run_view_in(self._view_environment) page = self.body.getvalue() return page
def install(app_name, filename, data, overwrite=True): f = StringIO(base64.b64decode(data)) installed = app_install(app_name, f, request, filename, overwrite=overwrite) return installed
def dummy_open(path, mode): if path == pjoin('views', 'layout.html'): return StringIO("{{block left_sidebar}}left{{end}}" "{{include}}" "{{block right_sidebar}}right{{end}}") elif path == pjoin('views', 'layoutbrackets.html'): return StringIO("[[block left_sidebar]]left[[end]]" "[[include]]" "[[block right_sidebar]]right[[end]]") elif path == pjoin('views', 'default', 'index.html'): return StringIO("{{extend 'layout.html'}}" "{{block left_sidebar}}{{super}} {{end}}" "to" "{{block right_sidebar}} {{super}}{{end}}") elif path == pjoin('views', 'default', 'indexbrackets.html'): return StringIO("[[extend 'layoutbrackets.html']]" "[[block left_sidebar]][[super]] [[end]]" "to" "[[block right_sidebar]] [[super]][[end]]") elif path == pjoin('views', 'default', 'missing.html'): return StringIO("{{extend 'wut'}}" "{{block left_sidebar}}{{super}} {{end}}" "to" "{{block right_sidebar}} {{super}}{{end}}") elif path == pjoin('views', 'default', 'noescape.html'): return StringIO("""{{=NOESCAPE('<script></script>')}}""") raise IOError
def __init__(self): Storage.__init__(self) self.status = 200 self.headers = dict() self.headers['X-Powered-By'] = 'web2py' self.body = StringIO() self.session_id = None self.cookies = Cookie.SimpleCookie() self.postprocessing = [] self.flash = '' # used by the default view layout self.meta = Storage() # used by web2py_ajax.html self.menu = [] # used by the default view layout self.files = [] # used by web2py_ajax.html self._vars = None self._caller = lambda f: f() self._view_environment = None self._custom_commit = None self._custom_rollback = None self.generic_patterns = ['*'] self.delimiters = ('{{', '}}') self.formstyle = 'table3cols' self.form_label_separator = ': '
def b64pack(app): """ Given an app's name, return the base64 representation of its packed version. """ folder = apath(app, r=request) tmpfile = StringIO() tar = tarfile.TarFile(fileobj=tmpfile, mode='w') try: filenames = listdir( folder, '^[\w\.\-]+$', add_dirs=True, exclude_content_from=['cache', 'sessions', 'errors']) for fname in filenames: tar.add(os.path.join(folder, fname), fname, False) finally: tar.close() tmpfile.seek(0) gzfile = StringIO() w2pfp = gzip.GzipFile(fileobj=gzfile, mode='wb') w2pfp.write(tmpfile.read()) w2pfp.close() gzfile.seek(0) return base64.b64encode(gzfile.read())
def copystream_progress(request, chunk_size=10**5): """ Copies request.env.wsgi_input into request.body and stores progress upload status in cache_ram X-Progress-ID:length and X-Progress-ID:uploaded """ env = request.env if not env.get('CONTENT_LENGTH', None): return StringIO() source = env['wsgi.input'] try: size = int(env['CONTENT_LENGTH']) except ValueError: raise HTTP(400, "Invalid Content-Length header") try: # Android requires this dest = tempfile.NamedTemporaryFile() except NotImplementedError: # and GAE this dest = tempfile.TemporaryFile() if not 'X-Progress-ID' in request.get_vars: copystream(source, dest, size, chunk_size) return dest cache_key = 'X-Progress-ID:' + request.get_vars['X-Progress-ID'] cache_ram = CacheInRam(request) # same as cache.ram because meta_storage cache_ram(cache_key + ':length', lambda: size, 0) cache_ram(cache_key + ':uploaded', lambda: 0, 0) while size > 0: if size < chunk_size: data = source.read(size) cache_ram.increment(cache_key + ':uploaded', size) else: data = source.read(chunk_size) cache_ram.increment(cache_key + ':uploaded', chunk_size) length = len(data) if length > size: (data, length) = (data[:size], size) size -= length if length == 0: break dest.write(data) if length < chunk_size: break dest.seek(0) cache_ram(cache_key + ':length', None) cache_ram(cache_key + ':uploaded', None) return dest
def b64pack(app): """ Given an app's name, return the base64 representation of its packed version. """ folder = apath(app, r=request) tmpfile = StringIO() tar = tarfile.TarFile(fileobj=tmpfile, mode='w') try: filenames = listdir(folder, '^[\w\.\-]+$', add_dirs=True, exclude_content_from=['cache', 'sessions', 'errors']) for fname in filenames: tar.add(os.path.join(folder, fname), fname, False) finally: tar.close() tmpfile.seek(0) gzfile = StringIO() w2pfp = gzip.GzipFile(fileobj=gzfile, mode='wb') w2pfp.write(tmpfile.read()) w2pfp.close() gzfile.seek(0) return base64.b64encode(gzfile.read())
class DummyResponse(): def __init__(self): self.body = StringIO() def write(self, data, escape=True): if not escape: self.body.write(str(data)) elif hasattr(data, 'xml') and callable(data.xml): self.body.write(data.xml()) else: # make it a string if not isinstance(data, (str, unicodeT)): data = str(data) elif isinstance(data, unicodeT): data = data.encode('utf8', 'xmlcharrefreplace') data = cgi.escape(data, True).replace("'", "'") self.body.write(data)
def render(content="hello world", stream=None, filename=None, path=None, context={}, lexers={}, delimiters=('{{', '}}'), writer='response.write'): """ Generic render function Args: content: default content stream: file-like obj to read template from filename: where to find template path: base path for templates context: env lexers: custom lexers to use delimiters: opening and closing tags writer: where to inject the resulting stream Example:: >>> render() 'hello world' >>> render(content='abc') 'abc' >>> render(content="abc'") "abc'" >>> render(content=''''a"'bc''') 'a"'bc' >>> render(content='a\\nbc') 'a\\nbc' >>> render(content='a"bcd"e') 'a"bcd"e' >>> render(content="'''a\\nc'''") "'''a\\nc'''" >>> render(content="'''a\\'c'''") "'''a\'c'''" >>> render(content='{{for i in range(a):}}{{=i}}<br />{{pass}}', context=dict(a=5)) '0<br />1<br />2<br />3<br />4<br />' >>> render(content='{%for i in range(a):%}{%=i%}<br />{%pass%}', context=dict(a=5),delimiters=('{%','%}')) '0<br />1<br />2<br />3<br />4<br />' >>> render(content="{{='''hello\\nworld'''}}") 'hello\\nworld' >>> render(content='{{for i in range(3):\\n=i\\npass}}') '012' """ # here to avoid circular Imports try: from gluon.globals import Response except ImportError: # Working standalone. Build a mock Response object. Response = DummyResponse # Add it to the context so we can use it. if not 'NOESCAPE' in context: context['NOESCAPE'] = NOESCAPE if isinstance(content, unicodeT): content = content.encode('utf8') # save current response class if context and 'response' in context: old_response_body = context['response'].body context['response'].body = StringIO() else: old_response_body = None context['response'] = Response() # If we don't have anything to render, why bother? if not content and not stream and not filename: raise SyntaxError("Must specify a stream or filename or content") # Here for legacy purposes, probably can be reduced to # something more simple. close_stream = False if not stream: if filename: stream = open(filename, 'rb') close_stream = True elif content: stream = StringIO(to_native(content)) # Execute the template. code = str( TemplateParser(stream.read(), context=context, path=path, lexers=lexers, delimiters=delimiters, writer=writer)) try: exec(code, context) except Exception: # for i,line in enumerate(code.split('\n')): print i,line raise if close_stream: stream.close() # Returned the rendered content. text = context['response'].body.getvalue() if old_response_body is not None: context['response'].body = old_response_body return text
class Response(Storage): """ Defines the response object and the default values of its members response.write( ) can be used to write in the output html """ def __init__(self): Storage.__init__(self) self.status = 200 self.headers = dict() self.headers['X-Powered-By'] = 'web2py' self.body = StringIO() self.session_id = None self.cookies = Cookie.SimpleCookie() self.postprocessing = [] self.flash = '' # used by the default view layout self.meta = Storage() # used by web2py_ajax.html self.menu = [] # used by the default view layout self.files = [] # used by web2py_ajax.html self._vars = None self._caller = lambda f: f() self._view_environment = None self._custom_commit = None self._custom_rollback = None self.generic_patterns = ['*'] self.delimiters = ('{{', '}}') self.formstyle = 'table3cols' self.form_label_separator = ': ' def write(self, data, escape=True): if not escape: self.body.write(str(data)) else: # FIXME PY3: self.body.write(to_native(xmlescape(data))) def render(self, *a, **b): from gluon.compileapp import run_view_in if len(a) > 2: raise SyntaxError( 'Response.render can be called with two arguments, at most') elif len(a) == 2: (view, self._vars) = (a[0], a[1]) elif len(a) == 1 and isinstance(a[0], str): (view, self._vars) = (a[0], {}) elif len(a) == 1 and hasattr(a[0], 'read') and callable(a[0].read): (view, self._vars) = (a[0], {}) elif len(a) == 1 and isinstance(a[0], dict): (view, self._vars) = (None, a[0]) else: (view, self._vars) = (None, {}) self._vars.update(b) self._view_environment.update(self._vars) if view: from gluon._compat import StringIO (obody, oview) = (self.body, self.view) (self.body, self.view) = (StringIO(), view) run_view_in(self._view_environment) page = self.body.getvalue() self.body.close() (self.body, self.view) = (obody, oview) else: run_view_in(self._view_environment) page = self.body.getvalue() return page def include_meta(self): s = "\n" for meta in iteritems((self.meta or {})): k, v = meta if isinstance(v, dict): s += '<meta' + ''.join( ' %s="%s"' % (xmlescape(key), to_native(xmlescape(v[key]))) for key in v) + ' />\n' else: s += '<meta name="%s" content="%s" />\n' % ( k, to_native(xmlescape(v))) self.write(s, escape=False) def include_files(self, extensions=None): """ Includes files (usually in the head). Can minify and cache local files By default, caches in ram for 5 minutes. To change, response.cache_includes = (cache_method, time_expire). Example: (cache.disk, 60) # caches to disk for 1 minute. """ files = [] ext_files = [] has_js = has_css = False for item in self.files: if isinstance(item, (list, tuple)): ext_files.append(item) continue if extensions and not item.rpartition('.')[2] in extensions: continue if item in files: continue if item.endswith('.js'): has_js = True if item.endswith('.css'): has_css = True files.append(item) if have_minify and ((self.optimize_css and has_css) or (self.optimize_js and has_js)): # cache for 5 minutes by default key = hashlib.md5(repr(files)).hexdigest() cache = self.cache_includes or (current.cache.ram, 60 * 5) def call_minify(files=files): return minify.minify(files, URL('static', 'temp'), current.request.folder, self.optimize_css, self.optimize_js) if cache: cache_model, time_expire = cache files = cache_model('response.files.minified/' + key, call_minify, time_expire) else: files = call_minify() files.extend(ext_files) s = [] for item in files: if isinstance(item, str): f = item.lower().split('?')[0] ext = f.rpartition('.')[2] # if static_version we need also to check for # static_version_urls. In that case, the _.x.x.x # bit would have already been added by the URL() # function if self.static_version and not self.static_version_urls: item = item.replace('/static/', '/static/_%s/' % self.static_version, 1) tmpl = template_mapping.get(ext) if tmpl: s.append(tmpl % item) elif isinstance(item, (list, tuple)): f = item[0] tmpl = template_mapping.get(f) if tmpl: s.append(tmpl % item[1]) self.write(''.join(s), escape=False) def stream(self, stream, chunk_size=DEFAULT_CHUNK_SIZE, request=None, attachment=False, filename=None): """ If in a controller function:: return response.stream(file, 100) the file content will be streamed at 100 bytes at the time Args: stream: filename or read()able content chunk_size(int): Buffer size request: the request object attachment(bool): prepares the correct headers to download the file as an attachment. Usually creates a pop-up download window on browsers filename(str): the name for the attachment Note: for using the stream name (filename) with attachments the option must be explicitly set as function parameter (will default to the last request argument otherwise) """ headers = self.headers # for attachment settings and backward compatibility keys = [item.lower() for item in headers] if attachment: if filename is None: attname = "" else: attname = filename headers["Content-Disposition"] = \ 'attachment;filename="%s"' % attname if not request: request = current.request if isinstance(stream, (str, unicodeT)): stream_file_or_304_or_206(stream, chunk_size=chunk_size, request=request, headers=headers, status=self.status) # ## the following is for backward compatibility if hasattr(stream, 'name'): filename = stream.name if filename and not 'content-type' in keys: headers['Content-Type'] = contenttype(filename) if filename and not 'content-length' in keys: try: headers['Content-Length'] = \ os.path.getsize(filename) except OSError: pass env = request.env # Internet Explorer < 9.0 will not allow downloads over SSL unless caching is enabled if request.is_https and isinstance(env.http_user_agent, str) and \ not re.search(r'Opera', env.http_user_agent) and \ re.search(r'MSIE [5-8][^0-9]', env.http_user_agent): headers['Pragma'] = 'cache' headers['Cache-Control'] = 'private' if request and env.web2py_use_wsgi_file_wrapper: wrapped = env.wsgi_file_wrapper(stream, chunk_size) else: wrapped = streamer(stream, chunk_size=chunk_size) return wrapped def download(self, request, db, chunk_size=DEFAULT_CHUNK_SIZE, attachment=True, download_filename=None): """ Example of usage in controller:: def download(): return response.download(request, db) Downloads from http://..../download/filename """ from pydal.exceptions import NotAuthorizedException, NotFoundException current.session.forget(current.response) if not request.args: raise HTTP(404) name = request.args[-1] items = re.compile('(?P<table>.*?)\.(?P<field>.*?)\..*').match(name) if not items: raise HTTP(404) (t, f) = (items.group('table'), items.group('field')) try: field = db[t][f] except AttributeError: raise HTTP(404) try: (filename, stream) = field.retrieve(name, nameonly=True) except NotAuthorizedException: raise HTTP(403) except NotFoundException: raise HTTP(404) except IOError: raise HTTP(404) headers = self.headers headers['Content-Type'] = contenttype(name) if download_filename is None: download_filename = filename if attachment: headers['Content-Disposition'] = \ 'attachment; filename="%s"' % download_filename.replace('"', '\"') return self.stream(stream, chunk_size=chunk_size, request=request) def json(self, data, default=None, indent=None): if 'Content-Type' not in self.headers: self.headers['Content-Type'] = 'application/json' return json(data, default=default or custom_json, indent=indent) def xmlrpc(self, request, methods): from gluon.xmlrpc import handler """ assuming:: def add(a, b): return a+b if a controller function \"func\":: return response.xmlrpc(request, [add]) the controller will be able to handle xmlrpc requests for the add function. Example:: import xmlrpclib connection = xmlrpclib.ServerProxy( 'http://hostname/app/contr/func') print(connection.add(3, 4)) """ return handler(request, self, methods) def toolbar(self): from gluon.html import DIV, SCRIPT, BEAUTIFY, TAG, A BUTTON = TAG.button admin = URL("admin", "default", "design", extension='html', args=current.request.application) from gluon.dal import DAL dbstats = [] dbtables = {} infos = DAL.get_instances() for k, v in iteritems(infos): dbstats.append( TABLE(*[ TR(PRE(row[0]), '%.2fms' % (row[1] * 1000)) for row in v['dbstats'] ])) dbtables[k] = dict(defined=v['dbtables']['defined'] or '[no defined tables]', lazy=v['dbtables']['lazy'] or '[no lazy tables]') u = web2py_uuid() backtotop = A('Back to top', _href="#totop-%s" % u) # Convert lazy request.vars from property to Storage so they # will be displayed in the toolbar. request = copy.copy(current.request) request.update(vars=current.request.vars, get_vars=current.request.get_vars, post_vars=current.request.post_vars) return DIV(BUTTON('design', _onclick="document.location='%s'" % admin), BUTTON('request', _onclick="jQuery('#request-%s').slideToggle()" % u), BUTTON('response', _onclick="jQuery('#response-%s').slideToggle()" % u), BUTTON('session', _onclick="jQuery('#session-%s').slideToggle()" % u), BUTTON('db tables', _onclick="jQuery('#db-tables-%s').slideToggle()" % u), BUTTON('db stats', _onclick="jQuery('#db-stats-%s').slideToggle()" % u), DIV(BEAUTIFY(request), backtotop, _class="w2p-toolbar-hidden", _id="request-%s" % u), DIV(BEAUTIFY(current.session), backtotop, _class="w2p-toolbar-hidden", _id="session-%s" % u), DIV(BEAUTIFY(current.response), backtotop, _class="w2p-toolbar-hidden", _id="response-%s" % u), DIV(BEAUTIFY(dbtables), backtotop, _class="w2p-toolbar-hidden", _id="db-tables-%s" % u), DIV(BEAUTIFY(dbstats), backtotop, _class="w2p-toolbar-hidden", _id="db-stats-%s" % u), SCRIPT("jQuery('.w2p-toolbar-hidden').hide()"), _id="totop-%s" % u)
def __init__(self): """ """ self.buffer = StringIO()
def run(history, statement, env={}): """ Evaluates a python statement in a given history and returns the result. """ history.unpicklables = INITIAL_UNPICKLABLES # extract the statement to be run if not statement: return '' # the python compiler doesn't like network line endings statement = statement.replace('\r\n', '\n') # add a couple newlines at the end of the statement. this makes # single-line expressions such as 'class Foo: pass' evaluate happily. statement += '\n\n' # log and compile the statement up front try: logging.info('Compiling and evaluating:\n%s' % statement) compiled = compile(statement, '<string>', 'single') except: return str(traceback.format_exc()) # create a dedicated module to be used as this statement's __main__ statement_module = new.module('__main__') # use this request's __builtin__, since it changes on each request. # this is needed for import statements, among other things. import __builtin__ statement_module.__builtins__ = __builtin__ # load the history from the datastore history = History() # swap in our custom module for __main__. then unpickle the history # globals, run the statement, and re-pickle the history globals, all # inside it. old_main = sys.modules.get('__main__') output = StringIO() try: sys.modules['__main__'] = statement_module statement_module.__name__ = '__main__' statement_module.__dict__.update(env) # re-evaluate the unpicklables for code in history.unpicklables: exec(code, statement_module.__dict__) # re-initialize the globals for name, val in history.globals_dict().items(): try: statement_module.__dict__[name] = val except: msg = 'Dropping %s since it could not be unpickled.\n' % name output.write(msg) logging.warning(msg + traceback.format_exc()) history.remove_global(name) # run! old_globals = dict((key, represent( value)) for key, value in statement_module.__dict__.items()) try: old_stdout, old_stderr = sys.stdout, sys.stderr try: sys.stderr = sys.stdout = output locker.acquire() exec(compiled, statement_module.__dict__) finally: locker.release() sys.stdout, sys.stderr = old_stdout, old_stderr except: output.write(str(traceback.format_exc())) return output.getvalue() # extract the new globals that this statement added new_globals = {} for name, val in statement_module.__dict__.items(): if name not in old_globals or represent(val) != old_globals[name]: new_globals[name] = val if True in [isinstance(val, tuple(UNPICKLABLE_TYPES)) for val in new_globals.values()]: # this statement added an unpicklable global. store the statement and # the names of all of the globals it added in the unpicklables. history.add_unpicklable(statement, new_globals.keys()) logging.debug('Storing this statement as an unpicklable.') else: # this statement didn't add any unpicklables. pickle and store the # new globals back into the datastore. for name, val in new_globals.items(): if not name.startswith('__'): try: history.set_global(name, val) except (TypeError, pickle.PicklingError) as ex: UNPICKLABLE_TYPES.append(type(val)) history.add_unpicklable(statement, new_globals.keys()) finally: sys.modules['__main__'] = old_main return output.getvalue()
def sorting_dumps(obj, protocol=None): file = StringIO() SortingPickler(file, protocol).dump(obj) return file.getvalue()
def __init__(self): self.body = StringIO()
def render(content="hello world", stream=None, filename=None, path=None, context={}, lexers={}, delimiters=('{{', '}}'), writer='response.write' ): """ Generic render function Args: content: default content stream: file-like obj to read template from filename: where to find template path: base path for templates context: env lexers: custom lexers to use delimiters: opening and closing tags writer: where to inject the resulting stream Example:: >>> render() 'hello world' >>> render(content='abc') 'abc' >>> render(content="abc'") "abc'" >>> render(content=''''a"'bc''') 'a"'bc' >>> render(content='a\\nbc') 'a\\nbc' >>> render(content='a"bcd"e') 'a"bcd"e' >>> render(content="'''a\\nc'''") "'''a\\nc'''" >>> render(content="'''a\\'c'''") "'''a\'c'''" >>> render(content='{{for i in range(a):}}{{=i}}<br />{{pass}}', context=dict(a=5)) '0<br />1<br />2<br />3<br />4<br />' >>> render(content='{%for i in range(a):%}{%=i%}<br />{%pass%}', context=dict(a=5),delimiters=('{%','%}')) '0<br />1<br />2<br />3<br />4<br />' >>> render(content="{{='''hello\\nworld'''}}") 'hello\\nworld' >>> render(content='{{for i in range(3):\\n=i\\npass}}') '012' """ # here to avoid circular Imports try: from gluon.globals import Response except ImportError: # Working standalone. Build a mock Response object. Response = DummyResponse # Add it to the context so we can use it. if 'NOESCAPE' not in context: context['NOESCAPE'] = NOESCAPE if isinstance(content, unicodeT): content = content.encode('utf8') # save current response class if context and 'response' in context: old_response_body = context['response'].body context['response'].body = StringIO() else: old_response_body = None context['response'] = Response() # If we don't have anything to render, why bother? if not content and not stream and not filename: raise SyntaxError("Must specify a stream or filename or content") # Here for legacy purposes, probably can be reduced to # something more simple. close_stream = False if not stream: if filename: stream = open(filename, 'rb') close_stream = True elif content: stream = StringIO(to_native(content)) # Execute the template. code = str(TemplateParser(stream.read( ), context=context, path=path, lexers=lexers, delimiters=delimiters, writer=writer)) try: exec(code, context) except Exception: # for i,line in enumerate(code.split('\n')): print i,line raise if close_stream: stream.close() # Returned the rendered content. text = context['response'].body.getvalue() if old_response_body is not None: context['response'].body = old_response_body return text
class Response(Storage): """ Defines the response object and the default values of its members response.write( ) can be used to write in the output html """ def __init__(self): Storage.__init__(self) self.status = 200 self.headers = dict() self.headers['X-Powered-By'] = 'web2py' self.body = StringIO() self.session_id = None self.cookies = Cookie.SimpleCookie() self.postprocessing = [] self.flash = '' # used by the default view layout self.meta = Storage() # used by web2py_ajax.html self.menu = [] # used by the default view layout self.files = [] # used by web2py_ajax.html self._vars = None self._caller = lambda f: f() self._view_environment = None self._custom_commit = None self._custom_rollback = None self.generic_patterns = ['*'] self.delimiters = ('{{', '}}') self.formstyle = 'table3cols' self.form_label_separator = ': ' def write(self, data, escape=True): if not escape: self.body.write(str(data)) else: self.body.write(to_native(xmlescape(data))) def render(self, *a, **b): from gluon.compileapp import run_view_in if len(a) > 2: raise SyntaxError( 'Response.render can be called with two arguments, at most') elif len(a) == 2: (view, self._vars) = (a[0], a[1]) elif len(a) == 1 and isinstance(a[0], str): (view, self._vars) = (a[0], {}) elif len(a) == 1 and hasattr(a[0], 'read') and callable(a[0].read): (view, self._vars) = (a[0], {}) elif len(a) == 1 and isinstance(a[0], dict): (view, self._vars) = (None, a[0]) else: (view, self._vars) = (None, {}) self._vars.update(b) self._view_environment.update(self._vars) if view: from gluon._compat import StringIO (obody, oview) = (self.body, self.view) (self.body, self.view) = (StringIO(), view) page = run_view_in(self._view_environment) self.body.close() (self.body, self.view) = (obody, oview) else: page = run_view_in(self._view_environment) return page def include_meta(self): s = "\n" for meta in iteritems((self.meta or {})): k, v = meta if isinstance(v, dict): s += '<meta' + ''.join(' %s="%s"' % (to_native(xmlescape(key)), to_native(xmlescape(v[key]))) for key in v) + ' />\n' else: s += '<meta name="%s" content="%s" />\n' % (k, to_native(xmlescape(v))) self.write(s, escape=False) def include_files(self, extensions=None): """ Includes files (usually in the head). Can minify and cache local files By default, caches in ram for 5 minutes. To change, response.cache_includes = (cache_method, time_expire). Example: (cache.disk, 60) # caches to disk for 1 minute. """ app = current.request.application # We start by building a files list in which adjacent files internal to # the application are placed in a list inside the files list. # # We will only minify and concat adjacent internal files as there's # no way to know if changing the order with which the files are apppended # will break things since the order matters in both CSS and JS and # internal files may be interleaved with external ones. files = [] # For the adjacent list we're going to use storage List to both distinguish # from the regular list and so we can add attributes internal = List() internal.has_js = False internal.has_css = False done = set() # to remove duplicates for item in self.files: if not isinstance(item, list): if item in done: continue done.add(item) if isinstance(item, (list, tuple)) or not item.startswith('/' + app): # also consider items in other web2py applications to be external if internal: files.append(internal) internal = List() internal.has_js = False internal.has_css = False files.append(item) continue if extensions and not item.rpartition('.')[2] in extensions: continue internal.append(item) if item.endswith('.js'): internal.has_js = True if item.endswith('.css'): internal.has_css = True if internal: files.append(internal) # We're done we can now minify if have_minify: for i, f in enumerate(files): if isinstance(f, List) and ((self.optimize_css and f.has_css) or (self.optimize_js and f.has_js)): # cache for 5 minutes by default key = hashlib_md5(repr(f)).hexdigest() cache = self.cache_includes or (current.cache.ram, 60 * 5) def call_minify(files=f): return List(minify.minify(files, URL('static', 'temp'), current.request.folder, self.optimize_css, self.optimize_js)) if cache: cache_model, time_expire = cache files[i] = cache_model('response.files.minified/' + key, call_minify, time_expire) else: files[i] = call_minify() def static_map(s, item): if isinstance(item, str): f = item.lower().split('?')[0] ext = f.rpartition('.')[2] # if static_version we need also to check for # static_version_urls. In that case, the _.x.x.x # bit would have already been added by the URL() # function if self.static_version and not self.static_version_urls: item = item.replace( '/static/', '/static/_%s/' % self.static_version, 1) tmpl = template_mapping.get(ext) if tmpl: s.append(tmpl % item) elif isinstance(item, (list, tuple)): f = item[0] tmpl = template_mapping.get(f) if tmpl: s.append(tmpl % item[1]) s = [] for item in files: if isinstance(item, List): for f in item: static_map(s, f) else: static_map(s, item) self.write(''.join(s), escape=False) def stream(self, stream, chunk_size=DEFAULT_CHUNK_SIZE, request=None, attachment=False, filename=None ): """ If in a controller function:: return response.stream(file, 100) the file content will be streamed at 100 bytes at the time Args: stream: filename or read()able content chunk_size(int): Buffer size request: the request object attachment(bool): prepares the correct headers to download the file as an attachment. Usually creates a pop-up download window on browsers filename(str): the name for the attachment Note: for using the stream name (filename) with attachments the option must be explicitly set as function parameter (will default to the last request argument otherwise) """ headers = self.headers # for attachment settings and backward compatibility keys = [item.lower() for item in headers] if attachment: if filename is None: attname = "" else: attname = filename headers["Content-Disposition"] = \ 'attachment;filename="%s"' % attname if not request: request = current.request if isinstance(stream, (str, unicodeT)): stream_file_or_304_or_206(stream, chunk_size=chunk_size, request=request, headers=headers, status=self.status) # ## the following is for backward compatibility if hasattr(stream, 'name'): filename = stream.name if filename and 'content-type' not in keys: headers['Content-Type'] = contenttype(filename) if filename and 'content-length' not in keys: try: headers['Content-Length'] = \ os.path.getsize(filename) except OSError: pass env = request.env # Internet Explorer < 9.0 will not allow downloads over SSL unless caching is enabled if request.is_https and isinstance(env.http_user_agent, str) and \ not re.search(r'Opera', env.http_user_agent) and \ re.search(r'MSIE [5-8][^0-9]', env.http_user_agent): headers['Pragma'] = 'cache' headers['Cache-Control'] = 'private' if request and env.web2py_use_wsgi_file_wrapper: wrapped = env.wsgi_file_wrapper(stream, chunk_size) else: wrapped = streamer(stream, chunk_size=chunk_size) return wrapped def download(self, request, db, chunk_size=DEFAULT_CHUNK_SIZE, attachment=True, download_filename=None): """ Example of usage in controller:: def download(): return response.download(request, db) Downloads from http://..../download/filename """ from pydal.exceptions import NotAuthorizedException, NotFoundException current.session.forget(current.response) if not request.args: raise HTTP(404) name = request.args[-1] items = re.compile('(?P<table>.*?)\.(?P<field>.*?)\..*').match(name) if not items: raise HTTP(404) (t, f) = (items.group('table'), items.group('field')) try: field = db[t][f] except AttributeError: raise HTTP(404) try: (filename, stream) = field.retrieve(name, nameonly=True) except NotAuthorizedException: raise HTTP(403) except NotFoundException: raise HTTP(404) except IOError: raise HTTP(404) headers = self.headers headers['Content-Type'] = contenttype(name) if download_filename is None: download_filename = filename if attachment: # Browsers still don't have a simple uniform way to have non ascii # characters in the filename so for now we are percent encoding it if isinstance(download_filename, unicodeT): download_filename = download_filename.encode('utf-8') download_filename = urllib_quote(download_filename) headers['Content-Disposition'] = \ 'attachment; filename="%s"' % download_filename.replace('"', '\"') return self.stream(stream, chunk_size=chunk_size, request=request) def json(self, data, default=None, indent=None): if 'Content-Type' not in self.headers: self.headers['Content-Type'] = 'application/json' return json(data, default=default or custom_json, indent=indent) def xmlrpc(self, request, methods): from gluon.xmlrpc import handler """ assuming:: def add(a, b): return a+b if a controller function \"func\":: return response.xmlrpc(request, [add]) the controller will be able to handle xmlrpc requests for the add function. Example:: import xmlrpclib connection = xmlrpclib.ServerProxy( 'http://hostname/app/contr/func') print(connection.add(3, 4)) """ return handler(request, self, methods) def toolbar(self): from gluon.html import DIV, SCRIPT, BEAUTIFY, TAG, A BUTTON = TAG.button admin = URL("admin", "default", "design", extension='html', args=current.request.application) from gluon.dal import DAL dbstats = [] dbtables = {} infos = DAL.get_instances() for k, v in iteritems(infos): dbstats.append(TABLE(*[TR(PRE(row[0]), '%.2fms' % (row[1]*1000)) for row in v['dbstats']])) dbtables[k] = dict(defined=v['dbtables']['defined'] or '[no defined tables]', lazy=v['dbtables']['lazy'] or '[no lazy tables]') u = web2py_uuid() backtotop = A('Back to top', _href="#totop-%s" % u) # Convert lazy request.vars from property to Storage so they # will be displayed in the toolbar. request = copy.copy(current.request) request.update(vars=current.request.vars, get_vars=current.request.get_vars, post_vars=current.request.post_vars) return DIV( BUTTON('design', _onclick="document.location='%s'" % admin), BUTTON('request', _onclick="jQuery('#request-%s').slideToggle()" % u), BUTTON('response', _onclick="jQuery('#response-%s').slideToggle()" % u), BUTTON('session', _onclick="jQuery('#session-%s').slideToggle()" % u), BUTTON('db tables', _onclick="jQuery('#db-tables-%s').slideToggle()" % u), BUTTON('db stats', _onclick="jQuery('#db-stats-%s').slideToggle()" % u), DIV(BEAUTIFY(request), backtotop, _class="w2p-toolbar-hidden", _id="request-%s" % u), DIV(BEAUTIFY(current.session), backtotop, _class="w2p-toolbar-hidden", _id="session-%s" % u), DIV(BEAUTIFY(current.response), backtotop, _class="w2p-toolbar-hidden", _id="response-%s" % u), DIV(BEAUTIFY(dbtables), backtotop, _class="w2p-toolbar-hidden", _id="db-tables-%s" % u), DIV(BEAUTIFY(dbstats), backtotop, _class="w2p-toolbar-hidden", _id="db-stats-%s" % u), SCRIPT("jQuery('.w2p-toolbar-hidden').hide()"), _id="totop-%s" % u )
def console(): """ Defines the behavior of the console web2py execution """ import optparse import textwrap usage = "python web2py.py" description = """\ web2py Web Framework startup script. ATTENTION: unless a password is specified (-a 'passwd') web2py will attempt to run a GUI. In this case command line options are ignored.""" description = textwrap.dedent(description) parser = optparse.OptionParser(usage, None, optparse.Option, ProgramVersion) parser.description = description msg = ('IP address of the server (e.g., 127.0.0.1 or ::1); ' 'Note: This value is ignored when using the \'interfaces\' option.') parser.add_option('-i', '--ip', default='127.0.0.1', dest='ip', help=msg) parser.add_option('-p', '--port', default='8000', dest='port', type='int', help='port of server (8000)') parser.add_option( '-G', '--GAE', default=None, dest='gae', help="'-G configure' will create app.yaml and gaehandler.py") msg = ('password to be used for administration ' '(use -a "<recycle>" to reuse the last password))') parser.add_option('-a', '--password', default='<ask>', dest='password', help=msg) parser.add_option('-c', '--ssl_certificate', default='', dest='ssl_certificate', help='file that contains ssl certificate') parser.add_option('-k', '--ssl_private_key', default='', dest='ssl_private_key', help='file that contains ssl private key') msg = ('Use this file containing the CA certificate to validate X509 ' 'certificates from clients') parser.add_option('--ca-cert', action='store', dest='ssl_ca_certificate', default=None, help=msg) parser.add_option('-d', '--pid_filename', default='httpserver.pid', dest='pid_filename', help='file to store the pid of the server') parser.add_option('-l', '--log_filename', default='httpserver.log', dest='log_filename', help='file to log connections') parser.add_option('-n', '--numthreads', default=None, type='int', dest='numthreads', help='number of threads (deprecated)') parser.add_option('--minthreads', default=None, type='int', dest='minthreads', help='minimum number of server threads') parser.add_option('--maxthreads', default=None, type='int', dest='maxthreads', help='maximum number of server threads') parser.add_option('-s', '--server_name', default=socket.gethostname(), dest='server_name', help='server name for the web server') msg = 'max number of queued requests when server unavailable' parser.add_option('-q', '--request_queue_size', default='5', type='int', dest='request_queue_size', help=msg) parser.add_option('-o', '--timeout', default='10', type='int', dest='timeout', help='timeout for individual request (10 seconds)') parser.add_option('-z', '--shutdown_timeout', default='5', type='int', dest='shutdown_timeout', help='timeout on shutdown of server (5 seconds)') parser.add_option('--socket-timeout', default=5, type='int', dest='socket_timeout', help='timeout for socket (5 second)') parser.add_option('-f', '--folder', default=os.getcwd(), dest='folder', help='folder from which to run web2py') parser.add_option('-v', '--verbose', action='store_true', dest='verbose', default=False, help='increase --test verbosity') parser.add_option('-Q', '--quiet', action='store_true', dest='quiet', default=False, help='disable all output') parser.add_option('-e', '--errors_to_console', action='store_true', dest='print_errors', default=False, help='log all errors to console') msg = ('set debug output level (0-100, 0 means all, 100 means none; ' 'default is 30)') parser.add_option('-D', '--debug', dest='debuglevel', default=30, type='int', help=msg) msg = ('run web2py in interactive shell or IPython (if installed) with ' 'specified appname (if app does not exist it will be created). ' 'APPNAME like a/c/f (c,f optional)') parser.add_option('-S', '--shell', dest='shell', metavar='APPNAME', help=msg) msg = ('run web2py in interactive shell or bpython (if installed) with ' 'specified appname (if app does not exist it will be created).\n' 'Use combined with --shell') parser.add_option('-B', '--bpython', action='store_true', default=False, dest='bpython', help=msg) msg = 'only use plain python shell; should be used with --shell option' parser.add_option('-P', '--plain', action='store_true', default=False, dest='plain', help=msg) msg = ('auto import model files; default is False; should be used ' 'with --shell option') parser.add_option('-M', '--import_models', action='store_true', default=False, dest='import_models', help=msg) msg = ('run PYTHON_FILE in web2py environment; ' 'should be used with --shell option') parser.add_option('-R', '--run', dest='run', metavar='PYTHON_FILE', default='', help=msg) msg = ('run scheduled tasks for the specified apps: expects a list of ' 'app names as -K app1,app2,app3 ' 'or a list of app:groups as -K app1:group1:group2,app2:group1 ' 'to override specific group_names. (only strings, no spaces ' 'allowed. Requires a scheduler defined in the models') parser.add_option('-K', '--scheduler', dest='scheduler', default=None, help=msg) msg = 'run schedulers alongside webserver, needs -K app1 and -a too' parser.add_option('-X', '--with-scheduler', action='store_true', default=False, dest='with_scheduler', help=msg) msg = ('run doctests in web2py environment; ' 'TEST_PATH like a/c/f (c,f optional)') parser.add_option('-T', '--test', dest='test', metavar='TEST_PATH', default=None, help=msg) msg = 'trigger a cron run manually; usually invoked from a system crontab' parser.add_option('-C', '--cron', action='store_true', dest='extcron', default=False, help=msg) msg = 'triggers the use of softcron' parser.add_option('--softcron', action='store_true', dest='softcron', default=False, help=msg) parser.add_option('-Y', '--run-cron', action='store_true', dest='runcron', default=False, help='start the background cron process') parser.add_option('-J', '--cronjob', action='store_true', dest='cronjob', default=False, help='identify cron-initiated command') parser.add_option('-L', '--config', dest='config', default='', help='config file') parser.add_option('-F', '--profiler', dest='profiler_dir', default=None, help='profiler dir') parser.add_option('-t', '--taskbar', action='store_true', dest='taskbar', default=False, help='use web2py gui and run in taskbar (system tray)') parser.add_option('', '--nogui', action='store_true', default=False, dest='nogui', help='text-only, no GUI') msg = ('should be followed by a list of arguments to be passed to script, ' 'to be used with -S, -A must be the last option') parser.add_option('-A', '--args', action='store', dest='args', default=None, help=msg) parser.add_option('--no-banner', action='store_true', default=False, dest='nobanner', help='Do not print header banner') msg = ('listen on multiple addresses: ' '"ip1:port1:key1:cert1:ca_cert1;ip2:port2:key2:cert2:ca_cert2;..." ' '(:key:cert:ca_cert optional; no spaces; IPv6 addresses must be in ' 'square [] brackets)') parser.add_option('--interfaces', action='store', dest='interfaces', default=None, help=msg) msg = 'runs web2py tests' parser.add_option('--run_system_tests', action='store_true', dest='run_system_tests', default=False, help=msg) msg = ('adds coverage reporting (needs --run_system_tests), ' 'python 2.7 and the coverage module installed. ' 'You can alter the default path setting the environmental ' 'var "COVERAGE_PROCESS_START". ' 'By default it takes gluon/tests/coverage.ini') parser.add_option('--with_coverage', action='store_true', dest='with_coverage', default=False, help=msg) if '-A' in sys.argv: k = sys.argv.index('-A') elif '--args' in sys.argv: k = sys.argv.index('--args') else: k = len(sys.argv) sys.argv, other_args = sys.argv[:k], sys.argv[k + 1:] (options, args) = parser.parse_args() options.args = [options.run] + other_args copy_options = copy.deepcopy(options) copy_options.password = '******' global_settings.cmd_options = copy_options global_settings.cmd_args = args if options.gae: if not os.path.exists('app.yaml'): name = raw_input("Your GAE app name: ") content = open(os.path.join('examples', 'app.example.yaml'), 'rb').read() open('app.yaml', 'wb').write(content.replace("yourappname", name)) else: print("app.yaml alreday exists in the web2py folder") if not os.path.exists('gaehandler.py'): content = open(os.path.join('handlers', 'gaehandler.py'), 'rb').read() open('gaehandler.py', 'wb').write(content) else: print("gaehandler.py alreday exists in the web2py folder") sys.exit(0) try: options.ips = list( set( # no duplicates [ addrinfo[4][0] for addrinfo in getipaddrinfo(socket.getfqdn()) if not is_loopback_ip_address(addrinfo=addrinfo) ])) except socket.gaierror: options.ips = [] if options.run_system_tests: run_system_tests(options) if options.quiet: capture = StringIO() sys.stdout = capture logger.setLevel(logging.CRITICAL + 1) else: logger.setLevel(options.debuglevel) if options.config[-3:] == '.py': options.config = options.config[:-3] if options.cronjob: global_settings.cronjob = True # tell the world options.plain = True # cronjobs use a plain shell options.nobanner = True options.nogui = True options.folder = os.path.abspath(options.folder) # accept --interfaces in the form # "ip1:port1:key1:cert1:ca_cert1;[ip2]:port2;ip3:port3:key3:cert3" # (no spaces; optional key:cert indicate SSL) if isinstance(options.interfaces, str): interfaces = options.interfaces.split(';') options.interfaces = [] for interface in interfaces: if interface.startswith('['): # IPv6 ip, if_remainder = interface.split(']', 1) ip = ip[1:] if_remainder = if_remainder[1:].split(':') if_remainder[0] = int(if_remainder[0]) # numeric port options.interfaces.append(tuple([ip] + if_remainder)) else: # IPv4 interface = interface.split(':') interface[1] = int(interface[1]) # numeric port options.interfaces.append(tuple(interface)) # accepts --scheduler in the form # "app:group1,group2,app2:group1" scheduler = [] options.scheduler_groups = None if isinstance(options.scheduler, str): if ':' in options.scheduler: for opt in options.scheduler.split(','): scheduler.append(opt.split(':')) options.scheduler = ','.join([app[0] for app in scheduler]) options.scheduler_groups = scheduler if options.numthreads is not None and options.minthreads is None: options.minthreads = options.numthreads # legacy create_welcome_w2p() if not options.cronjob: # If we have the applications package or if we should upgrade if not os.path.exists('applications/__init__.py'): write_file('applications/__init__.py', '') return options, args
def run(history, statement, env={}): """ Evaluates a python statement in a given history and returns the result. """ history.unpicklables = INITIAL_UNPICKLABLES # extract the statement to be run if not statement: return '' # the python compiler doesn't like network line endings statement = statement.replace('\r\n', '\n') # add a couple newlines at the end of the statement. this makes # single-line expressions such as 'class Foo: pass' evaluate happily. statement += '\n\n' # log and compile the statement up front try: logging.info('Compiling and evaluating:\n%s' % statement) compiled = compile(statement, '<string>', 'single') except: return str(traceback.format_exc()) # create a dedicated module to be used as this statement's __main__ statement_module = new.module('__main__') # use this request's __builtin__, since it changes on each request. # this is needed for import statements, among other things. import __builtin__ statement_module.__builtins__ = __builtin__ # load the history from the datastore history = History() # swap in our custom module for __main__. then unpickle the history # globals, run the statement, and re-pickle the history globals, all # inside it. old_main = sys.modules.get('__main__') output = StringIO() try: sys.modules['__main__'] = statement_module statement_module.__name__ = '__main__' statement_module.__dict__.update(env) # re-evaluate the unpicklables for code in history.unpicklables: exec(code, statement_module.__dict__) # re-initialize the globals for name, val in history.globals_dict().items(): try: statement_module.__dict__[name] = val except: msg = 'Dropping %s since it could not be unpickled.\n' % name output.write(msg) logging.warning(msg + traceback.format_exc()) history.remove_global(name) # run! old_globals = dict((key, represent(value)) for key, value in statement_module.__dict__.items()) try: old_stdout, old_stderr = sys.stdout, sys.stderr try: sys.stderr = sys.stdout = output locker.acquire() exec(compiled, statement_module.__dict__) finally: locker.release() sys.stdout, sys.stderr = old_stdout, old_stderr except: output.write(str(traceback.format_exc())) return output.getvalue() # extract the new globals that this statement added new_globals = {} for name, val in statement_module.__dict__.items(): if name not in old_globals or represent(val) != old_globals[name]: new_globals[name] = val if True in [ isinstance(val, tuple(UNPICKLABLE_TYPES)) for val in new_globals.values() ]: # this statement added an unpicklable global. store the statement and # the names of all of the globals it added in the unpicklables. history.add_unpicklable(statement, new_globals.keys()) logging.debug('Storing this statement as an unpicklable.') else: # this statement didn't add any unpicklables. pickle and store the # new globals back into the datastore. for name, val in new_globals.items(): if not name.startswith('__'): try: history.set_global(name, val) except (TypeError, pickle.PicklingError) as ex: UNPICKLABLE_TYPES.append(type(val)) history.add_unpicklable(statement, new_globals.keys()) finally: sys.modules['__main__'] = old_main return output.getvalue()
def start(cron=True): """ Starts server and other services """ # get command line arguments (options, args) = console() if options.gae: # write app.yaml, gaehandler.py, and exit if not os.path.exists('app.yaml'): name = options.gae # for backward compatibility if name == 'configure': if PY2: input = raw_input name = input("Your GAE app name: ") content = open(os.path.join('examples', 'app.example.yaml'), 'rb').read() open('app.yaml', 'wb').write(content.replace("yourappname", name)) else: print("app.yaml alreday exists in the web2py folder") if not os.path.exists('gaehandler.py'): content = open(os.path.join('handlers', 'gaehandler.py'), 'rb').read() open('gaehandler.py', 'wb').write(content) else: print("gaehandler.py alreday exists in the web2py folder") return create_welcome_w2p() if options.run_system_tests: # run system test and exit run_system_tests(options) if options.quiet: capture = StringIO() sys.stdout = capture logger.setLevel(logging.CRITICAL + 1) else: logger.setLevel(options.debuglevel) if not options.nobanner: # banner print(ProgramName) print(ProgramAuthor) print(ProgramVersion) from pydal.drivers import DRIVERS print('Database drivers available: %s' % ', '.join(DRIVERS)) if options.test: # run doctests and exit test(options.test, verbose=options.verbose) return if options.shell: # run interactive shell and exit if options.folder: os.chdir(options.folder) sys.argv = [options.run] + options.args run(options.shell, plain=options.plain, bpython=options.bpython, import_models=options.import_models, startfile=options.run, cronjob=options.cronjob) return if options.extcron: # run cron (extcron) and exit logger.debug('Starting extcron...') global_settings.web2py_crontype = 'external' if options.scheduler: # run cron for applications listed with --scheduler (-K) apps = [ app.strip() for app in options.scheduler.split(',') if check_existent_app(options, app.strip()) ] else: apps = None extcron = newcron.extcron(options.folder, apps=apps) extcron.start() extcron.join() return if options.scheduler and not options.with_scheduler: # run schedulers and exit try: start_schedulers(options) except KeyboardInterrupt: pass return if cron and options.runcron: if options.softcron: print('Using softcron (but this is not very efficient)') global_settings.web2py_crontype = 'soft' else: # start hardcron thread logger.debug('Starting hardcron...') global_settings.web2py_crontype = 'hard' newcron.hardcron(options.folder).start() # if no password provided and have Tk library start GUI (when not # explicitly disabled), we also need a GUI to put in taskbar (system tray) # when requested # FIXME: this check should be done first if options.taskbar and os.name != 'nt': die('taskbar not supported on this platform') root = None if (not options.nogui and options.password == '<ask>') or options.taskbar: try: if PY2: import Tkinter as tkinter else: import tkinter root = tkinter.Tk() except (ImportError, OSError): logger.warn( 'GUI not available because Tk library is not installed') options.nogui = True except: logger.exception('cannot get Tk root window, GUI disabled') options.nogui = True if root: # run GUI and exit root.focus_force() # Mac OS X - make the GUI window rise to the top if os.path.exists("/usr/bin/osascript"): applescript = """ tell application "System Events" set proc to first process whose unix id is %d set frontmost of proc to true end tell """ % (os.getpid()) os.system("/usr/bin/osascript -e '%s'" % applescript) # web2pyDialog takes care of schedulers master = web2pyDialog(root, options) signal.signal(signal.SIGTERM, lambda a, b: master.quit()) try: root.mainloop() except: master.quit() sys.exit() if options.password == '<ask>': options.password = getpass.getpass('choose a password:'******'no password, disable admin interface') spt = None if options.scheduler and options.with_scheduler: # start schedulers in a separate thread spt = threading.Thread(target=start_schedulers, args=(options, )) spt.start() # start server # Use first interface IP and port if interfaces specified, since the # interfaces option overrides the IP (and related) options. if not options.interfaces: ip = options.ip port = options.port else: first_if = options.interfaces[0] ip = first_if[0] port = first_if[1] if options.ssl_certificate or options.ssl_private_key: proto = 'https' else: proto = 'http' url = get_url(ip, proto=proto, port=port) if not options.nobanner: message = '\nplease visit:\n\t%s\n' if sys.platform.startswith('win'): message += 'use "taskkill /f /pid %i" to shutdown the web2py server\n\n' else: message += 'use "kill -SIGTERM %i" to shutdown the web2py server\n\n' print(message % (url, os.getpid())) # enhance linecache.getline (used by debugger) to look at the source file # if the line was not found (under py2exe & when file was modified) import linecache py2exe_getline = linecache.getline def getline(filename, lineno, *args, **kwargs): line = py2exe_getline(filename, lineno, *args, **kwargs) if not line: try: with open(filename, "rb") as f: for i, line in enumerate(f): line = line.decode('utf-8') if lineno == i + 1: break else: line = '' except (IOError, OSError): line = '' return line linecache.getline = getline server = main.HttpServer(ip=ip, port=port, password=options.password, pid_filename=options.pid_filename, log_filename=options.log_filename, profiler_dir=options.profiler_dir, ssl_certificate=options.ssl_certificate, ssl_private_key=options.ssl_private_key, ssl_ca_certificate=options.ssl_ca_certificate, min_threads=options.minthreads, max_threads=options.maxthreads, server_name=options.server_name, request_queue_size=options.request_queue_size, timeout=options.timeout, socket_timeout=options.socket_timeout, shutdown_timeout=options.shutdown_timeout, path=options.folder, interfaces=options.interfaces) try: server.start() except KeyboardInterrupt: server.stop() if spt is not None: try: spt.join() except: logger.exception('error terminating schedulers') pass logging.shutdown()