Is it possible to get string from function?

Chris Angelico rosuav at gmail.com
Thu Jan 16 00:25:18 EST 2014


On Thu, Jan 16, 2014 at 2:46 PM, Roy Smith <roy at panix.com> wrote:
> So, I figured I would write a meta-test, which used introspection to
> find all the methods in the class, extract the strings from them (they
> are all assigned to a variable named RECEIPT), and check to make sure
> they're all different.

In theory, it should be. You can disassemble the function and find the
assignment. Check out Lib/dis.py - or just call it and process its
output. Names of local variables are found in
test_t1.__code__.co_names, the constants themselves are in
test_1.__code__.co_consts, and then it's just a matter of matching up
which constant got assigned to the slot represented by the name
RECEIPT.

But you might be able to shortcut it enormously. You say the strings
are "about 2500 characters long, hex-encoded". What are the chances of
having another constant, somewhere in the test function, that also
happens to be roughly that long and hex-encoded? If the answer is
"practically zero", then skip the code, skip co_names, and just look
through co_consts.



class TestCase:
  pass # not running this in the full environment

class Foo(TestCase):
  def test_t1(self):
    RECEIPT = "some string"

  def test_t2(self):
    RECEIPT = "some other string"

  def test_t3(self):
    RECEIPT = "yet a third string"

  def test_oops(self):
    RECEIPT = "some other string"

unique = {}
for funcname in dir(Foo):
    if funcname.startswith("test_"):
        for const in getattr(Foo,funcname).__code__.co_consts:
            if isinstance(const, str) and const.endswith("string"):
                if const in unique:
                    print("Collision!", unique[const], "and", funcname)
                unique[const] = funcname



This depends on your RECEIPT strings ending with the word "string" -
change the .endswith() check to be whatever it takes to distinguish
your critical constants from everything else you might have. Maybe:

CHARSET = set("0123456789ABCDEF") # or use lower-case letters, or
both, according to your hex encoding

if isinstance(const, str) and len(const)>2048 and set(const)<=CHARSET:

Anything over 2KB with no characters outside of that set is highly
likely to be what you want. Of course, this whole theory goes out the
window if your test functions can reference another test's RECEIPT;
though if you can guarantee that this is the *first* such literal (if
RECEIPT="..." is the first thing the function does), then you could
just add a 'break' after the unique[const]=funcname assignment and
it'll check only the first - co_consts is ordered.

An interesting little problem!

ChrisA



More information about the Python-list mailing list