Bengt Richter wrote: > On Wed, 05 Oct 2005 11:10:58 GMT, Ron Adam <[EMAIL PROTECTED]> wrote:
>>Looking at it from a different direction, how about adding a keyword to >>say, "from this point on, in this local name space, disallow new >>names". Then you can do... >> >>def few(x,y): >> a = 'a' >> b = 'b' >> i = j = k = l = None >> no_new_names >> # raise an error after here if a new name is used. >> ... >> for I in range(10): <-- error >> ... >> >>This is more suitable to Pythons style than declaring types or variables >>I think. Add to this explicit name-object locking to implement >>constants and I think you would have most of the features you want. >> > > You can do that now with a decorator, if you are willing to assign something > to no_new_names (so it won't give you a name error if it doesn't exist). E.g., Works for me. __lock_names__ = True It's not too different than __name__ == '__main__'... > >>> def nnn(f): > ... names = f.func_code.co_names > ... assert 'no_new_names' not in names or names[-1]=='no_new_names', > 'Bad name:%r'%names[-1] > ... return f > ... > >>> @nnn > ... def few(x,y): > ... a = 'a' > ... b = 'b' > ... i = j = k = l = None > ... no_new_names=None > ... for i in range(10): print i, > ... > Traceback (most recent call last): > File "<stdin>", line 1, in ? > File "<stdin>", line 3, in nnn > AssertionError: Bad name:'range' Hmm... To make it work this way, the globals and arguments need to have local references. @nnn def few(x,y): global range range = range x,y = x,y a = 'a' b = 'b' i = j = k = l = None L = 1 __no_new_names__ = True L += 1 for i in range(x,y): print I > >>> @nnn > ... def few(x,y): > ... a = 'a' > ... b = 'b' > ... i = j = k = l = None > ... no_new_names=None > ... return a,b,i,j,k,l > ... > >>> few(123,456) > ('a', 'b', None, None, None, None) > > No guarantees, since this depends on the unguaranteed order of > f.func_code.co_names ;-) I had the thought that collecting the names from the 'STORE FAST' lines of dis.dis(f) would work nicely, but... dis.dis() doesn't return a string like I expected, but prints the output as it goes. This seems like it would be easy to fix and it would make the dis module more useful. I'd like to be able to do... D = dis.dis(f) An alternate option to output the disassembly to a list of of tuples. That would make analyzing the output really easy. ;-) Something like... good_names = [] nnnames = False for line in dis.dislist(f): if line[2] = 'SAVE_FAST': if not nnnames: if line[-1] = '(__no_new_names__)': nnnames=True continue good_names.append(line[-1]) else: assert line[-1]in good_names, 'Bad name:%r'% line[-1] So, I wonder what kind of errors can be found by analyzing the disassembly? >>so... >> >> no_new_names # limit any new names >> lock_name name # lock a name to it's current object > > That last one you could probably do with a decorator that imports dis and > checks the disassembly (or does the equivalent check of the byte code) of f > for STORE_FASTs directed to particular names after the lock_name name > declaration, > which you would have to spell as a legal dummy statement like > lock_name = 'name' > > or perhaps better, indicating a locked assignment e.g. to x by > > x = lock_name = expr # lock_name is dummy target to notice in > disassembly, to lock x from there on Using dis.dis it becomes two sequential 'STORE_FAST' operations. So add (x) to the don't change list, and catch it on the next 'STORE_FAST' for (x). ;-) 28 12 LOAD_GLOBAL 2 (True) 15 DUP_TOP 16 STORE_FAST 0 (x) 19 STORE_FAST 8 (__lock_name__) >>Since names are stored in dictionaries, a dictionary attribute to >>disallow/allow new keys, and a way to set individual elements in a >>dictionary to read only would be needed. Once you can do that and it >>proves useful, then maybe you can propose it as a language feature. > > I would want to explore how to compose functionality with existing elements > before introducing either new elements or new syntax. E.g., the dictionaries > used for instance attribute names and values already exist, and you can > already > build all kinds of restrictions on the use of attribute names via properties > and descriptors of other kinds and via __getattribute__ etc. That was more or less what I had in mind, but I think keeping things as passive as possible is what is needed. One thought is to use this type of thing along with __debug__. if __debug__: __nnn__ = True Wouldn't a debug block or suite be better than an if __debug__:? Just a thought. Even if the -0 option is given the if __debug__: check is still there. Which means you still need to comment it out if it's in an inner loop. debug: __nnn__ = True # Is not included if __debug__ is false. or... MAX = 256 MIN = 0 debug: __lock__ = MIN, MAX # helps checker app __no_new_names__ = True # find bugs. for MAX in range(1000): # If __debug__, checker app catches this. if m<MIN or m>MAX: print m >>These might also be checked for in the compile stage and would probably >>be better as it wouldn't cause any slow down in the code or need a new >>dictionary type. > > Although note that the nnn decorator above does its checking at run time, > when the decorator is executed just after the _def_ is anonymously _executed_ > to create the function nnn gets handed to check or modify before what it > returns is bound to the def function name. ;-) Yes. ;-) Is there a way to conditionally decorate? For example if __debug__ is True, but not if it's False? I think I've asked this question before. (?) Cheers, Ron -- http://mail.python.org/mailman/listinfo/python-list