소스 검색

Merge branch 'lodelsites-dev' of git@git.labocleo.org:lodel2 into lodelsites-dev

Conflicts:
	lodel/buildconf.py.am.in
quentin 8 년 전
부모
커밋
6586fbebfc
4개의 변경된 파일150개의 추가작업 그리고 31개의 파일을 삭제
  1. 8
    2
      lodel/buildconf.py.am.in
  2. 129
    26
      lodel/context.py
  3. 12
    2
      lodel/plugins/multisite/confspecs.py
  4. 1
    1
      plugins

+ 8
- 2
lodel/buildconf.py.am.in 파일 보기

@@ -7,8 +7,14 @@ WEBUI=@WEBUI@
7 7
 LODEL2VARDIR="[@]LODEL2_VARDIR[@]"
8 8
 LODEL2LOGDIR="[@]LODEL2_LOGDIR[@]"
9 9
 
10
-import os
11 10
 
11
+<<<<<<< HEAD
12 12
 MULTISITE_DATADIR = os.path.join(LODEL2VARDIR, 'sites_datas')
13 13
 MULTISITE_CONTEXTDIR = os.path.join(LODEL2VARDIR, '.sites_context')
14
-LODEL_PKG_PATH = '/usr/lib/python3/dist-package/lodel'
14
+LODEL_PKG_PATH = '/usr/lib/python3/dist-package/lodel'
15
+=======
16
+MULTISITE_DATADIR = 'sites_datas'
17
+MULTISITE_CONTEXTDIR = '.site_contexts'
18
+MULTISITE_DYNCODE_MODULENAME = 'leapi_dyncode'
19
+
20
+>>>>>>> branch 'lodelsites-dev' of git@git.labocleo.org:lodel2

+ 129
- 26
lodel/context.py 파일 보기

@@ -16,11 +16,13 @@ if 'lodel' not in sys.modules:
16 16
 else:
17 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 22
     globals()['lodelsites'] = sys.modules['lodelsites']
23 23
 
24
+from lodel import buildconf
25
+
24 26
 ##@brief Name of the package that will contains all the virtual lodel
25 27
 #packages
26 28
 CTX_PKG = "lodelsites"
@@ -60,6 +62,7 @@ def dir_for_context(site_identifier):
60 62
 #to represent the lodel package os the FS and then we make python import
61 63
 #files from the symlink.
62 64
 #
65
+#
63 66
 #@note Current implementation is far from perfection. In fact no deletion
64 67
 #mechanisms is written and the virtual package cannot be a subpackage of
65 68
 #the lodel package for the moment...
@@ -68,6 +71,13 @@ def dir_for_context(site_identifier):
68 71
 #lodelsites/SITENAME/ folder
69 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 81
     def find_spec(fullname, path, target = None):
72 82
         if fullname.startswith(CTX_PKG):
73 83
             spl = fullname.split('.')
@@ -84,6 +94,38 @@ class LodelMetaPathFinder(importlib.abc.MetaPathFinder):
84 94
 ##@brief Class designed to handle context switching and virtual module
85 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
+#@par Notes about dyncode exposure
116
+#In MULTISITE mode the dyncode will be stored as a python module in 
117
+#buildconf.MULTISITE_CONTEXTDIR/SITE_ID/leapi_dyncode.py . The dyncode
118
+#exposale process in MULTISITE mode is simply done by asking LodelContext
119
+#to expose a module named leapi_dyncode. The LodelContext::_translate()
120
+#method is able to produce a correct name for this module.
121
+#In MONOSITE mode the dyncode will be stored as a python module in 
122
+#the site directory. In this case the _translate method will do the same
123
+#transformation than for the others modules. But in MONOSITE mode the 
124
+#module preffix is empty. Resulting in import leapi_dyncode. This will
125
+#work asserting that cwd in MONOSITE mode is the instance directory.
126
+#
127
+#
128
+#
87 129
 #@note a dedicated context named LOAD_CTX is used as context for the 
88 130
 #loading process
89 131
 class LodelContext(object):
@@ -102,7 +144,14 @@ class LodelContext(object):
102 144
 
103 145
     ##@brief Flag indicating if the classe is initialized
104 146
     __initialized = False
105
-    
147
+
148
+    ##@brief Stores path used by MULTISITE instance
149
+    #
150
+    #This variable is a tuple with 2 elements (in this order):
151
+    #- lodelsites datadir (ex: /var/lodel2/MULTISITE_NAME/datadir/)
152
+    #- lodelsites contextdir (ex: /varL/lodel2/MULTISITE_NAME/.ctx/lodelsites)
153
+    __lodelsites_paths = None
154
+
106 155
     ##@brief Create a new context
107 156
     #@see LodelContext.new()
108 157
     def __init__(self, site_id, instance_path = None):
@@ -121,7 +170,7 @@ site_id set to None when we are in MULTISITE beahavior")
121 170
             else:
122 171
                 #More verification can be done here (singleton specs ? )
123 172
                 self.__class__._current = self.__class__._contexts = self
124
-                self.__pkg_name = 'lodel'
173
+                self.__pkg_name = ''
125 174
                 self.__package = lodel
126 175
                 self.__instance_path = os.getcwd()
127 176
                 return
@@ -137,7 +186,7 @@ site_id when we are in MONOSITE beahvior")
137 186
                 raise ContextError(
138 187
                     "A context named '%s' allready exists." % site_id)
139 188
             self.__id = site_id
140
-            self.__pkg_name = '%s.%s' % (CTX_PKG, site_id)
189
+            self.__pkg_name = '%s.%s.' % (CTX_PKG, site_id)
141 190
 
142 191
             if instance_path is None:
143 192
                 """
@@ -194,6 +243,11 @@ site_id set to None when we are in MULTISITE beahavior")
194 243
                 raise ContextError("Cannot have a context with \
195 244
     site_id set when we are in MONOSITE beahavior")
196 245
     
246
+    ##@return identifier for current context
247
+    @classmethod
248
+    def current_id(cls):
249
+        return cls._current.__id
250
+
197 251
     ##@return True if the class is in MULTISITE mode
198 252
     @classmethod
199 253
     def multisite(cls):
@@ -208,20 +262,38 @@ site_id set to None when we are in MULTISITE beahavior")
208 262
         return cls.get(target_ctx_id)
209 263
 
210 264
     ##@brief Set a context as active
265
+    #
266
+    #This method handle the context switching operations. Some static 
267
+    #attributes are set at this step.
268
+    #@note if not in LOAD_CTX a sys.path update is done
269
+    #@warning Inconsistency with lodelsites_datasource, we build again the
270
+    #site context dir path using site_id. This information should come
271
+    #from only one source
211 272
     #@param site_id str : site identifier (identify a context)
273
+    #@todo unify the generation of the site specific context dir path
212 274
     @classmethod
213 275
     def set(cls, site_id):
214 276
         if cls._type == cls.MONOSITE:
215 277
             raise ContextError("Context cannot be set in MONOSITE beahvior")
278
+
216 279
         site_id = LOAD_CTX if site_id is None else site_id
217 280
         if not cls.validate_identifier(site_id):
218 281
             raise ContextError("Given context name is not a valide identifier \
219 282
 : '%s'" % site_id)
220 283
         if site_id not in cls._contexts:
221 284
             raise ContextError("No context named '%s' found." % site_id)
285
+        if cls.current_id != LOAD_CTX and site_id != LOAD_CTX:
286
+            raise ContextError("Not allowed to switch into a site context \
287
+from another site context. You have to switch back to LOAD_CTX before")
288
+        if site_id != LOAD_CTX and cls.__lodelsites_paths is None:
289
+            #The paths are not set yet. That mean that we switch into another
290
+            #context for the first time. We should be able to access the 
291
+            #LOAD_CTX settings (containing the sites_handler_name)
292
+            cls._generate_lodelsites_path()
293
+
222 294
         wanted_ctx = cls._contexts[site_id]
223 295
         if hasattr(wanted_ctx, '__instance_path'):
224
-            os.chdir(self.__instance_path) #May cause problems
296
+            os.chdir(self.__instance_path) #May cause problems and may be obsolete
225 297
         cls._current = wanted_ctx
226 298
         return cls._current
227 299
     
@@ -285,6 +357,9 @@ site_id set to None when we are in MULTISITE beahavior")
285 357
     
286 358
     ##@brief Return a module from current context
287 359
     #@param fullname str : module fullname
360
+    #@todo check if not globals are set when getting a module ! (if so
361
+    #checks all calls to this method to check that this assertion was not 
362
+    #made)
288 363
     @classmethod
289 364
     def module(cls, fullname):
290 365
         return cls.get().get_module(fullname)
@@ -292,12 +367,13 @@ site_id set to None when we are in MULTISITE beahavior")
292 367
     ##@brief Expose leapi_dyncode module
293 368
     @classmethod
294 369
     def expose_dyncode(cls, globs, alias = 'leapi_dyncode'):
295
-        cls.get()._expose_dyncode(globs, alias)
370
+        cls.get().expose_module(globs, 'leapi_dyncode')
296 371
 
297 372
     ##@brief Initialize the context manager
298 373
     #
299 374
     #@note Add the LodelMetaPathFinder class to sys.metapath if type is
300 375
     #LodelContext.MULTISITE
376
+    #@note lodelsites package name is hardcoded and has to be
301 377
     #@param type FLAG : takes value in LodelContext.MONOSITE or
302 378
     #LodelContext.MULTISITE
303 379
     @classmethod
@@ -315,6 +391,7 @@ initialize it anymore")
315 391
             #Create and set __loader__ context
316 392
             cls.new(LOAD_CTX)
317 393
             cls.set(LOAD_CTX)
394
+            
318 395
         else:
319 396
             #Add a single context with no site_id
320 397
             cls._contexts = cls._current = cls(None)
@@ -386,8 +463,39 @@ MONOSITE mode")
386 463
                 "Unable to create a context named '%s'" % site_id)
387 464
         cls.new(site_id, path)
388 465
         return site_id
466
+    
467
+    ##@brief Return a tuple containing lodelsites datadir & contextdir (
468
+    #in this order)
469
+    @classmethod
470
+    def lodelsites_paths(cls):
471
+        return copy.copy(cls.__lodelsites_paths)
389 472
 
390
-
473
+    ##@brief Generate lodelsites paths from LOAD_CTX configuration
474
+    #
475
+    #@warning Handles import of the lodelsites package
476
+    #
477
+    #Called when the first context switch outside LOAD_CTX is done
478
+    @classmethod
479
+    def _generate_lodelsites_path(cls):
480
+        if cls.__current.__id != LOAD_CTX:
481
+            raise ContextError("Not allowed when not in LOAD_CTX !!!")
482
+        if cls.__lodelsites_paths is not None:
483
+            raise ContextError("Allready done !!!")
484
+        load_ctx_settings = cls.module('lodel.settings.setting.SettingsRO')
485
+        lodesites_name = load_ctx_settings.lodelsites.name
486
+        del(globals()['lodel.settings.setting.SettingsRO']) #Not sure !
487
+        lodelsites_path = os.path.join(
488
+            buildconf.LODEL2VARDIR, lodelsites_name)
489
+        cls.__lodelsites_paths = (
490
+            os.path.join(lodelsites_path, buildconf.MULTISITE_DATADIR),
491
+            os.path.join(lodelsites_path, os.path.join(
492
+                buildconf.MULTISITE_CONTEXTDIR, 'lodelsites')))
493
+        #Now we are able to import lodelsites package
494
+        sys.path.append(cls.__lodelsites_paths[1])
495
+        if 'lodelsites' not in sys.modules:
496
+            import lodelsites
497
+        else:
498
+            globals()['lodelsites'] = sys.modules['lodelsites']
391 499
 
392 500
     ##@brief Utility method to expose a module with an alias name in globals
393 501
     #@param globs globals() : concerned globals dict
@@ -420,27 +528,22 @@ MONOSITE mode")
420 528
             msg = "Module %s does not have any of [%s] as attribute" % (
421 529
                 fullname, ','.join(errors))
422 530
             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
-    
531
+
436 532
     ##@brief Translate a module fullname to the context equivalent
533
+    #
534
+    #Two transformation are possible :
535
+    #- we are importing a submodule of the lodel package : resulting module
536
+    #name will be : self.__pkg_name + module_fullname
537
+    #- we are importing the dyncode : resulting module name is :
538
+    #self.__pkg_name + dyncode_modulename
437 539
     #@param module_fullname str : a module fullname
438 540
     #@return The module name in the current context
439 541
     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)
542
+        if not module_fullname.startswith('lodel') and \
543
+                not module_fullname.startswith('leapi_dyncode'):
544
+            raise ContextModuleError("Given module is not lodel nor dyncode \
545
+or any submodule : '%s'" % module_fullname)
546
+        return self.__pkg_name+module_fullname
444 547
 
445 548
     ##@brief Implements the with statement behavior
446 549
     #@see https://www.python.org/dev/peps/pep-0343/

+ 12
- 2
lodel/plugins/multisite/confspecs.py 파일 보기

@@ -6,8 +6,18 @@ LodelContext.expose_modules(globals(), {
6 6
 LODEL2_CONFSPECS = {
7 7
     'lodel2': {
8 8
         'debug': (True, SettingValidator('bool')),
9
-        'sites_handler_name': (None,
10
-            SettingValidator('string', none_is_valid = False)),
9
+    },
10
+    'lodel2.lodelsites': {
11
+        'name': (None,
12
+            SettingValidator('string', none_is_valid = False)), #Bad validator
13
+        'lodelsites_emfile': (None,
14
+            SettingValidator('string', none_is_valid = False)), #Bad validator
15
+        'lodelsites_emtranslator': ('picklefile',
16
+            SettingValidator('strip', none_is_valid = False)), #Bad validator
17
+        'sites_emfile': (None,
18
+            SettingValidator('string', none_is_valid = False)), #Bad validator
19
+        'site_emtranslator': ('picklefile',
20
+            SettingValidator('string', none_is_valid = False)), #Bad validator
11 21
     },
12 22
     'lodel2.server': {
13 23
         'listen_address': ('127.0.0.1', SettingValidator('dummy')),

+ 1
- 1
plugins 파일 보기

@@ -1 +1 @@
1
-lodel/plugins/
1
+lodel/plugins

Loading…
취소
저장