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:
_srcfile = __file__
_srcfile = os.path.normcase(_srcfile)
# next bit filched from 1.5.2's inspect.py
def currentframe():
"""Return the frame object for the caller's stack frame."""
try:
raise Exception
except:
return sys.exc_info()[2].tb_frame.f_back
if hasattr(sys, '_getframe'): currentframe = lambda: sys._getframe(3)
# pyston changes: we don't support tb_frame or f_back, so always use sys._getframe
currentframe = lambda: sys._getframe(4)
start_getframe = 4
# done filching
# _srcfile is only used in conjunction with sys._getframe().
......@@ -1229,20 +1223,22 @@ class Logger(Filterer):
file name, line number and function name.
"""
# Pyston change:
return "(unknown file)", 0, "(unknown function)"
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
#IronPython isn't run with -X:Frames.
if f is not None:
f = f.f_back
#if f is not None:
# f = f.f_back
rv = "(unknown file)", 0, "(unknown function)"
while hasattr(f, "f_code"):
co = f.f_code
filename = os.path.normcase(co.co_filename)
if filename == _srcfile:
f = f.f_back
fn += 1
f = sys._getframe(fn);
#f = f.f_back
continue
rv = (co.co_filename, f.f_lineno, co.co_name)
break
......
......@@ -644,8 +644,11 @@ FrameStackState getFrameStackState() {
RELEASE_ASSERT(0, "Internal error: unable to find any python frames");
}
Box* fastLocalsToBoxedLocals() {
Box* fastLocalsToBoxedLocals(int framesToSkip) {
for (PythonFrameIterator& frame_iter : unwindPythonFrames()) {
if (--framesToSkip >= 0)
continue;
BoxedDict* d;
BoxedClosure* closure;
FrameInfo* frame_info;
......@@ -798,6 +801,25 @@ ExecutionPoint getExecutionPoint() {
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() {
return new TracebacksEventListener();
}
......
......@@ -32,7 +32,7 @@ BoxedModule* getCurrentModule();
BoxedTraceback* getTraceback();
// 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,
// calculating it if necessary (from previous frames).
......@@ -43,6 +43,7 @@ struct ExecutionPoint {
AST_stmt* current_stmt;
};
ExecutionPoint getExecutionPoint();
std::unique_ptr<ExecutionPoint> getExecutionPoint(int framesToSkip);
struct FrameStackState {
// This includes all # variables (but not the ! ones).
......
......@@ -99,6 +99,22 @@ Box* getSysStdout() {
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() {
return boxStrConstant(PyUnicode_GetDefaultEncoding());
}
......@@ -267,6 +283,8 @@ void setupSys() {
main_fn = llvm::sys::fs::getMainExecutable(NULL, NULL);
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(
"getdefaultencoding",
new BoxedBuiltinFunctionOrMethod(boxRTFunction((void*)sysGetDefaultEncoding, STR, 0), "getdefaultencoding"));
......
......@@ -39,6 +39,16 @@ public:
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*) {
RELEASE_ASSERT(b->cls == code_cls, "");
......@@ -80,12 +90,18 @@ Box* codeForFunction(BoxedFunction* f) {
return new BoxedCode(f->f);
}
Box* codeForCLFunction(CLFunction* f) {
return new BoxedCode(f);
}
void setupCode() {
code_cls
= 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("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_varnames", new (pyston_getset_cls) BoxedGetsetDescriptor(BoxedCode::varnames, 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() {
setupDescr();
setupTraceback();
setupCode();
setupFrame();
function_cls->giveAttr("__dict__", dict_descr);
function_cls->giveAttr("__name__", new (pyston_getset_cls) BoxedGetsetDescriptor(funcName, funcSetName, NULL));
......
......@@ -67,6 +67,7 @@ void setupGenerator();
void setupDescr();
void teardownDescr();
void setupCode();
void setupFrame();
void setupSys();
void setupBuiltins();
......@@ -703,5 +704,8 @@ extern "C" PyObject* PystonType_GenericAlloc(BoxedClass* cls, Py_ssize_t nitems)
extern Box* dict_descr;
Box* codeForFunction(BoxedFunction*);
Box* codeForCLFunction(CLFunction*);
Box* getFrame(int depth);
}
#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