From 5d48a7a62c2bfaef43dcca3bfdadafd455fa1164 Mon Sep 17 00:00:00 2001
From: Arnaud Fontaine <arnaud.fontaine@nexedi.com>
Date: Wed, 18 Jan 2012 17:00:34 +0900
Subject: [PATCH] Avoid creating a temporary file when loading the source code
 of a Component.

---
 product/ERP5Type/Core/DocumentComponent.py  | 46 +++++---------------
 product/ERP5Type/dynamic/component_class.py | 48 ++++++---------------
 2 files changed, 23 insertions(+), 71 deletions(-)

diff --git a/product/ERP5Type/Core/DocumentComponent.py b/product/ERP5Type/Core/DocumentComponent.py
index d4e27b841e..f1c82c3de2 100644
--- a/product/ERP5Type/Core/DocumentComponent.py
+++ b/product/ERP5Type/Core/DocumentComponent.py
@@ -28,9 +28,6 @@
 #
 ##############################################################################
 
-import imp
-import os
-
 from AccessControl import ClassSecurityInfo
 from Products.ERP5Type import Permissions
 from Products.ERP5Type.Base import Base
@@ -58,36 +55,13 @@ class DocumentComponent(Base):
                        'Reference',
                        'TextDocument')
 
-
-    # XXX-arnau: per-component reset, global for now
-    # def reset(self):
-    #   import erp5
-    #
-    #   try:
-    #     component_namespace_name, module_name = self.getId().split('.')[1:]
-    #     component_namespace = getattr(erp5, component_namespace_name)
-    #   except (ValueError, AttributeError):
-    #     LOG("ERP5Type.Core.DocumentComponent", DEBUG,
-    #         "Invalid namespace %s..." % self.getId())
-    #   else:
-    #     if module_name in component_namespace.__dict__:
-    #       LOG("ERP5Type.Core.DocumentComponent", INFO, "Reset %s..." % self.getId())
-    #       getattr(component_namespace, module_name).restoreGhostState()
-
-    def load(self):
-      # XXX-arnau: There should be a load_source() taking a string rather than
-      # creating a temporary file
-      from App.config import getConfiguration
-      instance_home = getConfiguration().instancehome
-      path = '%s/Component' % instance_home
-      if not os.path.isdir(path):
-        os.mkdir(path)
-
-      component_path = '%s/%s.py' % (path, self.getId())
-      with open(component_path, 'w') as component_file:
-        component_file.write(self.getTextContent())
-
-      try:
-        return imp.load_source(self.getReference(), component_path)
-      finally:
-        os.remove(component_path)
+    def load(self, namespace_dict={}):
+      """
+      Load the source code into the given dict. Using exec() rather than
+      imp.load_source() as the latter would required creating an intermediary
+      file. Also, for traceback readability sake, the destination module
+      __dict__ is given rather than creating an empty dict and returning
+      it. By default namespace_dict is an empty dict to allow checking the
+      source code before validate.
+      """
+      exec self.getTextContent() in namespace_dict
diff --git a/product/ERP5Type/dynamic/component_class.py b/product/ERP5Type/dynamic/component_class.py
index 3b9c581e94..f60353bee5 100644
--- a/product/ERP5Type/dynamic/component_class.py
+++ b/product/ERP5Type/dynamic/component_class.py
@@ -26,55 +26,33 @@
 #
 ##############################################################################
 
+from types import ModuleType
 from zLOG import LOG, INFO
 
-class ComponentProxyClass(object):
-  """
-  XXX-arnau: should maybe use Ghost class?
-  """
-  def __init__(self, component, module):
-    self._component = component
-    self._module = module
-
-    self.__isghost__ = False
-
-    # XXX-arnau: metaclass!
-    self.__class__.__name__ = component.getReference()
-    self.__class__.__module__ = component.getId().rsplit('.', 1)[0]
-    description = component.getDescription()
-    if description:
-      self.__doc__ = description
-
-  def restoreGhostState(self):
-    self.__isghost__ = True
-
-  def __getattr__(self, name):
-    if self.__isghost__:
-      self._module = self._component.load()
-      self.__isghost__ = False
-      LOG("ERP5Type.dynamic", INFO, "Reloaded %s" % self._component.getId())
-
-    return getattr(self._module, name)
-
 def generateComponentClassWrapper(namespace):
   def generateComponentClass(component_name):
     from Products.ERP5.ERP5Site import getSite
     site = getSite()
 
-    component_name = '%s.%s' % (namespace, component_name)
+    component_id = '%s.%s' % (namespace, component_name)
     try:
-      component = getattr(site.portal_components, component_name)
+      component = getattr(site.portal_components, component_id)
     except AttributeError:
       LOG("ERP5Type.dynamic", INFO,
-          "Could not find %s, perhaps it has not been migrated yet?" % component_name)
+          "Could not find %s, perhaps it has not been migrated yet?" % \
+            component_id)
 
       raise
     else:
       if component.getValidationState() == 'validated':
-        klass = ComponentProxyClass(component, component.load())
-        LOG("ERP5Type.dynamic", INFO, "Loaded successfully %s" % component_name)
-        return klass
+        new_module = ModuleType(component_name,
+                                component.getDescription())
+
+        new_module.__module__ = component_id
+        component.load(new_module.__dict__)
+        LOG("ERP5Type.dynamic", INFO, "Loaded successfully %s" % component_id)
+        return new_module
       else:
-        raise AttributeError("Component %s not validated" % component_name)
+        raise AttributeError("Component %s not validated" % component_id)
 
   return generateComponentClass
-- 
2.30.9