Commit 636aca17 authored by Kevin Modzelewski's avatar Kevin Modzelewski

Implement Python's name mangling

ie names that begin with two underscores but don't end in two underscores have
the classname added to them.

Do this early on in the pipeline so that all the analyses operate post-mangling.

The implementation is kind of hacky and I couldn't think of a good way to make it
super systematic; there may be more cases I missed.
parent 381e2fcd
......@@ -49,6 +49,34 @@ bool containsYield(AST* ast) {
return visitor.containsYield;
}
static void mangleNameInPlace(std::string& id, const std::string* private_name) {
if (!private_name)
return;
int len = id.size();
if (len < 2 || id[0] != '_' || id[1] != '_')
return;
if ((id[len - 2] == '_' && id[len - 1] == '_') || id.find('.') != std::string::npos)
return;
const char* p = private_name->c_str();
while (*p == '_') {
p++;
len--;
}
if (*p == '\0')
return;
id = "_" + (p + id);
}
static std::string mangleName(const std::string& id, const std::string* private_name) {
std::string rtn(id);
mangleNameInPlace(rtn, private_name);
return rtn;
}
static bool isCompilerCreatedName(const std::string& name) {
return name[0] == '!' || name[0] == '#';
}
......@@ -74,11 +102,14 @@ public:
bool saveInClosure(const std::string& name) override { return false; }
const std::unordered_set<std::string>& getClassDefLocalNames() override { RELEASE_ASSERT(0, ""); }
std::string mangleName(const std::string& id) override { return id; }
};
struct ScopingAnalysis::ScopeNameUsage {
AST* node;
ScopeNameUsage* parent;
const std::string* private_name;
typedef std::unordered_set<std::string> StrSet;
......@@ -106,6 +137,14 @@ struct ScopingAnalysis::ScopeNameUsage {
}
}
}
if (node->type == AST_TYPE::ClassDef) {
private_name = &ast_cast<AST_ClassDef>(node)->name;
} else if (parent) {
private_name = parent->private_name;
} else {
private_name = NULL;
}
}
};
......@@ -113,11 +152,14 @@ class ScopeInfoBase : public ScopeInfo {
private:
ScopeInfo* parent;
ScopingAnalysis::ScopeNameUsage* usage;
AST* ast;
public:
ScopeInfoBase(ScopeInfo* parent, ScopingAnalysis::ScopeNameUsage* usage) : parent(parent), usage(usage) {
ScopeInfoBase(ScopeInfo* parent, ScopingAnalysis::ScopeNameUsage* usage, AST* ast)
: parent(parent), usage(usage), ast(ast) {
assert(parent);
assert(usage);
assert(ast);
}
~ScopeInfoBase() override { delete this->usage; }
......@@ -158,6 +200,8 @@ public:
RELEASE_ASSERT(usage->node->type == AST_TYPE::ClassDef, "");
return usage->written;
}
std::string mangleName(const std::string& id) override { return pyston::mangleName(id, usage->private_name); }
};
class NameCollectorVisitor : public ASTVisitor {
......@@ -173,13 +217,19 @@ private:
public:
void doWrite(const std::string& name) {
assert(name == mangleName(name, cur->private_name));
cur->read.insert(name);
cur->written.insert(name);
}
void doRead(const std::string& name) { cur->read.insert(name); }
void doRead(const std::string& name) {
assert(name == mangleName(name, cur->private_name));
cur->read.insert(name);
}
bool visit_name(AST_Name* node) override {
mangleNameInPlace(node->id, cur->private_name);
switch (node->ctx_type) {
case AST_TYPE::Load:
doRead(node->id);
......@@ -243,19 +293,12 @@ public:
bool visit_jump(AST_Jump* node) override { return false; }
bool visit_delete(AST_Delete* node) override {
for (auto t : node->targets) {
if (t->type == AST_TYPE::Name) {
doWrite(ast_cast<AST_Name>(t)->id);
}
}
return false;
}
bool visit_delete(AST_Delete* node) override { return false; }
bool visit_global(AST_Global* node) override {
for (int i = 0; i < node->names.size(); i++) {
const std::string& name = node->names[i];
cur->forced_globals.insert(name);
mangleNameInPlace(node->names[i], cur->private_name);
cur->forced_globals.insert(node->names[i]);
}
return true;
}
......@@ -271,7 +314,10 @@ public:
for (auto* e : node->decorator_list)
e->accept(this);
doWrite(node->name);
// TODO: this is one of very few places we don't mangle in place.
// The AST doesn't have a way of representing that the class name is the
// unmangled name but the name it gets stored as is the mangled name.
doWrite(mangleName(node->name, cur->private_name));
(*map)[node] = new ScopingAnalysis::ScopeNameUsage(node, cur);
collect(node, map);
return true;
......@@ -282,10 +328,14 @@ public:
if (node == orig_node) {
for (AST_expr* e : node->args->args)
e->accept(this);
if (node->args->vararg.size())
if (node->args->vararg.size()) {
mangleNameInPlace(node->args->vararg, cur->private_name);
doWrite(node->args->vararg);
if (node->args->kwarg.size())
}
if (node->args->kwarg.size()) {
mangleNameInPlace(node->args->kwarg, cur->private_name);
doWrite(node->args->kwarg);
}
for (AST_stmt* s : node->body)
s->accept(this);
return true;
......@@ -295,7 +345,10 @@ public:
for (auto* e : node->decorator_list)
e->accept(this);
doWrite(node->name);
// TODO: this is one of very few places we don't mangle in place.
// The AST doesn't have a way of representing that the class name is the
// unmangled name but the name it gets stored as is the mangled name.
doWrite(mangleName(node->name, cur->private_name));
(*map)[node] = new ScopingAnalysis::ScopeNameUsage(node, cur);
collect(node, map);
return true;
......@@ -326,10 +379,14 @@ public:
if (node == orig_node) {
for (AST_expr* e : node->args->args)
e->accept(this);
if (node->args->vararg.size())
if (node->args->vararg.size()) {
mangleNameInPlace(node->args->vararg, cur->private_name);
doWrite(node->args->vararg);
if (node->args->kwarg.size())
}
if (node->args->kwarg.size()) {
mangleNameInPlace(node->args->kwarg, cur->private_name);
doWrite(node->args->kwarg);
}
node->body->accept(this);
} else {
for (auto* e : node->args->defaults)
......@@ -344,6 +401,8 @@ public:
bool visit_import(AST_Import* node) override {
for (int i = 0; i < node->names.size(); i++) {
AST_alias* alias = node->names[i];
mangleNameInPlace(alias->asname, cur->private_name);
mangleNameInPlace(alias->name, cur->private_name);
if (alias->asname.size())
doWrite(alias->asname);
else
......@@ -355,6 +414,8 @@ public:
bool visit_importfrom(AST_ImportFrom* node) override {
for (int i = 0; i < node->names.size(); i++) {
AST_alias* alias = node->names[i];
mangleNameInPlace(alias->asname, cur->private_name);
mangleNameInPlace(alias->name, cur->private_name);
if (alias->asname.size())
doWrite(alias->asname);
else
......@@ -447,12 +508,12 @@ void ScopingAnalysis::processNameUsages(ScopingAnalysis::NameUsageMap* usages) {
case AST_TYPE::ClassDef:
case AST_TYPE::FunctionDef:
case AST_TYPE::Lambda: {
ScopeInfoBase* scopeInfo = new ScopeInfoBase(parent_info, usage);
ScopeInfoBase* scopeInfo = new ScopeInfoBase(parent_info, usage, usage->node);
this->scopes[node] = scopeInfo;
break;
}
case AST_TYPE::GeneratorExp: {
ScopeInfoBase* scopeInfo = new ScopeInfoBase(parent_info, usage);
ScopeInfoBase* scopeInfo = new ScopeInfoBase(parent_info, usage, usage->node);
this->scopes[node] = scopeInfo;
break;
}
......
......@@ -40,6 +40,8 @@ public:
// the metaclass constructor.
// An error to call this on a non-classdef node.
virtual const std::unordered_set<std::string>& getClassDefLocalNames() = 0;
virtual std::string mangleName(const std::string& id) = 0;
};
class ScopingAnalysis {
......
......@@ -731,7 +731,7 @@ Value ASTInterpreter::visit_functionDef(AST_FunctionDef* node) {
for (int i = decorators.size() - 1; i >= 0; i--)
func = runtimeCall(decorators[i], ArgPassSpec(1), func, 0, 0, 0, 0);
doStore(node->name, func);
doStore(source_info->mangleName(node->name), func);
return Value();
}
......@@ -758,7 +758,7 @@ Value ASTInterpreter::visit_classDef(AST_ClassDef* node) {
for (int i = decorators.size() - 1; i >= 0; i--)
classobj = runtimeCall(decorators[i], ArgPassSpec(1), classobj, 0, 0, 0, 0);
doStore(node->name, classobj);
doStore(source_info->mangleName(node->name), classobj);
return Value();
}
......
......@@ -61,6 +61,13 @@ SourceInfo::ArgNames::ArgNames(AST* ast) {
}
}
std::string SourceInfo::mangleName(const std::string& id) {
assert(ast);
if (ast->type == AST_TYPE::Module)
return id;
return getScopeInfo()->mangleName(id);
}
const std::string SourceInfo::getName() {
assert(ast);
switch (ast->type) {
......
......@@ -1591,7 +1591,7 @@ private:
decorators[i]->decvref(emitter);
}
_doSet(node->name, cls, unw_info);
_doSet(irstate->getSourceInfo()->mangleName(node->name), cls, unw_info);
cls->decvref(emitter);
}
......@@ -1751,7 +1751,7 @@ private:
decorators[i]->decvref(emitter);
}
_doSet(node->name, func, unw_info);
_doSet(irstate->getSourceInfo()->mangleName(node->name), func, unw_info);
func->decvref(emitter);
}
......
......@@ -522,13 +522,12 @@ AST_Module* read_module(BufferedReader* reader) {
}
AST_Name* read_name(BufferedReader* reader) {
AST_Name* rtn = new AST_Name();
auto col_offset = readColOffset(reader);
auto ctx_type = (AST_TYPE::AST_TYPE)reader->readByte();
auto id = readString(reader);
auto lineno = reader->readULL();
rtn->col_offset = readColOffset(reader);
rtn->ctx_type = (AST_TYPE::AST_TYPE)reader->readByte();
rtn->id = readString(reader);
rtn->lineno = reader->readULL();
return rtn;
return new AST_Name(std::move(id), ctx_type, lineno, col_offset);
}
AST_Num* read_num(BufferedReader* reader) {
......
......@@ -441,18 +441,12 @@ struct expr_dispatcher {
}
ResultPtr read(pypa::AstName& a) {
AST_Name* ptr = new AST_Name();
location(ptr, a);
ptr->ctx_type = readItem(a.context);
ptr->id = a.id;
AST_Name* ptr = new AST_Name(a.id, readItem(a.context), a.line, a.column);
return ptr;
}
ResultPtr read(pypa::AstNone& n) {
AST_Name* ptr = new AST_Name();
location(ptr, n);
ptr->ctx_type = AST_TYPE::Load;
ptr->id = "None";
AST_Name* ptr = new AST_Name("None", AST_TYPE::Load, n.line, n.column);
return ptr;
}
......
......@@ -158,6 +158,8 @@ public:
#else
AST(AST_TYPE::AST_TYPE type) : type(type) {}
#endif
AST(AST_TYPE::AST_TYPE type, uint32_t lineno, uint32_t col_offset = 0)
: type(type), lineno(lineno), col_offset(col_offset) {}
};
class AST_expr : public AST {
......@@ -165,6 +167,7 @@ public:
virtual void* accept_expr(ExprVisitor* v) = 0;
AST_expr(AST_TYPE::AST_TYPE type) : AST(type) {}
AST_expr(AST_TYPE::AST_TYPE type, uint32_t lineno, uint32_t col_offset = 0) : AST(type, lineno, col_offset) {}
};
class AST_stmt : public AST {
......@@ -178,7 +181,7 @@ public:
class AST_alias : public AST {
public:
const std::string name, asname;
std::string name, asname;
virtual void accept(ASTVisitor* v);
......@@ -658,7 +661,8 @@ public:
virtual void accept(ASTVisitor* v);
virtual void* accept_expr(ExprVisitor* v);
AST_Name() : AST_expr(AST_TYPE::Name) {}
AST_Name(const std::string& id, AST_TYPE::AST_TYPE ctx_type, int lineno, int col_offset = 0)
: AST_expr(AST_TYPE::Name, lineno, col_offset), ctx_type(ctx_type), id(id) {}
static const AST_TYPE::AST_TYPE TYPE = AST_TYPE::Name;
};
......
......@@ -55,25 +55,32 @@ void CFGBlock::unconnectFrom(CFGBlock* successor) {
successor->predecessors.end());
}
static AST_Name* makeName(const std::string& id, AST_TYPE::AST_TYPE ctx_type, int lineno, int col_offset = 0) {
AST_Name* name = new AST_Name();
name->id = id;
name->col_offset = col_offset;
name->lineno = lineno;
name->ctx_type = ctx_type;
return name;
}
static const std::string RETURN_NAME("#rtnval");
class CFGVisitor : public ASTVisitor {
private:
SourceInfo* source;
AST_TYPE::AST_TYPE root_type;
FutureFlags future_flags;
CFG* cfg;
CFGBlock* curblock;
ScopingAnalysis* scoping_analysis;
public:
CFGVisitor(SourceInfo* source, AST_TYPE::AST_TYPE root_type, FutureFlags future_flags,
ScopingAnalysis* scoping_analysis, CFG* cfg)
: source(source), root_type(root_type), future_flags(future_flags), cfg(cfg),
scoping_analysis(scoping_analysis) {
curblock = cfg->addBlock();
curblock->info = "entry";
}
~CFGVisitor() {
assert(regions.size() == 0);
assert(exc_handlers.size() == 0);
}
private:
enum Why : int8_t {
FALLTHROUGH,
CONTINUE,
......@@ -95,6 +102,11 @@ private:
did_why(0), why_name(why_name) {}
};
AST_Name* makeName(const std::string& id, AST_TYPE::AST_TYPE ctx_type, int lineno, int col_offset = 0) {
AST_Name* name = new AST_Name(id, ctx_type, lineno, col_offset);
return name;
}
std::vector<RegionInfo> regions;
struct ExcBlockInfo {
......@@ -203,6 +215,8 @@ private:
return makeName(name, AST_TYPE::Load, e->lineno);
}
AST_Name* remapName(AST_Name* name) { return name; }
AST_expr* applyComprehensionCall(AST_DictComp* node, AST_Name* name) {
AST_expr* key = remapExpr(node->key);
AST_expr* value = remapExpr(node->value);
......@@ -422,7 +436,7 @@ private:
assign->lineno = val->lineno;
if (target->type == AST_TYPE::Name) {
assign->targets.push_back(target);
assign->targets.push_back(remapName(ast_cast<AST_Name>(target)));
push_back(assign);
} else if (target->type == AST_TYPE::Subscript) {
AST_Subscript* s = ast_cast<AST_Subscript>(target);
......@@ -443,7 +457,7 @@ private:
AST_Attribute* a_target = new AST_Attribute();
a_target->value = remapExpr(a->value);
a_target->attr = a->attr;
a_target->attr = source->mangleName(a->attr);
a_target->ctx_type = AST_TYPE::Store;
a_target->col_offset = a->col_offset;
a_target->lineno = a->lineno;
......@@ -534,7 +548,7 @@ private:
rtn->col_offset = node->col_offset;
rtn->lineno = node->lineno;
rtn->ctx_type = node->ctx_type;
rtn->attr = node->attr;
rtn->attr = source->mangleName(node->attr);
rtn->value = remapExpr(node->value);
return rtn;
}
......@@ -560,11 +574,7 @@ private:
if (val->type == AST_TYPE::Name) {
AST_Name* orig = ast_cast<AST_Name>(val);
AST_Name* made = new AST_Name();
made->id = orig->id;
made->col_offset = orig->col_offset;
made->lineno = orig->lineno;
made->ctx_type = orig->ctx_type;
AST_Name* made = makeName(orig->id, orig->ctx_type, orig->lineno, orig->col_offset);
return made;
} else if (val->type == AST_TYPE::Num) {
AST_Num* orig = ast_cast<AST_Num>(val);
......@@ -1048,7 +1058,7 @@ private:
rtn = remapComprehension<AST_List>(ast_cast<AST_ListComp>(node));
break;
case AST_TYPE::Name:
rtn = node;
rtn = remapName(ast_cast<AST_Name>(node));
break;
case AST_TYPE::Num:
return node;
......@@ -1089,17 +1099,6 @@ private:
}
public:
CFGVisitor(AST_TYPE::AST_TYPE root_type, FutureFlags future_flags, ScopingAnalysis* scoping_analysis, CFG* cfg)
: root_type(root_type), future_flags(future_flags), cfg(cfg), scoping_analysis(scoping_analysis) {
curblock = cfg->addBlock();
curblock->info = "entry";
}
~CFGVisitor() {
assert(regions.size() == 0);
assert(exc_handlers.size() == 0);
}
void push_back(AST_stmt* node) {
assert(node->type != AST_TYPE::Invoke);
......@@ -1539,7 +1538,7 @@ public:
break;
}
case AST_TYPE::Name: {
target = t;
target = remapName(ast_cast<AST_Name>(t));
break;
}
default:
......@@ -2298,7 +2297,7 @@ CFG* computeCFG(SourceInfo* source, std::vector<AST_stmt*> body) {
ScopingAnalysis* scoping_analysis = source->scoping;
CFGVisitor visitor(source->ast->type, source->parent_module->future_flags, scoping_analysis, rtn);
CFGVisitor visitor(source, source->ast->type, source->parent_module->future_flags, scoping_analysis, rtn);
bool skip_first = false;
......@@ -2307,7 +2306,7 @@ CFG* computeCFG(SourceInfo* source, std::vector<AST_stmt*> body) {
Box* module_name = source->parent_module->getattr("__name__", NULL);
assert(module_name->cls == str_cls);
AST_Assign* module_assign = new AST_Assign();
module_assign->targets.push_back(makeName("__module__", AST_TYPE::Store, source->ast->lineno));
module_assign->targets.push_back(new AST_Name("__module__", AST_TYPE::Store, source->ast->lineno));
module_assign->value = new AST_Str(static_cast<BoxedString*>(module_name)->s);
module_assign->lineno = 0;
visitor.push_back(module_assign);
......@@ -2317,7 +2316,7 @@ CFG* computeCFG(SourceInfo* source, std::vector<AST_stmt*> body) {
AST_Expr* first_expr = ast_cast<AST_Expr>(body[0]);
if (first_expr->value->type == AST_TYPE::Str) {
AST_Assign* doc_assign = new AST_Assign();
doc_assign->targets.push_back(makeName("__doc__", AST_TYPE::Store, source->ast->lineno));
doc_assign->targets.push_back(new AST_Name("__doc__", AST_TYPE::Store, source->ast->lineno));
doc_assign->value = first_expr->value;
doc_assign->lineno = 0;
visitor.push_back(doc_assign);
......
......@@ -245,6 +245,7 @@ public:
const std::vector<AST_stmt*> body;
const std::string getName();
std::string mangleName(const std::string& id);
SourceInfo(BoxedModule* m, ScopingAnalysis* scoping, AST* ast, const std::vector<AST_stmt*>& body);
};
......
......@@ -202,10 +202,7 @@ int main(int argc, char** argv) {
if (m->body.size() > 0 && m->body[0]->type == AST_TYPE::Expr) {
AST_Expr* e = ast_cast<AST_Expr>(m->body[0]);
AST_Call* c = new AST_Call();
AST_Name* r = new AST_Name();
r->id = "repr";
r->ctx_type = AST_TYPE::Load;
r->lineno = 0;
AST_Name* r = new AST_Name("repr", AST_TYPE::Load, 0);
c->func = r;
c->starargs = NULL;
c->kwargs = NULL;
......
......@@ -2278,7 +2278,7 @@ static std::string getFunctionName(CLFunction* f) {
static void placeKeyword(const std::vector<AST_expr*>& arg_names, std::vector<bool>& params_filled,
const std::string& kw_name, Box* kw_val, Box*& oarg1, Box*& oarg2, Box*& oarg3, Box** oargs,
BoxedDict* okwargs) {
BoxedDict* okwargs, CLFunction* cl) {
assert(kw_val);
bool found = false;
......@@ -2290,8 +2290,8 @@ static void placeKeyword(const std::vector<AST_expr*>& arg_names, std::vector<bo
AST_Name* n = ast_cast<AST_Name>(e);
if (n->id == kw_name) {
if (params_filled[j]) {
raiseExcHelper(TypeError, "<function>() got multiple values for keyword argument '%s'",
kw_name.c_str());
raiseExcHelper(TypeError, "%.200s() got multiple values for keyword argument '%s'",
getFunctionName(cl).c_str(), kw_name.c_str());
}
getArg(j, oarg1, oarg2, oarg3, oargs) = kw_val;
......@@ -2306,12 +2306,13 @@ static void placeKeyword(const std::vector<AST_expr*>& arg_names, std::vector<bo
if (okwargs) {
Box*& v = okwargs->d[boxString(kw_name)];
if (v) {
raiseExcHelper(TypeError, "<function>() got multiple values for keyword argument '%s'",
kw_name.c_str());
raiseExcHelper(TypeError, "%.200s() got multiple values for keyword argument '%s'",
getFunctionName(cl).c_str(), kw_name.c_str());
}
v = kw_val;
} else {
raiseExcHelper(TypeError, "<function>() got an unexpected keyword argument '%s'", kw_name.c_str());
raiseExcHelper(TypeError, "%.200s() got an unexpected keyword argument '%s'", getFunctionName(cl).c_str(),
kw_name.c_str());
}
}
}
......@@ -2497,7 +2498,7 @@ Box* callFunc(BoxedFunction* func, CallRewriteArgs* rewrite_args, ArgPassSpec ar
assert(arg_names);
placeKeyword(*arg_names, params_filled, *(*keyword_names)[i], kw_val, oarg1, oarg2, oarg3, oargs, okwargs);
placeKeyword(*arg_names, params_filled, *(*keyword_names)[i], kw_val, oarg1, oarg2, oarg3, oargs, okwargs, f);
}
if (argspec.has_kwargs) {
......@@ -2515,7 +2516,7 @@ Box* callFunc(BoxedFunction* func, CallRewriteArgs* rewrite_args, ArgPassSpec ar
BoxedString* s = static_cast<BoxedString*>(p.first);
if (arg_names) {
placeKeyword(*arg_names, params_filled, s->s, p.second, oarg1, oarg2, oarg3, oargs, okwargs);
placeKeyword(*arg_names, params_filled, s->s, p.second, oarg1, oarg2, oarg3, oargs, okwargs, f);
} else {
assert(okwargs);
......
# Simple test:
class MyClass(object):
__a = 1
print sorted(locals().items()) # This should contain the mangled name
print hasattr(MyClass, "__a")
print hasattr(MyClass, "_MyClass__a")
# Names in functions get mangled:
class MyClass(object):
def __init__(self):
# attributes get mangled:
self.__x = 1
def f(self):
print __g
__local = 1
print sorted(locals().keys()) # This should contain the mangled name
print __k
del __k # this should delete the mangled name
print sorted(locals().keys())
print self.__x
__g = 5
_MyClass__g = 6
try:
MyClass().f()
except NameError, e:
print e
print MyClass()._MyClass__x
# This includes function arguments!
class MyClass(object):
def f(self, __x):
print __x
def g(self, *__args, **__kw):
print __args, __kw
MyClass().f(5)
try:
MyClass().f(__x=5)
except TypeError, e:
print e
MyClass().f(_MyClass__x=6)
MyClass().g(5, a=5)
# Function names get mangled
class MyClass(object):
def __f(self):
pass
# But they still keep their old name for display:
print MyClass._MyClass__f.im_func.__name__
# And classdefs get mangled too I guess:
class MyClass(object):
class __InnerClass(object):
pass
# And imports do too??
class MyClass(object):
try:
import __foo
except ImportError, e:
print e
class MyClass(object):
try:
import __foo as __bar
except ImportError, e:
print e
class MyClass(object):
import sys as __bar
print __bar
class MyClass(object):
try:
# Except if it's a dotted name:
import __foo.__bar
except ImportError, e:
# CPython says "no module named __foo.__bar__" but Pyston (and PyPy) say "no module named __foo".
# Canonicalize it slightly:
print e.message.split('.')[0]
# names inside classes with mangled names don't get the mangled class name:
class MyClass(object):
class __InnerClass(object):
__inner_inner = "hi"
print sorted(locals().items())
print MyClass._MyClass__InnerClass._InnerClass__inner_inner
# This class gets accessed through the mangled name, but its stored name is still the original name.
print MyClass._MyClass__InnerClass
class MyClass(object):
def f(self):
self.__x = 1
def inner():
# Names inner functions also get mangled:
return self.__x
return inner
print MyClass().f()()
# Eval not supported:
"""
class MyClass(object):
def f(self):
# Sanity check:
y = 2
print eval("y")
__x = 1
try:
# Names do not get mangled inside of an eval:
print eval("__x")
except NameError, e:
print e
MyClass().f()
"""
# Things get mangled in different types of sub-scopes: lambdas, generator expressions, generators:
class MyClass(object):
def f(self):
__x = 2
def g():
yield __x
print list(g())
print list((__k * __x for __k in xrange(5)))
print map(lambda x: x ** __x, range(5))
print [x - __x for x in xrange(4)]
MyClass().f()
_MyClass__x = 0
class MyClass(object):
def f(self):
global __x
__x = 1
MyClass().f()
print "_MyClass__x:", _MyClass__x
# Random tests from looking at the behavior of _Py_Mangle:
# Leading underscores on the class name get stripped:
class _MyClass(object):
__a = 3
print _MyClass._MyClass__a
# But if the class is all underscores, it doesn't mangle anything
# (weird corner case but makes sense, since that new name would be hard
# to reference)
class ___(object):
__a = 2
print ___.__a
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