Procházet zdrojové kódy

Merge branch 'newlodel' of git.labocleo.org:lodel2 into newlodel

prieto před 7 roky
rodič
revize
a774b016c3

+ 50
- 1
lodel/editorial_model/__init__.py Zobrazit soubor

@@ -1 +1,50 @@
1
-__author__ = 'roland'
1
+##@defgroup lodel2_em Editorial Model
2
+#@brief Data organisation description
3
+
4
+##@package lodel.editorial_model
5
+#@brief Editorial model package
6
+#
7
+#The editorial model defines objects with fields. This objects will be later
8
+#manipulated via @ref lodel.leapi "LeAPI"
9
+
10
+##@page lodel2_em_page Editorial Model
11
+#@ingroup lodel2_em
12
+#
13
+#@section lodel2_em_what What is an editorial model ?
14
+#
15
+#The editorial model is a kind of entity-relationship model describing
16
+#editorial objects.
17
+#
18
+#@warning The lodel.editorial_model package does not contains code executed by
19
+#instances. The editorial model is used to generate python code named 
20
+#@ref lodel2_leapi "LeAPI" and the package contains Classes that made easy
21
+#the Em manipulation
22
+#
23
+# @subsection lodel2_em_class EmClass
24
+#
25
+#An editorial object is named @ref components.EmClass "EmClass" for "Editorial
26
+# Model Class". A class is characterized by a uniq name a 
27
+#@ref lodel2_datasources "Datasource", a group, an optionnal parent EmClass and
28
+#the @ref components.EmField "EmFields" it contains.
29
+#
30
+#@par Example
31
+#<code>An "Article" is an EmClass with "article" as name, the EmClass "Text" as
32
+#parent etc...</code>
33
+#
34
+# @subsection lodel2_em_field EmField
35
+#
36
+#@ref components.EmClass "EmClasses" contains 
37
+#@ref components.EmField "EmFields". An EmField as a name (uniq in the EmClass)
38
+# and is associated to a @ref lodel2_datahandlers "DataHandler" specification.
39
+#
40
+# @subsection lodel2_em_group EmGroup
41
+#
42
+#@ref components.EmClass "EmClasses" and @ref components.EmField "EmFields"
43
+#are in @ref components.EmGroup "EmGroups". EmGroups represent a consistent
44
+#EditorialModel part that can be activated or deactivated.
45
+#
46
+#@par Example
47
+#<pre>EmGroup "authors" contains the EmClass authors and all its EmField
48
+# + "written_by" EmField (a reference field on "Author" EmClass) in the EmClass
49
+#"Text"</pre>
50
+#

+ 8
- 0
lodel/editorial_model/components.py Zobrazit soubor

@@ -1,5 +1,9 @@
1 1
 #-*- coding: utf-8 -*-
2 2
 
3
+##@package lodel.editorial_model.components
4
+#@brief Defines all @ref lodel2_em "EM" components
5
+#@ingroup lodel2_em
6
+
3 7
 import itertools
4 8
 import warnings
5 9
 import copy
@@ -14,6 +18,7 @@ from lodel.leapi.leobject import CLASS_ID_FIELDNAME
14 18
 ##@brief Abstract class to represent editorial model components
15 19
 # @see EmClass EmField
16 20
 # @todo forbid '.' in uid
21
+#@ingroup lodel2_em
17 22
 class EmComponent(object):
18 23
     
19 24
     ##@brief Instanciate an EmComponent
@@ -46,6 +51,7 @@ class EmComponent(object):
46 51
 
47 52
 
48 53
 ##@brief Handles editorial model objects classes
54
+#@ingroup lodel2_em
49 55
 class EmClass(EmComponent):
50 56
     
51 57
     ##@brief Instanciate a new EmClass
@@ -206,6 +212,7 @@ class EmClass(EmComponent):
206 212
 
207 213
 
208 214
 ##@brief Handles editorial model classes fields
215
+#@ingroup lodel2_em
209 216
 class EmField(EmComponent):
210 217
 
211 218
     ##@brief Instanciate a new EmField
@@ -259,6 +266,7 @@ class EmField(EmComponent):
259 266
         ).digest(), byteorder='big')
260 267
 
261 268
 ##@brief Handles functionnal group of EmComponents
269
+#@ingroup lodel2_em
262 270
 class EmGroup(object):
263 271
         
264 272
     ##@brief Create a new EmGroup

+ 1
- 0
lodel/editorial_model/model.py Zobrazit soubor

@@ -13,6 +13,7 @@ from lodel.editorial_model.exceptions import *
13 13
 from lodel.editorial_model.components import EmClass, EmField, EmGroup
14 14
 
15 15
 ##@brief Describe an editorial model
16
+#@ingroup lodel2_em
16 17
 class EditorialModel(object):
17 18
     
18 19
     ##@brief Create a new editorial model

+ 208
- 1
lodel/leapi/__init__.py Zobrazit soubor

@@ -1 +1,208 @@
1
-__author__ = 'roland'
1
+##@defgroup lodel2_leapi LeAPI
2
+#@brief Lodel2 Editorial API
3
+#
4
+#Provide access to datas via objects as defined in the Editorial Model
5
+
6
+##@package lodel.leapi
7
+#@brief Lodel2 Editorial API
8
+#
9
+# Defines an API to access to objects as described by 
10
+#@ref lodel.editorial_model "Editorial Model"
11
+
12
+##@page lodel2_leapi_page LeAPI
13
+#@ingroup lodel2_leapi
14
+#
15
+# @section lodel2_leapi_base LeAPI (Lodel Editorial API)
16
+#
17
+# LeAPI is an API that provide access to datas as defined in an 
18
+#@ref lodel2_em "Editorial Model".
19
+#
20
+#@subsection lodel2_leapi_gen LeAPI dynamic code & LeFactory
21
+#
22
+#LeAPI python code is programmaticaly generated by 
23
+#@ref lodel.leapi.lefactory "LeFactory". LeFactory generates 
24
+#@ref lodel.leapi.leobject.LeObject "LeObject" child classes.
25
+#
26
+#@par Example
27
+#<pre>
28
+#
29
+#    from lodel.leapi.leobject import LeObject
30
+#    from lodel.leapi.datahandlers.base_classes import DataField
31
+#    from lodel.plugin.hooks import LodelHook
32
+#
33
+#    class Abstract_Object(LeObject):
34
+#        _abstract = True
35
+#        _fields = None
36
+#        _uid = []
37
+#        _ro_datasource = None
38
+#        _rw_datasource = None
39
+#        _datasource_name = 'default'
40
+#        _child_classes = None
41
+#
42
+#
43
+#    class User(LeObject):
44
+#        _abstract = False
45
+#        _fields = None
46
+#        _uid = ['id']
47
+#        _ro_datasource = None
48
+#        _rw_datasource = None
49
+#        _datasource_name = 'default'
50
+#        _child_classes = None
51
+#
52
+#
53
+#    class Object(Abstract_Object):
54
+#        _abstract = True
55
+#        _fields = None
56
+#        _uid = ['lodel_id']
57
+#        _ro_datasource = None
58
+#        _rw_datasource = None
59
+#        _datasource_name = 'default'
60
+#        _child_classes = None
61
+#
62
+#
63
+#    class Entry(Object):
64
+#        _abstract = True
65
+#        _fields = None
66
+#        _uid = ['lodel_id']
67
+#        _ro_datasource = None
68
+#        _rw_datasource = None
69
+#        _datasource_name = 'default'
70
+#        _child_classes = None
71
+#
72
+#
73
+#    class Entitie(Object):
74
+#        _abstract = True
75
+#        _fields = None
76
+#        _uid = ['lodel_id']
77
+#        _ro_datasource = None
78
+#        _rw_datasource = None
79
+#        _datasource_name = 'default'
80
+#        _child_classes = None
81
+#
82
+#
83
+#    class Person(Object):
84
+#        _abstract = False
85
+#        _fields = None
86
+#        _uid = ['lodel_id']
87
+#        _ro_datasource = None
88
+#        _rw_datasource = None
89
+#        _datasource_name = 'default'
90
+#        _child_classes = None
91
+#
92
+#    class Text(Entitie):
93
+#        _abstract = True
94
+#        _fields = None
95
+#        _uid = ['lodel_id']
96
+#        _ro_datasource = None
97
+#        _rw_datasource = None
98
+#        _datasource_name = 'default'
99
+#        _child_classes = None
100
+#
101
+#
102
+#    class Publication(Entitie):
103
+#        _abstract = False
104
+#        _fields = None
105
+#        _uid = ['lodel_id']
106
+#        _ro_datasource = None
107
+#        _rw_datasource = None
108
+#        _datasource_name = 'default'
109
+#        _child_classes = None
110
+#
111
+#    Abstract_Object._set__fields({})
112
+#    Abstract_Object._child_classes = (Section, Text, Object, Entry, Collection, Text_Person, Entitie, Indextheme, Person, Indexabs, Publication, Subsection,)
113
+#    User._set__fields({
114
+#            'firstname': DataField.from_name('varchar')(**{ 'internal': False }), 
115
+#            'lastname': DataField.from_name('varchar')(**{ 'internal': False }), 
116
+#            'classname': DataField.from_name('LeobjectSubclassIdentifier')(**{ 'internal': True }), 
117
+#            'login': DataField.from_name('varchar')(**{ 'internal': True, 'uniq': True }), 
118
+#            'id': DataField.from_name('uniqid')(**{ 'internal': True }), 
119
+#            'password': DataField.from_name('password')(**{ 'internal': False })})
120
+#    User._child_classes = tuple()
121
+#    Object._set__fields({
122
+#            'date_update': DataField.from_name('datetime')(**{ 'now_on_update': True, 'internal': True }), 
123
+#            'lodel_id': DataField.from_name('uniqid')(**{ 'internal': True }), 
124
+#            'help_text': DataField.from_name('text')(**{ 'internal': True }), 
125
+#            'date_create': DataField.from_name('datetime')(**{ 'internal': True, 'now_on_create': True })})
126
+#    Object._child_classes = (Section, Text, Entry, Collection, Text_Person, Entitie, Indextheme, Person, Indexabs, Publication, Subsection,)
127
+#    Entry._set__fields({
128
+#            'date_update': DataField.from_name('datetime')(**{ 'now_on_update': True, 'internal': True }), 
129
+#            'lodel_id': DataField.from_name('uniqid')(**{ 'internal': True }), 
130
+#            'help_text': DataField.from_name('text')(**{ 'internal': True }), 
131
+#            'date_create': DataField.from_name('datetime')(**{ 'internal': True, 'now_on_create': True })})
132
+#    Entry._child_classes = tuple()
133
+#    Entitie._set__fields({
134
+#            'date_update': DataField.from_name('datetime')(**{ 'now_on_update': True, 'internal': True }), 
135
+#            'lodel_id': DataField.from_name('uniqid')(**{ 'internal': True }), 
136
+#            'help_text': DataField.from_name('text')(**{ 'internal': True }), 
137
+#            'date_create': DataField.from_name('datetime')(**{ 'internal': True, 'now_on_create': True })})
138
+#    Entitie._child_classes = (Section, Text, Text_Person, Collection, Publication, Subsection,)
139
+#    Person._set__fields({
140
+#            'date_update': DataField.from_name('datetime')(**{ 'now_on_update': True, 'internal': True }), 
141
+#            'firstname': DataField.from_name('varchar')(**{  }), 
142
+#            'lodel_id': DataField.from_name('uniqid')(**{ 'internal': True }), 
143
+#            'linked_texts': DataField.from_name('list')(**{ 'nullable': True, 'default': None, 'allowed_classes': [Text], 'back_reference': (Text, 'linked_persons') }), 
144
+#            'help_text': DataField.from_name('text')(**{ 'internal': True }), 
145
+#            'lastname': DataField.from_name('varchar')(**{  }), 
146
+#            'fullname': DataField.from_name('Concat')(**{ 'field_list': ['firstname', 'lastname'], 'immutable': True }), 
147
+#            'classname': DataField.from_name('LeobjectSubclassIdentifier')(**{ 'internal': True }), 
148
+#            'alias': DataField.from_name('set')(**{ 'nullable': True, 'default': None, 'allowed_classes': [Person] }), 
149
+#            'date_create': DataField.from_name('datetime')(**{ 'internal': True, 'now_on_create': True })})
150
+#    Person._child_classes = tuple()
151
+#    Text._set__fields({
152
+#            'date_update': DataField.from_name('datetime')(**{ 'now_on_update': True, 'internal': True }), 
153
+#            'subtitle': DataField.from_name('varchar')(**{ 'nullable': True, 'default': None }), 
154
+#            'lodel_id': DataField.from_name('uniqid')(**{ 'internal': True }), 
155
+#            'help_text': DataField.from_name('text')(**{ 'internal': True }), 
156
+#            'linked_persons': DataField.from_name('list')(**{ 'nullable': True, 'default': None, 'allowed_classes': [Person], 'back_reference': (Person, 'linked_texts') }), 
157
+#            'indexes': DataField.from_name('list')(**{ 'nullable': True, 'default': None, 'allowed_classes': [Indexabs], 'back_reference': (Indexabs, 'texts') }), 
158
+#            'title': DataField.from_name('varchar')(**{ 'nullable': True }), 
159
+#            'date_create': DataField.from_name('datetime')(**{ 'internal': True, 'now_on_create': True })})
160
+#    Text._child_classes = (Section, Subsection,)
161
+#    Publication._set__fields({
162
+#            'date_update': DataField.from_name('datetime')(**{ 'now_on_update': True, 'internal': True }), 
163
+#            'classname': DataField.from_name('LeobjectSubclassIdentifier')(**{ 'internal': True }), 
164
+#            'lodel_id': DataField.from_name('uniqid')(**{ 'internal': True }), 
165
+#            'collection': DataField.from_name('link')(**{ 'back_reference': (Collection, 'publications') }), 
166
+#            'help_text': DataField.from_name('text')(**{ 'internal': True }), 
167
+#            'date_create': DataField.from_name('datetime')(**{ 'internal': True, 'now_on_create': True })})
168
+#    Publication._child_classes = tuple()
169
+#
170
+#    #List of dynamically generated classes
171
+#    dynclasses = [Abstract_Object, User, Object, Entry, Entitie, Person, Indexabs, Text, Publication, Collection, Indextheme, Text_Person, Section, Subsection]
172
+#    #Dict of dynamically generated classes indexed by name
173
+#    dynclasses_dict = {'Abstract_Object': Abstract_Object, 'User': User, 'Object': Object, 'Entry': Entry, 'Entitie': Entitie, 'Person': Person, 'Indexabs': Indexabs, 'Text': Text, 'Publication': Publication, 'Collection': Collection, 'Indextheme': Indextheme, 'Text_Person': Text_Person, 'Section': Section, 'Subsection': Subsection}
174
+#
175
+#
176
+#    ##@brief Return a dynamically generated class given it's name
177
+#    #@param name str : The dynamic class name
178
+#    #@return False or a child class of LeObject
179
+#    def name2class(name):
180
+#        if name not in dynclasses_dict:
181
+#            return False
182
+#        return dynclasses_dict[name]
183
+#
184
+#
185
+#    ##@brief Return a dynamically generated class given it's name
186
+#    #@note Case insensitive version of name2class
187
+#    #@param name str
188
+#    #@retrun False or a child class of LeObject
189
+#    def lowername2class(name):
190
+#        name = name.lower()
191
+#        new_dict = {k.lower():v for k,v in dynclasses_dict.items()}
192
+#        if name not in new_dict:
193
+#            return False
194
+#        return new_dict[name]
195
+#
196
+#
197
+#    ##@brief Trigger dynclasses datasources initialisation
198
+#    @LodelHook("lodel2_plugins_loaded")
199
+#    def lodel2_dyncode_datasources_init(self, caller, payload):
200
+#        for cls in dynclasses:
201
+#            cls._init_datasources()
202
+#        from lodel.plugin.hooks import LodelHook
203
+#        LodelHook.call_hook("lodel2_dyncode_loaded", __name__, dynclasses)
204
+#
205
+#
206
+#
207
+#</pre>
208
+#

+ 1
- 1
lodel/leapi/leobject.py Zobrazit soubor

@@ -116,7 +116,7 @@ class LeObject(object):
116 116
     #   Fields datas handling methods   #
117 117
     #-----------------------------------#
118 118
 
119
-    ##@brief @property True if LeObject is initialized else False
119
+    ##@brief Property method True if LeObject is initialized else False
120 120
     @property
121 121
     def initialized(self):
122 122
         return self.__is_initialized

+ 0
- 1
lodel/leapi/query.py Zobrazit soubor

@@ -454,7 +454,6 @@ the relational filter %s"
454 454
                     msg %= (ref_class.__name__, ref_field)
455 455
                     logger.debug(msg)
456 456
         if len(ref_dict) == 0:
457
-            print(ref_classes, 'DEBUG')
458 457
             return NameError(   "No field named '%s' in referenced objects [%s]"
459 458
                                 % (ref_field,
460 459
                                     ','.join([rc.__name__ for rc in ref_classes])))

+ 91
- 38
lodel/plugin/__init__.py Zobrazit soubor

@@ -1,43 +1,96 @@
1 1
 #-*- coding: utf-8 -*-
2 2
 
3
-## @page howto_writeplugin Write a plugin howto
4
-#
5
-# @section howto_writeplugin_basicstruct Plugin basic structure
6
-# A plugins is a python package that have to contains 3 files :
7
-#- <code>__init__.py</code>
8
-#- <code>main.py</code> ( defined in @ref lodel.plugin.plugins.MAIN_FILENAME )
9
-#- <code>confspec.py</code> ( defined in
10
-#@ref lodel.plugin.plugins.CONFSPEC_FILENAME )
11
-#
12
-# There is an example plugin in @ref plugins/dummy
13
-#
14
-# @subsection howto_writreplugin_confspec Plugin configuration specification
15
-# First of all a good practice is to preffix all plugin specific configuration
16
-# key with <code>lodel2.plugin.PLUGIN_NAME</code>.
17
-#
18
-# A configuration specification is a dict containing dict containing
19
-# tupe(DEFAULT_VALUE, VALIDATOR). The first level dict keys are sections, and
20
-# the dictionnary contained in it contains conf keys. More information on 
21
-# validators : @ref lodel.settings.validator
22
-# 
23
-# @subsubsection howto_writreplugin_confspec_example Example :
24
-#
25
-#A confspec that matches this peace of configuration file
26
-#<pre>
27
-#[lodel2.plugin.fooplugin]
28
-#hello = ...
29
-#foo = ...
30
-#bar = ...
31
-#</pre>
32
-#would be
33
-#<pre>
34
-#{
35
-#   'lodel2.plugin.fooplugin': {
36
-#                                   'foo': ...,
37
-#                                   'bar': ...,
38
-#                                   'hello': ..., } }
39
-#</pre>
40
-# 
3
+##@defgroup lodel2_plugins Plugins
4
+#
5
+#Groups all stuff that concerns plugins
6
+
7
+## @page plugin_doc Lodel2 plugin documentation
8
+#@ingroup lodel2_plugins
9
+# @section plugin_doc_type Plugin types
10
+#
11
+# Plugins are organized into types. A type specify a behavior. For the moment
12
+# Lodel2 has 4 plugin types :
13
+# - **datasource** : A datasource connector plugin expose CRUD operation on a
14
+#particular datasource
15
+# - **ui** : A user interface plugin provide an interface to lodel2. For the
16
+#moment two ui are implemented
17
+#  - interactive python : the default interface, provides access to LeApi 
18
+#through interactive python interpreter
19
+#  - webui : a plugin providing a web interface to lodel2
20
+# - **session_handler** : A session handler plugin expose functions that handles
21
+#user sessions.
22
+# - **extensions** : An extension plugin can define 2 kinds of objects :
23
+#  - hooks using @ref lodel.plugin.hooks.LodelHook decorator
24
+#  - custom LeApi obect methods using @ref lodel.plugin.plugins.CustomMethod 
25
+#decorator
26
+#
27
+# @subsection Lodel2 scripts
28
+#
29
+# In instances an utility is provided : @ref install.lodel_admin . This 
30
+# utility can be runned as a CLI script 
31
+#<code>usage: lodel_admin.py [-h] [-L] [ACTION] [OPTIONS [OPTIONS ...]]</code>
32
+#
33
+# Each actions is a "lodel2 script". Thoses scripts are parts of plugins.
34
+# @ref lodel2_script "More informations on lodel2 scripting utilities"
35
+#
36
+# @section plugin_doc_struct Common plugin structure
37
+#
38
+# All plugin types has to provide mandatories information in order to be 
39
+# loaded :
40
+#
41
+# - A plugin name
42
+# - A plugin version
43
+# - A confspec indicating where to find the wanted plugin list (for example 
44
+#datasources plugins list are indicated in lodel2.datasource_connectors 
45
+#configuration key see @ref datasource_plugin.DatasourcePlugin::_plist_confspecs ). In
46
+#fact settings MUST begin by loading wanted plugin list in order to build
47
+#a "full" confspec
48
+# - A confspec indicating the plugins allowed settings (will be merged with
49
+#lodel2 confspecs)
50
+# - A loader module filename. This module is imported once settings are
51
+#fully bootstraped and loader. It triggers the module "startup".
52
+#
53
+# In order to provide this informations the develloper can use the plugin's
54
+#package <code>__init__.py</code> file. Some informations are stored in 
55
+#variables in this file. Available variables are documented in 
56
+#@ref plugin_init_specs . Here a list of basics variables :
57
+# - the plugin's name @ref plugins.PLUGIN_NAME_VARNAME
58
+# - the plugin's version @ref plugins.PLUGIN_VERSION_VARNAME
59
+# - the plugin's loader filename @ref plugins.LOADER_FILENAME_VARNAME
60
+# - the plugin's confspec filename @ref plugins.CONFSPEC_FILENAME_VARNAME (
61
+#set this variable only if you want your confspecs to be in a separated file,
62
+#else you can put the confspecs directly in a CONFSPEC variable in the
63
+#<code>__init__.py</code> file see @ref plugins.CONFSPEC_VARNAME )
64
+# - the plugin's type @ref plugins.PLUGIN_TYPE_VARNAME (if not set use 
65
+# @ref plugins.DEFAULT_PLUGIN_TYPE )
66
+# - the plugin's dependencies list @ref plugins.PLUGIN_DEPS_VARNAME
67
+#
68
+# This was the variable specification of the <code>__init__.py</code> file.
69
+#plugins can provide (in the same file) an _activate function ( 
70
+#<code>def _activate(): returns bool</code>) that return True if the plugin
71
+#is activable else False
72
+#
73
+#An example dummy plugin exists in @ref plugins.dummy
74
+#
75
+#@section plugin_doc_childclasses Plugin types implementation
76
+#
77
+# Concretely a plugin type is a child class of @ref plugins.Plugin . Plugin 
78
+# type registration is done automatically using a metaclass for 
79
+# @ref plugins.Plugin : @ref plugins.MetaPlugType . Doing this way
80
+# plugin's type list is automatically generated.
81
+#@note Plugin type handling is not fully automatic because child classes files
82
+#are not automaticaaly imported. We have to add an import instruction into 
83
+#@ref plugin file in order to trigger the registration
84
+#
85
+#The Plugin child class must set the _plist_confspecs class attribute.
86
+#
87
+#More informations :
88
+# - @ref lodel.plugin.datasource_plugin.DatasourcePlugin "DatasourcePlugin"
89
+#  - @ref lodel2_datasources "datasources"
90
+# - @ref lodel.plugin.extensions.Extension "Extensions"
91
+# - @ref lodel.plugin.interface.InterfacePlugin "InterfacePlugin"
92
+# - @ref lodel.plugin.sessionhandler.SessionHandlerPlugin "SessionHandlerPlugin"
93
+#
41 94
 
42 95
 from .hooks import LodelHook
43 96
 from .plugins import Plugin, CustomMethod

+ 1
- 0
lodel/plugin/core_hooks.py Zobrazit soubor

@@ -6,6 +6,7 @@ from lodel import logger
6 6
 
7 7
 ##@package lodel.plugin.core_hooks
8 8
 #@brief Lodel2 internal hooks declaration
9
+#@ingroup lodel2_plugins
9 10
 
10 11
 ##@brief Bootstrap hook to check datasources configuration
11 12
 @LodelHook('lodel2_bootstraped')

+ 9
- 1
lodel/plugin/core_scripts.py Zobrazit soubor

@@ -1,8 +1,16 @@
1 1
 import lodel.plugin.scripts as lodel_script
2 2
 
3
+##@package lodel.plugin.core_scripts
4
+#@brief Lodel2 internal scripts declaration
5
+#@ingroup lodel2_plugins
6
+#@ingroup lodel2_script
7
+
8
+
3 9
 ##@brief Implements lodel_admin.py discover-plugin action
10
+#@ingroup lodel2_plugins
11
+#@ingroup lodel2_script
4 12
 #
5
-#In depth directory scan to find plugins.
13
+#In depth directory scan to find plugins in order to build a plugin list.
6 14
 class DiscoverPlugin(lodel_script.LodelScript):
7 15
     _action = 'discover-plugin'
8 16
     _description = 'Walk through given folders looking for plugins'

+ 96
- 5
lodel/plugin/datasource_plugin.py Zobrazit soubor

@@ -6,9 +6,14 @@ from lodel.settings.validator import SettingValidator
6 6
 #
7 7
 #A datasource provide data access to LeAPI typically a connector on a DB
8 8
 #or an API
9
+#
10
+#Provide methods to initialize datasource attribute in LeAPI LeObject child
11
+#classes (see @ref leapi.leobject.LeObject._init_datasources() )
12
+#
9 13
 #@note For the moment implementation is done with a retro-compatibilities
10 14
 #priority and not with a convenience priority.
11 15
 #@todo Refactor and rewrite lodel2 datasource handling
16
+#@todo Write abstract classes for Datasource and MigrationHandler !!!
12 17
 class DatasourcePlugin(Plugin):
13 18
     
14 19
     ##@brief Stores confspecs indicating where DatasourcePlugin list is stored
@@ -18,16 +23,23 @@ class DatasourcePlugin(Plugin):
18 23
         'default': None,
19 24
         'validator': SettingValidator('strip', none_is_valid = False) }
20 25
     _type_conf_name = 'datasource'
21
-    
26
+ 
27
+    ##@brief Construct a DatasourcePlugin 
28
+    #@param name str : plugin name
29
+    #@see plugins.Plugin
22 30
     def __init__(self, name):
23 31
         super().__init__(name)
24 32
         self.__datasource_cls = None
25
-
33
+    
34
+    ##@brief Accessor to the datasource class
35
+    #@return A python datasource class
26 36
     def datasource_cls(self):
27 37
         if self.__datasource_cls is None:
28 38
             self.__datasource_cls = self.loader_module().Datasource
29 39
         return self.__datasource_cls
30
-
40
+    
41
+    ##@brief Accessor to migration handler class
42
+    #@return A python migration handler class
31 43
     def migration_handler_cls(self):
32 44
         return self.loader_module().migration_handler_class()
33 45
 
@@ -139,8 +151,87 @@ but %s is a %s" % (ds_name, pinstance.__class__.__name__))
139 151
     @classmethod
140 152
     def get_datasource(cls, ds_plugin_name):
141 153
         return cls.get(ds_plugin_name).datasource_cls()
142
-
154
+    
155
+    ##@brief Given a plugin name returns a migration handler class
156
+    #@param ds_plugin_name str : a datasource plugin name
143 157
     @classmethod
144 158
     def get_migration_handler(cls, ds_plugin_name):
145 159
         return cls.get(ds_plugin_name).migration_handler_cls()
146
- 
160
+
161
+##@page lodel2_datasources Lodel2 datasources
162
+#
163
+#@par lodel2_datasources_intro Intro
164
+# A single lodel2 website can interact with multiple datasources. This page
165
+# aims to describe configuration & organisation of datasources in lodel2.
166
+# Each object is attached to a datasource. This association is done in the
167
+# editorial model, the datasource is identified by a name.
168
+#
169
+#@par Datasources declaration
170
+# To define a datasource you have to write something like this in confs file :
171
+#<pre>
172
+#[lodel2.datasources.DATASOURCE_NAME]
173
+#identifier = DATASOURCE_FAMILY.SOURCE_NAME
174
+#</pre>
175
+# See below for DATASOURCE_FAMILY & SOURCE_NAME
176
+#
177
+#@par Datasources plugins
178
+# Each datasource family is a plugin ( 
179
+#@ref plugin_doc "More informations on plugins" ). For example mysql or a 
180
+#mongodb plugins. Here is the CONFSPEC variable templates for datasources 
181
+#plugin
182
+#<pre>
183
+#CONFSPEC = {
184
+#                'lodel2.datasource.example.*' : {
185
+#                    'conf1' : VALIDATOR_OPTS,
186
+#                    'conf2' : VALIDATOR_OPTS,
187
+#                    ...
188
+#                }
189
+#}
190
+#</pre>
191
+#MySQL example
192
+#<pre>
193
+#CONFSPEC = {
194
+#                'lodel2.datasource.mysql.*' : {
195
+#                    'host': (   'localhost',
196
+#                                SettingValidator('host')),
197
+#                    'db_name': (    'lodel',
198
+#                                    SettingValidator('string')),
199
+#                    'username': (   None,
200
+#                                    SettingValidator('string')),
201
+#                    'password': (   None,
202
+#                                    SettingValidator('string')),
203
+#                }
204
+#}
205
+#</pre>
206
+#
207
+#@par Configuration example
208
+#<pre>
209
+# [lodel2.datasources.main]
210
+# identifier = mysql.Core
211
+# [lodel2.datasources.revues_write]
212
+# identifier = mysql.Revues
213
+# [lodel2.datasources.revues_read]
214
+# identifier = mysql.Revues
215
+# [lodel2.datasources.annuaire_persons]
216
+# identifier = persons_web_api.example
217
+# ;
218
+# ; Then, in the editorial model you are able to use "main", "revues_write", 
219
+# ; etc as datasource
220
+# ;
221
+# ; Here comes the datasources declarations
222
+# [lodel2.datasource.mysql.Core]
223
+# host = db.core.labocleo.org
224
+# db_name = core
225
+# username = foo
226
+# password = bar
227
+# ;
228
+# [lodel2.datasource.mysql.Revues]
229
+# host = revues.org
230
+# db_name = RO
231
+# username = foo
232
+# password = bar
233
+# ;
234
+# [lodel2.datasource.persons_web_api.example]
235
+# host = foo.bar
236
+# username = cleo
237
+#</pre>

+ 1
- 0
lodel/plugin/hooks.py Zobrazit soubor

@@ -27,6 +27,7 @@ class DecoratedWrapper(object):
27 27
             self._hook.__name__, self._priority)
28 28
 
29 29
 ##@brief Decorator designed to register hook's callbacks
30
+#@ingroup lodel2_plugins
30 31
 #
31 32
 # @note Decorated functions are expected to take 3 arguments :
32 33
 #  - hook_name : the called hook name

+ 62
- 10
lodel/plugin/plugins.py Zobrazit soubor

@@ -14,6 +14,7 @@ from .exceptions import *
14 14
 from lodel.exceptions import *
15 15
 
16 16
 ## @package lodel.plugins Lodel2 plugins management
17
+#@ingroup lodel2_plugins
17 18
 #
18 19
 # Lodel2 plugins are stored in directories
19 20
 # A typicall lodel2 plugin directory structure looks like :
@@ -21,31 +22,54 @@ from lodel.exceptions import *
21 22
 # - main.py containing hooks registration etc
22 23
 # - confspec.py containing a configuration specification dictionary named CONFSPEC
23 24
 
25
+##@defgroup plugin_init_specs Plugins __init__.py specifications
26
+#@ingroup lodel2_plugins
27
+#@{
28
+
24 29
 ##@brief The package in which we will load plugins modules
25 30
 VIRTUAL_PACKAGE_NAME = 'lodel.plugins'
26 31
 ##@brief The temporary package to import python sources
27 32
 VIRTUAL_TEMP_PACKAGE_NAME = 'lodel.plugin_tmp'
28 33
 ##@brief Plugin init filename
29 34
 INIT_FILENAME = '__init__.py' # Loaded with settings
35
+##@brief Name of the variable containing the plugin name
30 36
 PLUGIN_NAME_VARNAME = '__plugin_name__'
37
+##@brief Name of the variable containing the plugin type
31 38
 PLUGIN_TYPE_VARNAME = '__plugin_type__'
39
+##@brief Name of the variable containing the plugin version
32 40
 PLUGIN_VERSION_VARNAME = '__version__'
41
+##@brief Name of the variable containing the confpsec filename
33 42
 CONFSPEC_FILENAME_VARNAME = '__confspec__'
43
+##@brief Name of the variable containing the confspecs
34 44
 CONFSPEC_VARNAME = 'CONFSPEC'
45
+##@brief Name of the variable containing the loader filename
35 46
 LOADER_FILENAME_VARNAME = '__loader__'
47
+##@brief Name of the variable containing the plugin dependencies
36 48
 PLUGIN_DEPS_VARNAME = '__plugin_deps__'
49
+##@brief Name of the optionnal activate method
37 50
 ACTIVATE_METHOD_NAME = '_activate'
38 51
 ##@brief Discover stage cache filename
39 52
 DISCOVER_CACHE_FILENAME = '.plugin_discover_cache.json'
40 53
 ##@brief Default & failover value for plugins path list
41 54
 DEFAULT_PLUGINS_PATH_LIST = ['./plugins']
42 55
 
56
+##@brief List storing the mandatory variables expected in a plugin __init__.py
57
+#file
43 58
 MANDATORY_VARNAMES = [PLUGIN_NAME_VARNAME, LOADER_FILENAME_VARNAME, 
44 59
     PLUGIN_VERSION_VARNAME]
45 60
 
61
+##@brief Default plugin type
46 62
 DEFAULT_PLUGIN_TYPE = 'extension' #Value found in lodel/plugin/extensions.py::Extensions._type_conf_name
47 63
 
64
+## @}
65
+
48 66
 ##@brief Describe and handle version numbers
67
+#@ingroup lodel2_plugins
68
+#
69
+#A version number can be represented by a string like MAJOR.MINOR.PATCH
70
+#or by a list [MAJOR, MINOR,PATCH ].
71
+#
72
+#The class implements basics comparison function and string repr
49 73
 class PluginVersion(object):
50 74
 
51 75
     PROPERTY_LIST = ['major', 'minor', 'revision' ]
@@ -82,14 +106,17 @@ but %d arguments found" % len(args))
82 106
             for i,v in enumerate(args):
83 107
                 self.__version[i] = v
84 108
     
109
+    ##@brief Property to access major version number
85 110
     @property
86 111
     def major(self):
87 112
         return self.__version[0]
88 113
 
114
+    ##@brief Property to access minor version number
89 115
     @property
90 116
     def minor(self):
91 117
         return self.__version[1]
92 118
 
119
+    ##@brief Property to access patch version number
93 120
     @property
94 121
     def revision(self):
95 122
         return self.__version[2]
@@ -147,8 +174,8 @@ to generic PluginVersion comparison function : '%s'" % cmp_fun_name)
147 174
         return {'major': self.major, 'minor': self.minor,
148 175
             'revision': self.revision}
149 176
 
150
-##@brief Plugin metaclass that allows to "catch" child class
151
-#declaration
177
+##@brief Plugin metaclass that allows to "catch" child class declaration
178
+#@ingroup lodel2_plugins
152 179
 #
153 180
 #Automatic script registration on child class declaration
154 181
 class MetaPlugType(type):
@@ -158,6 +185,7 @@ class MetaPlugType(type):
158 185
     #key is the _type_conf_name and value is the class
159 186
     _all_ptypes = dict()
160 187
 
188
+    ##@brief type constructor reimplementation
161 189
     def __init__(self, name, bases, attrs):
162 190
         #Here we can store all child classes of Plugin
163 191
         super().__init__(name, bases, attrs)
@@ -165,15 +193,24 @@ class MetaPlugType(type):
165 193
             return
166 194
         #Regitering a new plugin type
167 195
         MetaPlugType._all_ptypes[self._type_conf_name] = self
168
-
196
+    
197
+    ##@brief Accessor to the list of plugin types
198
+    #@return A copy of _all_ptypes attribute (a dict with typename as key
199
+    #and the class as value)
169 200
     @classmethod
170 201
     def all_types(cls):
171 202
         return copy.copy(cls._all_ptypes)
172
-
203
+    
204
+    ##@brief Accessor to the list of plugin names
205
+    #@return a list of plugin name
173 206
     @classmethod
174 207
     def all_ptype_names(cls):
175 208
         return list(cls._all_ptypes.keys())
176
-
209
+    
210
+    ##@brief Given a plugin type name return a Plugin child class
211
+    #@param ptype_name str : a plugin type name
212
+    #@return A Plugin child class
213
+    #@throw PluginError if ptype_name is not an exsiting plugin type name
177 214
     @classmethod
178 215
     def type_from_name(cls, ptype_name):
179 216
         if ptype_name not in cls._all_ptypes:
@@ -181,6 +218,7 @@ class MetaPlugType(type):
181 218
         return cls._all_ptypes[ptype_name]
182 219
 
183 220
 ##@brief Handle plugins
221
+#@ingroup lodel2_plugins
184 222
 #
185 223
 # An instance represent a loaded plugin. Class methods allow to load/preload
186 224
 # plugins.
@@ -225,14 +263,18 @@ class Plugin(object, metaclass=MetaPlugType):
225 263
     # @throw PluginError
226 264
     def __init__(self, plugin_name):
227 265
         
266
+        ##@brief The plugin name
228 267
         self.name = plugin_name
268
+        ##@brief The plugin package path
229 269
         self.path = self.plugin_path(plugin_name)
230 270
         
231 271
         ##@brief Stores the plugin module
232 272
         self.module = None
233
-        ##@breif Stores the plugin loader module
273
+        ##@brief Stores the plugin loader module
234 274
         self.__loader_module = None
275
+        ##@brief The plugin confspecs
235 276
         self.__confspecs = dict()
277
+        ##@brief Boolean flag telling if the plugin is loaded or not
236 278
         self.loaded = False
237 279
         
238 280
         # Importing __init__.py infos in it
@@ -295,7 +337,6 @@ class Plugin(object, metaclass=MetaPlugType):
295 337
             self.__type = DEFAULT_PLUGIN_TYPE
296 338
         self.__type = str(self.__type).lower()
297 339
         if self.__type not in MetaPlugType.all_ptype_names():
298
-            print("FUCK : ", MetaPlugType.all_ptype_names())
299 340
             raise PluginError("Unknown plugin type '%s'" % self.__type)
300 341
         # Load plugin name from init file (just for checking)
301 342
         try:
@@ -474,6 +515,9 @@ name differ from the one found in plugin's init file"
474 515
     def confspecs(self):
475 516
         return copy.copy(self.__confspecs)
476 517
 
518
+    ##@brief Accessor to confspec indicating where we can find the plugin list
519
+    #@note Abtract method implemented only for Plugin child classes
520
+    #This attribute indicate where we fetch the plugin list.
477 521
     @classmethod
478 522
     def plist_confspecs(cls):
479 523
         if cls._plist_confspecs is None:
@@ -546,7 +590,6 @@ file : '%s'. Running discover again..." % DISCOVER_CACHE_FILENAME)
546 590
         if ptype not in MetaPlugType.all_ptype_names():
547 591
             raise PluginError("Unknown plugin type '%s'" % ptype)
548 592
         pcls = MetaPlugType.type_from_name(ptype)
549
-        print("\n\n\nINSTANCIATING : ", pcls, " from name : ", ptype)
550 593
         plugin = pcls(plugin_name)
551 594
         cls._plugin_instances[plugin_name] = plugin
552 595
         logger.debug("Plugin %s available." % plugin)
@@ -582,7 +625,14 @@ file : '%s'. Running discover again..." % DISCOVER_CACHE_FILENAME)
582 625
             pass
583 626
 
584 627
         return plist[plugin_name]['path']
585
-        
628
+    
629
+    ##@brief Return the plugin module name
630
+    #
631
+    #This module name is the "virtual" module where we imported the plugin.
632
+    #
633
+    #Typically composed like VIRTUAL_PACKAGE_NAME.PLUGIN_NAME
634
+    #@param plugin_name str : a plugin name
635
+    #@return a string representing a module name
586 636
     @classmethod
587 637
     def plugin_module_name(cls, plugin_name):
588 638
         return "%s.%s" % (VIRTUAL_PACKAGE_NAME, plugin_name)
@@ -596,7 +646,8 @@ file : '%s'. Running discover again..." % DISCOVER_CACHE_FILENAME)
596 646
     def start(cls, plugins):
597 647
         for plugin_name in plugins:
598 648
             cls.register(plugin_name)
599
-        
649
+    
650
+    ##@brief Attempt to "restart" the Plugin class
600 651
     @classmethod
601 652
     def clear(cls):
602 653
         if cls._plugin_directories is not None:
@@ -778,6 +829,7 @@ file : '%s'. Running discover again..." % DISCOVER_CACHE_FILENAME)
778 829
 
779 830
 ##@brief Decorator class designed to allow plugins to add custom methods
780 831
 #to LeObject childs (dyncode objects)
832
+#@ingroup lodel2_plugins
781 833
 #
782 834
 class CustomMethod(object):
783 835
     ##@brief Stores registered custom methods

+ 15
- 1
lodel/plugin/scripts.py Zobrazit soubor

@@ -4,11 +4,22 @@ import sys
4 4
 from lodel import logger
5 5
 from lodel.exceptions import *
6 6
 
7
+##@defgroup lodel2_script Administration scripts
8
+#@ingroup lodel2_plugins
9
+
10
+##@package lodel.plugin.script
11
+#@brief Lodel2 utility for writting administration scripts
12
+#@ingroup lodel2_plugins
13
+#@ingroup lodel2_script
14
+
7 15
 ##@brief Stores registered scripts
16
+#@todo store it in MetaLodelScript
8 17
 __registered_scripts = dict()
9 18
 
10 19
 ##@brief LodelScript metaclass that allows to "catch" child class
11 20
 #declaration
21
+#@ingroup lodel2_script
22
+#@ingroup lodel2_plugins
12 23
 #
13 24
 #Automatic script registration on child class declaration
14 25
 class MetaLodelScript(type):
@@ -48,6 +59,10 @@ action identifier" % name)
48 59
     def __str__(self):
49 60
         return '%s : %s' % (self._action, self._description)
50 61
 
62
+
63
+##@brief Class designed to facilitate custom script writting
64
+#@ingroup lodel2_plugins
65
+#@ingroup lodel2_script
51 66
 class LodelScript(object, metaclass=MetaLodelScript):
52 67
     
53 68
     ##@brief A string to identify the action
@@ -146,7 +161,6 @@ def main_run():
146 161
     action = sys.argv[1].lower()
147 162
     if action not in __registered_scripts:
148 163
         #Trying to parse argument with default parser
149
-        print("PASSAGE")
150 164
         args = default_parser.parse_args()
151 165
         if args.list_actions:
152 166
             print("Available actions :")

+ 4
- 8
lodel/plugin/sessionhandler.py Zobrazit soubor

@@ -5,6 +5,7 @@ from lodel.settings.validator import SettingValidator
5 5
 ##@brief SessionHandlerPlugin metaclass designed to implements a wrapper
6 6
 #between SessionHandlerPlugin classmethod and plugin loader functions
7 7
 class SessionPluginWrapper(MetaPlugType):
8
+
8 9
     ##@brief Constant that stores all possible session actions
9 10
     #
10 11
     #Key is the SessionHandlerPlugin method name and value is SessionHandler
@@ -32,15 +33,10 @@ functions, but no session handler initialized")
32 33
         return super().__getattribute__(name)
33 34
 
34 35
 
35
-##@page lodel2_plugins Lodel2 plugins system
36
-#
37
-# @par Plugin structure
38
-#A plugin is  a package (a folder containing, at least, an __init__.py file.
39
-#This file should expose multiple things :
40
-# - a CONFSPEC variable containing configuration specifications
41
-# - an _activate() method that returns True if the plugin can be activated (
42
-# optionnal)
36
+##@brief Singleton class designed to handle session handler plugin
43 37
 #
38
+#@note This class is a singleton because only one session handler can be
39
+#loaded by instance
44 40
 class SessionHandlerPlugin(Plugin, metaclass=SessionPluginWrapper): 
45 41
     ##@brief Stores the singleton instance
46 42
     _instance = None

+ 1
- 75
plugins/dummy_datasource/__init__.py Zobrazit soubor

@@ -13,78 +13,4 @@ CONFSPEC = {
13 13
                     SettingValidator('dummy'))}
14 14
 }
15 15
 
16
-##@page lodel2_datasources Lodel2 datasources
17
-#
18
-#@par lodel2_datasources_intro Intro
19
-# A single lodel2 website can interact with multiple datasources. This page
20
-# aims to describe configuration & organisation of datasources in lodel2.
21
-# Each object is attached to a datasource. This association is done in the
22
-# editorial model, the datasource is identified by a name.
23
-#
24
-#@par Datasources declaration
25
-# To define a datasource you have to write something like this in confs file :
26
-#<pre>
27
-#[lodel2.datasources.DATASOURCE_NAME]
28
-#identifier = DATASOURCE_FAMILY.SOURCE_NAME
29
-#</pre>
30
-# See below for DATASOURCE_FAMILY & SOURCE_NAME
31
-#
32
-#@par Datasources plugins
33
-# Each datasource family is a plugin. For example mysql or a mongodb plugins.
34
-# Here is the CONFSPEC variable templates for datasources plugins
35
-#<pre>
36
-#CONFSPEC = {
37
-#                'lodel2.datasource.example.*' : {
38
-#                    'conf1' : VALIDATOR_OPTS,
39
-#                    'conf2' : VALIDATOR_OPTS,
40
-#                    ...
41
-#                }
42
-#}
43
-#</pre>
44
-#MySQL example
45
-#<pre>
46
-#CONFSPEC = {
47
-#                'lodel2.datasource.mysql.*' : {
48
-#                    'host': (   'localhost',
49
-#                                SettingValidator('host')),
50
-#                    'db_name': (    'lodel',
51
-#                                    SettingValidator('string')),
52
-#                    'username': (   None,
53
-#                                    SettingValidator('string')),
54
-#                    'password': (   None,
55
-#                                    SettingValidator('string')),
56
-#                }
57
-#}
58
-#</pre>
59
-#
60
-#@par Configuration example
61
-#<pre>
62
-# [lodel2.datasources.main]
63
-# identifier = mysql.Core
64
-# [lodel2.datasources.revues_write]
65
-# identifier = mysql.Revues
66
-# [lodel2.datasources.revues_read]
67
-# identifier = mysql.Revues
68
-# [lodel2.datasources.annuaire_persons]
69
-# identifier = persons_web_api.example
70
-# ;
71
-# ; Then, in the editorial model you are able to use "main", "revues_write", 
72
-# ; etc as datasource
73
-# ;
74
-# ; Here comes the datasources declarations
75
-# [lodel2.datasource.mysql.Core]
76
-# host = db.core.labocleo.org
77
-# db_name = core
78
-# username = foo
79
-# password = bar
80
-# ;
81
-# [lodel2.datasource.mysql.Revues]
82
-# host = revues.org
83
-# db_name = RO
84
-# username = foo
85
-# password = bar
86
-# ;
87
-# [lodel2.datasource.persons_web_api.example]
88
-# host = foo.bar
89
-# username = cleo
90
-#</pre>
16
+

Loading…
Zrušit
Uložit