[Subversion] / PEAK / src / peak / binding / components.py |
No default branch
Bookmark a link to HEAD:
(view)
(download)
(as text)
Fix component acquisition defaults and doc/test lookupComponent()
Drop use of dispatch.as(), replace w/DecoratorTools' decorate()
PEAK no longer bundles any software that can be obtained automatically from PyPI. Running PEAK's setup script will attempt to download and install the needed packages. (Note that development snapshots of PEAK may require development snapshots of related packages.)
Make the 'binding.hasParentComponent()' criterion enumerable (see recent PyProtocols changes).
Tweak to work correctly w/latest PyProtocols.
Update to match new dispatch_function signature used by PyProtocols.
Fix a problem with the custom dispatch criterion for binding.hasParent(); it was causing the new view traversal generic function in peak.web to fail.
Added a 'binding.hasParent(component,parent)' API function, which is specially optimized for use with generic functions, so that you can define generic function methods that apply only within some part of a component hierarchy. Also, added a 'components.txt' doctest to the binding package, that illustrates not only the new 'hasParent()' function, but also some other hierarchy-related APIs as well.
Added a 'binding.initAttrs()' function that can be used to initialize an object's attributes from e.g. constructor keyword arguments, similar to how the 'binding.Component' and 'binding.Attribute' constructors work. (Also, refactored them to use this new function instead of harcoding their own versions.)
DEPRECATED 'permissionNeeded' attribute of descriptors, and removed 'security.IGuardedDescriptor'. Please see CHANGES.txt for more details, and information on upgrading. Now 'peak.security' is decoupled from the PEAK core frameworks, or more precisely, there are no "forward references" from the PEAK core to 'peak.security', except for the temporary vestigial implementations of 'permissionNeeded' that will be removed in the next release.
Added a new "attribute metadata" mini-framework to 'peak.binding'. This framework makes it possible to declare arbitrary metadata about attributes, using either a class advisor ('binding.metadata()', similar in form and function to the existing 'security.allow()') or using a 'metadata' attribute of attribute bindings (which is the second positional parameter in all the standard bindings like 'Make', 'Obtain', etc.). Over time, existing metadata mechanisms will be refactored to use this new mini-framework, instead of the various integrated ad-hoc mechanisms that exist now (like the 'permissionNeeded' attribute). For more information on how the new metadata hooks work, including doctest examples, see the 'attributes.txt' file in the 'peak.binding' package, under the heading "Attribute Metadata".
Added a new function, 'binding.activateClass()', that can be used to activate any bindings in the class. This can now be used in place of subclassing a PEAK base class or using a PEAK metaclass. In future, this will be integrated into PEAK attribute descriptors such that defining a descriptor within a class' body is sufficient to cause this function to be invoked. This is the first part of a refactoring to implement the attribute metadata framework for PEAK.
'binding.IBindingNode' was REMOVED, consolidated into 'binding.IComponent', as its various individual methods have been replaced with generic functions in the existing 'binding' API. For example, 'binding.getParentComponent(x)' should be used in preference to 'x.getParentComponent()' unless it is a requirement that 'x' implement the full 'binding.IComponent' interface. This makes it easier to define what 'binding.getParentComponent()' and 'binding.getComponentName()' will mean for non-component types, as you do not have to define an adapter class with all of the 'IBindingNode' methods. Also, this makes PEAK itself cleaner, as we often weren't bothering to properly implement the full 'IBindingNode' interface anyway. In addition, 'binding.suggestParentComponent()' is now also a generic function, dispatching on the target (i.e. child) object.
Recreate checkins lost when hard drive died (1 of 4); see: http://www.eby-sarna.com/pipermail/source-changes/2004q3/001807.html for original log info.
Change all adapter factories to use only a single argument.
Fix editing error from previous check-in, that caused a minor inefficiency due to redundant code in 'binding.getComponentPath()'.
The 'config.iterParents' API has moved to 'binding.iterParents', and all 'binding' functions that walk the component hierarchy use it. It has also been changed to avoid infinite loops in the case of a pathological component structure.
Remove items that were deprecated during alpha 3 development. If you've got code that breaks with this version, check HISTORY.txt for replacements, or ask on the mailing list.
Fixed 'PluginKeys' not working with 'Obtain'.
Plugins have landed! * Added 'binding.PluginKeys' and 'binding.PluginsFor'. These are component keys that can be used to 'Obtain' plugins registered within a property namespace. 'PluginKeys' obtains a list of the plugins' configuration keys, while 'PluginsFor' obtains a list of the actual plugins. * Replaced 'peak.config.registries.EigenRegistry' with 'peak.config.registries.ImmutableConfig'. The only use we had for 'EigenRegistry' was to keep track of 'offerAs' settings within classes, and it didn't need all the extra complexity of eigenstate management. The new, more-specialized class is shorter, simpler, and easier to use.
MAJOR API REFACTORING for 'peak.config', to support iterating over configuration keys, 'n2' navigability of properties, and much more. Many APIs are now DEPRECATED or renamed; please see CHANGES.txt for a complete description. A few highlights: - 'config.lookup()' replaces 'findUtility' and 'getProperty' - 'config.iterValues()' replaces 'findUtilities' - 'config.iterKeys()' finds keys in a specific namespace - NEW: 'config.parentProviding()' and 'config.parentsProviding()' APIs to find parent components that support a protocol. - NEW: You can now use 'peak n2 config:' to explore the default property namespace. For example 'ls -l peak.naming.schemes' will list all defined naming schemes. Please see CHANGES.txt for the list of deprecated or removed APIs.
Misc. __repr__ fixes
Clean up __repr__'s for bindings, for convenient use of help() and other documentation tools.
Removed all uses of deprecated binding APIs.
First phase of the binding API simplification, including detailed docs for 'Descriptor' and 'Attribute'. (Second phase will be mass search-and-replace of the API's usage in PEAK, third will be additional tests and docs.) Added 'binding.Require', 'binding.Obtain', 'binding.Make', and 'binding.Delegate'. *ALL* other binding types are now DEPRECATED, and will go away before 0.5 beta is released. Please see CHANGES.txt for a detailed mapping of how to translate from the old to new API, and let me know if anything's missing. Note that the new 'Make' and 'Obtain' bindings also support sequences of recipes and keys, and in those cases will produce a sequence of the results from those recipes or keys. Also, 'Make' will accept no-argument and one-argument callables, where 'Once' always required three-argument functions. This should make it a lot easier to write short binding functions. Also, note that the 'activateUponAssembly' keyword is now 'uponAssembly', and 'isVolatile' is now 'noCache'. (The old names will work as keyword arguments until the alpha 4 development cycle begins.) The 'binding.IActiveDescriptor' interface also changed as a result of this. Last, but not least, a 'binding.IRecipe' interface was added, to support the new 'binding.Make' type.
A list or tuple of 'IComponentKey' instances is now treated as a single component key, that returns a tuple of the values returned by each constituent component key. This means that 'binding.bindTo()' and 'lookupComponent()' can now accept a list or tuple of component keys. This makes 'bindSequence()' obsolete, so 'bindSequence()' is now DEPRECATED. 'binding.bindSequence(key1,key2,...)' can now be replaced with 'binding.bindTo([key1,key,...])', and will produce the same results.
'naming.IBasicContext.lookup()' and 'naming.lookup()' now accept a 'default' argument, similar to that used by 'lookupComponent()' and most other lookup-like APIs in PEAK. This change was made so that component lookups don't need to rely on catching 'exceptions.NameNotFound' errors to tell them when to use the default value. This could hide 'NameNotFound' errors that were actually from a broken component somewhere in the lookup process. (In general, it's probably a bad idea to have an exception that's used for both control flow and real errors!) Also, removed most trapping of 'NameNotFound' errors throughout PEAK, except for those in N2, which I'll leave for Ty. :) Last, but not least, cleaned up documentation of related interfaces/methods.
Misc. peak.binding cleanups and DEPRECATIONS, as follows: - The following 'binding' forms are now deprecated, and will go away before 0.5 beta is released: 'bindToProperty(x,y)' -- use 'bindTo(PropertyName(x),default=y)' 'bindToParent()' -- use 'bindTo("..")' 'bindToSelf()' -- use 'bindTo(".")' 'bindToUtilities()' -- no replacement; let me know if you're using this. - 'naming.IName' is now derived from 'binding.IComponentKey', so names and addresses must now support the 'findComponent()' method. All PEAK name and address types provide support for this. - The 'lookup()' method of 'binding.IComponentKey' is now called 'findComponent()', to better distinguish it from 'lookup()' in 'naming.IBasicContext', which does something very different. - 'binding.bindTo()' and 'binding.bindSequence()' now pre-adapt their arguments to 'IComponentKey', to speed lookups at runtime, and to ensure that errors due to an unusable parameter type occur at class creation time instead of waiting until lookup time.
It's now possible to declare an attribute as offering a wildcard property; such lookups now follow the same rules as other wildcard property lookups. The 'config.IConfigKey' interface has been changed to cleanly support implied keys at both registration and lookup time, so you can implement your own key types that work the way interfaces or property names do for configuration lookups. I also moved the 'EigenRegistry' class from 'peak.util.EigenData' to 'peak.config.registries', as it hasn't really been useful outside PEAK for a while now.
'peak.naming' no longer automatically converts all addresses to the addressed objects. You must specifically request the interface you want by adapting the retrieved object to that interface. This can be done by supplying an 'adaptTo=ISomething' keyword argument to the attribute binding definition or your 'lookupComponent()' call. The naming system no longer has 'objectFactories' and 'stateFactories'; these have been replaced with adaptation. Writable naming contexts must have a 'serializationProtocol' attribute specifying what interface an object should be adapted to before attempting to store it in that context. The naming system no longer processes the 'creationName' keyword argument; this is now considered the sole responsibility of 'peak.binding'. The 'IComponent.lookupComponent()' method still accepts the keyword argument, and attribute bindings still handle the creation name transparently. It is just not available via naming system APIs, and naming contexts no longer have to deal with it. Also, removed 'naming.ParsedURL'; it was deprecated as of 0.5 alpha 2. (Note: 'smtp:' URLs are currently broken, in that we don't have an interface to adapt them to for retrieval, and I didn't want to create an SMTP naming context, since what we want to do with SMTP is still up in the air.)
All 'peak.binding' APIs now only accept positional parameters for items unique to that API. Items common to multiple APIs (such as 'offerAs', 'doc', 'attrName', etc.) should now be supplied as keyword arguments. Bindings also now automatically "suggest" the containing object as a parent component for the contained object, whenever a value is assigned to them or computed. If a non-None 'adaptTo' is set on the binding, the value assigned or computed will be adapted to the specified protocol before the parent component is suggested. 'binding.New()' no longer relies on the 'IComponentFactory' interface, but instead uses the new adapt/suggest mechanisms. Previously, parent components were only "suggested" when a binding was set via component constructor keyword arguments. Now, this is done at any time bindings are set, but *not* for non-binding keyword arguments. In other words, ordinary attributes of a component do not receive "suggested parent" notices, even when set via constructor keyword arguments. If you want an attribute to do this, you must define the attribute with the binding API; e.g. via 'requireBinding()' or 'binding.Constant()'.
The 'provides' keyword argument to various 'peak.binding' APIs has been renamed to 'offerAs', and it must be a sequence of configuration keys. (Previously, it accepted either a single key or a tuple of keys.) The signature of 'binding.Constant()' was changed as well; the first positional argument is now the constant value, and 'offerAs' is now a keyword argument. (Previously, 'provides' was the first positional argument of 'binding.Constant()'.) The 'registerProvider()' method of 'config.IConfigurable()' also now accepts only a single configuration key, as does 'EigenRegistry.register()'.
First phase of descriptor refactoring. Once bindings now place a proxy for themselves in a class when activated in that class. This avoids the need for Once-derivatives to "copy" themselves when they are found under more than one name (e.g in the same class or when they are created without a name initially). Eventually this will let us do something similar with model.StructuralFeature objects. Added hooks to the Once class family to allow an 'onSet()' method to be called that can alter a value being setattr'd. This will let us do automatic 'adapt()' and 'setParentComponent()' when attributes are set or looked up. Also, simplified the structure of the Pyrex OnceDescriptor base, using Pyrex 0.7.2's ability to declare C-level attributes visible from Python. (Previously, we used a custom descriptor to deal with this.) NOTE: Error checking of bindings is partly BROKEN right now; bindings do not verify that they haven't been placed in a class that doesn't support them; they only verify that they have been told what name to use. This needs a little review, and the tutorial may need to be adjusted as well.
Removed special-case for ModuleType in getParentComponent/getComponentName; they now use 'adapt()'. This means that you should now be able to define 'IBindingNode' adapters for foreign classes and use them in a component tree.
Removed custom '__conform__' implementations, replace w/support from 'protocols' package. Fixed a 'supertype()' hole.
Added a ZConfig schema for 'running.commands.EventDriven' applications, a ZConfig component definition for adaptive tasks, and a running shortcut called 'EventDriven'. It should now be possible to do this:: #!/usr/bin/env peak EventDriven at the top of a ZConfig file formatted according to the new schema, and have it run. Unfortunately, so far the only type of task that can be included is 'running.daemons.AdaptiveTask', which doesn't actually *do* anything. So, right now doing this is equivalent to creating an over- engineered 'sleep' command. ;) Also, fixed a problem in ZConfig 'schema.dtd'; I used 'PCDATA' where I should've used 'CDATA', and I fixed various problems with the 'fromZConfig()' component constructor. It's kind of annoying that ZConfig defaults unspecified items to 'None', even if you said they weren't required and had no default. :( I'll see if I can do something about this later; for now I just delete 'None' values from the data.
Added ZConfig URL support. ZConfig schemas and files can be loaded from any PEAK-supported stream URL, including 'pkgfile:'. Interfile references aren't tested yet, nor are there any unit tests. Those will be added once we have some meaningful schemas and configurations to test with. There is a 'zconfig.schema' URL scheme that loads a command interpreter, and a 'peak ZConfig' option as well. See 'CHANGES.txt' for details on the new features. Also, fixed up some issues with the 'fromZConfig()' component constructor. All we need now are some useful base schemas for daemons, and the last piece for 0.5a2 (besides PyProtocols) will be in place.
Fixed bugs found by Alexander Smishlajev's "Echo" class. Specifically, I put back code to ensure that EigenRegistry and PropertyMap always use the "narrowest" interface possible, but it now uses a depth value to do so, which I find easier to understand than the old 'extends()' logic. It also corresponds to the same logic used in the protocols package. Anyway, the absence of this meant that ITwistedReactor was looked for even if IBasicReactor would suffice. I put back the tests that checked this sort of thing, at least for EigenRegistry. The second bug was that if a component with an "assembled" parent didn't have any children already waiting for an assembly event, it didn't request notification for events, and thus never notified any children that subscribed later. I fixed this by having components register with their parents when their children register with them, if it is the first such registration and the component would not have otherwise registered with its parent. Added a test case to cover this scenario.
Finished switchover to using protocols.advise() API to declare interfaces.
Moved to new protocols API. Also, made composed adapters check whether the base adapter returned 'None', so that an adapter can choose to "pass" on providing an implementation for the requested object and protocol.
And so we bid a fond farewell to introspection as well. 'isImplementedBy' is now a thing of the past. Refactored anything that used it, to now use adapt(), which has now opened up a lot of extension possibilities in places that were previously not extensible without modifying PEAK core code. See below. Also, the interface module has been refactored to support 'classProvides()' in a clean and kludge-free fashion, along with support for faster adaptation of old and new-style classes (by caching lookups, and by safely adding '__conform__' methods to classic classes), and the ability for classes to manage their own "implements" information. The core interface code is now quite elegant in the way it uses its own adaptation system as the basis for implementing adaptation. :) 'binding.Acquire()' now accepts a 'default' value argument, and 'binding.New()' no longer accepts the 'bindToOwner' flag. There is a new 'binding.IComponentKey' interface that is used to implement 'IComponent.lookupComponent()'. Now you can implement this interface, or create an adapter for it, in order to make an object usable as an argument to 'binding.lookupComponent()' - and therefore usable as a key for 'binding.bindTo()' or 'binding.bindToSequence()'. Not that it's necessarily very useful to do so; you're probably better off simply creating a naming scheme. But it might be useful for lookups done in the context of classes, since naming schemes aren't usable there. (It was actually added in order to factor out all the type testing that 'lookupComponent' used to do, so it doesn't matter if it's useful for much else.) PEAK has been refactored to avoid the use of 'isImplementedBy()' and similar introspection, in favor of 'adapt()'. As a result, some 'peak.naming' interfaces have changed. This should not affect you if you are only subclassing PEAK-provided naming components and not implementing these interfaces "from scratch". However, the various 'isAddress', 'isAddressClass', 'isResolver', and 'isName' APIs have also been removed, as they were based on 'isImplementedBy()'. The ability to use 'isImplementedBy()' with interfaces declared by PEAK is REMOVED. You can still use 'isImplementedBy()' with Zope interfaces, of course, but we recommend you switch to 'adapt()', which will work with both PEAK and Zope interfaces.
DEPRECATED use of '__implements__' and '__class_implements__' to declare support for interfaces. Use 'implements()', 'classProvides()', 'directlyProvides()', 'moduleProvides()', etc. to do this now; they are now available automatically from 'peak.api'. Please note that alpha 2 will *not* support the use of __implements__ attributes at all! Start using the APIs for this *now*, even if you're using alpha 1 (which at least supports the 'implements()' and 'classProvides()' functions).
'EigenRegistry' and 'PropertyMap' no longer attempt to figure out whether implied (i.e. inherited) interfaces are more or less general with respect to a previous registration. This was behavior that emulated Zope adapter registries, but what we really wanted was more akin to a Zope "type" registry. The only parts of the test suite that used the old behavior were the tests specifically written to ensure that behavior! Also, changed the implementation of '__class_provides__' so that the registry becomes immutable as soon as it is computed by the class.
'binding.bindTo()' now accepts a 'default=' argument, whose value will be used in case of a 'NameNotFound' error.
Fixed a problem where bindTo would use the attribute name as the default value for a lookup. (It'd be nice if we could turn this into the ability to specify a default value for the lookup.)
Removed unused imports, found by Martijn Faassen's clever 'importchecker' script. (http://www.zope.org/Members/faassen/importchecker)
Added 'binding.whenAssembled(...)' as syntax sugar for 'binding.Once(...,activateUponAssembly=True)'.
Finally got rid of 'x.__class__.foo(x,...)' hacks! Now, if a metaclass needs to have a method that one of its instances might also have as an instance method, you can wrap it with 'binding.metamethod(foo)'. This is nice and clean because it doesn't force every caller of the 'foo' method to try and guess whether it's working with a class or an instance. Even nicer, this solution requires no special work by the person writing these methods, unless the method is going into a metaclass, and even then they need only wrap it with 'metamethod'. Last, but not least, this cleans up some of our __class__ usage so that we'll be able to work correctly with 'zope.security'. As far as I know, the 'x.__class__.foo(x,..)' trick wouldn't have worked consistently when 'x' was a proxied object, whereas a normal call to 'foo' would properly pass through the proxy barrier as long as the caller had permission to access 'foo'. (Note: at the moment 'foo' translates to the 'getParentComponent', '_getConfigData', 'getComponentName', and 'notifyUponAssembly' methods.)
Fixed a problem where 'suggestParentComponent()' would attempt to consume all available input when the child component was a readable file. As a result, 'suggestParentComponent()' now only tries to suggest parents for the contents of lists and tuples. (We may want to add dictionaries when we go for ZConfig support in 0.5a2.)
Paramegeddon! Adjusted API signatures so that all calls that have a context component, have it as the first parameter. Changed functions, methods, and classes are: * binding.acquireComponent() * binding.lookupComponent() * config.getProperty() * config.findUtility() * config.findUtilities() * config.PropertyMap.getValueFor() * config.IConfigSource._getConfigData() * config.PropertySet() * naming.lookup() * naming.parseURL() Also, renamed 'config.LazyLoader' -> 'config.LazyRule' to reduce confusion with 'storage.LazyLoader', which has a very different purpose/function.
Stamp out '_provides', replace with 'declareAsProviderOf'. Removed extraneous '_provides' declarations left over from the days when classes could effectively be their own 'binding.New'. I decided not to document the interface of attribute binding objects at this time, because the implementation is tied to the classes right now anyway. Also, misc. TODO edits.
More "root" cleanups. 'config.makeRoot()' can now take an 'iniFiles' argument listing file paths (or '(modulename,filename)' tuples) to load; it defaults to '[('peak','peak.ini')]', providing the default behavior we know and love. :) If you create a binding.Component with a parent of 'None', it will now believe you and make itself a root. The default value of parentComponent is now 'NOT_GIVEN', so that if you don't specify a parent, the object will be "undecided" until it really needs to know who its parent is (at which point it will be 'None' if it hasn't been "adopted" in the meantime). Interface docs and tests have been updated to reflect/take advantage of this. (Specifically, the tests used to use 'getParentComponent()' to force a root to acknowledge its orphan status; now they can just *tell* it.) Finally, I removed the use of weak references from the assembly events system. If you created an object, intending that it register itself with some kind of registry, it could get GC'd before the assembly event kicked in, unless you specifically held onto a reference until then. Since components that don't need assembly events don't register for them, it seems harmless to use simple references rather than weak ones.
Simplified setParentComponent logic by dropping the EigenCell out of the picture. We needed such a specialized variation of EigenCell semantics, that it was cleaner not to reuse EigenCell to implement it. (It also saved us 16 bytes per component and cut out some method calls and memory allocation overhead, but that was a bonus, not a goal.)
"You are in a maze of twisty events, all different." Added more tests for "assembly events", and fixed some very twisty bugs. Setting parents, keyword arguments, suggesting parent components, requesting and initiating assembly notifications all must take place in a very specific order, to prevent objects seeing other objects in an unitialized state or otherwise creating circularity. Specifically, attributes must be set first, then children told about their parents, and finally the component can set its own parent. This means that keyword arguments should never have their 'uponAssembly()' event called before they are attached to their parent, and the parent is attached to its parent. Previously, this condition could be false. Also, components that only got suggested parents, weren't registering for assembly events. Anyway, I can't think of anything else to test for on this, so I guess assembly events are done for the time being.
Began work on unit tests for "assembly events". Fixed yet another recursion bug in the assembly events mechanism. I'll be glad when we have a comprehensive test suite for this. The mechanism is simple enough in principle, but implementing it in a way that doesn't infinitely recurse is rather difficult.
Replace uses of the deprecated 'binding.Base' with 'binding.Component'. Updated tutorial for this and other binding.interfaces name changes; note that the tutorial has *not* been fully reviewed for semantic issues yet.
Changed misc. components to use the assembly events framework. Fixed re-entrancy bugs in 'uponAssembly()' so that when activate attributes do hierarchy lookups, it doesn't try to recompute the attribute again. Changed 'config.Application()' to issue assembly events as soon as it is used for a configuration lookup. All this appears to work, but it definitely needs a test suite, and we *really* need tests for the event-driven stuff.
Added ability to designate standard bindings as 'activateUponAssembly'; you can now supply this as a keyword argument to just about everything but 'bindToParent()', 'bindToSelf()', 'requireBinding()' and 'delegateTo()'. (There's no point in activating them, that I can think of.) Removed checking for overridden 'uponAssembly()' method, since the standard way to do things upon assembly is to use a once binding with the new keyword argument.
Initial implementation of "assembly events" framework. Components now have 'uponAssembly()' method that gets called as soon as the component's hierarchy -- that is, all its parents up to the root -- are known. Some binding package interfaces were renamed or revised to accomodate this. This is still a work-in-progress: there are no tests for the new functions, there needs to be a way to designate bindings as "activate upon assembly", and this needs to be integrated with the systems that need it (e.g. periodic tasks, config.Application, etc.)
Refactoring prep for "assembly events". Deprecated 'binding.Base' in favor of 'binding.Component'; adjustments to binding interfaces, change 'model' bases to use a new "superlight" attribute handling base. Added support for 'lookupComponent()' to take a default value, so that it's now the most convenient way to look up *anything*, be it property, utility, URL, or component name. Dropped the pointless 'binding.globalLookup()' API, as it was just syntactic sugar for a 'naming.lookup()'. Made __class_provides__ a Once binding on the metaclass of 'binding.Component', instead of a hard-wired collaboration between Once and Activator, so that it doesn't get computed unless it's needed. Note that 'peak.model' objects of primitive or immutable types will now no longer participate in component hierarchies; this didn't make much sense for value types, anyway.
Added a 'fromZConfig()' class method to 'binding.Base', so all PEAK components are now potentially creatable from ZConfig section objects. Many component types will want to refine the method, however, to deal with the niceties of their individual ZConfig schemas.
"As We Planned-it of the APES, The Final Chapter". Removed AppConfig/SysConfig classes and APIs. Renamed 'instancePerApp()' to 'provideInstance()' (since it now just provides a single instance), and added 'instancePerComponent()' (which provides a fresh instance per target component). Some files have whitespace-only changes.
Plan-it of the APES, phase 1. Configuration APIs now require an explicit context argument. There is a 'config.IConfigurationRoot' interface, that is currently implemented by AppConfig and SystemConfig. There is also a 'NullConfigRoot' object that implements it by giving "invalid root" errors. Right now, however, you'll never see that message because 'iterParents' still goes to the special config objects once a root is reached. That'll be changed in the next phase.
Added ability for components to "suggest" themselves as a parent to their contents. This should make it much harder to accidentally create a "dangling root". One drawback: it's now not a good idea to use constructor keyword arguments to assign large iterables. E.g. executing something like: 'SomeClass(someAttr=range(100000))' will be very slow. (But it will issue a warning after the 100th non-component in the sequence.)
Fixed the mysterious "UML tests fail on Linux" bug. Apparently, under the old 'importString()' algorithm, specifying a trailing ':' works on Windows but not Linux. (Which is *really* weird, since in theory '__import__()' shouldn't behave differently. I think it might have something to do with an empty string being a valid directory name on Unix and not on Windows.) Anyway, I still need to track down and kill all the remaining ':' usage in import strings, but now at least the test suite works.
Added 'binding.delegateTo("foo")' as a shortcut for doing a bunch of 'bar = binding.bindTo("foo/bar")' operations.
Partial cleanup of '__class_provides__'; now it's supplied by the most basic metaclass (Activator), instead of being poked in by 'Once' attributes after the fact. There probably really should be a 'registerAttribute' interface method or something for this, but at least now it's not *too* unbearably ugly.
Added code-generation skeleton function to 'peak.model.mof2py', with usage examples that generate UML 1.3-1.5 and CWM 1.0-1.1 from XMI files. Also, fixed a problem with getting configuration properties on 'model.Element' instances, but the fix should really have a bit more refactoring. Last, but not least, fixed a problem with writing non-ASCII characters in MOF model annotations, at least for the CWM 1.1 model.
Fixed a problem where 'binding.Base' derivatives never "provided" anything.
4X speedup for peak.model attribute reads. Unfortunately, they still can be as much as 40x slower than regular Python attributes. :( There's probably more that could be shaved out, if I write some more Pyrex code. This change affects how derived attributes are defined: now you override the 'get()' method instead of '_getList()', which is a lot clearer.
Fixed problem with 'getParentComponent()' of a module potentially importing an object that isn't the parent module (in the case where the parent's parent has an attribute with the same name as the parent, but that is not the parent).
Added support for Python modules to be binding components whose parent component is their enclosing package, and whose component name is the rightmost part of their '__name__'. This means that class components can now acquire up the package tree to reference sibling modules in (for example) their 'referencedType' fields. Of course, it will only works if the sibling module has been imported, so it may not be of much interest for packages with empty '__init__.py' files. It should be helpful, however, for supporting cross-module type references in generated code.
Minor code simplification for binding.Component, inspired by recent discussion on the mailing list.
Improved support for slots and initialization of unchangeable features at instance creation time. This required revisions to the 'IBindingAPI' interface and implementation to accept a 'useSlot' flag on many routines, and the addition of new '_postGet()' and '_bindingChanging()' hook methods in 'binding.Base'. But now the persistence implementation is orthogonal to how attributes are stored. No tests for slot-based features yet, but maybe that can happen soon. In principle it should now also be possible to create components that subclass 'Classifier' and thus share in many of the benefits of 'peak.model', for solution-domain components. For example, such components could be loaded from a DM that reads a configuration file.
Fixed "silent failure" of component constructors when invalid keyword arguments are supplied, and managed connections breaking because they didn't have an 'address' attribute. The first bug was masking the second, at least from the point of view of our tests. This bug has been in here since I added keyword "validation", but wasn't caught because we evidently haven't tried to use any connection objects since. We really need some extra tests that actually connect to databases, LDAP, etc., so that we can prevent regressions like this. :(
More docs and code cleanup for 'peak.naming'. Moved syntax-related classes to a separate module, 'naming.syntax'. Renamed 'Syntax' class to 'PathSyntax', to distinguish from the 'URLSyntax' helper that I'll be adding later. Got rid of legacy names for NameContext/AddressContext classes, and made NameContext and AddressContext accessible via the API, so context implementers don't have to import from peak.naming.contexts. (If you're using the naming system at all, it's pretty much guaranteed that something's going to import those classes, so we might as well put them in the API.)
Starting implementation of "name arithmetic". Composite and compound names can now be added to each other or themselves, with correct handling of namespace boundaries. (Well, the test cases work correctly, but I need to add some more of them.) Got rid of the 'isComposite/isCompound/isURL' flags, and replaced them with a 'nameKind' enumeration. The base 'Name' class is now 'AbstractName', since it's not a useful thing to instantiate.
Added keyword argument validation for component __init__. You now can only supply keyword arguments to a component constructor when the class has an attribute of the appropriate name. This should help avoid misspellings of keyword arguments. It should really use more specific criteria, like which attributes are actual descriptors with __set__ methods. That could help distinguish between class and instance attributes. But before we do that, we'd need to rethink 'binding.Constant()' and make it required to define class-level default values for attributes, instead of just putting the value directly in the class. It's worth thinking about.
Completed conversion to C of the base class descriptor methods for 'Once' attributes. Due to constraints in how Pyrex creates extension types, it was not possible to continue support for the (deprecated anyway) 'OnceClass' and 'AutoCreatable' metaclasses. This is now cleaned up pretty decently and runs the tests at least as fast as before I began the metadata refactoring yesterday (~4.1 seconds on my Windows PC). Note that if you want to build from Pyrex source or have Pyrex installed, you *must* patch Pyrex.Compiler.TypeSlots so that these two lines: descrgetfunc = Signature("TOO", "O") # typedef ... descrsetfunc = Signature("TOO", "O") # typedef ... Look like *this* instead: descrgetfunc = Signature("Tpp", "O") # typedef ... descrsetfunc = Signature("TOp", "i") # typedef ... This is a work-around for improper generation of descriptor __get__/__set__ code by Pyrex.
Removed all known usage of AutoCreated et al, and marked the appropriate docstrings as "DEPRECATED". There really weren't that many places where we relied on AutoCreated behavior, and making them explicit with binding.New() actually does seem to improve the clarity of the code.
Moved remaining utility/property functions from binding -> config. This should make it a little easier to finish tutorial chapter 2... ;) Seriously, these functions aren't really about binding, even though they're called by certain attribute bindings. They're about finding configuration data.
Refactored TraversableClass + ActiveDecscriptors -> ActiveClass, creating a uniform metaclass heterarchy (whatever that means). ActiveClasses don't need setupModule() to tell them their parents; they assume that if they're activated inside a class from the same module, then that class is their parent. So I was able to put back in the optimization of skipping class re-creation when a module is first loaded. Anyway, ActiveClass is now the base metaclass for pretty much everything in PEAK, and so all PEAK classes are now valid components in much the same way that their instances are.
Note to self: don't use 'binding.' when writing code inside the binding package! (Can cause recursive import attempts under some conditions.)
Added 'binding.TraversableClass' - a metaclass whose instances implement 'binding.IBindingSPI' and are therefore usable in a component hierarchy. Classes that have this as one of their metaclasses will know their parent (containing) class or module, and return it from 'getParentComponent()'. This will be key to creating metamodels whose features can easily reference other types. Also, fixed missing export of the 'binding.AutoCreatable' metaclass.
Changes to allow component classes to be part of a component hierarchy. The problem was that if a class defines 'getParentComponent()' for its instances, there was no way for it to define 'getParentComponent()' for itself! Now all such API's are called on an object's '__class__', passing in the instance. This means that if a class is used as a component, its metaclass 'getParentComponent()' will be called on it, producing the right result in context. Similar changes were made for other IBindingSPI methods such as '_getConfigData()' and 'getComponentName()'. These changes are needed to support peak.model class families that need their features to refer to other classes in the class family -- the element classes need to be used as context components for the features.
Moved Provider and CachingProvider from binding->config, where they're a better fit.
Added/fixed various docstrings. ComponentName is also now a class.
Made bindToParent and bindToSelf take the same extra args as other bindings
Fixed problems with running help() on a class with attribute bindings.
Trying again to fix the docstring formatting...
Docstring formatting fix.
PropertyName -> peak.api; Improved lazy import facility * naming.PropertyName is now simply PropertyName; it never really had much to do with naming in the first place, doesn't depend on anything else, and needs to be accessible in all major subpackages and in end-user API calls. So now it's in-line in the peak.api module. * Added a 'lazyModule(moduleName)' function to peak.util.imports. This new "next generation" way of creating a lazily-loaded module actually creates an object that *will be* the real module in sys.modules, and once loaded will be indistinguishable from an ordinary Python module. (Well, under Python 2.3 the type() will be different, but you can't have everything.) I was getting really fed up of tracing into lazyImport.__getattr__ during debugging sessions, so I was inspired to come up with something that would only have code overhead when it's first touched. Subclassing ModuleType, adding a __getattribute__ method, and clever use of the Python 'reload()' function saved the day. * This now completes all the non-documentation items I want for 0.5a1, although I may "work ahead" a little on the features list, especially if Ty keeps pressing for SQL type maps and LDAP schemas. ;) (In truth, I'd like those and some of the other database features Real Soon Now myself.)
Components now support names and paths: * Objects created by naming system lookup, binding.New(), or AutoCreated, can all now automatically receive information about their name as well as their parent component. * binding.getComponentName() gets an object's name or 'None' * binding.getComponentPath(c, relativeTo=None) gets an object's path from its root component, or as a relative path from 'relativeTo' (assumes 'relativeTo' is a parent of 'c'). Unknown names are rendered as '*' in the path. * Added 'binding.Acquire' after all. * Improved management of _p_jar/_p_oid by model.Element, using _p_jar as the parent component, and _p_oid as the component name for elements. * Added unit tests for name and path support.
Reorgs and and misc. cleanups; updated TODO for 0.5a1 vs 0.5final Moved ActiveDescriptor(s) classes from binding.meta to binding.once and the rest of binding.meta to model.method_exporter. Moved the method-exporter tests that lived in metamodels.tests.General to model.tests, where they really belonged, and added model.tests to the install and API test suite.
Note to self: don't use list comprehensions when 'list()' will suffice. :)
Arbitrary Python objects are now treated as stand-alone component roots by all APIs that accept a component for looking up configuration data, utilities, etc.
Moved lookupComponent() from IBindingSPI to IBindingAPI, and made it no longer necessary for an object to support this method in order to be part of a component hierarchy. Possibly with a little more work, we could get arbitrary Python objects to be considered component roots...
Refactored so that config.loadConfigFile() and config.loadMapping() can be called on any IConfigurable - which means any Component. So now you can do things like 'config.loadConfigFile(config.getGlobal(), somefile)' or 'config.loadMapping(myComponent, config.fileNearModule('foo','bar.ini'))'. Also fixed an issue where PropertyMap objects wouldn't overwrite an existing provider if the existing one was registered under the same interface or property name. Last, but not least, re-arranged and enhanced both binding.interfaces and config.interfaces to reflect the nature of things that supply configuration data, things which can be configured, and the ways in which these things are accomplished.
Made peak.model StructuralFeatures use a uniform API for manipulating object attributes, which will make it easier to implement "lazy load" attributes later, and made it easier to implement _p_change support, which was also added. IBindingAPI now includes _getBinding, _setBinding, _delBinding, and _hasBinding methods, and model.Element sets _p_changed when a modifier method is called. Also did some more TODO reprioritizing; it now reflects the 0.5 priority order much better, even though it still lumps items together by package.
* Added 'binding.Constant(key, value)', which allows you to specify a constant value to be used as a property or utility in that component subtree. * Added storage.get/begin/commit/abortTransaction() APIs for ease of use in short scripts. Defined a default provider for ITransactionService in peak.ini that provides each component root with its own TransactionService instance. It's easily replaceable with other allocation strategies, if it should be needed. I may change this later to a per-root strategy, since the current strategy requires creating localConfigs for each component root. We'll see what actual use has to say about the best way of doing it. * Fixed a problem with local/global config object startup that could cause "phantom" configuration objects to appear via recursion of getLocal() or getGlobal(). * Got rid of binding.meta.assertInterfaces, which is no longer needed because the Interface package now correctly handles metaclass instances. * Got rid of Singleton, since it's not used for anything and I could neither think of any use cases nor remember why I added it in the first place! * Made it possible for CachingProviders to cache per-localConfig as well as per-object. This is handy for implementing per-hierarchy services, like the TransactionService provider mentioned above. * Misc. TODO updates.
Got rid of the '_componentName' attribute, since it isn't used anywhere and is of dubious value. None of the use cases that it was created for need it any longer to accomplish their goals, either, to the best of my awareness.
Made lookupComponent() match all the other lookup signatures. Made the __init__(self, parent=None, **kwattrs) signature the default constructor for all bindable classes, since it has been working out to be a convenient idiom. Added a binding.interfaces module with IBindingSPI and IBindingAPI interfaces to document what's required by the framework, and provided by the framework, respectively.
Made 'acquireComponent' use the same parameter order (thing to find, place to look) as findUtility, getProperty, globalLookup, etc. Also made its target and that of iterParents optional, for the same reason as it was done on the others: to allow more compact expression in short scripts without any long-lived components outside the local/global config.
Reversed parameter order for both findUtility and getProperty. Now the name or interface comes first, and the (now optional) target component second. This means that when writing short scripts or using PEAK at an interpreter prompt, it won't be necessary to pass config.getLocal() into either function just to get access to a global utility or property. I'd like to make lookupComponent and certain other functions follow this same pattern, but there are some hitches to doing so right away. I'll take another stab at it later. Also, I added '!' and ':' to the allowable characters in a property name, since cygwin Python puts things like '!C:' into os.environ.keys(). Last, but not least, I added an 'onJoinTxn()' method to TransactionComponent, as I'm about to need it in order to implement transaction operations on ManagedConnection objects.
Modifications to make property lookup more space/time efficient for apps with lots of components. A property map was being auto-created for each component a lookup passed through, even though it was a read-only operation. So I added a 'getBindingIfPresent()' method to allow read-only access to a Once attribute if present, and changed the lookup code to use it. I also changed property map objects to do the same thing, and also changed them to use themselves as their source for their own configuration data!
Simplified naming system creation mechanisms. URL contexts and parsers are now looked up as properties in 'peak.naming.schemes.*'. Object factories and state factories can be specified as keyword arguments to an InitialContext, and then "inherited" by child contexts. Also, moved config.Property -> naming.PropertyName. Still need to convert IInitialContextFactory from a utility to a property. Also, we need a way for scheme address parsers to be asked what schemes they support; right now I've hacked URL contexts to assume their parser supports anything. :(
Properties and utilities are unified at last! Both properties and utilities are now handled by the __instance_provides__ and __class_provides__ attributes of component classes. This simplified the code and interfaces for both PropertyMap and for registering dynamic utility providers. Property names now have an additional syntax form: 'some.prop?' which is used as a default value that is checked after 'some.prop', 'some.*', and 'some.prop' (a second time). This is handled transparently for you by way of the 'setDefault()' method of 'IPropertyMap', so effectively it's an implementation detail. Documentation is still a little sketchy, but it should now be straightforward to get the naming system using properties and utilities in a harmonious way. ALSO: Fixed config.getLocal() to ensure it's working with a root component. binding.New() can now use an import string instead of a class/type/callable. The object referenced by the import string will be loaded and called as though the object itself had been used. GlobalConfig and LocalConfig now have 'setup()' methods that can be overridden to create default rule and provider registrations. This'll be handy for making LocalConfig register a default transaction service provider, for example.
Moved all PEAK API exceptions to 'peak.exceptions' module, because it was getting pretty darn inconvenient to keep importing them across subpackages. 'from peak.api import exceptions' or 'from peak import exceptions' can be used to access them. It's worth the trouble of referring to 'exceptions.SomeException', because it allows the names to be shorter (no need for Exception or Error in the name) and makes the exceptions globally available. Added 'binding.iterParents()', which walks from a component up through its root parent, into the local and global configuration objects, if available. It's now used in getProperty() and findUtilities(). Synchronized interface of 'binding.findUtility()' and 'config.getProperty()'. Now both take an optional 'default' argument which will be returned if the sought item is not found, or, if a default is not supplied and the item isn't found, an 'exceptions.NameNotFound' error is raised. (findUtility() used to return 'None' to mean not-found). Fixed a bug in 'binding.lookupComponent()' for looking up utilities (which also caused 'bindTo(ISomething)' to not work correctly). Added a 'config.Property' class, a subclass of 'str', which parses and validates property names, as well as handling wildcard iteration. It also implements enough of 'IInterface' to allow it to be used as a key in an EigenRegistry. There's a new interface 'config.IConfigKey', which is implemented by both Interface objects and Property objects, so everything that can be used as a configuration key now implements it. This is another step towards unification of configuration properties (keyed by name) and utilities (keyed by Interface). After a few more passes, I should be able to eliminate the rest of the parallel logic that exists to process both Utilities and Properties. Fixed a typo in 'storage.TransactionComponent' which would have caused it to raise an error upon joining a transaction.
Modifications to naming system to allow specification of a 'creationParent' which can be used by object factories to 'setParentComponent()' of newly created components upon lookup from a naming system. This is so that (for example) URLs used to create ManagedConnection objects will be able to bind the ManagedConnection to the parent component that is to contain it.
Removed the use of setupModule() from peak.binding.components to speed its loading on Windows. :(
Fixed the attributes-as-utilities problems. Also unified AutoCreatable and OnceClass, as they should've been a long time ago. This is what I was trying to do last night, when the Python bug raised its ugly head out of the murky swamp that was peak.metamodels.FeatureObject! Now that all this cruft's out of the way, I can start in on actually testing the peak.config properties system!
Added binding.bindProperty(), which completes the new PEAK API for configuration properties. Now all we need are actual, useful PropertyMaps and Rules, in order to use this stuff...
Added DBCursor and LDAPCursor to peak.storage, along with their corresponding interfaces. ManagedConnections now support registerCursor() and closeCursors() methods, as well as __call__() to create a new cursor and optionally execute a query on it. Cursor objects can be iterated over to return rows, inverted to return a single row, or processed for multi-sets as in:: row = aConn('SQL that returns exactly one row').justOne() # shortcut for the above row = ~ aConn('SQL that returns exactly one row') # multiple result sets rs1, rs2 = aConn('SQL that returns 2 result sets').allSets() # iteration for row in aConn('SQL that returns 0-n rows, in 1 set'): # do something Rows returned are 'struct' objects (a cross between a tuple, dict, and object w/attributes), unless the underlying DB returns non-standard rows. The LDAPCursor class also iterates over asynchronously retrieved results, but isn't very well tested at this point. None of this has been tested with actual databases, just a few mockup test harnesses. To support all this, I also needed to add a 'hasBinding()' method to binding.Base and an 'addField' method to the 'structType' metaclass.
Added __name__ and __doc__ capability for most attribute bindings, so that Python's help() can give more meaningful info. (Mostly, though, bindings confuse the heck out of help(), as it thinks that only methods have descriptors that have '__get__' without '__set__'.)
Killed off remaining weakref binding facilities. We now only use weakrefs in caches, and even then only via WeakKeyDictionary and WeakValueDictionary.
Tightened restriction on setParentComponent() such that it can only be used until getParentComponent() has been used on the component. This will ensure a highly stable component structure.
First pass at removing weakrefs from the core binding system. Made parentComponent a simple attribute, albeit still set via the setParentComponent() method, which now ensures that you can't change an object's parent once it has one. Split the Component class into Base and Component, and made model.Classifier derive from Base instead of Component. This allows problem-domain (i.e. application data element) components to be part of a component hierarchy, without themselves being providers of utilities, etc.
Moved config to its own top-level subpackage, peak.config, since it doesn't really fit well with the rest of 'running' as it's shaping up. Also, removed lockfiles and dameons from the running.api, as they aren't really part of the API. Lockfiles will normally be instantiated via a URL reference, as will most Daemon providers. To create daemons, the preferred method will be: from peak.running import daemons class MyDaemon(daemons.AbstractDaemon): .... Similarly, when we have logging base classes, 'from peak.running import logs' and 'aLog = logs.SomeLogger()' will be reasonable usage as well, since logs will also normally be instantiated via URL references.
Moved binding.setupModule() et al to config.setupModule(). Also, 'binding.configure()' is now 'config.setupObject()', for better association with the 'setupModule()' function.
Changes to help the tutorial be clearer, when I write it: bindToNames() -> bindSequence() # emphasize/clarify its nature WeakBinding() -> WeakRefBinding() # ditto New function: bindToUtilities(iface) # [x for x in findUtilities(iface)] Also, some fixes to Once.usageError() and the code that calls it, to improve usability with non-ActiveDescriptors instances (e.g. old-style classes). This'll make the first part of the binding tutorial work better.
Re-factored utility search mechanism, and associated configuration rules. acquireUtility has been replaced with the functions binding.findUtility() and binding.findUtilities(). The latter is a generator that yields in hierarchical order all the utilities available for the requested interface. This is more helpful in dealing with most utilities that need to be used as a set. Components now implement a simpler '_getUtility()' method that is called by the find() functions. Configuration rules have now been adjusted to allow for getting a new default local configuration in the absence of a root, and to force getLocal(getGlobal()) to return None (which simplifies loop logic when traversing into configuration objects from arbitrary components, as is done by findUtilities().) And finally, changed the naming.spi module to register a default provider for all the interfaces it needs, and to use findUtilities() to iterate over factories.
Finished July 12 refactoring plan, minus tests for new functionality. The entire peak.naming package is now free of the Java 'environment' carry-over, and naming contexts now play in the same component tree as everything else. I'm not thrilled with the factory mechanisms at present, but I think that first I'll need to change utility acquisition to allow iteration upward through utilities in the component tree.
Refactored bindings to support 'provides=' keyword argument: an interface (or nested tuple thereof) specifying what interface(s) that attribute should be considered a utility for. Also fixed Once and its derivatives, plus all component lookup algorithms, to use a single recursion guard value: peak.api.NOT_FOUND. None of the new functionality has been tested, although all the old tests still pass.
Added config package, and integrated it into binding.Component's acquireUtility() method. Note that the config API signatures ended up slightly different than in the implementation plan posted to the mailing list; notably, it didn't make sense to have 'forRoot' as an optional parameter, since *every* root needs its own local configuration, unless you manually set the same one for a bunch of them.
Added test suite for EigenData module, and fixed some minor issues with it and binding.components.
Added pretty much everything needed to do utility lookups and register providers, except for the 'provides=' hook on binding classes, which I think will need some refactoring of the 'once'and 'components' modules to do cleanly. I changed the name from 'acquireProvider' to 'acquireUtility', since what it returns you is a utility, even though you register providers in order to provide the utilities. It's more correct, even if it may also be more confusing at first. But I'm guessing that it'll be uncommon to ever use 'acquireUtility()' directly in the first place, since 'lookupComponent()' will now take an interface as a name and look it up for you.
New semantics for 'lookupComponent()': it's now fully integrated with peak.naming, so URLs and names acquired "off the top of the hierarchy" should now work. It also takes '/'-separated paths now instead of '.'-separated ones, and includes support for absolute paths (beginning with '/') and relative ones ('.', '..'). The name is now a required argument (it used to be optional and would do the equivalent of '..'). In addition, there are many refinements to the binding package, including lots of new module-level component navigation functions like 'getRootComponent()', 'acquireComponent()', etc. See the docs for details. Almost all of them have tests, too. Last, but not least, I hacked on the naming API/SPI modules so that they'll work for now with an empty default environment and using 'AbstractContext' as the default 'InitialContext'. That'll let us use whatever URL context providers we want to put in before we get around to implementing the configuration system and its defaults...
Moved module inheritance from 'peak.api' to 'peak.binding', and reformatted peak.api package accordingly. Applied the following name changes:: .getSEFparent() -> .getParentComponent() ._setSEFparent() -> .setParentComponent() .getService() -> .lookupComponent() setupModule() -> binding.setupModule() adviseModule() -> binding.patchModule() configure() -> binding.configure() Also made the default '__proceed__' value a built-in, so that it's not necessary to import it. This concludes the essential code-level transformations from TransWarp to PEAK. Almost everything should be in its long-term home now, with its long-term name, although the 'model' package may receive some adjustments by the time a tutorial is written for it. Following this check-in, the repository will be considered stable, and CVS checkouts of PEAK will be possible.
Hacked on it until all tests pass... But still a lot of re-name, re-doc, re-org to go. Still need to move Caching over, and there are some test package splits that need to be done (metamodels->model). Need to make the Once and other peak.binding package import idioms consistent, too. But at least the tests run. The repository is not yet stable, however.
Fleshed out SPI factory functions; added references and lookupLink support.
Added support for pickling instances of nested classes; this required changing all uses of '__name__' which needed an unqualified class name. Notably, one should use 'feature.attrName' instead of 'feature.__name__' in verb definitions.
SEF.DynamicBinding is now SEF.AutoCreated, and all AutoCreatable classes like SEF.App and SEF.Service must now have an '__init__' method that accepts their SEF parent component. This lets such objects have access to the SEF hierarchy during initialization. (Note that this means any '__init__' methods of such classes must be revised to take this into consideration. See TW.Database.DataModel.Database for an example of such a revision.) Also, I dropped the unused SEF.StaticBinding class.
Added warnings for detectable module-level modifications of mutables in modules which are used for inheritances or advice. Added an API function, 'configure(object, attr1=val, attr2=val,...)' to safely set attributes of mutables that might have been defined in a derived module. Also, misc. updates to TODO.
Removed 'Meta.ClassInit' and '__class_init__' support. Use metaclass '__init__' methods instead; see 'TW.Database.DataModel.RecordTypeMC' for one example of the conversion.
Revised bindToParent() to eliminate circular reference, and added bindToSelf() shortcut for bindToParent(0).
Changed MethodExporter to use a concise and centralized definition of verbs instead of 'namingConvention' attributes on every function. This should make it easier to override method templates in subclasses and in derived modules, as well as reducing typing when there are multiple variants of a verb. The MethodExporter documentation has been lightly updated, but really needs an overhaul before release, especially with respect to terminology.
Removed the '__proceed__' limitaton on method templates by dropping the '__feature__' magic name rebinding. It turned out to be easier to fix this than to document it as a limitation. :) Method templates must now use a signature that looks like:: def verb(feature, self, ...): And drop the use of 'feature = self.__class__.__feature__', since it'll now be supplied as an argument. Updated documentation, tests, and current usages to match. Interestingly, the docs are now a little simpler, since *all* MethodExporter methods now take the same first parameter, 'feature'.
Minor cosmetic/typo doc fixes.
New-style features have landed! Lots o'docs and examples. CHANGES and TODO have been updated accordingly. Enjoy.
Added code to prevent methods exported from a feature overwriting methods which were explicitly defined in the class or its ancestors. Updated test suite to verify this behavior.
Refactoring and more tests for the method-exporting feature format. All the major classes work now, and I even found and fixed a problem in the old code that wasn't found before. The main things needed before I can take this off the 0.2 TODO are more docs, a bit of refactoring to make parts of the feature metaclass more generally reusable, and a cleanup of the SEF.Interfaces module to reflect the new API and get rid of all the junk hanging around in there from before the Python 2.2 port.
A first step at refactoring the SEF framework to use the new feature metaclass. Specifically, I moved the old-style feature implementation to TW.SEF.FeatureObjects, and changed everything that relies on the old API to use that implementation instead of TW.SEF.Basic, so that all the tests still work. Please note that the "new-style" features in TW.SEF.Basic aren't yet finished, let alone tested. If you're working from CVS and using features, be sure to use SimpleModel or FeatureObjects as your SEF module, until this stuff settles down some more.
Added __delete__ handling and test refinements to the new "method exporting" feature metaclass. (FYI: the source-changes announcement script broke on the last checkin, so here's what the previous log message would have said: """ First draft of a feature metaclass that exports methods to element classes. Supports filtering, renaming, naming conventions, etc., and has unit tests, but isn't yet integrated into the SEF framework. That's going to take quite a bit more work. Also, it's not properly documented, though I've made a brief stab at it. The docs should really go in the IFeature interface, though, and a couple other things are still rough at this point. """ Hopefully this time the log message will go through to the mailing list...
First draft of a feature metaclass that exports methods to element classes. Supports filtering, renaming, naming conventions, etc., and has unit tests, but isn't yet integrated into the SEF framework. That's going to take quite a bit more work. Also, it's not properly documented, though I've made a brief stab at it. The docs should really go in the IFeature interface, though, and a couple other things are still rough at this point.
* Added basic Specialist implementation to 'TW.SEF.Basic', and reverted naming from 'TypeService' and 'ITypeService' to 'Specialist' and 'ISpecialist'. Updated 'TW.SEF.Interfaces' to reflect "self"-less convention for documenting methods. * Dropped obsolete 'TW.Database.Records' module.
Many changes in preparation for 0.2 final, including: * Added 'SEF.bindToParent()' and 'SEF.bindToNames()' specifiers to allow more flexible component parameter bindings. * Fixed the base class "rebind by name" bugs in module inheritance, and updated the documentation to more clearly reflect what it is that metaclass generation and module inheritance does and does not do. Added test cases to prevent regression of the rebind-by-name problem. * The 'setup.py' script features a new command, 'happy', which can be used to generate the API reference docs, and this command runs as part of the 'sdist' command to build source distributions. * The API reference docs in the source distribution has been moved from the 'doc' directory to 'docs/html/reference'. * Added 'CHANGES.txt' file.
Changed 'NamedDescriptors' to 'ActiveDescriptors', making them a bit more flexible, adding a bit of interface documentation, and adding support for classes to keep a set of '__volatile_attrs__' for filtering '__getstate__' data to avoid persisting cached/computed attributes, without needing everything to use '_v_' names.
Refactored to separate the notions of Once and OnceClass from the notion of a NamedDescriptor. Now TW.API includes a Once() descriptor that wraps a function for computing a cached attribute value, and a OnceClass metaclass for creating classes that do the same.
Factored out the basis of bindTo/requireBinding into 'NamedDescriptor' and 'NamedDescriptors' support classes, so that they can be reused by other types of "once attributes". Also added tests for bindTo()/requireBinding.
Added new 'SEF.bindTo(targetService)' and 'SEF.requireBinding("description of needed value")' APIs. These allow SEF-pattern classes to define atttributes which will automatically look up a service when needed (using 'SEF.bindTo()'), or raise an error if a subclass didn't define a needed attribute (using 'SEF.requireBinding()').
Pre-release documentation and cleanup sweep, part 2. Cleaned up the rest of the 3-letter packages (SEF, MOF, and API), replacing StructuralModel with SEF along the way. Also moved some of the key metaclasses into their own module, TW.API.Meta.
cvs-admin@eby-sarna.com Powered by ViewCVS 1.0-dev |
ViewCVS and CVS Help |