"""Sketch of type-checking machinery. Note that I'm not particularly keen on type checking, I just want to show that it *can* be done within this framework, if only as yet another example of how much freedom we get this way. When we know what we want, we can build tools with which to do it. For the present sketch, I've taken the simple-minded approach of borrowing checkers off one's bases and taking whichever comes up in the usual way, without pretending this is necessarily what we want. It just makes the illustration plainer. I've taken the boolean test applied by checkers as answering `what is wrong with this value ?' (None means value is OK), rather than `is this value OK ?' (None means no), so that complaints can contain information. Checkers are also called on to cope with `what is wrong with unsetting this ?' when called with no argument. The checker lambda x=None: 'No' would suffice to forbid any modification of its attribute. Naturally, the right place to store type-checking information on an object is in a sub-namespace; take its name to be .__check__ for the present. Each object's .__check__ can then borrow from the corresponding sub-namespaces (where present) of the object's bases, to ensure borrowing of type constraints; in principal, the builder used for the .__check__ object could even give bases precedence over the borrower, or namespace lookup on the sub-object (which should only ever be returning [ value -> string-or-false ] functions) can gather up *all* matches its lookup-chain provides for a given name and return a function which combines their answers (using `or', with the `false-is-happy' protocol). $Id: check.py,v 1.4 2002/01/30 17:52:21 eddy Exp $ """ from python import class, builder, instance # we need a builder for classes whose instances' attributes are to be checked # in accordance with checkers stored, during the class' suite, on a .__check__ # sub-object. builder checker (class,): instance __magic__ (__magic__,): # stub to ensure __check__ sub-object propagates ... def check(src, wrap, lookups): """Type-checker scheme (does nothing). This scheme modifies the behaviour of setattr and delattr; it does not contribute a lookup to the object, though it does contribute a sub-namespace, __check__, to classes using it. Methods defined in this namespace will be called with no arguments when the thus-named attribute of an instance is deleted, with one argument (the new value) when the attribute is set. Such methods should return a false value if the operation can go ahead; otherwise, they should return a string explaining why not. """ pass def __setattr__(self, key, value, *, **, up=__setattr__): try: check = getattr(self.__class__.__check__, key) except AttributeError: pass else: grief = check(value) if grief: raise TypeError, ('Setting attribute to wrong type', key, grief) up(self, key, value) def __delattr__(self, key, *, **, up=__delattr__): try: check = getattr(self.__class__.__check__, key) except AttributeError: pass else: grief = check() # may type-error in its own right ... if grief: raise TypeError, ('Forbidden to delete attribute', key, grief) up(self, key) __check__ = None checker Checker: """Base-class for type-checked attributes.""" instance __check__ (__check__): # this is where you'd define checker functions; e.g. def __doc__(*args, **, string=type('string')): # This caricatures how to do it, though __doc__ is magic enough that # this probably doesn't get asked. if not args: return 'Please do not delete my doc-string' if type(args[0]) != string: return 'doc-string should only be set to a string' _rcs_log = """ $Log: check.py,v $ Revision 1.4 2002/01/30 17:52:21 eddy Major revolution. Inlined post-within precursors in instance and simplified the resulting mess; added __wrap__ to carry wrapper methods from class; shunted class-specific magic (redirected attribute modification) into class, unburdening (gennie renamed as) builder. Moved all surviving precursors of instance from python.py to bonnet.py, emptying old junk from bonnet.py into novelty.py. Made the function defining a scheme receive the lookups list as third parameter; removed access to this list from instance wrappers; made wrapper's __dict__-based attribute modifier fallbacks come early, like __wrap__-derived kit, rather than late. Changed __meth__ to __method__, `generate' to `build', __schemes__ to __magic__. Re-documented plenty of stuff, revised web pages. Revision 1.3 2002/01/23 04:02:34 eddy made __dir__ attribute universal; renamed initspace to extend; cleaned up scheme inheritance Revision 1.2 2002/01/22 18:58:13 eddy changed generators round to use __schemes__, with meth as just one such; added lazy.py Revision 1.1 2001/10/17 18:00:19 eddy added in all files previously missing from RCS Revision 1.4 2000/01/16 23:19:33 eddy Exploit (and mirror) new configurability of python's gennie and initspace. Revision 1.3 2000/01/15 18:07:59 eddy Converted to work with new python.py Revision 1.2 2000/01/02 18:23:34 eddy Tidy-up. Initial Revision 1.1 2000/01/02 17:36:09 eddy """