NTV.json_ntv.namespace

@author: Philippe@loco-labs.io

The namespace module is part of the NTV.json_ntv package (specification document).

It contains the Namespace, Datatype, DatatypeError classes and the functions agreg_type, from_file, mapping, relative_type and str_type.

Namespace and Datatype entities are used to define NTVtype.

For more information, see the user guide or the github repository.

  1# -*- coding: utf-8 -*-
  2"""
  3@author: Philippe@loco-labs.io
  4
  5The `namespace` module is part of the `NTV.json_ntv` package ([specification document](
  6https://loco-philippe.github.io/ES/JSON%20semantic%20format%20(JSON-NTV).htm)).
  7
  8It contains the `Namespace`, `Datatype`, `DatatypeError` classes and
  9the functions `agreg_type`, `from_file`, `mapping`, `relative_type` and `str_type`.
 10
 11Namespace and Datatype entities are used to define NTVtype.
 12
 13For more information, see the
 14[user guide](https://loco-philippe.github.io/NTV/documentation/user_guide.html)
 15or the [github repository](https://github.com/loco-philippe/NTV).
 16
 17"""
 18import configparser
 19from pathlib import Path
 20import json
 21import requests
 22
 23import json_ntv
 24from json_ntv.ntv_util import NtvUtil
 25from json_ntv.ntv_validate import Validator
 26
 27SCH_ORG = 'https://schema.org/'
 28
 29
 30def agreg_type(str_typ, def_type, single):
 31    '''aggregate str_typ and def_type to return an Datatype or a Namespace if not single
 32
 33    *Parameters*
 34
 35        - **str_typ** : Datatype or String (long_name) - Datatype to aggregate
 36        - **def_typ** : Datatype or String (long_name) - default Datatype or Namespace
 37        - **single** : Boolean - Ntv entity concerned (True if NtvSingle)'''
 38    if isinstance(str_typ, Datatype):
 39        str_typ = str_typ.long_name
 40    def_type = str_type(def_type, single)
 41    if not str_typ and (not single or isinstance(def_type, Datatype)):
 42        return def_type
 43    if not str_typ:
 44        return Datatype('json')
 45    clas = Namespace if str_typ[-1] == '.' else Datatype
 46    if not def_type:
 47        return clas.add(str_typ)
 48    if clas == Datatype or clas == Namespace and not single:
 49        try:
 50            return clas.add(str_typ)
 51        except DatatypeError:
 52            for typ in _join_type(def_type.long_name, str_typ):
 53                try:
 54                    return clas.add(typ)
 55                except DatatypeError:
 56                    pass
 57    raise DatatypeError(str_typ + ' and ' +
 58                        def_type.long_name + ' are incompatible')
 59
 60
 61def from_file(file, name, long_parent=None):
 62    '''create a set of Datatype and Namespace associated to a custom Namespace
 63
 64    *Parameters*
 65
 66        - **file** : .ini file - description of the Datatype and Namespace
 67        - **name** : string - name of the root custom Namespace
 68        - **long_parent** : longname of the parent Namespace of the root Namespace
 69    '''
 70    long_parent = '' if not long_parent else long_parent
 71    if name[0] != '$':
 72        raise DatatypeError(name + ' is not a custom Datatype')
 73    if not long_parent in Namespace.namespaces():
 74        raise DatatypeError(long_parent + ' is not a valid Datatype')
 75    schema_nsp = Namespace(name, long_parent)
 76    config = configparser.ConfigParser()
 77    config.read(file)
 78    if not name in config.sections():
 79        raise DatatypeError(name + ' is not present in ' + str(file))
 80    _add_namespace(config, schema_nsp)
 81
 82
 83def mapping(typ=None, func=None):
 84    if typ and func:
 85        Datatype.add(typ).validate = func
 86    else:
 87        if isinstance(typ, str):
 88            func_lis = [typ + '_valid']
 89        elif isinstance(typ, (list, tuple)):
 90            func_lis = [typ_str + '_valid' for typ_str in typ]
 91        else:
 92            func_lis = [typ_str + '_valid' for typ_str in Datatype._types_]
 93        for func_str in func_lis:
 94            if func_str in Validator.__dict__:
 95                Datatype.add(
 96                    func_str[:-6]).validate = Validator.__dict__[func_str]
 97
 98
 99def relative_type(str_def, str_typ):
100    '''return relative str_typ string from Datatype or Namespace str_def
101
102    *Parameters*
103
104        - **str_def** : String - long_name of the Namespace or Datatype
105        - **str_type** : String - long_name of Ntvtype to be relative '''
106    if not str_def and not str_typ:
107        return ''
108    if str_def == str_typ:
109        return ''
110    if not str_def or not str_def in str_typ:
111        return str_typ
112    if not str_typ and str_def[-1] != ".":
113        return str_def
114    str_def_split = str_def.split('.')[:-1]
115    str_typ_split = str_typ.split('.')
116    ind = 0
117    for ind, name in enumerate(str_typ_split):
118        if not name in str_def_split:
119            break
120    return '.'.join(str_typ_split[ind:])
121
122
123def str_type(long_name, single):
124    ''' create a Datatype or a Namespace from a string
125
126    *Parameters*
127
128        - **long_name** : String - name of the Namespace or Datatype
129        - **single** : Boolean - If True, default type is 'json', else None'''
130    if not long_name and single:
131        return Datatype('json')
132    if not long_name and not single:
133        return None
134    if long_name.__class__.__name__ in ['Datatype', 'Namespace']:
135        return long_name
136    if not isinstance(long_name, str):
137        raise DatatypeError('the long_name is not a string')
138    if long_name[-1] == '.':
139        return Namespace.add(long_name)
140    return Datatype.add(long_name)
141
142
143def _add_namespace(config, namesp):
144    '''create the child Namespace and the child Datatype of the parent namespace'''
145    if namesp.name in config.sections():
146        confname = config[namesp.name]
147        if 'namespace' in confname:
148            for nspname in json.loads(confname['namespace']):
149                nsp = Namespace(nspname, namesp, force=True)
150                _add_namespace(config, nsp)
151        if 'type' in confname:
152            for typ in json.loads(confname['type']):
153                Datatype(typ, namesp, force=True)
154
155
156def _join_type(namesp, str_typ):
157    '''join Namespace string and Datatype or Namespace string'''
158    namesp_sp = namesp.split('.')
159    str_typ_sp = str_typ.split('.')
160    idx = -1
161    if str_typ_sp[0] in namesp_sp:
162        idx = namesp_sp.index(str_typ_sp[0])
163        if namesp_sp[idx:] != str_typ_sp[:len(namesp_sp[idx:])]:
164            idx = -1
165    if idx > -1:
166        namesp_sp = namesp_sp[:idx]
167    return ['.'.join(namesp_sp[:i+1]+str_typ_sp) for i in range(len(namesp_sp)-1, -1, -1)]
168
169    """namesp_split = namesp.split('.')[:-1]
170    for name in str_typ.split('.'):
171        if not name in namesp_split:
172            namesp_split.append(name)
173    return '.'.join(namesp_split)"""
174
175
176def _isin_schemaorg(name, prop=True, typ=None):
177    ''' return True if a schema.org Property is present and associated to a Type
178    or if a schema.org Type is present'''
179    n_org = name if prop else name[:-1]
180    t_org = typ[:-1] if typ else None
181    req_name = requests.get(
182        SCH_ORG + n_org, allow_redirects=True).content.decode()
183    if not prop:
184        return 'Type</ti' in req_name
185    is_prop = 'Property</ti' in req_name
186    if not t_org:
187        return is_prop
188    if is_prop:
189        return '/' + n_org in requests.get(SCH_ORG + t_org, allow_redirects=True
190                                           ).content.decode()
191    return False
192
193
194class Datatype(NtvUtil):
195    ''' type of NTV entities.
196
197    *Attributes :*
198
199    - **name** : String - name of the type
200    - **nspace** : Namespace - namespace associated
201    - **custom** : boolean - True if not referenced
202
203    The methods defined in this class are :
204
205    *staticmethods*
206    - `types`
207    - `add`
208
209    *dynamic values (@property)*
210    - `gen_type`
211    - `long_name`
212
213    *instance methods*
214    - `isin_namespace`
215    - `validate`
216    '''
217
218    @staticmethod
219    def types():
220        '''return the list of Datatype created'''
221        return [nam.long_name for nam in NtvUtil._types_.values()]
222
223    @classmethod
224    def add(cls, long_name, module=False, force=False, validate=None):
225        '''activate and return a valid Datatype defined by the long name
226
227        *parameters :*
228
229        - **long_name** : String - absolut name of the Datatype
230        - **module** : boolean (default False) - if True search data in the
231        local .ini file, else in the distant repository
232        - **validate** : function (default None) - validate function to include
233        '''
234        if long_name == '':
235            return None
236        if long_name in Datatype.types():
237            return NtvUtil._types_[long_name]
238        split_name = long_name.rsplit('.', 1)
239        if split_name[-1] == '':
240            raise DatatypeError(long_name + ' is not a valid Datatype')
241        if len(split_name) == 1:
242            return cls(split_name[0], force=force, validate=validate)
243        if len(split_name) == 2:
244            nspace = Namespace.add(
245                split_name[0]+'.', module=module, force=force)
246            return cls(split_name[1], nspace, force=force)
247        raise DatatypeError(long_name + ' is not a valid Datatype')
248
249    def __init__(self, name, nspace=None, force=False, validate=None):
250        '''Datatype constructor.
251
252        *Parameters*
253
254        - **name** : string - name of the Type
255        - **nspace** : Namespace (default None) - namespace associated
256        - **force** : boolean (default False) - if True, no Namespace control
257        - **validate** : function (default None) - validate function to include'''
258        if isinstance(name, Datatype):
259            self.name = name.name
260            self.nspace = name.nspace
261            self.custom = name.custom
262            self.validate = name.validate
263            return
264        if not name or not isinstance(name, str):
265            raise DatatypeError('null name is not allowed')
266        if not name and not nspace:
267            name = 'json'
268        if not nspace:
269            nspace = NtvUtil._namespaces_['']
270        if nspace.file and SCH_ORG in nspace.file:
271            if not _isin_schemaorg(name, typ=nspace.name):
272                raise DatatypeError(
273                    name + ' is not a schema.org Property associated to the ' + nspace.name + 'Type ')
274        else:
275            if not (name[0] == '$' or force or name in nspace.content['type']):
276                raise DatatypeError(
277                    name + ' is not defined in ' + nspace.long_name)
278        self.name = name
279        self.nspace = nspace
280        self.custom = nspace.custom or name[0] == '$'
281        if validate:
282            self.validate = validate
283        NtvUtil._types_[self.long_name] = self
284        return
285
286    def __eq__(self, other):
287        ''' equal if name and nspace are equal'''
288        if self is None and other is None:
289            return True
290        if self is None or other is None:
291            return False
292        if self.__class__.__name__ != other.__class__.__name__:
293            return False
294        return self.name == other.name and self.nspace == other.nspace
295
296    def __hash__(self):
297        '''return hash(name) + hash(nspace)'''
298        return hash(self.name) + hash(self.nspace)
299
300    def __str__(self):
301        '''return string format'''
302        return self.long_name
303
304    def __repr__(self):
305        '''return classname and long name'''
306        return self.__class__.__name__ + '(' + self.long_name + ')'
307
308    @property
309    def gen_type(self):
310        '''return the generic type of the Datatype'''
311        if self.custom:
312            return ''
313        return self.nspace.content['type'][self.name]
314
315    @property
316    def long_name(self):
317        '''return a string with the absolute name'''
318        return self.nspace.long_name + self.name
319
320    def isin_namespace(self, long_name):
321        '''return the number of level between self and nspace, -1 if None'''
322        return self.nspace.is_child(Namespace.add(long_name))
323
324    def validate(self, value):
325        return None
326
327
328class Namespace(NtvUtil):
329    ''' Namespace of NTV entities.
330
331    *Attributes :*
332
333    - **name** : String - name of the namespace
334    - **file** : string - location of the file init
335    - **content** : dict - {'type': <list of ntv_type names>,
336                            'namespace': <list of namespace names>}
337    - **parent** : Namespace - parent namespace
338    - **custom** : boolean - True if not referenced
339
340    The methods defined in this class are :
341
342    *staticmethods*
343    - `namespaces`
344
345    *classmethods*
346    - `add`
347
348    *dynamic values (@property)*
349    - `long_name`
350
351    *instance methods*
352    - `is_child`
353    - `is_parent`
354    '''
355    _pathconfig_ = 'https://raw.githubusercontent.com/loco-philippe/NTV/master/json_ntv/config/'
356    _global_ = "NTV_global_namespace.ini"
357
358    @staticmethod
359    def namespaces():
360        '''return the list of Namespace created'''
361        return [nam.long_name for nam in NtvUtil._namespaces_.values()]
362
363    @classmethod
364    def add(cls, long_name, module=False, force=False):
365        '''activate and return a valid Namespace defined by the long_name.
366
367        *parameters :*
368
369        - **long_name** : String - absolut name of the Namespace
370        - **module** : boolean (default False) - if True search data in the
371        local .ini file, else in the distant repository
372        '''
373        if long_name in Namespace.namespaces():
374            return NtvUtil._namespaces_[long_name]
375        split_name = long_name.rsplit('.', 2)
376        if len(split_name) == 1 or split_name[-1] != '':
377            raise DatatypeError(long_name + ' is not a valid Namespace')
378        if len(split_name) == 2:
379            return cls(split_name[0]+'.', module=module, force=force)
380        if len(split_name) == 3:
381            parent = Namespace.add(split_name[0]+'.', force=force)
382            return cls(split_name[1]+'.', parent, module=module, force=force)
383        raise DatatypeError(long_name + ' is not a valid Namespace')
384
385    def __init__(self, name='', parent=None, module=False, force=False):
386        '''
387        Namespace constructor.
388
389        *Parameters*
390
391        - **name** : String - name of the namespace
392        - **parent** : Namespace - parent namespace
393        - **module** : boolean (default False) - if True search data in the
394                        local .ini file, else in the distant repository
395        - **content** : dict : {'type': <list of ntv_type names>,
396                                'namespace': <list of namespace names>}
397        '''
398        if name and not parent:
399            parent = NtvUtil._namespaces_['']
400        '''if name and name[0] != '$' and not force and not (
401           name in parent.content['namespace'] or 'schema.org' in parent.file):
402            raise DatatypeError(name + ' is not defined in ' + parent.long_name)'''
403
404        if parent and parent.file and SCH_ORG in parent.file:
405            if not _isin_schemaorg(name, False):
406                raise DatatypeError(name + ' is not a schema.org Type ')
407        else:
408            if name and not (name[0] == '$' or force or name in parent.content['namespace']):
409                raise DatatypeError(
410                    name + ' is not defined in ' + parent.long_name)
411
412        self.name = name
413        self.parent = parent
414        self.custom = parent.custom or name[0] == '$' if parent else False
415        self.file = Namespace._file(
416            self.parent, self.name, self.custom, module)
417        self.content = Namespace._content(
418            self.file, self.name, self.custom, module)
419        NtvUtil._namespaces_[self.long_name] = self
420
421    def __eq__(self, other):
422        ''' equal if name and parent are equal'''
423        if self is None and other is None:
424            return True
425        if self is None or other is None:
426            return False
427        if self.__class__.__name__ != other.__class__.__name__:
428            return False
429        return self.name == other.name and self.parent == other.parent
430
431    def __hash__(self):
432        '''return hash(name) + hash(parent)'''
433        return hash(self.name) + hash(self.parent)
434
435    def __str__(self):
436        '''return string format'''
437        return self.long_name
438
439    def __repr__(self):
440        '''return classname and long name'''
441        return self.__class__.__name__ + '(' + self.long_name + ')'
442
443    @staticmethod
444    def _file(parent, name, custom, module):
445        '''return the file name of the Namespace configuration
446
447        *parameters :*
448
449        - **parent** : Namespace - Parent of the Namespace
450        - **name** : String - name of the Namespace
451        - **custom** : boolean - if True, return None (custom Namespace)
452        - **module** : boolean (default False) - if True search data in the
453        local .ini file, else in the distant repository
454        '''
455        if custom:
456            return None
457        if parent:
458            if 'schema.org' in parent.file or name == 'org.':
459                return SCH_ORG
460            config = configparser.ConfigParser()
461            if module:
462                p_file = Path(parent.file).stem + Path(parent.file).suffix
463                config.read(Path(json_ntv.__file__).parent / 'config' / p_file)
464            else:
465                config.read_string(requests.get(
466                    parent.file, allow_redirects=True).content.decode())
467            return Namespace._pathconfig_ + json.loads(config['data']['namespace'])[name]
468        return Namespace._pathconfig_ + Namespace._global_
469
470    @staticmethod
471    def _content(file, name, custom, module):
472        '''return the content of the Namespace configuration
473
474        *parameters :*
475
476        - **file** : string - file name of the parent Namespace
477        - **name** : string - name of the Namespace
478        - **custom** : boolean - if True, return empty dict (custom Namespace)
479        - **module** : boolean (default False) - if True search data in the
480        local .ini file, else in the distant repository
481
482        *return :*
483
484        - dict : {'type': <list of ntv_type names>,
485                  'namespace': <list of namespace names>}
486        '''
487        if custom or 'schema.org' in file:
488            return {'type': {}, 'namespace': {}}
489        config = configparser.ConfigParser()
490        if module:
491            p_file = Path(file).stem + Path(file).suffix
492            config.read(Path(json_ntv.__file__).parent / 'config' / p_file)
493        else:
494            config.read_string(requests.get(
495                file, allow_redirects=True).content.decode())
496        config_name = config['data']['name']
497        if config_name != name:
498            raise DatatypeError(file + ' is not correct')
499        return {'type': json.loads(config['data']['type']),
500                'namespace': json.loads(config['data']['namespace'])}
501
502    @property
503    def long_name(self):
504        '''return a string with the absolute name'''
505        if self.parent is None or self.parent.name == '':
506            return self.name
507        return self.parent.long_name + self.name
508
509    def is_child(self, nspace):
510        '''return the number of level between self and nspace, -1 if None'''
511        parent = self.parent
512        if not self.name:
513            return -1
514        if self == nspace:
515            return 0
516        rang = 1
517        while parent.name != '' and parent != nspace:
518            rang += 1
519            parent = parent.parent
520        if parent == nspace:
521            return rang
522        if parent.name == '':
523            return -1
524
525    def is_parent(self, nspace):
526        '''return the number of level between self and nspace, -1 if None'''
527        return nspace.is_child(self)
528
529
530class DatatypeError(Exception):
531    ''' Datatype or Namespace Exception'''
532
533
534nroot = Namespace(module=True)
535for root_typ in nroot.content['type']:
536    #typ = Datatype.add(root_typ, module=True)
537    typ = Datatype.add(root_typ, module=True,
538                       validate=Validator.__dict__[root_typ + '_valid'])
539# mapping(Datatype.types())
540typ_json = Datatype('json')
def agreg_type(str_typ, def_type, single):
31def agreg_type(str_typ, def_type, single):
32    '''aggregate str_typ and def_type to return an Datatype or a Namespace if not single
33
34    *Parameters*
35
36        - **str_typ** : Datatype or String (long_name) - Datatype to aggregate
37        - **def_typ** : Datatype or String (long_name) - default Datatype or Namespace
38        - **single** : Boolean - Ntv entity concerned (True if NtvSingle)'''
39    if isinstance(str_typ, Datatype):
40        str_typ = str_typ.long_name
41    def_type = str_type(def_type, single)
42    if not str_typ and (not single or isinstance(def_type, Datatype)):
43        return def_type
44    if not str_typ:
45        return Datatype('json')
46    clas = Namespace if str_typ[-1] == '.' else Datatype
47    if not def_type:
48        return clas.add(str_typ)
49    if clas == Datatype or clas == Namespace and not single:
50        try:
51            return clas.add(str_typ)
52        except DatatypeError:
53            for typ in _join_type(def_type.long_name, str_typ):
54                try:
55                    return clas.add(typ)
56                except DatatypeError:
57                    pass
58    raise DatatypeError(str_typ + ' and ' +
59                        def_type.long_name + ' are incompatible')

aggregate str_typ and def_type to return an Datatype or a Namespace if not single

Parameters

- **str_typ** : Datatype or String (long_name) - Datatype to aggregate
- **def_typ** : Datatype or String (long_name) - default Datatype or Namespace
- **single** : Boolean - Ntv entity concerned (True if NtvSingle)
def from_file(file, name, long_parent=None):
62def from_file(file, name, long_parent=None):
63    '''create a set of Datatype and Namespace associated to a custom Namespace
64
65    *Parameters*
66
67        - **file** : .ini file - description of the Datatype and Namespace
68        - **name** : string - name of the root custom Namespace
69        - **long_parent** : longname of the parent Namespace of the root Namespace
70    '''
71    long_parent = '' if not long_parent else long_parent
72    if name[0] != '$':
73        raise DatatypeError(name + ' is not a custom Datatype')
74    if not long_parent in Namespace.namespaces():
75        raise DatatypeError(long_parent + ' is not a valid Datatype')
76    schema_nsp = Namespace(name, long_parent)
77    config = configparser.ConfigParser()
78    config.read(file)
79    if not name in config.sections():
80        raise DatatypeError(name + ' is not present in ' + str(file))
81    _add_namespace(config, schema_nsp)

create a set of Datatype and Namespace associated to a custom Namespace

Parameters

- **file** : .ini file - description of the Datatype and Namespace
- **name** : string - name of the root custom Namespace
- **long_parent** : longname of the parent Namespace of the root Namespace
def mapping(typ=None, func=None):
84def mapping(typ=None, func=None):
85    if typ and func:
86        Datatype.add(typ).validate = func
87    else:
88        if isinstance(typ, str):
89            func_lis = [typ + '_valid']
90        elif isinstance(typ, (list, tuple)):
91            func_lis = [typ_str + '_valid' for typ_str in typ]
92        else:
93            func_lis = [typ_str + '_valid' for typ_str in Datatype._types_]
94        for func_str in func_lis:
95            if func_str in Validator.__dict__:
96                Datatype.add(
97                    func_str[:-6]).validate = Validator.__dict__[func_str]
def relative_type(str_def, str_typ):
100def relative_type(str_def, str_typ):
101    '''return relative str_typ string from Datatype or Namespace str_def
102
103    *Parameters*
104
105        - **str_def** : String - long_name of the Namespace or Datatype
106        - **str_type** : String - long_name of Ntvtype to be relative '''
107    if not str_def and not str_typ:
108        return ''
109    if str_def == str_typ:
110        return ''
111    if not str_def or not str_def in str_typ:
112        return str_typ
113    if not str_typ and str_def[-1] != ".":
114        return str_def
115    str_def_split = str_def.split('.')[:-1]
116    str_typ_split = str_typ.split('.')
117    ind = 0
118    for ind, name in enumerate(str_typ_split):
119        if not name in str_def_split:
120            break
121    return '.'.join(str_typ_split[ind:])

return relative str_typ string from Datatype or Namespace str_def

Parameters

- **str_def** : String - long_name of the Namespace or Datatype
- **str_type** : String - long_name of Ntvtype to be relative
def str_type(long_name, single):
124def str_type(long_name, single):
125    ''' create a Datatype or a Namespace from a string
126
127    *Parameters*
128
129        - **long_name** : String - name of the Namespace or Datatype
130        - **single** : Boolean - If True, default type is 'json', else None'''
131    if not long_name and single:
132        return Datatype('json')
133    if not long_name and not single:
134        return None
135    if long_name.__class__.__name__ in ['Datatype', 'Namespace']:
136        return long_name
137    if not isinstance(long_name, str):
138        raise DatatypeError('the long_name is not a string')
139    if long_name[-1] == '.':
140        return Namespace.add(long_name)
141    return Datatype.add(long_name)

create a Datatype or a Namespace from a string

Parameters

- **long_name** : String - name of the Namespace or Datatype
- **single** : Boolean - If True, default type is 'json', else None
class Datatype(json_ntv.ntv_util.NtvUtil):
195class Datatype(NtvUtil):
196    ''' type of NTV entities.
197
198    *Attributes :*
199
200    - **name** : String - name of the type
201    - **nspace** : Namespace - namespace associated
202    - **custom** : boolean - True if not referenced
203
204    The methods defined in this class are :
205
206    *staticmethods*
207    - `types`
208    - `add`
209
210    *dynamic values (@property)*
211    - `gen_type`
212    - `long_name`
213
214    *instance methods*
215    - `isin_namespace`
216    - `validate`
217    '''
218
219    @staticmethod
220    def types():
221        '''return the list of Datatype created'''
222        return [nam.long_name for nam in NtvUtil._types_.values()]
223
224    @classmethod
225    def add(cls, long_name, module=False, force=False, validate=None):
226        '''activate and return a valid Datatype defined by the long name
227
228        *parameters :*
229
230        - **long_name** : String - absolut name of the Datatype
231        - **module** : boolean (default False) - if True search data in the
232        local .ini file, else in the distant repository
233        - **validate** : function (default None) - validate function to include
234        '''
235        if long_name == '':
236            return None
237        if long_name in Datatype.types():
238            return NtvUtil._types_[long_name]
239        split_name = long_name.rsplit('.', 1)
240        if split_name[-1] == '':
241            raise DatatypeError(long_name + ' is not a valid Datatype')
242        if len(split_name) == 1:
243            return cls(split_name[0], force=force, validate=validate)
244        if len(split_name) == 2:
245            nspace = Namespace.add(
246                split_name[0]+'.', module=module, force=force)
247            return cls(split_name[1], nspace, force=force)
248        raise DatatypeError(long_name + ' is not a valid Datatype')
249
250    def __init__(self, name, nspace=None, force=False, validate=None):
251        '''Datatype constructor.
252
253        *Parameters*
254
255        - **name** : string - name of the Type
256        - **nspace** : Namespace (default None) - namespace associated
257        - **force** : boolean (default False) - if True, no Namespace control
258        - **validate** : function (default None) - validate function to include'''
259        if isinstance(name, Datatype):
260            self.name = name.name
261            self.nspace = name.nspace
262            self.custom = name.custom
263            self.validate = name.validate
264            return
265        if not name or not isinstance(name, str):
266            raise DatatypeError('null name is not allowed')
267        if not name and not nspace:
268            name = 'json'
269        if not nspace:
270            nspace = NtvUtil._namespaces_['']
271        if nspace.file and SCH_ORG in nspace.file:
272            if not _isin_schemaorg(name, typ=nspace.name):
273                raise DatatypeError(
274                    name + ' is not a schema.org Property associated to the ' + nspace.name + 'Type ')
275        else:
276            if not (name[0] == '$' or force or name in nspace.content['type']):
277                raise DatatypeError(
278                    name + ' is not defined in ' + nspace.long_name)
279        self.name = name
280        self.nspace = nspace
281        self.custom = nspace.custom or name[0] == '$'
282        if validate:
283            self.validate = validate
284        NtvUtil._types_[self.long_name] = self
285        return
286
287    def __eq__(self, other):
288        ''' equal if name and nspace are equal'''
289        if self is None and other is None:
290            return True
291        if self is None or other is None:
292            return False
293        if self.__class__.__name__ != other.__class__.__name__:
294            return False
295        return self.name == other.name and self.nspace == other.nspace
296
297    def __hash__(self):
298        '''return hash(name) + hash(nspace)'''
299        return hash(self.name) + hash(self.nspace)
300
301    def __str__(self):
302        '''return string format'''
303        return self.long_name
304
305    def __repr__(self):
306        '''return classname and long name'''
307        return self.__class__.__name__ + '(' + self.long_name + ')'
308
309    @property
310    def gen_type(self):
311        '''return the generic type of the Datatype'''
312        if self.custom:
313            return ''
314        return self.nspace.content['type'][self.name]
315
316    @property
317    def long_name(self):
318        '''return a string with the absolute name'''
319        return self.nspace.long_name + self.name
320
321    def isin_namespace(self, long_name):
322        '''return the number of level between self and nspace, -1 if None'''
323        return self.nspace.is_child(Namespace.add(long_name))
324
325    def validate(self, value):
326        return None

type of NTV entities.

Attributes :

  • name : String - name of the type
  • nspace : Namespace - namespace associated
  • custom : boolean - True if not referenced

The methods defined in this class are :

staticmethods

dynamic values (@property)

instance methods

Datatype(name, nspace=None, force=False, validate=None)
250    def __init__(self, name, nspace=None, force=False, validate=None):
251        '''Datatype constructor.
252
253        *Parameters*
254
255        - **name** : string - name of the Type
256        - **nspace** : Namespace (default None) - namespace associated
257        - **force** : boolean (default False) - if True, no Namespace control
258        - **validate** : function (default None) - validate function to include'''
259        if isinstance(name, Datatype):
260            self.name = name.name
261            self.nspace = name.nspace
262            self.custom = name.custom
263            self.validate = name.validate
264            return
265        if not name or not isinstance(name, str):
266            raise DatatypeError('null name is not allowed')
267        if not name and not nspace:
268            name = 'json'
269        if not nspace:
270            nspace = NtvUtil._namespaces_['']
271        if nspace.file and SCH_ORG in nspace.file:
272            if not _isin_schemaorg(name, typ=nspace.name):
273                raise DatatypeError(
274                    name + ' is not a schema.org Property associated to the ' + nspace.name + 'Type ')
275        else:
276            if not (name[0] == '$' or force or name in nspace.content['type']):
277                raise DatatypeError(
278                    name + ' is not defined in ' + nspace.long_name)
279        self.name = name
280        self.nspace = nspace
281        self.custom = nspace.custom or name[0] == '$'
282        if validate:
283            self.validate = validate
284        NtvUtil._types_[self.long_name] = self
285        return

Datatype constructor.

Parameters

  • name : string - name of the Type
  • nspace : Namespace (default None) - namespace associated
  • force : boolean (default False) - if True, no Namespace control
  • validate : function (default None) - validate function to include
@staticmethod
def types():
219    @staticmethod
220    def types():
221        '''return the list of Datatype created'''
222        return [nam.long_name for nam in NtvUtil._types_.values()]

return the list of Datatype created

@classmethod
def add(cls, long_name, module=False, force=False, validate=None):
224    @classmethod
225    def add(cls, long_name, module=False, force=False, validate=None):
226        '''activate and return a valid Datatype defined by the long name
227
228        *parameters :*
229
230        - **long_name** : String - absolut name of the Datatype
231        - **module** : boolean (default False) - if True search data in the
232        local .ini file, else in the distant repository
233        - **validate** : function (default None) - validate function to include
234        '''
235        if long_name == '':
236            return None
237        if long_name in Datatype.types():
238            return NtvUtil._types_[long_name]
239        split_name = long_name.rsplit('.', 1)
240        if split_name[-1] == '':
241            raise DatatypeError(long_name + ' is not a valid Datatype')
242        if len(split_name) == 1:
243            return cls(split_name[0], force=force, validate=validate)
244        if len(split_name) == 2:
245            nspace = Namespace.add(
246                split_name[0]+'.', module=module, force=force)
247            return cls(split_name[1], nspace, force=force)
248        raise DatatypeError(long_name + ' is not a valid Datatype')

activate and return a valid Datatype defined by the long name

parameters :

  • long_name : String - absolut name of the Datatype
  • module : boolean (default False) - if True search data in the local .ini file, else in the distant repository
  • validate : function (default None) - validate function to include
gen_type

return the generic type of the Datatype

long_name

return a string with the absolute name

def isin_namespace(self, long_name):
321    def isin_namespace(self, long_name):
322        '''return the number of level between self and nspace, -1 if None'''
323        return self.nspace.is_child(Namespace.add(long_name))

return the number of level between self and nspace, -1 if None

def validate(self, value):
325    def validate(self, value):
326        return None
Inherited Members
json_ntv.ntv_util.NtvUtil
from_obj_name
decode_ntv_tab
to_ntvpointer
class Namespace(json_ntv.ntv_util.NtvUtil):
329class Namespace(NtvUtil):
330    ''' Namespace of NTV entities.
331
332    *Attributes :*
333
334    - **name** : String - name of the namespace
335    - **file** : string - location of the file init
336    - **content** : dict - {'type': <list of ntv_type names>,
337                            'namespace': <list of namespace names>}
338    - **parent** : Namespace - parent namespace
339    - **custom** : boolean - True if not referenced
340
341    The methods defined in this class are :
342
343    *staticmethods*
344    - `namespaces`
345
346    *classmethods*
347    - `add`
348
349    *dynamic values (@property)*
350    - `long_name`
351
352    *instance methods*
353    - `is_child`
354    - `is_parent`
355    '''
356    _pathconfig_ = 'https://raw.githubusercontent.com/loco-philippe/NTV/master/json_ntv/config/'
357    _global_ = "NTV_global_namespace.ini"
358
359    @staticmethod
360    def namespaces():
361        '''return the list of Namespace created'''
362        return [nam.long_name for nam in NtvUtil._namespaces_.values()]
363
364    @classmethod
365    def add(cls, long_name, module=False, force=False):
366        '''activate and return a valid Namespace defined by the long_name.
367
368        *parameters :*
369
370        - **long_name** : String - absolut name of the Namespace
371        - **module** : boolean (default False) - if True search data in the
372        local .ini file, else in the distant repository
373        '''
374        if long_name in Namespace.namespaces():
375            return NtvUtil._namespaces_[long_name]
376        split_name = long_name.rsplit('.', 2)
377        if len(split_name) == 1 or split_name[-1] != '':
378            raise DatatypeError(long_name + ' is not a valid Namespace')
379        if len(split_name) == 2:
380            return cls(split_name[0]+'.', module=module, force=force)
381        if len(split_name) == 3:
382            parent = Namespace.add(split_name[0]+'.', force=force)
383            return cls(split_name[1]+'.', parent, module=module, force=force)
384        raise DatatypeError(long_name + ' is not a valid Namespace')
385
386    def __init__(self, name='', parent=None, module=False, force=False):
387        '''
388        Namespace constructor.
389
390        *Parameters*
391
392        - **name** : String - name of the namespace
393        - **parent** : Namespace - parent namespace
394        - **module** : boolean (default False) - if True search data in the
395                        local .ini file, else in the distant repository
396        - **content** : dict : {'type': <list of ntv_type names>,
397                                'namespace': <list of namespace names>}
398        '''
399        if name and not parent:
400            parent = NtvUtil._namespaces_['']
401        '''if name and name[0] != '$' and not force and not (
402           name in parent.content['namespace'] or 'schema.org' in parent.file):
403            raise DatatypeError(name + ' is not defined in ' + parent.long_name)'''
404
405        if parent and parent.file and SCH_ORG in parent.file:
406            if not _isin_schemaorg(name, False):
407                raise DatatypeError(name + ' is not a schema.org Type ')
408        else:
409            if name and not (name[0] == '$' or force or name in parent.content['namespace']):
410                raise DatatypeError(
411                    name + ' is not defined in ' + parent.long_name)
412
413        self.name = name
414        self.parent = parent
415        self.custom = parent.custom or name[0] == '$' if parent else False
416        self.file = Namespace._file(
417            self.parent, self.name, self.custom, module)
418        self.content = Namespace._content(
419            self.file, self.name, self.custom, module)
420        NtvUtil._namespaces_[self.long_name] = self
421
422    def __eq__(self, other):
423        ''' equal if name and parent are equal'''
424        if self is None and other is None:
425            return True
426        if self is None or other is None:
427            return False
428        if self.__class__.__name__ != other.__class__.__name__:
429            return False
430        return self.name == other.name and self.parent == other.parent
431
432    def __hash__(self):
433        '''return hash(name) + hash(parent)'''
434        return hash(self.name) + hash(self.parent)
435
436    def __str__(self):
437        '''return string format'''
438        return self.long_name
439
440    def __repr__(self):
441        '''return classname and long name'''
442        return self.__class__.__name__ + '(' + self.long_name + ')'
443
444    @staticmethod
445    def _file(parent, name, custom, module):
446        '''return the file name of the Namespace configuration
447
448        *parameters :*
449
450        - **parent** : Namespace - Parent of the Namespace
451        - **name** : String - name of the Namespace
452        - **custom** : boolean - if True, return None (custom Namespace)
453        - **module** : boolean (default False) - if True search data in the
454        local .ini file, else in the distant repository
455        '''
456        if custom:
457            return None
458        if parent:
459            if 'schema.org' in parent.file or name == 'org.':
460                return SCH_ORG
461            config = configparser.ConfigParser()
462            if module:
463                p_file = Path(parent.file).stem + Path(parent.file).suffix
464                config.read(Path(json_ntv.__file__).parent / 'config' / p_file)
465            else:
466                config.read_string(requests.get(
467                    parent.file, allow_redirects=True).content.decode())
468            return Namespace._pathconfig_ + json.loads(config['data']['namespace'])[name]
469        return Namespace._pathconfig_ + Namespace._global_
470
471    @staticmethod
472    def _content(file, name, custom, module):
473        '''return the content of the Namespace configuration
474
475        *parameters :*
476
477        - **file** : string - file name of the parent Namespace
478        - **name** : string - name of the Namespace
479        - **custom** : boolean - if True, return empty dict (custom Namespace)
480        - **module** : boolean (default False) - if True search data in the
481        local .ini file, else in the distant repository
482
483        *return :*
484
485        - dict : {'type': <list of ntv_type names>,
486                  'namespace': <list of namespace names>}
487        '''
488        if custom or 'schema.org' in file:
489            return {'type': {}, 'namespace': {}}
490        config = configparser.ConfigParser()
491        if module:
492            p_file = Path(file).stem + Path(file).suffix
493            config.read(Path(json_ntv.__file__).parent / 'config' / p_file)
494        else:
495            config.read_string(requests.get(
496                file, allow_redirects=True).content.decode())
497        config_name = config['data']['name']
498        if config_name != name:
499            raise DatatypeError(file + ' is not correct')
500        return {'type': json.loads(config['data']['type']),
501                'namespace': json.loads(config['data']['namespace'])}
502
503    @property
504    def long_name(self):
505        '''return a string with the absolute name'''
506        if self.parent is None or self.parent.name == '':
507            return self.name
508        return self.parent.long_name + self.name
509
510    def is_child(self, nspace):
511        '''return the number of level between self and nspace, -1 if None'''
512        parent = self.parent
513        if not self.name:
514            return -1
515        if self == nspace:
516            return 0
517        rang = 1
518        while parent.name != '' and parent != nspace:
519            rang += 1
520            parent = parent.parent
521        if parent == nspace:
522            return rang
523        if parent.name == '':
524            return -1
525
526    def is_parent(self, nspace):
527        '''return the number of level between self and nspace, -1 if None'''
528        return nspace.is_child(self)

Namespace of NTV entities.

Attributes :

  • name : String - name of the namespace
  • file : string - location of the file init
  • content : dict - {'type': , 'namespace': }
  • parent : Namespace - parent namespace
  • custom : boolean - True if not referenced

The methods defined in this class are :

staticmethods

classmethods

dynamic values (@property)

instance methods

Namespace(name='', parent=None, module=False, force=False)
386    def __init__(self, name='', parent=None, module=False, force=False):
387        '''
388        Namespace constructor.
389
390        *Parameters*
391
392        - **name** : String - name of the namespace
393        - **parent** : Namespace - parent namespace
394        - **module** : boolean (default False) - if True search data in the
395                        local .ini file, else in the distant repository
396        - **content** : dict : {'type': <list of ntv_type names>,
397                                'namespace': <list of namespace names>}
398        '''
399        if name and not parent:
400            parent = NtvUtil._namespaces_['']
401        '''if name and name[0] != '$' and not force and not (
402           name in parent.content['namespace'] or 'schema.org' in parent.file):
403            raise DatatypeError(name + ' is not defined in ' + parent.long_name)'''
404
405        if parent and parent.file and SCH_ORG in parent.file:
406            if not _isin_schemaorg(name, False):
407                raise DatatypeError(name + ' is not a schema.org Type ')
408        else:
409            if name and not (name[0] == '$' or force or name in parent.content['namespace']):
410                raise DatatypeError(
411                    name + ' is not defined in ' + parent.long_name)
412
413        self.name = name
414        self.parent = parent
415        self.custom = parent.custom or name[0] == '$' if parent else False
416        self.file = Namespace._file(
417            self.parent, self.name, self.custom, module)
418        self.content = Namespace._content(
419            self.file, self.name, self.custom, module)
420        NtvUtil._namespaces_[self.long_name] = self

Namespace constructor.

Parameters

  • name : String - name of the namespace
  • parent : Namespace - parent namespace
  • module : boolean (default False) - if True search data in the local .ini file, else in the distant repository
  • content : dict : {'type': , 'namespace': }
@staticmethod
def namespaces():
359    @staticmethod
360    def namespaces():
361        '''return the list of Namespace created'''
362        return [nam.long_name for nam in NtvUtil._namespaces_.values()]

return the list of Namespace created

@classmethod
def add(cls, long_name, module=False, force=False):
364    @classmethod
365    def add(cls, long_name, module=False, force=False):
366        '''activate and return a valid Namespace defined by the long_name.
367
368        *parameters :*
369
370        - **long_name** : String - absolut name of the Namespace
371        - **module** : boolean (default False) - if True search data in the
372        local .ini file, else in the distant repository
373        '''
374        if long_name in Namespace.namespaces():
375            return NtvUtil._namespaces_[long_name]
376        split_name = long_name.rsplit('.', 2)
377        if len(split_name) == 1 or split_name[-1] != '':
378            raise DatatypeError(long_name + ' is not a valid Namespace')
379        if len(split_name) == 2:
380            return cls(split_name[0]+'.', module=module, force=force)
381        if len(split_name) == 3:
382            parent = Namespace.add(split_name[0]+'.', force=force)
383            return cls(split_name[1]+'.', parent, module=module, force=force)
384        raise DatatypeError(long_name + ' is not a valid Namespace')

activate and return a valid Namespace defined by the long_name.

parameters :

  • long_name : String - absolut name of the Namespace
  • module : boolean (default False) - if True search data in the local .ini file, else in the distant repository
long_name

return a string with the absolute name

def is_child(self, nspace):
510    def is_child(self, nspace):
511        '''return the number of level between self and nspace, -1 if None'''
512        parent = self.parent
513        if not self.name:
514            return -1
515        if self == nspace:
516            return 0
517        rang = 1
518        while parent.name != '' and parent != nspace:
519            rang += 1
520            parent = parent.parent
521        if parent == nspace:
522            return rang
523        if parent.name == '':
524            return -1

return the number of level between self and nspace, -1 if None

def is_parent(self, nspace):
526    def is_parent(self, nspace):
527        '''return the number of level between self and nspace, -1 if None'''
528        return nspace.is_child(self)

return the number of level between self and nspace, -1 if None

Inherited Members
json_ntv.ntv_util.NtvUtil
from_obj_name
decode_ntv_tab
to_ntvpointer
class DatatypeError(builtins.Exception):
531class DatatypeError(Exception):
532    ''' Datatype or Namespace Exception'''

Datatype or Namespace Exception

Inherited Members
builtins.Exception
Exception
builtins.BaseException
with_traceback