Commit 9be533f9 authored by Chris Toshok's avatar Chris Toshok

implement sys._getframe + frame objects, and add co_name/co_filename properties to code object

also rework a bit of the logging module to use only sys._getframe, not f_back.
parent ed5f850c
...@@ -71,15 +71,9 @@ else: ...@@ -71,15 +71,9 @@ else:
_srcfile = __file__ _srcfile = __file__
_srcfile = os.path.normcase(_srcfile) _srcfile = os.path.normcase(_srcfile)
# next bit filched from 1.5.2's inspect.py # pyston changes: we don't support tb_frame or f_back, so always use sys._getframe
def currentframe(): currentframe = lambda: sys._getframe(4)
"""Return the frame object for the caller's stack frame.""" start_getframe = 4
try:
raise Exception
except:
return sys.exc_info()[2].tb_frame.f_back
if hasattr(sys, '_getframe'): currentframe = lambda: sys._getframe(3)
# done filching # done filching
# _srcfile is only used in conjunction with sys._getframe(). # _srcfile is only used in conjunction with sys._getframe().
...@@ -1229,20 +1223,22 @@ class Logger(Filterer): ...@@ -1229,20 +1223,22 @@ class Logger(Filterer):
file name, line number and function name. file name, line number and function name.
""" """
# Pyston change:
return "(unknown file)", 0, "(unknown function)"
f = currentframe() f = currentframe()
# pyston change: we can't use f_back to walk back up the stack, so increment a counter of
# frames to skip and repeatedly call sys._getframe
fn = start_getframe
#On some versions of IronPython, currentframe() returns None if #On some versions of IronPython, currentframe() returns None if
#IronPython isn't run with -X:Frames. #IronPython isn't run with -X:Frames.
if f is not None: #if f is not None:
f = f.f_back # f = f.f_back
rv = "(unknown file)", 0, "(unknown function)" rv = "(unknown file)", 0, "(unknown function)"
while hasattr(f, "f_code"): while hasattr(f, "f_code"):
co = f.f_code co = f.f_code
filename = os.path.normcase(co.co_filename) filename = os.path.normcase(co.co_filename)
if filename == _srcfile: if filename == _srcfile:
f = f.f_back fn += 1
f = sys._getframe(fn);
#f = f.f_back
continue continue
rv = (co.co_filename, f.f_lineno, co.co_name) rv = (co.co_filename, f.f_lineno, co.co_name)
break break
......
...@@ -644,8 +644,11 @@ FrameStackState getFrameStackState() { ...@@ -644,8 +644,11 @@ FrameStackState getFrameStackState() {
RELEASE_ASSERT(0, "Internal error: unable to find any python frames"); RELEASE_ASSERT(0, "Internal error: unable to find any python frames");
} }
Box* fastLocalsToBoxedLocals() { Box* fastLocalsToBoxedLocals(int framesToSkip) {
for (PythonFrameIterator& frame_iter : unwindPythonFrames()) { for (PythonFrameIterator& frame_iter : unwindPythonFrames()) {
if (--framesToSkip >= 0)
continue;
BoxedDict* d; BoxedDict* d;
BoxedClosure* closure; BoxedClosure* closure;
FrameInfo* frame_info; FrameInfo* frame_info;
...@@ -798,6 +801,25 @@ ExecutionPoint getExecutionPoint() { ...@@ -798,6 +801,25 @@ ExecutionPoint getExecutionPoint() {
return ExecutionPoint({.cf = cf, .current_stmt = current_stmt }); return ExecutionPoint({.cf = cf, .current_stmt = current_stmt });
} }
std::unique_ptr<ExecutionPoint> getExecutionPoint(int framesToSkip) {
CompiledFunction* cf = NULL;
AST_stmt* stmt = NULL;
for (PythonFrameIterator& frame_iter : unwindPythonFrames()) {
// skip depth frames
if (--framesToSkip >= 0)
continue;
cf = frame_iter.getCF();
stmt = frame_iter.getCurrentStatement();
break;
}
if (!cf)
return std::unique_ptr<ExecutionPoint>();
return std::unique_ptr<ExecutionPoint>(new ExecutionPoint({.cf = cf, .current_stmt = stmt }));
}
llvm::JITEventListener* makeTracebacksListener() { llvm::JITEventListener* makeTracebacksListener() {
return new TracebacksEventListener(); return new TracebacksEventListener();
} }
......
...@@ -32,7 +32,7 @@ BoxedModule* getCurrentModule(); ...@@ -32,7 +32,7 @@ BoxedModule* getCurrentModule();
BoxedTraceback* getTraceback(); BoxedTraceback* getTraceback();
// Adds stack locals and closure locals into the locals dict, and returns it. // Adds stack locals and closure locals into the locals dict, and returns it.
Box* fastLocalsToBoxedLocals(); Box* fastLocalsToBoxedLocals(int framesToSkip = 0);
// Fetches a writeable pointer to the frame-local excinfo object, // Fetches a writeable pointer to the frame-local excinfo object,
// calculating it if necessary (from previous frames). // calculating it if necessary (from previous frames).
...@@ -43,6 +43,7 @@ struct ExecutionPoint { ...@@ -43,6 +43,7 @@ struct ExecutionPoint {
AST_stmt* current_stmt; AST_stmt* current_stmt;
}; };
ExecutionPoint getExecutionPoint(); ExecutionPoint getExecutionPoint();
std::unique_ptr<ExecutionPoint> getExecutionPoint(int framesToSkip);
struct FrameStackState { struct FrameStackState {
// This includes all # variables (but not the ! ones). // This includes all # variables (but not the ! ones).
......
...@@ -99,6 +99,22 @@ Box* getSysStdout() { ...@@ -99,6 +99,22 @@ Box* getSysStdout() {
return sys_stdout; return sys_stdout;
} }
Box* sysGetFrame(Box* val) {
int depth = 0;
if (val) {
if (!isSubclass(val->cls, int_cls)) {
raiseExcHelper(TypeError, "TypeError: an integer is required");
}
depth = static_cast<BoxedInt*>(val)->n;
}
Box* frame = getFrame(depth);
if (!frame) {
raiseExcHelper(ValueError, "call stack is not deep enough");
}
return frame;
}
Box* sysGetDefaultEncoding() { Box* sysGetDefaultEncoding() {
return boxStrConstant(PyUnicode_GetDefaultEncoding()); return boxStrConstant(PyUnicode_GetDefaultEncoding());
} }
...@@ -267,6 +283,8 @@ void setupSys() { ...@@ -267,6 +283,8 @@ void setupSys() {
main_fn = llvm::sys::fs::getMainExecutable(NULL, NULL); main_fn = llvm::sys::fs::getMainExecutable(NULL, NULL);
sys_module->giveAttr("executable", boxString(main_fn.str())); sys_module->giveAttr("executable", boxString(main_fn.str()));
sys_module->giveAttr("_getframe",
new BoxedFunction(boxRTFunction((void*)sysGetFrame, UNKNOWN, 1, 1, false, false), { NULL }));
sys_module->giveAttr( sys_module->giveAttr(
"getdefaultencoding", "getdefaultencoding",
new BoxedBuiltinFunctionOrMethod(boxRTFunction((void*)sysGetDefaultEncoding, STR, 0), "getdefaultencoding")); new BoxedBuiltinFunctionOrMethod(boxRTFunction((void*)sysGetDefaultEncoding, STR, 0), "getdefaultencoding"));
......
...@@ -39,6 +39,16 @@ public: ...@@ -39,6 +39,16 @@ public:
boxGCHandler(v, b); boxGCHandler(v, b);
} }
static Box* name(Box* b, void*) {
RELEASE_ASSERT(b->cls == code_cls, "");
return boxString(static_cast<BoxedCode*>(b)->f->source->getName());
}
static Box* filename(Box* b, void*) {
RELEASE_ASSERT(b->cls == code_cls, "");
return boxString(static_cast<BoxedCode*>(b)->f->source->parent_module->fn);
}
static Box* argcount(Box* b, void*) { static Box* argcount(Box* b, void*) {
RELEASE_ASSERT(b->cls == code_cls, ""); RELEASE_ASSERT(b->cls == code_cls, "");
...@@ -80,12 +90,18 @@ Box* codeForFunction(BoxedFunction* f) { ...@@ -80,12 +90,18 @@ Box* codeForFunction(BoxedFunction* f) {
return new BoxedCode(f->f); return new BoxedCode(f->f);
} }
Box* codeForCLFunction(CLFunction* f) {
return new BoxedCode(f);
}
void setupCode() { void setupCode() {
code_cls code_cls
= BoxedHeapClass::create(type_cls, object_cls, &BoxedCode::gcHandler, 0, 0, sizeof(BoxedCode), false, "code"); = BoxedHeapClass::create(type_cls, object_cls, &BoxedCode::gcHandler, 0, 0, sizeof(BoxedCode), false, "code");
code_cls->giveAttr("__new__", None); // Hacky way of preventing users from instantiating this code_cls->giveAttr("__new__", None); // Hacky way of preventing users from instantiating this
code_cls->giveAttr("co_name", new (pyston_getset_cls) BoxedGetsetDescriptor(BoxedCode::name, NULL, NULL));
code_cls->giveAttr("co_filename", new (pyston_getset_cls) BoxedGetsetDescriptor(BoxedCode::filename, NULL, NULL));
code_cls->giveAttr("co_argcount", new (pyston_getset_cls) BoxedGetsetDescriptor(BoxedCode::argcount, NULL, NULL)); code_cls->giveAttr("co_argcount", new (pyston_getset_cls) BoxedGetsetDescriptor(BoxedCode::argcount, NULL, NULL));
code_cls->giveAttr("co_varnames", new (pyston_getset_cls) BoxedGetsetDescriptor(BoxedCode::varnames, NULL, NULL)); code_cls->giveAttr("co_varnames", new (pyston_getset_cls) BoxedGetsetDescriptor(BoxedCode::varnames, NULL, NULL));
code_cls->giveAttr("co_flags", new (pyston_getset_cls) BoxedGetsetDescriptor(BoxedCode::flags, NULL, NULL)); code_cls->giveAttr("co_flags", new (pyston_getset_cls) BoxedGetsetDescriptor(BoxedCode::flags, NULL, NULL));
......
// Copyright (c) 2014-2015 Dropbox, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include "codegen/unwinding.h"
#include "runtime/types.h"
namespace pyston {
BoxedClass* frame_cls;
class BoxedFrame : public Box {
public:
BoxedFrame() __attribute__((visibility("default"))) {}
Box* _locals;
Box* _globals;
Box* _code;
uint32_t _lineno;
// cpython frame objects have the following attributes
// read-only attributes
//
// f_back[*] : previous stack frame (toward caller)
// f_code : code object being executed in this frame
// f_locals : dictionary used to look up local variables in this frame
// f_globals : dictionary used to look up global variables in this frame
// f_builtins[*] : dictionary to look up built-in (intrinsic) names
// f_restricted[*] : whether this function is executing in restricted execution mode
// f_lasti[*] : precise instruction (this is an index into the bytecode string of the code object)
// writable attributes
//
// f_trace[*] : if not None, is a function called at the start of each source code line (used by debugger)
// f_exc_type[*], : represent the last exception raised in the parent frame provided another exception was
// f_exc_value[*], : ever raised in the current frame (in all other cases they are None).
// f_exc_traceback[*] :
// f_lineno[**] : the current line number of the frame -- writing to this from within a trace function jumps
// to
// : the given line (only for the bottom-most frame). A debugger can implement a Jump command
// (aka
// : Set Next Statement) by writing to f_lineno
//
// * = unsupported in Pyston
// ** = getter supported, but setter unsupported
static void gchandler(GCVisitor* v, Box* b) {
boxGCHandler(v, b);
auto f = static_cast<BoxedFrame*>(b);
v->visit(f->_code);
v->visit(f->_locals);
v->visit(f->_globals);
}
static Box* code(Box* obj, void*) {
auto f = static_cast<BoxedFrame*>(obj);
return f->_code;
}
static Box* locals(Box* obj, void*) {
auto f = static_cast<BoxedFrame*>(obj);
return f->_locals;
}
static Box* globals(Box* obj, void*) {
auto f = static_cast<BoxedFrame*>(obj);
return f->_globals;
}
static Box* lineno(Box* obj, void*) {
auto f = static_cast<BoxedFrame*>(obj);
return boxInt(f->_lineno);
}
DEFAULT_CLASS(frame_cls);
};
Box* getFrame(int depth) {
std::unique_ptr<ExecutionPoint> fr = getExecutionPoint(depth);
if (!fr)
return NULL;
Box* locals = fastLocalsToBoxedLocals(depth);
BoxedFrame* rtn = new BoxedFrame();
rtn->_locals = locals;
// basically the same as builtin globals()
// q: TODO is it ok that we don't return a real dict here?
rtn->_globals = makeAttrWrapper(fr->cf->clfunc->source->parent_module);
rtn->_code = codeForCLFunction(fr->cf->clfunc);
rtn->_lineno = fr->current_stmt->lineno;
return rtn;
}
void setupFrame() {
frame_cls = BoxedHeapClass::create(type_cls, object_cls, &BoxedFrame::gchandler, 0, 0, sizeof(BoxedFrame), false,
"frame");
frame_cls->giveAttr("f_code", new (pyston_getset_cls) BoxedGetsetDescriptor(BoxedFrame::code, NULL, NULL));
frame_cls->giveAttr("f_locals", new (pyston_getset_cls) BoxedGetsetDescriptor(BoxedFrame::locals, NULL, NULL));
frame_cls->giveAttr("f_globals", new (pyston_getset_cls) BoxedGetsetDescriptor(BoxedFrame::globals, NULL, NULL));
frame_cls->giveAttr("f_lineno", new (pyston_getset_cls) BoxedGetsetDescriptor(BoxedFrame::lineno, NULL, NULL));
frame_cls->freeze();
}
}
...@@ -2121,6 +2121,7 @@ void setupRuntime() { ...@@ -2121,6 +2121,7 @@ void setupRuntime() {
setupDescr(); setupDescr();
setupTraceback(); setupTraceback();
setupCode(); setupCode();
setupFrame();
function_cls->giveAttr("__dict__", dict_descr); function_cls->giveAttr("__dict__", dict_descr);
function_cls->giveAttr("__name__", new (pyston_getset_cls) BoxedGetsetDescriptor(funcName, funcSetName, NULL)); function_cls->giveAttr("__name__", new (pyston_getset_cls) BoxedGetsetDescriptor(funcName, funcSetName, NULL));
......
...@@ -67,6 +67,7 @@ void setupGenerator(); ...@@ -67,6 +67,7 @@ void setupGenerator();
void setupDescr(); void setupDescr();
void teardownDescr(); void teardownDescr();
void setupCode(); void setupCode();
void setupFrame();
void setupSys(); void setupSys();
void setupBuiltins(); void setupBuiltins();
...@@ -703,5 +704,8 @@ extern "C" PyObject* PystonType_GenericAlloc(BoxedClass* cls, Py_ssize_t nitems) ...@@ -703,5 +704,8 @@ extern "C" PyObject* PystonType_GenericAlloc(BoxedClass* cls, Py_ssize_t nitems)
extern Box* dict_descr; extern Box* dict_descr;
Box* codeForFunction(BoxedFunction*); Box* codeForFunction(BoxedFunction*);
Box* codeForCLFunction(CLFunction*);
Box* getFrame(int depth);
} }
#endif #endif
import logging
import warnings
# output something using the root logger
def foo():
logging.warning("Houston, we have a %s", "bit of a problem")
foo()
import warnings
def fxn():
warnings.warn("deprecated", DeprecationWarning)
with warnings.catch_warnings(record=True) as w:
# Cause all warnings to always be triggered.
warnings.simplefilter("always")
# Trigger a warning.
fxn()
# Verify some things
assert len(w) == 1
assert issubclass(w[-1].category, DeprecationWarning)
assert "deprecated" in str(w[-1].message)
print "warnings module works"
"""
Frame Hack Recipe #1: Ruby-style string interpolation (version 1)
"""
# from http://farmdev.com/src/secrets/framehack/interpolate/solutions/interpolate1.py
import sys
from string import Template
def interpolate(templateStr):
frame = sys._getframe(1)
framedict = frame.f_locals
t = Template(templateStr)
return t.substitute(**framedict)
name = 'Feihong'
place = 'Chicago'
print interpolate("My name is ${name}. I work in ${place}.")
import sys
def sysframetest():
return sys._getframe(0)
def sysframetestwrapper():
return sysframetest()
fr = sysframetest()
print sysframetest.__name__
print fr.f_code.co_name
print fr.f_code.co_filename
fr = sysframetestwrapper()
print sysframetestwrapper.__name__
print fr.f_code.co_name
print fr.f_code.co_filename
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment