Browse Source

Modification of exposure process in LodelContext

Thoses modification has two goals :
- make lodelcontext fit with the new FS organisation implemented by the
lodelsites datasource
- make the dyncode exposure more "flexible"

Note : no solution implemented for MONOSITE
Yann Weber 8 years ago
parent
commit
0b337fd4b5
2 changed files with 72 additions and 24 deletions
  1. 6
    0
      lodel/buildconf.py.am.in
  2. 66
    24
      lodel/context.py

+ 6
- 0
lodel/buildconf.py.am.in View File

7
 LODEL2VARDIR="[@]LODEL2_VARDIR[@]"
7
 LODEL2VARDIR="[@]LODEL2_VARDIR[@]"
8
 LODEL2LOGDIR="[@]LODEL2_LOGDIR[@]"
8
 LODEL2LOGDIR="[@]LODEL2_LOGDIR[@]"
9
 
9
 
10
+
11
+#Static declarations
12
+
13
+MULTISITE_DYNCODE_MODULENAME = 'leapi_dyncode'
14
+
10
 import os
15
 import os
11
 
16
 
12
 MULTISITE_DATADIR = os.path.join(LODEL2VARDIR, 'sites_datas')
17
 MULTISITE_DATADIR = os.path.join(LODEL2VARDIR, 'sites_datas')
13
 MULTISITE_CONTEXTDIR = os.path.join(LODEL2VARDIR, '.sites_context')
18
 MULTISITE_CONTEXTDIR = os.path.join(LODEL2VARDIR, '.sites_context')
19
+

+ 66
- 24
lodel/context.py View File

16
 else:
16
 else:
17
     globals()['lodel'] = sys.modules['lodel']
17
     globals()['lodel'] = sys.modules['lodel']
18
 
18
 
19
-if 'lodelsites' not in sys.modules:
20
-    import lodelsites
21
-else:
19
+if 'lodelsites' in sys.modules:
20
+    #This should be true since LodelContext init method is called
21
+    #for a MULTISITE context handling
22
     globals()['lodelsites'] = sys.modules['lodelsites']
22
     globals()['lodelsites'] = sys.modules['lodelsites']
23
 
23
 
24
+from lodel import buildconf
25
+
24
 ##@brief Name of the package that will contains all the virtual lodel
26
 ##@brief Name of the package that will contains all the virtual lodel
25
 #packages
27
 #packages
26
 CTX_PKG = "lodelsites"
28
 CTX_PKG = "lodelsites"
60
 #to represent the lodel package os the FS and then we make python import
62
 #to represent the lodel package os the FS and then we make python import
61
 #files from the symlink.
63
 #files from the symlink.
62
 #
64
 #
65
+#
63
 #@note Current implementation is far from perfection. In fact no deletion
66
 #@note Current implementation is far from perfection. In fact no deletion
64
 #mechanisms is written and the virtual package cannot be a subpackage of
67
 #mechanisms is written and the virtual package cannot be a subpackage of
65
 #the lodel package for the moment...
68
 #the lodel package for the moment...
68
 #lodelsites/SITENAME/ folder
71
 #lodelsites/SITENAME/ folder
69
 class LodelMetaPathFinder(importlib.abc.MetaPathFinder):
72
 class LodelMetaPathFinder(importlib.abc.MetaPathFinder):
70
     
73
     
74
+    ##@brief implements the find_spec method of MetaPathFinder
75
+    #
76
+    #@param fullname str : module fullname
77
+    #@param path str : with be the value of __path__ of the parent package
78
+    #@param target module : is a module object that the finder may use to
79
+    #make a more educated guess about what spec to return
80
+    #@see https://docs.python.org/3/library/importlib.html#importlib.abc.MetaPathFinder
71
     def find_spec(fullname, path, target = None):
81
     def find_spec(fullname, path, target = None):
72
         if fullname.startswith(CTX_PKG):
82
         if fullname.startswith(CTX_PKG):
73
             spl = fullname.split('.')
83
             spl = fullname.split('.')
84
 ##@brief Class designed to handle context switching and virtual module
94
 ##@brief Class designed to handle context switching and virtual module
85
 #exposure
95
 #exposure
86
 #
96
 #
97
+#The main entrypoint of this class is the expose_module method. A kind of
98
+#equivalent of the various import X [as Y], from X import Y [as Z] etc.
99
+#existing in Python.
100
+#The expose_module method add a preffix to the module fullname in order
101
+#to make it reconizable by the LodelMetaPathfinder::find_spec() method.
102
+#All module names are translated before import. The preffix is set at
103
+#__init__ call in __pkg_name. The resulting name is __pkg_name + fullname
104
+#
105
+#@par examples 
106
+#When asking for lodel.leapi.leobject :
107
+#- in MONOSITE resulting module will be lodel.leapi.leobject
108
+#- in MULTISITE resulting module name will be 
109
+#lodelsites.SITE_ID.lodel.leapi.leobject
110
+#
111
+#The lodelsites package will be a subdir of buildconf.MULTISITE_CONTEXTDIR
112
+#that will be itself added to sys.path in order to be able to import
113
+#lodelsites
114
+#
115
+#This class is also responsible to expose leapi_dyncode module.
116
+#
87
 #@note a dedicated context named LOAD_CTX is used as context for the 
117
 #@note a dedicated context named LOAD_CTX is used as context for the 
88
 #loading process
118
 #loading process
89
 class LodelContext(object):
119
 class LodelContext(object):
102
 
132
 
103
     ##@brief Flag indicating if the classe is initialized
133
     ##@brief Flag indicating if the classe is initialized
104
     __initialized = False
134
     __initialized = False
105
-    
135
+
106
     ##@brief Create a new context
136
     ##@brief Create a new context
107
     #@see LodelContext.new()
137
     #@see LodelContext.new()
108
     def __init__(self, site_id, instance_path = None):
138
     def __init__(self, site_id, instance_path = None):
121
             else:
151
             else:
122
                 #More verification can be done here (singleton specs ? )
152
                 #More verification can be done here (singleton specs ? )
123
                 self.__class__._current = self.__class__._contexts = self
153
                 self.__class__._current = self.__class__._contexts = self
124
-                self.__pkg_name = 'lodel'
154
+                self.__pkg_name = ''
125
                 self.__package = lodel
155
                 self.__package = lodel
126
                 self.__instance_path = os.getcwd()
156
                 self.__instance_path = os.getcwd()
127
                 return
157
                 return
137
                 raise ContextError(
167
                 raise ContextError(
138
                     "A context named '%s' allready exists." % site_id)
168
                     "A context named '%s' allready exists." % site_id)
139
             self.__id = site_id
169
             self.__id = site_id
140
-            self.__pkg_name = '%s.%s' % (CTX_PKG, site_id)
170
+            self.__pkg_name = '%s.%s.%s.' % (CTX_PKG, site_id)
141
 
171
 
142
             if instance_path is None:
172
             if instance_path is None:
143
                 """
173
                 """
208
         return cls.get(target_ctx_id)
238
         return cls.get(target_ctx_id)
209
 
239
 
210
     ##@brief Set a context as active
240
     ##@brief Set a context as active
241
+    #
242
+    #This method handle the context switching operations. Some static 
243
+    #attributes are set at this step.
244
+    #@note if not in LOAD_CTX a sys.path update is done
245
+    #@warning Inconsistency with lodelsites_datasource, we build again the
246
+    #site context dir path using site_id. This information should come
247
+    #from only one source
211
     #@param site_id str : site identifier (identify a context)
248
     #@param site_id str : site identifier (identify a context)
249
+    #@todo unify the generation of the site specific context dir path
212
     @classmethod
250
     @classmethod
213
     def set(cls, site_id):
251
     def set(cls, site_id):
214
         if cls._type == cls.MONOSITE:
252
         if cls._type == cls.MONOSITE:
215
             raise ContextError("Context cannot be set in MONOSITE beahvior")
253
             raise ContextError("Context cannot be set in MONOSITE beahvior")
254
+
216
         site_id = LOAD_CTX if site_id is None else site_id
255
         site_id = LOAD_CTX if site_id is None else site_id
217
         if not cls.validate_identifier(site_id):
256
         if not cls.validate_identifier(site_id):
218
             raise ContextError("Given context name is not a valide identifier \
257
             raise ContextError("Given context name is not a valide identifier \
292
     ##@brief Expose leapi_dyncode module
331
     ##@brief Expose leapi_dyncode module
293
     @classmethod
332
     @classmethod
294
     def expose_dyncode(cls, globs, alias = 'leapi_dyncode'):
333
     def expose_dyncode(cls, globs, alias = 'leapi_dyncode'):
295
-        cls.get()._expose_dyncode(globs, alias)
334
+        cls.get().expose_module(globs, 'leapi_dyncode')
296
 
335
 
297
     ##@brief Initialize the context manager
336
     ##@brief Initialize the context manager
298
     #
337
     #
299
     #@note Add the LodelMetaPathFinder class to sys.metapath if type is
338
     #@note Add the LodelMetaPathFinder class to sys.metapath if type is
300
     #LodelContext.MULTISITE
339
     #LodelContext.MULTISITE
340
+    #@note lodelsites package name is hardcoded and has to be
301
     #@param type FLAG : takes value in LodelContext.MONOSITE or
341
     #@param type FLAG : takes value in LodelContext.MONOSITE or
302
     #LodelContext.MULTISITE
342
     #LodelContext.MULTISITE
303
     @classmethod
343
     @classmethod
315
             #Create and set __loader__ context
355
             #Create and set __loader__ context
316
             cls.new(LOAD_CTX)
356
             cls.new(LOAD_CTX)
317
             cls.set(LOAD_CTX)
357
             cls.set(LOAD_CTX)
358
+            #Modifying sys.path in order to be able to import 
359
+            #context specific packages
360
+            sys.path.append(MULTISITE_CONTEXTDIR)
361
+            if 'lodelsites' not in sys.modules:
362
+                import lodelsites
363
+            else:
364
+                globals()['lodelsites'] = sys.modules['lodelsites']
318
         else:
365
         else:
319
             #Add a single context with no site_id
366
             #Add a single context with no site_id
320
             cls._contexts = cls._current = cls(None)
367
             cls._contexts = cls._current = cls(None)
420
             msg = "Module %s does not have any of [%s] as attribute" % (
467
             msg = "Module %s does not have any of [%s] as attribute" % (
421
                 fullname, ','.join(errors))
468
                 fullname, ','.join(errors))
422
             raise ImportError(msg)
469
             raise ImportError(msg)
423
-    
424
-    ##@brief Implements LodelContext::expose_dyncode()
425
-    #@todo change hardcoded leapi_dyncode.py filename
426
-    def _expose_dyncode(self, globs, alias = 'leapi_dyncode'):
427
-        fullname = '%s.%s.dyncode' % (CTX_PKG, self.__id)
428
-        if fullname in sys.modules:
429
-            dyncode = sys.modules[fullname]
430
-        else:
431
-            path = os.path.join(self.__instance_path, 'leapi_dyncode.py')
432
-            sfl = importlib.machinery.SourceFileLoader(fullname, path)
433
-            dyncode = sfl.load_module()
434
-        self.safe_exposure(globs, dyncode, alias)
435
-    
470
+
436
     ##@brief Translate a module fullname to the context equivalent
471
     ##@brief Translate a module fullname to the context equivalent
472
+    #
473
+    #Two transformation are possible :
474
+    #- we are importing a submodule of the lodel package : resulting module
475
+    #name will be : self.__pkg_name + module_fullname
476
+    #- we are importing the dyncode : resulting module name is :
477
+    #self.__pkg_name + dyncode_modulename
437
     #@param module_fullname str : a module fullname
478
     #@param module_fullname str : a module fullname
438
     #@return The module name in the current context
479
     #@return The module name in the current context
439
     def _translate(self, module_fullname):
480
     def _translate(self, module_fullname):
440
-        if not module_fullname.startswith('lodel'):
441
-            raise ContextModuleError("Given module is not lodel or any \
442
-submodule : '%s'" % module_fullname)
443
-        return module_fullname.replace('lodel', self.__pkg_name)
481
+        if not module_fullname.startswith('lodel') and \
482
+                not module_fullname.startswith('leapi_dyncode'):
483
+            raise ContextModuleError("Given module is not lodel nor dyncode \
484
+or any submodule : '%s'" % module_fullname)
485
+        return self.__pkg_name+module_fullname
444
 
486
 
445
     ##@brief Implements the with statement behavior
487
     ##@brief Implements the with statement behavior
446
     #@see https://www.python.org/dev/peps/pep-0343/
488
     #@see https://www.python.org/dev/peps/pep-0343/

Loading…
Cancel
Save