Commit 7b9c4682 authored by Kevin Modzelewski's avatar Kevin Modzelewski Committed by Kevin Modzelewski

Support the inspect module

Add a simple 'code' type (ex f.func_code), and some related
things like f.func_defaults.

This implementation isn't super performant or consistent, but
I think these particular features are relatively rarely used?
parent 191d7d5b
// This file is originally from CPython 2.7, with modifications for Pyston
/* Definitions for bytecode */
#ifndef Py_CODE_H
#define Py_CODE_H
#ifdef __cplusplus
extern "C" {
#endif
// Pyston change: we're not using this
#if 0
/* Bytecode object */
typedef struct {
PyObject_HEAD
int co_argcount; /* #arguments, except *args */
int co_nlocals; /* #local variables */
int co_stacksize; /* #entries needed for evaluation stack */
int co_flags; /* CO_..., see below */
PyObject *co_code; /* instruction opcodes */
PyObject *co_consts; /* list (constants used) */
PyObject *co_names; /* list of strings (names used) */
PyObject *co_varnames; /* tuple of strings (local variable names) */
PyObject *co_freevars; /* tuple of strings (free variable names) */
PyObject *co_cellvars; /* tuple of strings (cell variable names) */
/* The rest doesn't count for hash/cmp */
PyObject *co_filename; /* string (where it was loaded from) */
PyObject *co_name; /* string (name, for reference) */
int co_firstlineno; /* first source line number */
PyObject *co_lnotab; /* string (encoding addr<->lineno mapping) See
Objects/lnotab_notes.txt for details. */
void *co_zombieframe; /* for optimization only (see frameobject.c) */
PyObject *co_weakreflist; /* to support weakrefs to code objects */
} PyCodeObject;
#endif
typedef struct _PyCodeObject PyCodeObject;
/* Masks for co_flags above */
#define CO_OPTIMIZED 0x0001
#define CO_NEWLOCALS 0x0002
#define CO_VARARGS 0x0004
#define CO_VARKEYWORDS 0x0008
#define CO_NESTED 0x0010
#define CO_GENERATOR 0x0020
/* The CO_NOFREE flag is set if there are no free or cell variables.
This information is redundant, but it allows a single flag test
to determine whether there is any extra work to be done when the
call frame it setup.
*/
#define CO_NOFREE 0x0040
#if 0
/* This is no longer used. Stopped defining in 2.5, do not re-use. */
#define CO_GENERATOR_ALLOWED 0x1000
#endif
#define CO_FUTURE_DIVISION 0x2000
#define CO_FUTURE_ABSOLUTE_IMPORT 0x4000 /* do absolute imports by default */
#define CO_FUTURE_WITH_STATEMENT 0x8000
#define CO_FUTURE_PRINT_FUNCTION 0x10000
#define CO_FUTURE_UNICODE_LITERALS 0x20000
/* This should be defined if a future statement modifies the syntax.
For example, when a keyword is added.
*/
#if 1
#define PY_PARSER_REQUIRES_FUTURE_KEYWORD
#endif
#define CO_MAXBLOCKS 20 /* Max static block nesting within a function */
// Pyston change: this is no longer a static object
//PyAPI_DATA(PyTypeObject) PyCode_Type;
PyAPI_DATA(PyTypeObject*) code_cls;
#define PyCode_Type (*code_cls)
#define PyCode_Check(op) (Py_TYPE(op) == &PyCode_Type)
#define PyCode_GetNumFree(op) (PyTuple_GET_SIZE((op)->co_freevars))
/* Public interface */
PyAPI_FUNC(PyCodeObject *) PyCode_New(
int, int, int, int, PyObject *, PyObject *, PyObject *, PyObject *,
PyObject *, PyObject *, PyObject *, PyObject *, int, PyObject *) PYSTON_NOEXCEPT;
/* same as struct above */
/* Creates a new empty code object with the specified source location. */
PyAPI_FUNC(PyCodeObject *)
PyCode_NewEmpty(const char *filename, const char *funcname, int firstlineno) PYSTON_NOEXCEPT;
/* Return the line number associated with the specified bytecode index
in this code object. If you just need the line number of a frame,
use PyFrame_GetLineNumber() instead. */
PyAPI_FUNC(int) PyCode_Addr2Line(PyCodeObject *, int) PYSTON_NOEXCEPT;
/* for internal use only */
#define _PyCode_GETCODEPTR(co, pp) \
((*Py_TYPE((co)->co_code)->tp_as_buffer->bf_getreadbuffer) \
((co)->co_code, 0, (void **)(pp)))
typedef struct _addr_pair {
int ap_lower;
int ap_upper;
} PyAddrPair;
/* Update *bounds to describe the first and one-past-the-last instructions in the
same line as lasti. Return the number of that line.
*/
PyAPI_FUNC(int) _PyCode_CheckLineNumber(PyCodeObject* co,
int lasti, PyAddrPair *bounds) PYSTON_NOEXCEPT;
PyAPI_FUNC(PyObject*) PyCode_Optimize(PyObject *code, PyObject* consts,
PyObject *names, PyObject *lineno_obj) PYSTON_NOEXCEPT;
#ifdef __cplusplus
}
#endif
#endif /* !Py_CODE_H */
......@@ -3,8 +3,10 @@
import sys
import types
from opcode import *
from opcode import __all__ as _opcodes_all
# Pyston change: we don't have the opcode module
# from opcode import *
# from opcode import __all__ as _opcodes_all
_opcodes_all = []
__all__ = ["dis", "disassemble", "distb", "disco",
"findlinestarts", "findlabels"] + _opcodes_all
......
......@@ -41,11 +41,14 @@ import linecache
from operator import attrgetter
from collections import namedtuple
# Pyston change: we don't support all of these flags yet, so just enable the ones that we do:
CO_VARARGS = 0x4
CO_VARKEYWORDS = 0x8
# These constants are from Include/code.h.
CO_OPTIMIZED, CO_NEWLOCALS, CO_VARARGS, CO_VARKEYWORDS = 0x1, 0x2, 0x4, 0x8
CO_NESTED, CO_GENERATOR, CO_NOFREE = 0x10, 0x20, 0x40
# CO_OPTIMIZED, CO_NEWLOCALS, CO_VARARGS, CO_VARKEYWORDS = 0x1, 0x2, 0x4, 0x8
# CO_NESTED, CO_GENERATOR, CO_NOFREE = 0x10, 0x20, 0x40
# See Include/object.h
TPFLAGS_IS_ABSTRACT = 1 << 20
# TPFLAGS_IS_ABSTRACT = 1 << 20
# ----------------------------------------------------------- type-checking
def ismodule(object):
......@@ -810,9 +813,6 @@ def getargspec(func):
'defaults' is an n-tuple of the default values of the last n arguments.
"""
# Pyston change:
return ArgSpec((), "args", "kw", ())
if ismethod(func):
func = func.im_func
if not isfunction(func):
......
......@@ -4,6 +4,9 @@ opcode module - potentially shared between dis and other modules which
operate on bytecodes (e.g. peephole optimizers).
"""
# Pyston change: disable this module
raise NotImplementedError()
__all__ = ["cmp_op", "hasconst", "hasname", "hasjrel", "hasjabs",
"haslocal", "hascompare", "hasfree", "opname", "opmap",
"HAVE_ARGUMENT", "EXTENDED_ARG"]
......
......@@ -43,9 +43,7 @@ DictType = DictionaryType = dict
def _f(): pass
FunctionType = type(_f)
LambdaType = type(lambda: None) # Same as FunctionType
# Pyston change: there is no concept of a "code object" yet:
# CodeType = type(_f.func_code)
CodeType = None
CodeType = type(_f.func_code)
def _g():
yield 1
......
......@@ -1436,6 +1436,7 @@ public:
}
CompilerType* getattrType(const std::string* attr, bool cls_only) override {
// Any changes here need to be mirrored in getattr()
if (canStaticallyResolveGetattrs()) {
Box* rtattr = cls->getattr(*attr);
if (rtattr == NULL)
......@@ -1446,7 +1447,8 @@ public:
return AbstractFunctionType::fromRT(static_cast<BoxedFunction*>(rtattr), true);
// return typeFromClass(instancemethod_cls);
} else {
return typeFromClass(rtattr->cls);
// Have to follow the descriptor protocol here
return UNKNOWN;
}
}
......@@ -1460,7 +1462,7 @@ public:
CompilerVariable* getattr(IREmitter& emitter, const OpInfo& info, ConcreteCompilerVariable* var,
const std::string* attr, bool cls_only) override {
// printf("%s.getattr %s\n", debugName().c_str(), attr->c_str());
// Any changes here need to be mirrored in getattrType()
if (canStaticallyResolveGetattrs()) {
Box* rtattr = cls->getattr(*attr);
if (rtattr == NULL) {
......@@ -2284,6 +2286,11 @@ public:
return undefVariable();
}
CompilerVariable* getitem(IREmitter& emitter, const OpInfo& info, ConcreteCompilerVariable* var,
CompilerVariable* slice) override {
return undefVariable();
}
ConcreteCompilerType* getBoxType() override { return UNKNOWN; }
ConcreteCompilerType* getConcreteType() override { return this; }
......
// 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 <sstream>
#include "Python.h"
#include "code.h"
#include "gc/collector.h"
#include "runtime/objmodel.h"
#include "runtime/set.h"
namespace pyston {
BoxedClass* code_cls;
class BoxedCode : public Box {
public:
CLFunction* f;
BoxedCode(CLFunction* f) : f(f) {}
DEFAULT_CLASS(code_cls);
static void gcHandler(GCVisitor* v, Box* b) {
assert(b->cls == code_cls);
boxGCHandler(v, b);
}
static Box* argcount(Box* b, void*) {
RELEASE_ASSERT(b->cls == code_cls, "");
return boxInt(static_cast<BoxedCode*>(b)->f->num_args);
}
static Box* varnames(Box* b, void*) {
RELEASE_ASSERT(b->cls == code_cls, "");
BoxedCode* code = static_cast<BoxedCode*>(b);
auto& param_names = code->f->param_names;
if (!param_names.takes_param_names)
return EmptyTuple;
BoxedTuple::GCVector elts;
for (auto sr : param_names.args)
elts.push_back(new BoxedString(sr));
if (param_names.vararg.size())
elts.push_back(new BoxedString(param_names.vararg));
if (param_names.kwarg.size())
elts.push_back(new BoxedString(param_names.kwarg));
return new BoxedTuple(std::move(elts));
}
static Box* flags(Box* b, void*) {
RELEASE_ASSERT(b->cls == code_cls, "");
BoxedCode* code = static_cast<BoxedCode*>(b);
int flags = 0;
if (code->f->param_names.vararg.size())
flags |= CO_VARARGS;
if (code->f->param_names.kwarg.size())
flags |= CO_VARKEYWORDS;
return boxInt(flags);
}
};
Box* codeForFunction(BoxedFunction* f) {
return new BoxedCode(f->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_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));
code_cls->freeze();
}
}
......@@ -628,8 +628,8 @@ void setupDict() {
dict_cls->giveAttr("__init__", new BoxedFunction(boxRTFunction((void*)dictInit, NONE, 1, 0, true, true)));
dict_cls->giveAttr("__repr__", new BoxedFunction(boxRTFunction((void*)dictRepr, STR, 1)));
dict_cls->giveAttr("__eq__", new BoxedFunction(boxRTFunction((void*)dictEq, BOXED_BOOL, 2)));
dict_cls->giveAttr("__ne__", new BoxedFunction(boxRTFunction((void*)dictNe, BOXED_BOOL, 2)));
dict_cls->giveAttr("__eq__", new BoxedFunction(boxRTFunction((void*)dictEq, UNKNOWN, 2)));
dict_cls->giveAttr("__ne__", new BoxedFunction(boxRTFunction((void*)dictNe, UNKNOWN, 2)));
dict_cls->giveAttr("__iter__",
new BoxedFunction(boxRTFunction((void*)dictIterKeys, typeFromClass(dict_iterator_cls), 1)));
......
......@@ -799,6 +799,21 @@ static Box* builtinFunctionOrMethodName(Box* b, void*) {
return func->name;
}
static Box* functionCode(Box* self, void*) {
assert(self->cls == function_cls);
BoxedFunction* func = static_cast<BoxedFunction*>(self);
// This fails "f.func_code is f.func_code"
return codeForFunction(func);
}
static Box* functionDefaults(Box* self, void*) {
assert(self->cls == function_cls);
BoxedFunction* func = static_cast<BoxedFunction*>(self);
if (!func->ndefaults)
return None;
return new BoxedTuple(BoxedTuple::GCVector(&func->defaults->elts[0], &func->defaults->elts[func->ndefaults]));
}
static Box* functionNonzero(BoxedFunction* self) {
return True;
}
......@@ -2040,6 +2055,7 @@ void setupRuntime() {
_PyUnicode_Init();
setupDescr();
setupTraceback();
setupCode();
function_cls->giveAttr("__dict__", dict_descr);
function_cls->giveAttr("__name__", new (pyston_getset_cls) BoxedGetsetDescriptor(funcName, funcSetName, NULL));
......@@ -2052,6 +2068,9 @@ void setupRuntime() {
function_cls->giveAttr("__call__",
new BoxedFunction(boxRTFunction((void*)functionCall, UNKNOWN, 1, 0, true, true)));
function_cls->giveAttr("__nonzero__", new BoxedFunction(boxRTFunction((void*)functionNonzero, BOXED_BOOL, 1)));
function_cls->giveAttr("func_code", new (pyston_getset_cls) BoxedGetsetDescriptor(functionCode, NULL, NULL));
function_cls->giveAttr("func_defaults",
new (pyston_getset_cls) BoxedGetsetDescriptor(functionDefaults, NULL, NULL));
function_cls->freeze();
builtin_function_or_method_cls->giveAttr(
......
......@@ -65,6 +65,7 @@ void teardownCAPI();
void setupGenerator();
void setupDescr();
void teardownDescr();
void setupCode();
void setupSys();
void setupBuiltins();
......@@ -684,5 +685,7 @@ extern "C" PyObject* PystonType_GenericAlloc(BoxedClass* cls, Py_ssize_t nitems)
// Classes created in Python get this automatically, but builtin types (including extension types)
// are supposed to add one themselves. type_cls and function_cls do this, for example.
extern Box* dict_descr;
Box* codeForFunction(BoxedFunction*);
}
#endif
def f(a, b=2, *args, **kw):
pass
c = f.func_code
print c.co_argcount
print c.co_varnames
print hex(c.co_flags & 0x0c)
def f(l=[]):
print l
f()
f.func_defaults[0].append(5)
f()
def f():
pass
print f.func_defaults
import inspect
def f1(a, b=2, *args, **kw):
pass
def f2():
pass
print inspect.getargspec(f1)
print inspect.getargspec(f2)
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