merge with crew.
authorVadim Gelfer <vadim.gelfer@gmail.com>
Fri, 03 Mar 2006 09:40:18 -0800
changeset 1831 bf118f39afd7
parent 1830 4ced57680ce7 (current diff)
parent 1828 ca82f20b0c19 (diff)
child 1832 7a58bf789965
merge with crew.
--- a/hgext/patchbomb.py	Fri Mar 03 09:39:37 2006 -0800
+++ b/hgext/patchbomb.py	Fri Mar 03 09:40:18 2006 -0800
@@ -49,20 +49,11 @@
 # to = recipient1, recipient2, ...
 # cc = cc1, cc2, ...
 
-from email.MIMEMultipart import MIMEMultipart
-from email.MIMEText import MIMEText
-from email.Utils import parseaddr
-from mercurial import commands
-from mercurial import hg
-from mercurial import ui
+from mercurial.demandload import *
+demandload(globals(), '''email.MIMEMultipart email.MIMEText email.Utils
+                         mercurial:commands,hg,ui
+                         os popen2 smtplib socket sys tempfile time''')
 from mercurial.i18n import gettext as _
-import os
-import popen2
-import smtplib
-import socket
-import sys
-import tempfile
-import time
 
 try:
     # readline gives raw_input editing capabilities, but is not
@@ -149,7 +140,7 @@
         if opts['diffstat']:
             body += cdiffstat('\n'.join(desc), patch) + '\n\n'
         body += '\n'.join(patch)
-        msg = MIMEText(body)
+        msg = email.MIMEText.MIMEText(body)
         subj = '[PATCH %d of %d] %s' % (idx, total, desc[0].strip())
         if subj.endswith('.'): subj = subj[:-1]
         msg['Subject'] = subj
@@ -194,7 +185,7 @@
     sender = (opts['from'] or ui.config('patchbomb', 'from') or
               prompt('From', ui.username()))
 
-    msg = MIMEMultipart()
+    msg = email.MIMEMultipart.MIMEMultipart()
     msg['Subject'] = '[PATCH 0 of %d] %s' % (
         len(patches),
         opts['subject'] or
@@ -217,13 +208,13 @@
         if l == '.': break
         body.append(l)
 
-    msg.attach(MIMEText('\n'.join(body) + '\n'))
+    msg.attach(email.MIMEText.MIMEText('\n'.join(body) + '\n'))
 
     ui.write('\n')
 
     if opts['diffstat']:
         d = cdiffstat(_('Final summary:\n'), jumbo)
-        if d: msg.attach(MIMEText(d))
+        if d: msg.attach(email.MIMEText.MIMEText(d))
 
     msgs.insert(0, msg)
 
@@ -241,7 +232,7 @@
             s.login(username, password)
     parent = None
     tz = time.strftime('%z')
-    sender_addr = parseaddr(sender)[1]
+    sender_addr = email.Utils.parseaddr(sender)[1]
     for m in msgs:
         try:
             m['Message-Id'] = genmsgid(m['X-Mercurial-Node'])
--- a/mercurial/demandload.py	Fri Mar 03 09:39:37 2006 -0800
+++ b/mercurial/demandload.py	Fri Mar 03 09:40:18 2006 -0800
@@ -1,15 +1,125 @@
-def demandload(scope, modules):
-    class d:
-        def __getattr__(self, name):
-            mod = self.__dict__["mod"]
-            scope = self.__dict__["scope"]
-            scope[mod] = __import__(mod, scope, scope, [])
-            return getattr(scope[mod], name)
+'''Demand load modules when used, not when imported.'''
+
+__author__ = '''Copyright 2006 Vadim Gelfer <vadim.gelfer@gmail.com>.
+This software may be used and distributed according to the terms
+of the GNU General Public License, incorporated herein by reference.'''
+
+# this is based on matt's original demandload module.  it is a
+# complete rewrite.  some time, we may need to support syntax of
+# "import foo as bar".
+
+class _importer(object):
+    '''import a module.  it is not imported until needed, and is
+    imported at most once per scope.'''
+
+    def __init__(self, scope, modname, fromlist):
+        '''scope is context (globals() or locals()) in which import
+        should be made.  modname is name of module to import.
+        fromlist is list of modules for "from foo import ..."
+        emulation.'''
+
+        self.scope = scope
+        self.modname = modname
+        self.fromlist = fromlist
+        self.mod = None
+
+    def module(self):
+        '''import the module if needed, and return.'''
+        if self.mod is None:
+            self.mod = __import__(self.modname, self.scope, self.scope,
+                                  self.fromlist)
+            del self.modname, self.fromlist
+        return self.mod
+
+class _replacer(object):
+    '''placeholder for a demand loaded module. demandload puts this in
+    a target scope.  when an attribute of this object is looked up,
+    this object is replaced in the target scope with the actual
+    module.
+
+    we use __getattribute__ to avoid namespace clashes between
+    placeholder object and real module.'''
+
+    def __init__(self, importer, target):
+        self.importer = importer
+        self.target = target
+        # consider case where we do this:
+        #   demandload(globals(), 'foo.bar foo.quux')
+        # foo will already exist in target scope when we get to
+        # foo.quux.  so we remember that we will need to demandload
+        # quux into foo's scope when we really load it.
+        self.later = []
+
+    def module(self):
+        return object.__getattribute__(self, 'importer').module()
+
+    def __getattribute__(self, key):
+        '''look up an attribute in a module and return it. replace the
+        name of the module in the caller\'s dict with the actual
+        module.'''
 
-    for m in modules.split():
-        dl = d()
-        dl.mod = m
-        dl.scope = scope
-        scope[m] = dl
+        module = object.__getattribute__(self, 'module')()
+        target = object.__getattribute__(self, 'target')
+        importer = object.__getattribute__(self, 'importer')
+        later = object.__getattribute__(self, 'later')
+
+        if later:
+            demandload(module.__dict__, ' '.join(later))
+
+        importer.scope[target] = module
+
+        return getattr(module, key)
+
+class _replacer_from(_replacer):
+    '''placeholder for a demand loaded module.  used for "from foo
+    import ..." emulation. semantics of this are different than
+    regular import, so different implementation needed.'''
+
+    def module(self):
+        importer = object.__getattribute__(self, 'importer')
+        target = object.__getattribute__(self, 'target')
+
+        return getattr(importer.module(), target)
+
+def demandload(scope, modules):
+    '''import modules into scope when each is first used.
+
+    scope should be the value of globals() in the module calling this
+    function, or locals() in the calling function.
+
+    modules is a string listing module names, separated by white
+    space.  names are handled like this:
 
+    foo            import foo
+    foo bar        import foo, bar
+    foo.bar        import foo.bar
+    foo:bar        from foo import bar
+    foo:bar,quux   from foo import bar, quux
+    foo.bar:quux   from foo.bar import quux'''
 
+    for mod in modules.split():
+        col = mod.find(':')
+        if col >= 0:
+            fromlist = mod[col+1:].split(',')
+            mod = mod[:col]
+        else:
+            fromlist = []
+        importer = _importer(scope, mod, fromlist)
+        if fromlist:
+            for name in fromlist:
+                scope[name] = _replacer_from(importer, name)
+        else:
+            dot = mod.find('.')
+            if dot >= 0:
+                basemod = mod[:dot]
+                val = scope.get(basemod)
+                # if base module has already been demandload()ed,
+                # remember to load this submodule into its namespace
+                # when needed.
+                if isinstance(val, _replacer):
+                    later = object.__getattribute__(val, 'later')
+                    later.append(mod[dot+1:])
+                    continue
+            else:
+                basemod = mod
+            scope[basemod] = _replacer(importer, basemod)