11
2-
2+ from std / strutils import strip
33import ../../ Objects / [
44 stringobject/ codec,
55 noneobject,
@@ -46,9 +46,10 @@ proc compile*(
4646
4747 Py_CompileStringObject (str, filename, emode, cf, optimize)
4848
49- proc exec * (
50- source: PyObject , globals: PyObject = pyNone, locals: PyObject = pyNone,
51- closure: PyObject = nil ): PyObject {.bltin_clinicGen .} =
49+ template init_globals_locals_ensure_bltin (no_globals_err,
50+ globals_invalid, locals_invalid
51+ ){.dirty .} =
52+
5253 var globals = globals
5354 var fromframe = false
5455 if globals.isPyNone:
@@ -59,7 +60,7 @@ proc exec*(
5960 else :
6061 globals = PyEval_GetGlobalsFromRunningMain ()
6162 if globals.isNil:
62- return newSystemError newPyAscii " globals and locals cannot be NULL "
63+ return newSystemError newPyAscii no_globals_err
6364
6465 var locals = locals
6566 if locals.isPyNone:
@@ -70,16 +71,68 @@ proc exec*(
7071 locals = globals
7172
7273 if not globals.ofPyDictObject:
73- return type_errorn ( " exec() globals must be a dict, not $# " , globals)
74+ return type_error globals_invalid
7475 if not locals.ofPyMapping:
75- return type_errorn ( " exec() locals must be a mapping or None, not $# " , locals)
76+ return type_error locals_invalid
7677
7778 locals = nil
7879 TODO_locals locals
7980
8081 let dglobals = PyDictObject globals
8182 retIfExc PyEval_EnsureBuiltins (dglobals)
8283
84+ template asStringAndInitCf (source, funName){.dirty .} =
85+ var cf = initPyCompilerFlags ()
86+ cf.flags = typeof (cf.flags) PyCF .SOURCE_IS_UTF8
87+ var source_copy: PyObject
88+ var str: string
89+ retIfExc Py_SourceAsString (source, funName, " string, bytes or AST" , cf, source_copy, str)
90+
91+ proc eval * (
92+ source: PyObject , globals: PyObject = pyNone, locals: PyObject = pyNone,
93+ ): PyObject {.bltin_clinicGen .} =
94+ var globalsIsDict = globals.ofPyDictObject
95+ # if not globals.isPyNone and not (globalsIsDict = globals.ofPyDictObject; globalsIsDict):
96+ # return type_error(
97+ # )
98+ # if not locals.isPyNone and not locals.ofPyMapping:
99+ # return type_error
100+
101+ init_globals_locals_ensure_bltin (
102+ " eval must be given globals and locals when called without a frame" ,
103+ if globalsIsDict: " globals must be a real dict; try eval(expr, {}, mapping)"
104+ else : " globals must be a dict"
105+ ,
106+ " locals must be a mapping"
107+ )
108+ if source.ofPyCodeObject:
109+ let co = PyCodeObject source
110+ retIfExc audit (" exec" , source)
111+ if co.freeVars.len > 0 :
112+ return type_error " code object passed to eval() may not have free variables"
113+
114+ result = evalCode (co, dglobals, locals)
115+ else :
116+ asStringAndInitCf source, " eval"
117+
118+ str = str.strip (chars= {' ' , '\t ' }, trailing= false )
119+
120+ discard PyEval_MergeCompilerFlags (cf)
121+
122+ return PyRun_StringFlags (str, Mode .Eval , dglobals, locals, cf)
123+
124+
125+ proc exec * (
126+ source: PyObject , globals: PyObject = pyNone, locals: PyObject = pyNone,
127+ closure: PyObject = nil ): PyObject {.bltin_clinicGen .} =
128+
129+ init_globals_locals_ensure_bltin (
130+ " globals and locals cannot be NULL" ,
131+ " exec() globals must be a dict, not " & globals.typeName,
132+ " exec() locals must be a mapping or None, not " & locals.typeName
133+
134+ )
135+
83136 var closure = closure
84137 if closure.isPyNone:
85138 closure = nil
@@ -113,11 +166,7 @@ proc exec*(
113166 if not closure.isNil:
114167 return type_error " closure can only be used when source is a code object"
115168
116- var cf = initPyCompilerFlags ()
117- cf.flags = typeof (cf.flags) PyCF .SOURCE_IS_UTF8
118- var source_copy: PyObject
119- var str: string
120- retIfExc Py_SourceAsString (source, " exec" , " string, bytes or AST" , cf, source_copy, str)
169+ asStringAndInitCf source, " exec"
121170 result = if PyEval_MergeCompilerFlags (cf):
122171 PyRun_StringFlags (str, Mode .File , dglobals, locals, cf)
123172 else :
@@ -130,3 +179,4 @@ template reg(f) =
130179template register_compile_eval_exec * =
131180 reg compile
132181 reg exec
182+ reg eval
0 commit comments