Exemple #1
0
    def loadConfig(self, appVars):
        if appVars.get('beforeConfigHook'):
            appVars['beforeConfigHook'](appVars)

        self.config = utils.defaultattrdict(appVars)

        def initConstants(varlist, default):
            #add the given list of config properties as attributes
            #on this RequestProcessor
            return assignAttrs(self, appVars, varlist, default)

        initConstants( [ 'actions'], {})
        initConstants( ['default_mime_type'], '')        
        self.initLock(appVars)
        self.txnSvc = transactions.ProcessorTransactionService(self)
        initConstants( [ 'stores', 'storeDefaults'], {})
        addNewResourceHook = self.actions.get('before-new')
        self.defaultStore = None
        if 'stores' in appVars:
            stores = utils.attrdict()
            for name, storeConfig in appVars['stores'].items():
                stores[name] = self.loadDataStore(storeConfig, 
                                    self.storeDefaults, addNewResourceHook)
                if storeConfig.get('default_store') or name == 'default':
                    self.defaultStore = stores[name]
            if stores and not self.defaultStore:
                if len(stores) > 1:
                    raise VesperError('default store not set')
                else:
                    self.defaultStore = stores.values()[0]
            #XXX in order to allow cmdline and config file storage settings to be useful
            #merge appVars into the default store's config before loadDataStore
            self.stores = stores
        else:
            self.defaultStore = self.loadDataStore(appVars,self.storeDefaults,addNewResourceHook)
            self.stores = utils.attrdict(default=self.defaultStore)

        #app_name is a unique name for this request processor instance
        initConstants( ['app_name'], 'root')
        self.log = logging.getLogger("app." + self.app_name)
                        
        self.defaultRequestTrigger = appVars.get('default_trigger','http-request')
        initConstants( ['global_request_vars', 'static_path', 'template_path'], [])
        self.work_dir = appVars.get('work_dir', 'vesper_work')
        self.mako_module_dir = appVars.get('mako_module_dir', os.path.join(self.work_dir,'mako_modules'))
        initConstants( ['template_options'], {})        
        self.global_request_vars.extend( self.defaultGlobalVars )
        self.default_page_name = appVars.get('default_page_name', 'index')
        #cache settings:
        initConstants( ['secure_file_access', 'use_etags'], True)
        self.default_expires_in = appVars.get('default_expires_in', 0)
        initConstants( ['action_cache_size'], 0)
        self.validate_external_request = appVars.get('validate_external_request',
                                        lambda *args: True)
        self.get_principal_func = appVars.get('get_principal_func', lambda kw: '')        
        self.argsForConfig = appVars.get('argsForConfig', [])
        
        if appVars.get('configHook'):
            appVars['configHook'](appVars)
Exemple #2
0
def _importApp(baseapp):
    '''
    Executes the given app config file. If `createApp()` was 
    called during execution of the config file, the `_current_config`
    global will be set to the app configuration returned by `createApp()`.
    '''
    baseglobals = utils.attrdict(Action=Action, createApp=createApp)
    #assuming the baseapp file calls createApp(), it will set _current_config
    if os.path.exists(baseapp):
        #set this global so we can resolve relative paths against the location
        #of the config file they appear in
        _current_configpath.append( os.path.dirname(os.path.abspath(baseapp)) )
        execfile(baseapp, baseglobals)
    else:
        (path, isdir) = _get_module_path(baseapp)
        # print "_get_module_path for:" + str(baseapp) + " --> path:" + str(path) + " isdir:" + str(isdir)
        assert path
        #set this global so we can resolve relative paths against the location
        #of the config file they appear in
        _current_configpath.append( os.path.abspath(path) )
        basemod = sys.modules.get(baseapp)
        if basemod:
            reload(basemod)
        else:
            __import__(baseapp, baseglobals)
    _current_configpath.pop()
Exemple #3
0
    def callActions(self, actions, kw, retVal, errorSequence=None, globalVars=None, newTransaction=False):
        '''
        process another set of actions using the current context as input,
        but without modified the current context.
        Particularly useful for template processing.
        '''
        globalVars = self.global_request_vars + (globalVars or [])

        #merge previous prevkw, overriding vars as necessary
        prevkw = kw.get('_prevkw', {}).copy()
        templatekw = utils.attrdict()
        for k, v in kw.items():
            #initialize the templates variable map copying the
            #core request kws and copy the r est (the application
            #specific kws) to _prevkw this way the template
            #processing doesn't mix with the orginal request but
            #are made available in the 'previous' namespace (think
            #of them as template parameters)
            if k in globalVars:
                templatekw[k] = v
            elif k != '_metadatachanges':
                prevkw[k] = v
        templatekw['_prevkw'] = prevkw
        templatekw['_contents'] = Result(retVal)

        return self.doActions(actions, templatekw,
            errorSequence=errorSequence, newTransaction=newTransaction)
Exemple #4
0
    def testReplaceEmbeddedList(self):
        updates = utils.attrdict()        
        @vesper.app.Action
        def recordUpdates(kw, retval):
            updates.update(kw._dbchanges[0])

        store = vesper.app.createStore({
        "id": "hello", 
        "embedded": [{'foo' : 'a'}, {'foo' : 'b'}]
         }, storage_template_options=dict(generateBnode='counter'),
         actions = { 'after-commit' : [recordUpdates]}         
        )

        store.replace({"id":"hello",
          "embedded": [{'id' : '_:j:e:object:hello:3', 'foo' : 'c'}, {'id' : '_:j:e:object:hello:4', 'foo' : 'd'}]
        })

        removed = set(updates._removedStatements) - set(updates._addedStatements)
        expectedRemoved = set([('_:j:e:object:hello:2', 'foo', 'b', 'L', ''),
                                ('_:j:e:object:hello:1', 'foo', 'a', 'L', '')])
        self.assertEquals(removed, expectedRemoved)
        
        added = set(updates._addedStatements) - set(updates._removedStatements)
        expectedAdded = set([('_:j:e:object:hello:2', 'foo', 'd', 'L', ''), 
                            ('_:j:e:object:hello:1', 'foo', 'c', 'L', '')])
        self.assertEquals(added, expectedAdded)
Exemple #5
0
    def testUpdateEmbeddedList(self):
        updates = utils.attrdict()        
        @vesper.app.Action
        def recordUpdates(kw, retval):
            updates.update(kw._dbchanges[0])

        store = vesper.app.createStore({
        "id": "hello", 
        "embedded": [{'foo' : 'a', 'prop2':'a'}, {'foo' : 'b'}]
         }, storage_template_options=dict(generateBnode='counter'),
         actions = { 'after-commit' : [recordUpdates]}         
        )

        store.update({"id":"hello",
          "embedded": [{'id' : '_:j:e:object:hello:3', 'foo' : 'c', 'prop2':'a'}, {'id' : '_:j:e:object:hello:4', 'foo' : 'd'}]        
        })

        removed = [('_:j:e:object:hello:1', 'foo', 'a', 'L', ''), 
        ('_:j:e:object:hello:2', 'foo', 'b', 'L', '')] 
 
        #XXX shouldn't include the redundant proplist statements
        added = [('_:j:e:object:hello:1', 'foo', 'c', 'L', ''), ('_:j:e:object:hello:2', 'foo', 'd', 'L', ''), 
('_:j:proplist:hello;embedded', u'http://www.w3.org/1999/02/22-rdf-syntax-ns#type', u'http://www.w3.org/1999/02/22-rdf-syntax-ns#Seq', 'R', ''), 
('_:j:proplist:hello;embedded', u'http://www.w3.org/1999/02/22-rdf-syntax-ns#type', 'pjson:schema#propseqtype', 'R', ''), 
('_:j:proplist:hello;embedded', 'pjson:schema#propseqprop', 'embedded', 'R', ''), 
('_:j:proplist:hello;embedded', 'pjson:schema#propseqsubject', 'hello', 'R', '')]
        
        self.assertEquals(updates._removedStatements, removed)        
        self.assertEquals(updates._addedStatements, added)
Exemple #6
0
    def _runActions(self, trigger, morekw=None):
       actions = self.server.actions.get(trigger)
       if actions:
            state = self.state
            kw = state.kw.copy()            
            if morekw is None:
                morekw = {'_dbchanges' :
                  [ utils.attrdict({
              '_addedStatements' : dbstate.additions,
              '_removedStatements' : dbstate.removals,
              '_added' : pjson.tojson(dbstate.additions,
                **db.model_options.get('serializeOptions',{}).get('pjson',{})),
              '_removed' : pjson.tojson(dbstate.removals,
                **db.model_options.get('serializeOptions',{}).get('pjson',{})),
              '_newResources' : dbstate.newResources,
              '_db' : db
              })  for db, dbstate in self.state.dbstates.items() ]
                }

            kw.update(morekw)
            kw['_txninfo'] = self.state.info
            errorSequence= self.server.actions.get(trigger+'-error')
            self.server.callActions(actions, kw, state.retVal,
                    globalVars=morekw.keys(),
                    errorSequence=errorSequence)
Exemple #7
0
    def requestFromEnviron(self, environ):
        import Cookie, wsgiref.util
        _name = environ['PATH_INFO'].lstrip('/')
        if not _name or _name.endswith('/'):
            _name += self.default_page_name

        _responseCookies = Cookie.SimpleCookie()
        _responseHeaders = utils.attrdict(_status="200 OK") #include response code pseudo-header
        kw = utils.attrdict(_environ=utils.attrdict(environ),
            _uri = wsgiref.util.request_uri(environ),
            _baseUri = wsgiref.util.application_uri(environ),
            _responseCookies = _responseCookies,
            _requestCookies = Cookie.SimpleCookie(environ.get('HTTP_COOKIE', '')),
            _responseHeaders= _responseHeaders,
            _name=_name
        )
        paramsdict = get_http_params(environ)
        kw.update(paramsdict)
        return kw
Exemple #8
0
    def handleCommandLine(self, argv):
        '''  the command line is translated into the `_params`
        request variable as follows:

        * arguments beginning with a '-' are treated as a variable
        name with its value being the next argument unless that
        argument also starts with a '-'

        * the entire command line is assigned to the variable 'cmdline'
        '''
        kw = utils.attrdict()
        kw._params = utils.defaultattrdict(argsToKw(argv))
        kw['cmdline'] = '"' + '" "'.join(argv) + '"'
        self.runActions('run-cmds', kw)
Exemple #9
0
 def runActions(self, triggerName, kw = None, initVal=None, newTransaction=True):
     '''
     Retrieve the action sequences associated with the triggerName.
     Each Action has a list of RxPath expressions that are evaluated after
     mapping runActions keyword parameters to RxPath variables.  If an
     expression returns a non-empty nodeset the Action is invoked and the
     value it returns is passed to the next invoked Action until the end of
     the sequence, upon which the final return value is return by this function.
     '''
     kw = utils.attrdict(kw or {})
     sequence = self.actions.get(triggerName)
     if sequence:
         errorSequence = self.actions.get(triggerName+'-error')
         return self.doActions(sequence, kw, retVal=initVal,
             errorSequence=errorSequence, newTransaction=newTransaction)
Exemple #10
0
 def getErrorKWs():
     type, value, traceback = exc_info
     if (isinstance(value, utils.NestedException)
             and value.useNested):
         message, module, name, errorCode=extractErrorInfo(
              value.nested_exc_info[0],
              value.nested_exc_info[1])
     else:
         message, module, name, errorCode=extractErrorInfo(
                                          type, value)
     #these should always be the wrapper exception:
     (fileName, lineNumber, functionName,
         text) = traceback_module.extract_tb(
                                 traceback, 1)[0]
     details = ''.join(
         traceback_module.format_exception(
                     type, value, traceback) )
     return utils.attrdict(locals())
Exemple #11
0
 def _testUpdateEmbedded(self, op):
     #XXX test nested lists, nested embedded objects
     updates = utils.attrdict()        
     @vesper.app.Action
     def recordUpdates(kw, retval):
         updates.update(kw._dbchanges[0])
     
     store = vesper.app.createStore({
     "id": "hello", 
     "embedded": {
       'foo' : 'not first-class',
       'prop2' : 'a'
     }
      }, storage_template_options=dict(generateBnode='counter'),
      actions = { 'after-commit' : [recordUpdates]}         
     )
     self.assertEquals(store.model.getStatements(),
         [('_:j:e:object:hello:1', 'foo', 'not first-class', 'L', ''),
          ('_:j:e:object:hello:1', 'prop2', 'a', 'L', ''), 
         ('hello', 'embedded', '_:j:e:object:hello:1', 'R', '')])
      
     getattr(store, op)({"id":"hello",
     "embedded": {
       'id' : '_:j:e:object:hello:2', #here just to make sure a different bnode name is used
       'foo' : 'modified',
        'prop2' : 'a'
     }
     })
     
     #XXX this statement shouldn't be duplicated
     self.assertEquals(updates._removedStatements, [
         ('_:j:e:object:hello:1', 'foo', 'not first-class', 'L', ''), 
         ('_:j:e:object:hello:1', 'foo', 'not first-class', 'L', '')])            
     self.assertEquals(updates._addedStatements, 
               [('_:j:e:object:hello:1', 'foo', 'modified', 'L', '')])
     self.assertEquals(store.model.getStatements(),
         [('_:j:e:object:hello:1', 'foo', 'modified', 'L', ''), 
          ('_:j:e:object:hello:1', 'prop2', 'a', 'L', ''), 
         ('hello', 'embedded', '_:j:e:object:hello:1', 'R', '')]
     )
Exemple #12
0
def getResults(query, model, bindvars=None, explain=None, debug=False,
    forUpdate=False, captureErrors=False, contextShapes=None, useSerializer=True,
    printast=False, queryCache=None):
    '''
    Returns a dict with the following keys:
        
    - `results`: the result of the query (either a list or None if the query failed)
    - `errors`: An error string if the query failed or an empty list if it succeeded.
    
    :Parameters:
     query
       the query
     model
       the store upon which to execute the query
    bindvars
       a dictionary used to resolve any `bindvars` in the query
    explain
       if True, the result will include a key named `explain` whose value is a 
       string "explaining" the query plan to execute it.
    debug
       if True, the result will include a very verbose trace of the query execution.
    forUpdate
       include in the query results enough information so that the response 
       objects can be modified and those changes saved back into the store.
    captureErrors
       if True, exceptions raise during query execution will be caught and appended 
       to the `errors` key. By default, such exceptions will be propagated when they occur.
    contextShapes
       A dictionary that specifies alternative constructors used when creating
       `dicts` and `lists` in the query results.
    useSerializer
       If value is a boolean, indicates whether pjson serialization is used or 
       not (default: True). If value is a dict it is passed as keyword arguments
       to the `pjson.Serializer` constructor.
    '''
    #XXX? add option to include `resources` in the result,
    # a list describing the resources (used for track changes)
    start = time.clock()
    response = utils.attrdict()
    errors = []
    
    (ast, parseErrors) = buildAST(query)
    errors.extend(parseErrors)
    
    response['results'] = []
    
    if explain:
        explain = StringIO.StringIO()

    if debug and not hasattr(debug, 'write'):
        debug = StringIO.StringIO()
    
    if ast != None:        
        try:
            results = list(evalAST(ast, model, bindvars, explain, debug, 
                    forUpdate, contextShapes, useSerializer, queryCache))
            #XXX: if forUpdate add a pjson header including namemap
            #this we have a enough info to reconstruct refs and datatypes without guessing
            #if forUpdate: 
            #   #need a context.datamap
            #   pjson.addHeader(context.datamap, response)
            response['results'] = results
        except QueryException, qe:
            if captureErrors:
                errors.append('error: %s' % qe.message)
            else:
                raise
        except Exception, ex:
            if captureErrors:
                import traceback
                errors.append("unexpected exception: %s" % traceback.format_exc())
            else:
                raise
Exemple #13
0
    def doActions(self, sequence, kw=None, retVal=None,
                  errorSequence=None, newTransaction=False):
        if kw is None: 
            kw = utils.attrdict()

        kw['__requestor__'] = self.requestDispatcher
        kw['__server__'] = self

        try:
            if newTransaction:
                retVal = self._doActionsTxn(sequence, kw, retVal)
            else:
                retVal = self._doActionsBare(sequence, kw, retVal)
        except (KeyboardInterrupt, SystemExit):
            raise
        except:
            #print newTransaction, self.txnSvc.state.timestamp
            exc_info = sys.exc_info()
            if isinstance(exc_info[1], ActionWrapperException):
                retVal = exc_info[1].state
                exc_info = exc_info[1].nested_exc_info

            if self.inErrorHandler or kw.get('_noErrorHandling'):
                #avoid endless loops
                raise exc_info[1] or exc_info[0], None, exc_info[2]
            else:
                self.inErrorHandler += 1
            try:
                if isinstance(exc_info[1], DoNotHandleException):
                    raise exc_info[1] or exc_info[0], None, exc_info[2]

                if errorSequence and sequence is not errorSequence:
                    import traceback as traceback_module
                    def extractErrorInfo(type, value):
                        #value may be either the nested exception
                        #or the wrapper exception
                        message = str(value)
                        module = '.'.join( str(type).split('.')[:-1] )
                        name = str(type).split('.')[-1].strip("'>")
                        errorCode = getattr(value, 'errorCode', '')
                        return message, module, name, errorCode

                    def getErrorKWs():
                        type, value, traceback = exc_info
                        if (isinstance(value, utils.NestedException)
                                and value.useNested):
                            message, module, name, errorCode=extractErrorInfo(
                                 value.nested_exc_info[0],
                                 value.nested_exc_info[1])
                        else:
                            message, module, name, errorCode=extractErrorInfo(
                                                             type, value)
                        #these should always be the wrapper exception:
                        (fileName, lineNumber, functionName,
                            text) = traceback_module.extract_tb(
                                                    traceback, 1)[0]
                        details = ''.join(
                            traceback_module.format_exception(
                                        type, value, traceback) )
                        return utils.attrdict(locals())

                    kw['_errorInfo'] = getErrorKWs()
                    self.log.warning("invoking error handler on exception:\n"+
                                     kw['_errorInfo']['details'])
                    try:
                        #if we're creating a new transaction,
                        #it has been aborted by now, so start a new one
                        #however if the error was thrown during commit we're in the midst 
                        #of a bad transaction and its not safe to create a new one
                        newTransaction = newTransaction and not self.txnSvc.isActive()
                        return self.callActions(errorSequence, kw, retVal,
                            newTransaction=newTransaction)
                    finally:
                        del kw['_errorInfo']
                else:
                    #traceback.print_exception(*exc_info)
                    raise exc_info[1] or exc_info[0], None, exc_info[2]
            finally:
                self.inErrorHandler -= 1
        return retVal