E&S CVS Commit: pylib - Refactoring to add subclassing hooks and build-phase callbacks.

pje at eby-sarna.com pje at eby-sarna.com
Mon Dec 31 00:17:00 EST 2001

Module Name:	pylib
Committed By:	pje
Date:		Mon Dec 31 05:16:25 UTC 2001

Modified Files:
	pylib/TW: Callbacks.py Components.py Interfaces.py Utilities.py

Log Message:
Refactoring to add subclassing hooks and build-phase callbacks.

ParentBuilders now receive 'registerSubclass(builderPath)' calls informing
them that the builder at 'builderPath' is subclassing them.  Builders can
also subscribe to be notified by the root builder about global build
events such as 'specificationFinished' and 'inheritanceDetermined',
or by individual builders about local events such as
'subcomponentsFinished'.  The build phases themselves are a bit more
clear now, at least in my mind, and it should be easier/possible to write
very advanced builders now, using these callback hooks.

For example, an "advice" builder that wanted to modify methods of some
class and all its subclasses, could now register a method to be called
at 'inheritanceDetermined' time, then ask the targeted class for a list
of its subclasses.  It could then subscribe to the 'subcomponentsFinished'
event on all the targeted classes, and be called back just in time to
monkeypatch each one.  (Actually, implementing "advice" will probably be
a bit more complex than that, but at least it's now *possible*.)

I had intended to streamline 'SimplePostProcessor' on this refactoring
pass, making it use the 'subcomponentsFinished' hook instead of the nasty
'getDependencyPaths()' hackery it's using now.  'RegistryBuilder',
however, needs to be able to output a result, and IParentBuilder doesn't
include the necessary methods to do that from "outside" of the builder.
I'll have to look at this again in another pass, as I'd *really* like to
ditch the dependency hack, if I can.

I've been giving some thought to also ditching the entire dependency
system as it currently exists and replacing it with a 100% callback-driven
solution, or at least having builders "push" dependency information to
their parent builders as it becomes known, rather than waiting to gather
it all at once.  I'm pretty sure the latter is possible, but it'll have to
wait for another pass.  Pushing dependencies might cut back on the total
number of calls that have to occur during builds, since items without
dependencies won't be asked for them.  Even if it doesn't, the logic in
the kernel for dealing with dependencies should be simplified somewhat.

Miscellaneous changes:

* Made private methods of ComponentBuilder have more consistent names,
  including use of '_' to denote private methods.

* Simplified 'qualifyPath()' by dropping the self-match check, which is no
  longer needed since paths no longer include the root builder's name.

* Added 'subscribe()' method to 'TW.Callbacks.CallbackList', a synonym for

* Began using new 'InterfaceChecker' wrappers around interfaces, that
  allows faster assertion checking and cuts down on the number of calls to
  the 'implements()' method.  In future this might be revised to do some
  kind of more rapid interface checking, if speed becomes more of an
  issue.  (Profiling shows that TW spends about 10% of its time during a
  build just checking what interface various objects support!  Methinks
  the Zope Interfaces package may need to be either rewritten in C, or
  else have some kind of caching added to it.)

* Added 'subcomponentsFinished' callback list to 'IParentBuilder'

* Added subclass registration methods to 'IBuilder'

* Added 'IRootBuilder' interface, featuring 'specificationFinished' and
 'inheritanceDetermined' callback lists.

* 'AbstractBuilder' and its derivatives have some handy new (but as-yet
  undocumented) features for subclass overrides.  You can supply lists
  of methods you want hooked to various callbacks, and have them
  automatically installed on 'setContext()'.  You can also supply
  specialized setup methods that get called depending on whether the
  builder is being used as the root builder.

As before, if you have created your own builders (especially if they do
not derive from 'AbstractBuilder') you will probably want to examine the
diffs in more detail.

P.S. Please note that I run the unit tests continuously during refactoring
and all checkins should work.  If you run into any problems using these
"bleeding edge" revisions, *please* let me know ASAP, just in case I've
refactored away something you need or want.  Thanks.

To view diffs of this commit, you can use the following URL(s):

To generate a diff of this commit:
cvs rdiff -r1.1 -r1.2 pylib/TW/Callbacks.py
cvs rdiff -r1.34 -r1.35 pylib/TW/Components.py
cvs rdiff -r1.9 -r1.10 pylib/TW/Interfaces.py
cvs rdiff -r1.6 -r1.7 pylib/TW/Utilities.py

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

More information about the source-changes mailing list