Path: csiph.com!usenet.pasdenom.info!news.albasani.net!news.stack.nl!newsfeed.xs4all.nl!newsfeed3.news.xs4all.nl!xs4all!newsgate.cistron.nl!newsgate.news.xs4all.nl!post.news.xs4all.nl!not-for-mail Return-Path: X-Original-To: python-list@python.org Delivered-To: python-list@mail.python.org X-Spam-Status: OK 0.001 X-Spam-Evidence: '*H*': 1.00; '*S*': 0.00; '16,': 0.03; 'encoding': 0.05; 'assignment': 0.07; 'class,': 0.07; 'variables': 0.07; 'function,': 0.09; 'literal': 0.09; 'subject:string': 0.09; 'cc:addr:python-list': 0.11; 'def': 0.12; 'jan': 0.12; '"about': 0.16; '"some': 0.16; "'break'": 0.16; 'assignment.': 0.16; 'both,': 0.16; 'charset': 0.16; 'from:addr:rosuav': 0.16; 'from:name:chris angelico': 0.16; 'funcname': 0.16; 'funcname)': 0.16; 'hex': 0.16; 'lower-case': 0.16; 'problem!': 0.16; 'roy': 0.16; 'shortcut': 0.16; 'str)': 0.16; 'subject:possible': 0.16; 'wrote:': 0.18; 'variable': 0.18; 'skip:g 40': 0.19; 'thu,': 0.19; 'code,': 0.22; 'cc:addr:python.org': 0.22; 'skip': 0.24; 'environment': 0.24; 'cc:2**0': 0.24; 'long,': 0.26; 'pass': 0.26; 'somewhere': 0.26; 'header:In-Reply-To:1': 0.27; 'function': 0.29; 'characters': 0.30; 'matching': 0.30; 'message- id:@mail.gmail.com': 0.30; 'constant': 0.31; 'extract': 0.31; 'slot': 0.31; 'class': 0.32; 'critical': 0.32; 'themselves': 0.32; 'another': 0.32; 'running': 0.33; 'third': 0.33; 'subject:from': 0.34; 'could': 0.34; 'skip:u 20': 0.35; 'test': 0.35; 'but': 0.35; 'received:google.com': 0.35; 'add': 0.35; '2500': 0.36; 'subject:?': 0.36; 'should': 0.36; 'so,': 0.37; 'depends': 0.38; 'window': 0.38; 'whatever': 0.38; 'pm,': 0.38; 'little': 0.38; 'anything': 0.39; 'sure': 0.39; 'according': 0.40; 'skip:u 10': 0.60; 'full': 0.61; 'matter': 0.61; 'first': 0.61; 'guarantee': 0.63; 'name': 0.63; 'such': 0.63; 'smith': 0.68; 'ending': 0.78; 'subject:get': 0.81; 'const': 0.84; 'different.': 0.84; 'distinguish': 0.84; 'ordered.': 0.84; 'to:none': 0.92; 'have.': 0.93 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=mime-version:in-reply-to:references:date:message-id:subject:from:cc :content-type; bh=kOha+G6gHvba3qDSJvVDPrNYg5g8DQCx+3NzmIqLS6M=; b=r0RI3sIEpZZ4WaL4n6/up9WxVg0iMEwLpxVyt80uWPt+AkTR9emPyiL0ccWRjWgLNO LSsZxwmTwlx4lsQbbDQQ3AMIEUwG8P34CPrqtv78q+/xYK7DIRB5mycrqzEtmUvelJfX JzXqCgnD5NcdCozsa2qPHWIYInQ3ukL7/Kbe6lkOxf1kyAXqP7lMhOB3xjS4++zDWtr5 2KYTAKkbOT4rsR5YSst9RpsBwtKwmIutanMuZp3l8jju/UqzgXKzvgidl0monzpWPkaO jnoZ9ImSwPt3LaaAhSMT5DslT9r8vcyXTpNqWc4kOeE61CUp049Qg9l/FujTjPniLuzK /8Kg== MIME-Version: 1.0 X-Received: by 10.66.26.176 with SMTP id m16mr7639728pag.142.1389849918909; Wed, 15 Jan 2014 21:25:18 -0800 (PST) In-Reply-To: References: Date: Thu, 16 Jan 2014 16:25:18 +1100 Subject: Re: Is it possible to get string from function? From: Chris Angelico Cc: "python-list@python.org" Content-Type: text/plain; charset=UTF-8 X-BeenThere: python-list@python.org X-Mailman-Version: 2.1.15 Precedence: list List-Id: General discussion list for the Python programming language List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Newsgroups: comp.lang.python Message-ID: Lines: 70 NNTP-Posting-Host: 2001:888:2000:d::a6 X-Trace: 1389849928 news.xs4all.nl 2906 [2001:888:2000:d::a6]:34229 X-Complaints-To: abuse@xs4all.nl Xref: csiph.com comp.lang.python:64055 On Thu, Jan 16, 2014 at 2:46 PM, Roy Smith 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