This question might seem silly. It's a simple useless task, but I believe it's a good way to learn things (I did learn a lot while doing this kind of tricks).
So, idea is in the title: No 'e' allowed => no eval() or exec() No 'p' allowed => no print() or import
What I tried is to get the print function from:
dir(__builtins__)[133]
but it's returned as a string, I found no way to get it as a callable function.
Since the order of the builtins dict is random, this is not a very useful thing to do, unless you're just as happy calling staticmethod
as print
.
But it may be worth knowing how to get from there to the next step you were trying to get to.
Modules are just objects, like any other objects. So, you can get their attributes with getattr
. OK, so that's against your rules. But most objects—including modules—store their attributes in a dict. So:
>>> dir(__builtins__)[133]
'staticmethod'
>>> __builtins__.__dict__[_]
<type 'staticmethod'>
Is there any way we could actually get print
? Well, maybe not guaranteed, but pretty close:
>>> [v for k, v in __builtins__.__dict__.items() if 'rint' in k]
[('print', <function print>)]
Oops, I used an e
for that items
, right? Fine:
>>> [__builtins__.__dict__[k] for k in __builtins__.__dict__ if 'rint' in k]
[<function print>)]
>>> [__builtins__.__dict__[k] for k in __builtins__.__dict__ if 'rint' in k][0]('zzz')
zzz
But meanwhile, how were you planning to build the string 'Hello World!'
without an e
? There are lots of options—\x
or \u
escapes, or something really silly like rot13
. And all of those options would work to get you the string 'print'
just as easily. So, I'm not sure why you're trying to pull print
out of the module's names in the first place.
>>> __builtins__.__dict__['\x70rint']
<function print>
As John Anderson pointed out in a comment, using the inspect
module is nicer than going to the __dict__
. For one thing, it works for objects that store attributes elsewhere—or even objects that generate them on the fly with a custom __dir__
and __getattr__
.
>>> [v for k, v in inspect.getmembers(__builtins__) if k == 'print'][0]
<function print>
The problem is, how do we get the inspect
module? We can use the same backslash/etc. tricks to get the name 'inspect'
easily, but (unless we can assume it was already imported into sys.modules
—and that sys
is also already imported), we need to import
or __import__
or use importlib
or… they've all got a p
in them. And then, to get at getmembers
, we need to go through the inspect
module's dict to look it up by name.
But still, maybe it's better to do that once than every time:
>>> i = __builtins__.__dict__['__im\x70ort__']
>>> ins = i('ins\x70\x65ct')
>>> gm = ins.__dict__['g\x65tm\x65mb\x65rs']
>>> builty = lambda nam: dict(gm(__builtins__))[nam]
>>> builty('\x70rint')
<function print>
>>> builty('\x65val')
<function eval>
Of course using lambda
to create a named function in a statement rather than an anonymous one in an expression is an anti-pattern, but then this whole post is a pile of anti-patterns. I'm doing it here to avoid the need for def
and return
, both of which have e
s (as pointed out by Elliot Frisch). (It's hard to notice all the e
s you type; even Gadsby had three accidental e's in it, and it's famous for being the novel with no e's…)
I'm sure someone is hoping that I'm going to show how to get around this by just using a compiled bytecode literal, right?
Unfortunately, there's only two ways to run a code object: either you wrap it up in a function object—which requires getting FunctionType
, which requires either an import
or a call to type
—or you exec
or eval
it—which are already out.
Plus, in Python 3, all you'd be doing is cramming the constants "print"
and "Hello, World"
into co_names
and co_consts
, and then it's just a LOAD_NAME
, LOAD_CONST
, CALL_FUNCTION
, RETURN_VALUE
(which, coincidentally, happens to start with e
: b'e\x00d\x00\x83\x01S\x00'
).
In Python 2, on the other hand, print
is a statement, and has a special PRINT_ITEM
bytecode, so it's maybe a bit more fun:
>>> ty = builty('typ\x65')
>>> gattr = builty('g\x65tattr')
>>> f = lambda: None
>>> c = gattr(f, 'func_cod\x65')
>>> ct = ty(c)
>>> cc = ct(0, 0, 1, 0x43, 'd\x00\x00\x04GS', ('H\x65llo World!',),
... (), (), '', 'h\x65llo', 0, '')
>>> xval = builty('\x65val')
>>> xval(cc)
Hello World!
'Hello World!'
(The extra quoted output at the end is because I just returned "Hello World!"
because I was too lazy to dig up None
. The code is LOAD_CONST 0; DUP_TOP; PRINT_ITEM; RETURN_VALUE
.)