Browse Source

HPCC-8030 Java/Python/Javascript language support in ECL

Reworked python embed manager to support functions, imports, etc.

Signed-off-by: Richard Chapman <rchapman@hpccsystems.com>
Richard Chapman 12 years ago
parent
commit
d967d6d7c1

+ 4 - 0
ecl/hql/hqlatoms.cpp

@@ -346,6 +346,8 @@ _ATOM storedAtom;
 _ATOM streamedAtom;
 _ATOM _streaming_Atom;
 _ATOM successAtom;
+_ATOM supportsImportAtom;
+_ATOM supportsScriptAtom;
 _ATOM sysAtom;
 _ATOM tempAtom;
 _ATOM templateAtom;
@@ -739,6 +741,8 @@ MODULE_INIT(INIT_PRIORITY_HQLATOM)
     MAKEATOM(streamed);
     MAKESYSATOM(streaming);
     MAKEATOM(success);
+    MAKEATOM(supportsImport);
+    MAKEATOM(supportsScript);
     MAKEATOM(sys);
     MAKEATOM(temp);
     MAKEATOM(template);

+ 2 - 0
ecl/hql/hqlatoms.hpp

@@ -350,6 +350,8 @@ extern HQL_API _ATOM storedAtom;
 extern HQL_API _ATOM streamedAtom;
 extern HQL_API _ATOM _streaming_Atom;
 extern HQL_API _ATOM successAtom;
+extern HQL_API _ATOM supportsImportAtom;
+extern HQL_API _ATOM supportsScriptAtom;
 extern HQL_API _ATOM sysAtom;
 extern HQL_API _ATOM tempAtom;
 extern HQL_API _ATOM templateAtom;

+ 1 - 1
ecl/hql/hqlattr.cpp

@@ -406,7 +406,7 @@ unsigned getOperatorMetaFlags(node_operator op)
     case no_null:
     case no_globalscope:
     case no_nothor:
-    case no_cppbody:
+    case no_embedbody:
     case no_alias_scope:
     case no_evalonce:
     case no_forcelocal:

+ 10 - 10
ecl/hql/hqlexpr.cpp

@@ -1303,7 +1303,7 @@ const char *getOpString(node_operator op)
     case no_id2blob: return "no_id2blob";
     case no_blob2id: return "no_blob2id";
     case no_anon: return "no_anon";
-    case no_cppbody: return "no_cppbody";
+    case no_embedbody: return "no_embedbody";
     case no_sortpartition: return "no_sortpartition";
     case no_define: return "DEFINE";
     case no_globalscope: return "GLOBAL";
@@ -1562,7 +1562,7 @@ bool checkConstant(node_operator op)
     case no_priority:
     case no_event:
     case no_independent:
-    case no_cppbody:
+    case no_embedbody:
     case no_translated:
     case no_assertkeyed:            // not sure about this - might be better to implement in the constant folder
     case no_assertstepped:
@@ -1785,7 +1785,7 @@ childDatasetType getChildDatasetType(IHqlExpression * expr)
     case no_externalcall:                       // None in the sense it is generally used for.
     case no_alias:
     case no_id2blob:
-    case no_cppbody:
+    case no_embedbody:
     case no_datasetfromrow:
     case no_datasetfromdictionary:
     case no_createrow:
@@ -2197,7 +2197,7 @@ inline unsigned doGetNumChildTables(IHqlExpression * dataset)
     case no_externalcall:                       // None in the sense it is generally used for.
     case no_alias:
     case no_id2blob:
-    case no_cppbody:
+    case no_embedbody:
     case no_datasetfromrow:
     case no_datasetfromdictionary:
     case no_param:
@@ -2488,7 +2488,7 @@ bool definesColumnList(IHqlExpression * dataset)
     case no_newsoapcall_ds:
     case no_alias:
     case no_id2blob:
-    case no_cppbody:
+    case no_embedbody:
     case no_externalcall:
     case no_projectrow:
     case no_datasetfromrow:
@@ -3469,7 +3469,7 @@ void CHqlExpression::updateFlagsAfterOperands()
             {
                 infoFlags2 |= HEF2containsCall;
                 IHqlExpression * bodycode = body->queryChild(0);
-                if (bodycode->getOperator() == no_cppbody)
+                if (bodycode->getOperator() == no_embedbody)
                 {
                     if (bodycode->queryProperty(actionAtom))
                         infoFlags |= HEFvolatile;
@@ -3486,7 +3486,7 @@ void CHqlExpression::updateFlagsAfterOperands()
                 infoFlags |= HEFvolatile;
             break;
         }
-    case no_cppbody:
+    case no_embedbody:
         {
             if (queryProperty(actionAtom))
                 infoFlags |= HEFvolatile;
@@ -4527,7 +4527,7 @@ IHqlExpression *CHqlExpressionWithType::clone(HqlExprArray &newkids)
     {
     case no_outofline:
         return createWrapper(op, newkids);
-    case no_cppbody:
+    case no_embedbody:
         {
             if (queryType()->getTypeCode() == type_transform)
             {
@@ -10993,7 +10993,7 @@ IHqlExpression *createDataset(node_operator op, HqlExprArray & parms)
     case no_xmlproject:
     case no_temptable:
     case no_id2blob:
-    case no_cppbody:
+    case no_embedbody:
         type.setown(makeTableType(makeRowType(createRecordType(&parms.item(1))), NULL, NULL, NULL));
         if (queryProperty(_linkCounted_Atom, parms))
             type.setown(setLinkCountedAttr(type, true));
@@ -11901,7 +11901,7 @@ extern IHqlExpression *createRow(node_operator op, HqlExprArray & args)
                 type = makeRowType(LINK(fieldType));
             break;
         }
-    case no_cppbody:
+    case no_embedbody:
     case no_id2blob:
     case no_temprow:
     case no_projectrow:         // arg(1) is actually a transform

+ 1 - 1
ecl/hql/hqlexpr.hpp

@@ -595,7 +595,7 @@ enum _node_operator {
         no_blob2id,
         no_anon,
         no_projectrow,
-        no_cppbody,
+        no_embedbody,
         no_sortpartition,
         no_define,
         no_globalscope,

+ 1 - 1
ecl/hql/hqlfold.cpp

@@ -5417,7 +5417,7 @@ HqlConstantPercolator * CExprFolderTransformer::gatherConstants(IHqlExpression *
     case no_libraryinput:
     case no_translated:
     case no_id2blob:
-    case no_cppbody:
+    case no_embedbody:
     case no_pipe:
     case no_keyindex:
     case no_newkeyindex:

+ 1 - 1
ecl/hql/hqlgram.hpp

@@ -542,7 +542,7 @@ public:
     IHqlExpression * processAlienType(const attribute & errpos);
     IHqlExpression * processIndexBuild(attribute & indexAttr, attribute * recordAttr, attribute * payloadAttr, attribute & filenameAttr, attribute & flagsAttr);
     IHqlExpression * processCompoundFunction(attribute & result, bool outOfLine);
-    IHqlExpression * processCppBody(const attribute & errpos, IHqlExpression * cpp, IHqlExpression * language);
+    IHqlExpression * processEmbedBody(const attribute & errpos, IHqlExpression * embedText, IHqlExpression * language, IHqlExpression *attribs);
     void processEnum(attribute & idAttr, IHqlExpression * value);
     void processError(bool full);
     void processLoadXML(attribute & a1, attribute * a2);

+ 17 - 8
ecl/hql/hqlgram.y

@@ -912,7 +912,7 @@ goodObject
     | transform
     | complexType
     | macro
-    | cppBodyText
+    | embedBody
     | eventObject
     | compoundAttribute
     | abstractModule
@@ -1003,24 +1003,33 @@ macro
                         }
     ;
 
-cppBodyText
+embedBody
     : CPPBODY           {
-                            OwnedHqlExpr cpp = $1.getExpr();
-                            $$.setExpr(parser->processCppBody($1, cpp, NULL), $1);
+                            OwnedHqlExpr embeddedCppText = $1.getExpr();
+                            $$.setExpr(parser->processEmbedBody($1, embeddedCppText, NULL, NULL), $1);
                         }
     | embedPrefix CPPBODY
                         {
                             OwnedHqlExpr language = $1.getExpr();
-                            OwnedHqlExpr cpp = $2.getExpr();
-                            $$.setExpr(parser->processCppBody($2, cpp, language), $1);
+                            OwnedHqlExpr embedText = $2.getExpr();
+                            $$.setExpr(parser->processEmbedBody($2, embedText, language, NULL), $1);
                         }
     | EMBED '(' abstractModule ',' expression ')'
                         {
                             parser->normalizeExpression($5, type_string, true);
                             OwnedHqlExpr language = $3.getExpr();
-                            OwnedHqlExpr cpp = $5.getExpr();
-                            $$.setExpr(parser->processCppBody($5, cpp, language), $1);
+                            OwnedHqlExpr embedText = $5.getExpr();
+                            $$.setExpr(parser->processEmbedBody($5, embedText, language, NULL), $1);
                         }
+    | IMPORT '(' abstractModule ',' expression attribs ')'
+                        {
+                            parser->normalizeExpression($5, type_string, true);
+                            OwnedHqlExpr language = $3.getExpr();
+                            OwnedHqlExpr funcname = $5.getExpr();
+                            OwnedHqlExpr attribs = createComma(createAttribute(importAtom), $6.getExpr());
+                            $$.setExpr(parser->processEmbedBody($6, funcname, language, attribs), $1);
+                        }
+    
     ;
 
 embedPrefix

+ 14 - 8
ecl/hql/hqlgram2.cpp

@@ -831,23 +831,29 @@ IHqlExpression * HqlGram::convertToOutOfLineFunction(const ECLlocation & errpos,
     return LINK(expr);
 }
 
-IHqlExpression * HqlGram::processCppBody(const attribute & errpos, IHqlExpression * cpp, IHqlExpression * language)
+IHqlExpression * HqlGram::processEmbedBody(const attribute & errpos, IHqlExpression * embedText, IHqlExpression * language, IHqlExpression *attribs)
 {
     HqlExprArray args;
-    cpp->unwindList(args, no_comma);
+    embedText->unwindList(args, no_comma);
     if (language)
     {
         IHqlScope *pluginScope = language->queryScope();
         OwnedHqlExpr getEmbedContextFunc = pluginScope->lookupSymbol(getEmbedContextAtom, LSFsharedOK, lookupCtx);
         if (!getEmbedContextFunc)
             reportError(ERR_PluginNoScripting, errpos, "Module %s does not export getEmbedContext() function", language->queryName()->getAtomNamePtr());
-        OwnedHqlExpr syntaxCheckFunc = pluginScope->lookupSymbol(cppAtom, LSFsharedOK, lookupCtx);
-        if (syntaxCheckFunc)
+        bool isImport = queryPropertyInList(importAtom, attribs) != NULL;
+        OwnedHqlExpr checkSupport = pluginScope->lookupSymbol(isImport ? supportsImportAtom : supportsScriptAtom, LSFsharedOK, lookupCtx);
+        if (!matchesBoolean(checkSupport, true))
+            reportError(ERR_PluginNoScripting, errpos, "Module %s does not support %s", language->queryName()->getAtomNamePtr(), isImport ? "import" : "script");
+        OwnedHqlExpr syntaxCheckFunc = pluginScope->lookupSymbol(syntaxCheckAtom, LSFsharedOK, lookupCtx);
+        if (syntaxCheckFunc && !importAtom)
         {
             // MORE - create an expression that calls it, and const fold it, I guess....
         }
         args.append(*createAttribute(languageAtom, getEmbedContextFunc.getClear()));
     }
+    if (attribs)
+        attribs->unwindList(args, no_comma);
     Linked<ITypeInfo> type = current_type;
     if (!type)
         type.setown(makeVoidType());
@@ -865,21 +871,21 @@ IHqlExpression * HqlGram::processCppBody(const attribute & errpos, IHqlExpressio
         {
         case type_row:
         case type_record:
-            result.setown(createRow(no_cppbody, args));
+            result.setown(createRow(no_embedbody, args));
             break;
         case type_table:
         case type_groupedtable:
-            result.setown(createDataset(no_cppbody, args));
+            result.setown(createDataset(no_embedbody, args));
             break;
         case type_transform:
-            result.setown(createValue(no_cppbody, makeTransformType(LINK(record->queryType())), args));
+            result.setown(createValue(no_embedbody, makeTransformType(LINK(record->queryType())), args));
             break;
         default:
             throwUnexpected();
         }
     }
     else
-        result.setown(createValue(no_cppbody, LINK(type), args));
+        result.setown(createValue(no_embedbody, LINK(type), args));
 
     result.setown(createLocationAnnotation(result.getClear(), errpos.pos));
 

+ 3 - 3
ecl/hql/hqlir.cpp

@@ -513,7 +513,7 @@ const char * getOperatorIRText(node_operator op)
     EXPAND_CASE(no,blob2id);
     EXPAND_CASE(no,anon);
     EXPAND_CASE(no,projectrow);
-    EXPAND_CASE(no,cppbody);
+    EXPAND_CASE(no,embedbody);
     EXPAND_CASE(no,sortpartition);
     EXPAND_CASE(no,define);
     EXPAND_CASE(no,globalscope);
@@ -2124,14 +2124,14 @@ extern HQL_API void getIRText(StringArray & target, unsigned options, IHqlExpres
 static StringBuffer staticDebuggingStringBuffer;
 extern HQL_API const char * getIRText(IHqlExpression * expr)
 {
-    StringBufferIRBuilder output(staticDebuggingStringBuffer, defaultDumpOptions);
+    StringBufferIRBuilder output(staticDebuggingStringBuffer.clear(), defaultDumpOptions);
     playIR(output, expr, NULL, NULL);
     return staticDebuggingStringBuffer.str();
 }
 
 extern HQL_API const char * getIRText(ITypeInfo * type)
 {
-    StringBufferIRBuilder output(staticDebuggingStringBuffer, defaultDumpOptions);
+    StringBufferIRBuilder output(staticDebuggingStringBuffer.clear(), defaultDumpOptions);
     playIR(output, NULL, NULL, type);
     return staticDebuggingStringBuffer.str();
 }

+ 8 - 7
ecl/hqlcpp/hqlcpp.cpp

@@ -3268,8 +3268,8 @@ void HqlCppTranslator::buildExpr(BuildCtx & ctx, IHqlExpression * expr, CHqlBoun
     case no_blob2id:
         doBuildExprBlobToId(ctx, expr, tgt);
         return;
-    case no_cppbody:
-        doBuildExprCppBody(ctx, expr, &tgt);
+    case no_embedbody:
+        doBuildExprEmbedBody(ctx, expr, &tgt);
         return;
     case no_null:
         tgt.length.setown(getSizetConstant(0));
@@ -3597,8 +3597,8 @@ void HqlCppTranslator::buildStmt(BuildCtx & _ctx, IHqlExpression * expr)
     case no_assert:
         doBuildStmtAssert(ctx, expr);
         return;
-    case no_cppbody:
-        doBuildExprCppBody(ctx, expr, NULL);
+    case no_embedbody:
+        doBuildExprEmbedBody(ctx, expr, NULL);
         return;
     case no_setworkflow_cond:
         {
@@ -7235,7 +7235,7 @@ void HqlCppTranslator::processCppBodyDirectives(IHqlExpression * expr)
     }
 }
 
-void HqlCppTranslator::doBuildExprCppBody(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr * tgt)
+void HqlCppTranslator::doBuildExprEmbedBody(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr * tgt)
 {
     if (!allowEmbeddedCpp())
         throwError(HQLERR_EmbeddedCppNotAllowed);
@@ -11419,6 +11419,7 @@ void HqlCppTranslator::buildScriptFunctionDefinition(BuildCtx &funcctx, IHqlExpr
     assertex(outofline->getOperator() == no_outofline);
     IHqlExpression * bodyCode = outofline->queryChild(0);
     IHqlExpression *language = queryPropertyChild(bodyCode, languageAtom, 0);
+    bool isImport = bodyCode->hasProperty(importAtom);
 
     funcctx.addQuotedCompound(proto);
 
@@ -11432,7 +11433,7 @@ void HqlCppTranslator::buildScriptFunctionDefinition(BuildCtx &funcctx, IHqlExpr
     HqlExprArray scriptArgs;
     scriptArgs.append(*LINK(ctxVar));
     scriptArgs.append(*LINK(bodyCode->queryChild(0)));
-    buildFunctionCall(funcctx, compileEmbeddedScriptAtom, scriptArgs);
+    buildFunctionCall(funcctx, isImport ? importAtom : compileEmbeddedScriptAtom, scriptArgs);
     IHqlExpression *formals = funcdef->queryChild(1);
     ForEachChild(i, formals)
     {
@@ -11501,7 +11502,7 @@ void HqlCppTranslator::buildFunctionDefinition(IHqlExpression * funcdef)
     }
     expandFunctionPrototype(proto, funcdef);
 
-    if (bodyCode->getOperator() == no_cppbody)
+    if (bodyCode->getOperator() == no_embedbody)
     {
         if (!allowEmbeddedCpp())
             throwError(HQLERR_EmbeddedCppNotAllowed);

+ 1 - 1
ecl/hqlcpp/hqlcpp.ipp

@@ -1267,8 +1267,8 @@ public:
     void doBuildExprCountDict(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & tgt);
     void doBuildExprCount(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & tgt);
     void doBuildExprCounter(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & tgt);
-    void doBuildExprCppBody(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr * tgt);
     void doBuildExprDivide(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & tgt);
+    void doBuildExprEmbedBody(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr * tgt);
     void doBuildExprEvaluate(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & tgt);
     void doBuildExprExists(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & tgt);
     void doBuildExprFailCode(BuildCtx & ctx, IHqlExpression * expr, CHqlBoundExpr & tgt);

+ 1 - 0
ecl/hqlcpp/hqlcppsys.ecl

@@ -817,6 +817,7 @@ const char * cppSystemText[]  = {
     "   unsigned getUnsignedResult() : method,entrypoint='getUnsignedResult';",
 
     "   compileEmbeddedScript(const varstring script) : method,entrypoint='compileEmbeddedScript';",
+    "   import(const varstring script) : method,entrypoint='importFunction';",
     "   END;",
     NULL };
 

+ 1 - 1
ecl/hqlcpp/hqlcse.cpp

@@ -576,7 +576,7 @@ bool CseSpotter::checkPotentialCSE(IHqlExpression * expr, CseSpotterInfo * extra
     case no_soapcall:
     case no_newsoapcall:
     case no_id2blob:
-    case no_cppbody:
+    case no_embedbody:
     case no_rows:
         return false;
 

+ 1 - 1
ecl/hqlcpp/hqlttcpp.cpp

@@ -5713,7 +5713,7 @@ IHqlExpression * WorkflowTransformer::transformInternalFunction(IHqlExpression *
     OwnedHqlExpr namedFuncDef = newFuncDef->clone(funcdefArgs);
     inheritDependencies(namedFuncDef);
 
-    if (ecl->getOperator() == no_cppbody)
+    if (ecl->getOperator() == no_embedbody)
         return namedFuncDef.getClear();
 
     WorkflowItem * item = new WorkflowItem(namedFuncDef);

+ 83 - 63
plugins/pyembed/pyembed.cpp

@@ -39,8 +39,10 @@ static const char * EclDefinition =
     "  boolean getEmbedContext():cpp,pure,namespace='pyembed',entrypoint='getEmbedContext',prototype='IEmbedContext* getEmbedContext()';\n"
     "  boolean syntaxCheck(const varstring src):cpp,pure,namespace='pyembed',entrypoint='syntaxCheck';\n"
     "END;"
-    "export getEmbedContext := Language.getEmbedContext;"
-    "export syntaxCheck := Language.syntaxCheck;";
+    "EXPORT getEmbedContext := Language.getEmbedContext;"
+    "EXPORT syntaxCheck := Language.syntaxCheck;"
+    "EXPORT boolean supportsImport := false;"
+    "EXPORT boolean supportsScript := true;";
 
 extern "C" EXPORT bool getECLPluginDefinition(ECLPluginDefinitionBlock *pb)
 {
@@ -79,6 +81,8 @@ public:
     inline operator PyObject *() const    { return ptr; }
     inline void clear()                     { if (ptr) Py_DECREF(ptr); ptr = NULL; }
     inline void setown(PyObject *_ptr)      { clear(); ptr = _ptr; }
+    inline void set(PyObject *_ptr)         { clear(); ptr = _ptr; if (ptr) Py_INCREF(ptr);}
+    inline PyObject **ref() { clear(); return &ptr; }
 };
 
 // call checkPythonError to throw an exception if Python error state is set
@@ -88,9 +92,13 @@ static void checkPythonError()
     PyObject* err = PyErr_Occurred();
     if (err)
     {
+        OwnedPyObject pType, pValue, pTraceBack;
+        PyErr_Fetch(pType.ref(), pType.ref(), pType.ref());
         OwnedPyObject errStr = PyObject_Str(err);
+        OwnedPyObject valStr = PyObject_Str(pValue);
         PyErr_Clear();
-        rtlFail(0, PyString_AsString(errStr));
+        VStringBuffer errMessage("%s: %s", PyString_AsString(errStr), PyString_AsString(valStr));
+        rtlFail(0, errMessage.str());
     }
 }
 
@@ -113,6 +121,28 @@ public:
     }
 };
 
+// Use a global object to ensure that the Python interpreter is initialized on main thread
+
+static class Python27GlobalState
+{
+public:
+    Python27GlobalState()
+    {
+        // Initialize the Python Interpreter
+        Py_Initialize();
+        PyEval_InitThreads();
+        tstate = PyEval_SaveThread();
+    }
+    ~Python27GlobalState()
+    {
+        PyEval_RestoreThread(tstate);
+        // Finish the Python Interpreter
+        Py_Finalize();
+    }
+protected:
+    PyThreadState *tstate;
+} globalState;
+
 // There is a singleton PythonThreadContext per thread. This allows us to
 // ensure that we can make repeated calls to a Python function efficiently.
 
@@ -124,42 +154,12 @@ public:
     PythonThreadContext()
     {
         threadState = PyEval_SaveThread();
-        locals.setown(PyDict_New());
-        globals.setown(PyDict_New());
     }
     ~PythonThreadContext()
     {
-        locals.clear();
-        globals.clear();
+        PyEval_RestoreThread(threadState);
         script.clear();
         result.clear();
-        PyEval_RestoreThread(threadState);
-    }
-
-    inline void bindRealParam(const char *name, double val)
-    {
-        OwnedPyObject vval = PyFloat_FromDouble(val);
-        PyDict_SetItemString(locals, name, vval);
-    }
-    inline void bindSignedParam(const char *name, __int64 val)
-    {
-        OwnedPyObject vval = PyLong_FromLongLong(val);
-        PyDict_SetItemString(locals, name, vval);
-    }
-    inline void bindUnsignedParam(const char *name, unsigned __int64 val)
-    {
-        OwnedPyObject vval = PyLong_FromUnsignedLongLong(val);
-        PyDict_SetItemString(locals, name, vval);
-    }
-    inline void bindStringParam(const char *name, size32_t len, const char *val)
-    {
-        OwnedPyObject vval = PyString_FromStringAndSize(val, len);
-        PyDict_SetItemString(locals, name, vval);
-    }
-    inline void bindVStringParam(const char *name, const char *val)
-    {
-        OwnedPyObject vval = PyString_FromString(val);
-        PyDict_SetItemString(locals, name, vval);
     }
 
     inline double getRealResult()
@@ -191,22 +191,44 @@ public:
         if (!prevtext || strcmp(text, prevtext) != 0)
         {
             prevtext.clear();
+            // Try compiling as a eval first... if that fails, try as a script.
             script.setown(Py_CompileString(text, "", Py_eval_input));
+            if (!script)
+            {
+                PyErr_Clear();
+                StringBuffer wrapped;
+                wrapPythonText(wrapped, text);
+                script.setown(Py_CompileString(wrapped, "<embed>", Py_file_input));
+            }
             checkPythonError();
             prevtext.set(text);
         }
 
     }
-    inline void callFunction()
+    inline void callFunction(PyObject *locals, PyObject *globals)
     {
-        checkPythonError();
         result.setown(PyEval_EvalCode((PyCodeObject *) script.get(), locals, globals));
         checkPythonError();
+        if (!result || result == Py_None)
+            result.set(PyDict_GetItemString(locals, "__result__"));
+        if (!result || result == Py_None)
+            result.set(PyDict_GetItemString(globals, "__result__"));
     }
 private:
+    static StringBuffer &wrapPythonText(StringBuffer &out, const char *in)
+    {
+        out.append("def __user__():\n  ");
+        char c;
+        while ((c = *in++) != '\0')
+        {
+            out.append(c);
+            if (c=='\n')
+                out.append("  ");
+        }
+        out.append("\n__result__ = __user__()\n");
+        return out;
+    }
     GILstateWrapper GILState;
-    OwnedPyObject locals;
-    OwnedPyObject globals;
     OwnedPyObject script;
     OwnedPyObject result;
     StringAttr prevtext;
@@ -223,6 +245,9 @@ public:
     : sharedCtx(_sharedCtx)
     {
         PyEval_RestoreThread(sharedCtx->threadState);
+        locals.setown(PyDict_New());
+        globals.setown(PyDict_New());
+        PyDict_SetItemString(locals, "__builtins__", PyEval_GetBuiltins( ));  // required for import to work
     }
     ~Python27EmbedFunctionContext()
     {
@@ -230,23 +255,28 @@ public:
     }
     virtual void bindRealParam(const char *name, double val)
     {
-        return sharedCtx->bindRealParam(name, val);
+        OwnedPyObject vval = PyFloat_FromDouble(val);
+        PyDict_SetItemString(locals, name, vval);
     }
     virtual void bindSignedParam(const char *name, __int64 val)
     {
-        return sharedCtx->bindSignedParam(name, val);
+        OwnedPyObject vval = PyLong_FromLongLong(val);
+        PyDict_SetItemString(locals, name, vval);
     }
     virtual void bindUnsignedParam(const char *name, unsigned __int64 val)
     {
-        return sharedCtx->bindUnsignedParam(name, val);
+        OwnedPyObject vval = PyLong_FromUnsignedLongLong(val);
+        PyDict_SetItemString(locals, name, vval);
     }
     virtual void bindStringParam(const char *name, size32_t len, const char *val)
     {
-        return sharedCtx->bindStringParam(name, len, val);
+        OwnedPyObject vval = PyString_FromStringAndSize(val, len);
+        PyDict_SetItemString(locals, name, vval);
     }
     virtual void bindVStringParam(const char *name, const char *val)
     {
-        return sharedCtx->bindVStringParam(name, val);
+        OwnedPyObject vval = PyString_FromString(val);
+        PyDict_SetItemString(locals, name, vval);
     }
 
     virtual double getRealResult()
@@ -266,16 +296,22 @@ public:
         sharedCtx->getStringResult(__len, __result);
     }
 
+    virtual void importFunction(const char *text)
+    {
+        UNIMPLEMENTED; // TBD
+    }
     virtual void compileEmbeddedScript(const char *text)
     {
         sharedCtx->compileEmbeddedScript(text);
     }
     virtual void callFunction()
     {
-        sharedCtx->callFunction();
+        sharedCtx->callFunction(locals, globals);
     }
 private:
     PythonThreadContext *sharedCtx;
+    OwnedPyObject locals;
+    OwnedPyObject globals;
 };
 
 __thread PythonThreadContext* threadContext;  // We reuse per thread, for speed
@@ -288,23 +324,9 @@ static void releaseContext()
         (*threadHookChain)();
 }
 
-static class Python27EmbedContext : public CInterfaceOf<IEmbedContext>
+class Python27EmbedContext : public CInterfaceOf<IEmbedContext>
 {
 public:
-    Python27EmbedContext()
-    {
-        // Initialize the Python Interpreter
-        Py_Initialize();
-        PyEval_InitThreads();
-        tstate = PyEval_SaveThread();
-        Link();  // Deliberately 'leak' in order to avoid freeing this global object prematurely
-    }
-    ~Python27EmbedContext()
-    {
-        PyEval_RestoreThread(tstate);
-        // Finish the Python Interpreter
-        Py_Finalize();
-    }
     virtual IEmbedFunctionContext *createFunctionContext()
     {
         if (!threadContext)
@@ -314,13 +336,11 @@ public:
         }
         return new Python27EmbedFunctionContext(threadContext);
     }
-protected:
-    PyThreadState *tstate;
-} theEmbedContext;
+};
 
 extern IEmbedContext* getEmbedContext()
 {
-    return LINK(&theEmbedContext);
+    return new Python27EmbedContext;
 }
 
 extern bool syntaxCheck(const char *script)

+ 7 - 1
plugins/v8embed/v8embed.cpp

@@ -39,7 +39,9 @@ static const char * EclDefinition =
     "  boolean syntaxCheck(const varstring src):cpp,pure,namespace='javascriptLanguageHelper',entrypoint='syntaxCheck';\n"
     "END;"
     "export getEmbedContext := Language.getEmbedContext;"
-    "export syntaxCheck := Language.syntaxCheck;";
+    "export syntaxCheck := Language.syntaxCheck;"
+    "EXPORT boolean supportsImport := false;"
+    "EXPORT boolean supportsScript := true;";
 
 extern "C" EXPORT bool getECLPluginDefinition(ECLPluginDefinitionBlock *pb)
 {
@@ -145,6 +147,10 @@ public:
         v8::Handle<v8::Script> lscript = v8::Script::Compile(source);
         script = v8::Persistent<v8::Script>::New(lscript);
     }
+    virtual void importFunction(const char *text)
+    {
+        UNIMPLEMENTED; // Not sure if meaningful for js
+    }
     virtual void callFunction()
     {
         assertex (!script.IsEmpty());

+ 1 - 0
rtl/eclrtl/eclrtl.hpp

@@ -744,6 +744,7 @@ interface IEmbedFunctionContext : extends IInterface
     virtual unsigned __int64 getUnsignedResult() = 0;
     virtual void getStringResult(size32_t &len, char * &result) = 0;
 
+    virtual void importFunction(const char *function) = 0;
     virtual void compileEmbeddedScript(const char *script) = 0;
     virtual void callFunction() = 0;
 };

+ 5 - 2
system/jlib/jthread.cpp

@@ -104,8 +104,6 @@ void *Thread::_threadmain(void *v)
     t->tidlog = threadLogID();
 #endif
     int ret = t->begin();
-    if (threadTerminationHook)
-        (*threadTerminationHook)();
     char *&threadname = t->cthreadname.threadname;
     if (threadname) {
         memsize_t l=strlen(threadname);
@@ -266,6 +264,8 @@ int Thread::begin()
         handleException(MakeStringException(0, "Unknown exception in Thread %s", getName()));
     }
 #endif
+    if (threadTerminationHook)
+        (*threadTerminationHook)();
 #ifdef _WIN32
 #ifndef _DEBUG
     CloseHandle(hThread);   // leak handle when debugging, 
@@ -554,6 +554,7 @@ void CThreadedPersistent::main()
         try
         {
             owner->main();
+            // Note we do NOT call the thread reset hook here - these threads are expected to be able to preserve state, I think
         }
         catch (IException *e)
         {
@@ -805,6 +806,8 @@ public:
                 handleException(MakeStringException(0, "Unknown exception in Thread from pool %s", parent.poolname.get()));
             }
 #endif
+            if (threadTerminationHook)
+                (*threadTerminationHook)();    // Reset any pre-thread state.
         } while (parent.notifyStopped(this));
         return 0;
     }

+ 17 - 0
testing/ecl/embedjs2.ecl

@@ -0,0 +1,17 @@
+import Javascript;
+
+string anagram(string word) := EMBED(Javascript)
+
+function anagram(word)
+{
+  if (word == 'cat')
+     return 'act';
+  else
+    return word;
+}
+
+anagram(word)
+ENDEMBED;
+
+anagram('dog');
+anagram('cat');

+ 17 - 0
testing/ecl/embedp2.ecl

@@ -0,0 +1,17 @@
+import python;
+
+string anagram(string word) := EMBED(Python)
+  def anagram(w):
+    if word == 'cat':
+      return 'act'
+    else:
+      return w
+
+  return anagram(word)
+ENDEMBED;
+
+anagram('dog');
+anagram('cat');
+
+
+