NTV.json_ntv.namespace

Created on Jan 20 2023

@author: Philippe@loco-labs.io

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

It contains the Namespace and the NtvType classes and the str_type method for NTV entities.

0 - Presentation

The NTVtype is defined by a name and a Namespace. The name is unique in the Namespace

A Namespace is represented by a string followed by a point. Namespaces may be nested (the global Namespace is represented by an empty string).

The Namespace representations are added to the value of an NTVtype to have an absolute representation of an NTVtype (long_name).

Example for an absolute representation of an NTVtype defined in two nested Namespace : “ns1.ns2.type” where:

  • ns1. is a Namespace defined in the global Namespace,
  • ns2. is a Namespace defined in the ns1. Namespace,
  • type is a NTVtype defined in the ns2. Namespace

1 - Global Namespace

The structure of types by namespace makes it possible to have types corresponding to recognized standards at the global level. Generic types can also be defined (calculation of the exact type when decoding the value).

The global namespace can include the following structures:

1.1 - Simple (JSON RFC8259)

type (generic type) value example
boolean (json) true
null (json) null
number (json) 45.2
string (json) "string"
array (json) [1, 2, 3]
object (json) { "str": "test", "bool": true}

1.2 - Datation (ISO8601 and Posix)

type (generic type) value example
year 1998
month 10
day 21
wday 2
yday 251
week 38
hour 20
minute 18
second 54
date (dat) “2022-01-28”
time (dat) “T18:23:54”, “18:23”, “T18”
datetime (dat) “2022-01-28T18-23-54Z”, “2022-01-28T18-23-54+0400”

1.3 - Duration (ISO8601 and Posix)

type (generic type) value example
period "2007-03-01T13:00:00Z/2008-05-11T15:30:00Z"
duration "P0002-10- 15T10:30:20"
timearray [date1, date2]

1.4 - Location (RFC7946 and Open Location Code):

type (generic type) value example
point (loc) [ 5.12, 45.256 ] (lon, lat)
line (loc) [ point1, point2, point3 ]
ring [ point1, point2, point3 ]
multiline [ line1, line2, line3]
polygon (loc) [ ring1, ring2, ring3]
multipolygon (loc) [ poly1, poly2, poly3 ]
box (loc) [ -10.0, -10.0, 10.0, 10.0 ]
geojson (loc) {“type”: “point”, “coordinates”: [40.0, 0.0] }
codeolc (loc) “8FW4V75V+8F6”

1.5 - Tabular data

NTVtype NTVvalue
row JSON-array of JSON-NTV
field JSON-array of NTVvalue (following JSON-TAB format)
table JSON-array of JSON-NTV fields with the same length

1.6 - Normalized strings

The type could be uri, cf exemples :

  • "https://www.ietf.org/rfc/rfc3986.txt"
  • "https://gallica.bnf.fr/ark:/12148/bpt6k107371t"
  • "urn:uuid:f81d4fae-7dec-11d0-a765-00a0c91e6bf6"
  • "ni:///sha-256;UyaQV-Ev4rdLoHyJJWCi11OHfrYv9E1aGQAlMO2X_-Q"
  • "geo:13.4125,103.86673" (RFC5870)
  • "info:eu-repo/dai/nl/12345"
  • "mailto:John.Doe@example.com"
  • "news:comp.infosystems.www.servers.unix"
  • "urn:oasis:names:specification:docbook:dtd:xml:4.1.2"

1.7 - Namespaces

Namespaces could also be defined to reference for example:

  • geopolitical entities: ISO3166-1 country code (for example "fr." for France)
  • data catalogs, for example:
NTVtype example JSON-NTV
schemaorg.
{ “:schemaorg.propertyID”: “NO2” }
{ “:schemaorg.unitText”:”µg/m3”}
darwincore. { “:darwincore.acceptedNameUsage”: “Tamias minimus” }

1.8 - Identifiers

For example :

type definition exemple
fr.uic code UIC station 8757449
fr.iata code IATA airport CDG

2 - Example of using a fr. namespace

This namespace is dedicated to datasets associated with the France geopolitical namespace (see also the presentation document).

A namespace defines:

  • identifiers used to access additional data,
  • namespaces associated with catalogs or data sets,
  • structured entities used to facilitate the use of data

2.1 - Identifiers

They could correspond to identifiers used in many referenced datasets (via a data schema or a data model).

For example :

type definition example
fr.dep code département 60
fr.cp code postal 76450
fr.naf code NAF 23
fr.siren code SIREN enterprise 418447363
fr.fantoir code FANTOIR voie 4500023086F
fr.uai code UAI établissement 0951099D
fr.aca code académies 22
fr.finessej code FINESS entité juridique 790001606
fr.rna code WALDEC association 843S0843004860
fr.spi code SPI numéro fiscal 1899582886173
fr.nir code NIR sécurité sociale 164026005705953

2.2 Namespaces

Namespaces could correspond to catalogs or data sets whose data types are identified in data models or in referenced data schemas.

For example :

type example JSON-NTV
fr.sandre.
{ ":fr.sandre.CdStationHydro": K163 3010 01 }
{ ":fr.sandre.TypStationHydro": "standard" }
fr.synop.
{ ":fr.synop.numer_sta": 07130 }
{ ":fr.synop.t": 300, ":fr.synop.ff": 5 }
fr.IRVE.
{ ":fr.IRVE.nom_station": "M2026" }
{ ":fr.IRVE.nom_operateur": "DEBELEC" }
fr.BAN.
{ ":fr.BAN.numero": 54 }
{ ":fr.BAN.lon": 3.5124 }

2.3 Entities

They could correspond to assemblies of data associated with a defined structure.

For example :

type example JSON-NTV
fr.parcelle
{“maParcelle:fr.parcelle”: [ 84500, 0, I, 97]}
(fr.cp, fr.cadastre.préfixe, fr.cadastre.section, fr.cadastre.numéro)
fr.adresse
{“monAdresse:fr.adresse”: [ 54, bis, rue de la mairie, 78730 ]
(fr.BAN.numero, fr.BAN.rep, fr.BAN.nom_voie, fr.cp)
  1# -*- coding: utf-8 -*-
  2"""
  3Created on Jan 20 2023
  4
  5@author: Philippe@loco-labs.io
  6
  7The `namespace` module is part of the `NTV.json_ntv` package ([specification document](
  8https://github.com/loco-philippe/NTV/blob/main/documentation/JSON-NTV-standard.pdf)).
  9 
 10It contains the `Namespace` and the `NtvType` classes and the `str_type` method for NTV entities.    
 11      
 12# 0 - Presentation
 13
 14The NTVtype is defined by a name and a Namespace. The name is unique in the Namespace
 15
 16A Namespace is represented by a string followed by a point.
 17Namespaces may be nested (the global Namespace is represented by an empty string).
 18
 19The Namespace representations are added to the value of an NTVtype to have an absolute
 20representation of an NTVtype (long_name).
 21
 22*Example for an absolute representation of an NTVtype defined in two nested Namespace :*
 23*“ns1.ns2.type”*
 24*where:*
 25- *ns1. is a Namespace defined in the global Namespace,*
 26- *ns2. is a Namespace defined in the ns1. Namespace,*
 27- *type is a NTVtype defined in the ns2. Namespace*
 28
 29# 1 - Global Namespace
 30
 31The structure of types by namespace makes it possible to have types corresponding
 32to recognized standards at the global level.
 33Generic types can also be defined (calculation of the exact type when decoding the value).
 34
 35The global namespace can include the following structures:
 36
 37## 1.1 - Simple (JSON RFC8259)
 38
 39| type (generic type)| value example                 |
 40|--------------------|-------------------------------|
 41| boolean (json)     | true                          |
 42| null (json)        | null                          |
 43| number (json)      | 45.2                          |
 44| string (json)      | "string"                      |
 45| array  (json)      | [1, 2, 3]                     |
 46| object (json)      | { "str": "test", "bool": true}|
 47
 48## 1.2 - Datation (ISO8601 and Posix)
 49
 50| type (generic type)| value example                 |
 51|--------------------|-------------------------------|
 52| year               | 1998                          |
 53| month              | 10                            |
 54| day                | 21                            |
 55| wday               | 2                             |
 56| yday               | 251                           |
 57| week               | 38                            |
 58| hour               | 20                            |
 59| minute             | 18                            |
 60| second             | 54                            |
 61| date (dat)         | “2022-01-28”                  |
 62| time (dat)         | “T18:23:54”,  “18:23”, “T18”  |
 63| datetime (dat)     | “2022-01-28T18-23-54Z”, “2022-01-28T18-23-54+0400”        |
 64
 65## 1.3 - Duration (ISO8601 and Posix)
 66
 67| type (generic type)| value example                 |
 68|--------------------|-------------------------------|
 69| period             | "2007-03-01T13:00:00Z/2008-05-11T15:30:00Z"  |
 70| duration           | "P0002-10- 15T10:30:20"       |
 71| timearray          | [date1, date2]                |
 72
 73## 1.4 - Location (RFC7946 and Open Location Code):
 74
 75| type (generic type) | value example                                |
 76|---------------------|------------------------------|
 77| point (loc)         | [ 5.12, 45.256 ] (lon, lat)  |
 78| line (loc)          | [ point1, point2, point3 ]   |
 79| ring                | [ point1, point2, point3 ]   |
 80| multiline           | [ line1, line2, line3]       |
 81| polygon (loc)       | [ ring1, ring2, ring3]       |
 82| multipolygon (loc)  | [ poly1, poly2, poly3 ]      |
 83| box (loc)           | [ -10.0, -10.0, 10.0, 10.0 ] |
 84| geojson (loc)       | {“type”: “point”, “coordinates”: [40.0, 0.0] } |
 85| codeolc (loc)       | “8FW4V75V+8F6”               |
 86
 87## 1.5 - Tabular data
 88
 89| NTVtype  | NTVvalue                                               |
 90|----------|--------------------------------------------------------|
 91| row      | JSON-array of JSON-NTV                                 |
 92| field    | JSON-array of NTVvalue (following JSON-TAB format)     |
 93| table    | JSON-array of JSON-NTV fields with the same length     |
 94
 95
 96## 1.6 - Normalized strings
 97
 98The type could be `uri`, cf exemples :
 99- "https://www.ietf.org/rfc/rfc3986.txt"
100- "https://gallica.bnf.fr/ark:/12148/bpt6k107371t"
101- "urn:uuid:f81d4fae-7dec-11d0-a765-00a0c91e6bf6"
102- "ni:///sha-256;UyaQV-Ev4rdLoHyJJWCi11OHfrYv9E1aGQAlMO2X_-Q"
103- "geo:13.4125,103.86673" *(RFC5870)*
104- "info:eu-repo/dai/nl/12345"
105- "mailto:John.Doe@example.com"
106- "news:comp.infosystems.www.servers.unix"
107- "urn:oasis:names:specification:docbook:dtd:xml:4.1.2"
108
109## 1.7 - Namespaces
110
111Namespaces could also be defined to reference for example:
112- geopolitical entities: ISO3166-1 country code (for example "fr." for France)
113- data catalogs, for example:
114
115| NTVtype      | example JSON-NTV                                                     |
116|--------------|----------------------------------------------------------------------|
117| schemaorg.   | <div>{ “:schemaorg.propertyID”: “NO2” }</div><div>{ “:schemaorg.unitText”:”µg/m3”}</div>  |
118| darwincore.  | { “:darwincore.acceptedNameUsage”: “Tamias minimus” }                |
119
120## 1.8 - Identifiers
121
122For example :
123
124| type         | definition                      | exemple               |
125|--------------|---------------------------------|-----------------------|
126| fr.uic       | code UIC station                | 8757449               |
127| fr.iata      | code IATA airport               | CDG                   |
128
129
130# 2 - Example of using a `fr.` namespace
131
132This namespace is dedicated to datasets associated with the France geopolitical namespace
133(see also the [presentation document](
134https://github.com/loco-philippe/NTV/blob/main/documentation/JSON-NTV-namespace-fr.pdf)).
135
136A namespace defines:
137- identifiers used to access additional data,
138- namespaces associated with catalogs or data sets,
139- structured entities used to facilitate the use of data
140
141## 2.1 - Identifiers
142They could correspond to identifiers used in many referenced datasets
143(via a data schema or a data model).
144
145For example :
146
147| type         | definition                      | example               |
148|--------------|---------------------------------|-----------------------|
149| fr.dep       | code département                | 60                    |
150| fr.cp        | code postal                     | 76450                 |
151| fr.naf       | code NAF                        | 23                    |
152| fr.siren     | code SIREN enterprise           | 418447363             |
153| fr.fantoir   | code FANTOIR voie               | 4500023086F           |
154| fr.uai       | code UAI établissement          | 0951099D              |
155| fr.aca       | code académies                  | 22                    |
156| fr.finessej  | code FINESS entité juridique    | 790001606             |
157| fr.rna       | code WALDEC association         | 843S0843004860        |
158| fr.spi       | code SPI numéro fiscal          | 1899582886173         |
159| fr.nir       | code NIR sécurité sociale       | 164026005705953       |
160
161## 2.2 Namespaces
162Namespaces could correspond to catalogs or data sets whose data types are identified
163in data models or in referenced data schemas.
164
165For example :
166
167|    type     | example JSON-NTV                                          |
168|-------------|-----------------------------------------------------------|
169| fr.sandre.  | <div>{ ":fr.sandre.CdStationHydro": K163 3010 01 }</div><div>{ ":fr.sandre.TypStationHydro": "standard" }</div>    |
170| fr.synop.   | <div>{ ":fr.synop.numer_sta": 07130 }</div><div>{  ":fr.synop.t": 300, ":fr.synop.ff": 5 }</div>                   |
171| fr.IRVE.    | <div>{ ":fr.IRVE.nom_station": "M2026" }</div><div>{ ":fr.IRVE.nom_operateur": "DEBELEC" }</div>                   |
172| fr.BAN.     | <div>{ ":fr.BAN.numero": 54 }</div><div>{ ":fr.BAN.lon": 3.5124 }</div>|
173
174## 2.3 Entities
175They could correspond to assemblies of data associated with a defined structure.
176
177For example :
178
179|    type      | example JSON-NTV                                         |
180|--------------|----------------------------------------------------------|
181| fr.parcelle  | <div>{“maParcelle:fr.parcelle”: [ 84500, 0, I, 97]}</div><div><i>(fr.cp, fr.cadastre.préfixe, fr.cadastre.section, fr.cadastre.numéro)</i></div> |
182| fr.adresse   | <div>{“monAdresse:fr.adresse”: [ 54, bis, rue de la mairie, 78730 ]</div><div><i>(fr.BAN.numero, fr.BAN.rep, fr.BAN.nom_voie, fr.cp)</i></div>  |
183
184"""
185import configparser
186from pathlib import Path
187import json
188import requests
189from time import time
190
191import json_ntv
192
193def agreg_type(str_typ, def_type, single):
194    '''aggregate str_typ and def_type to return an NtvType or a Namespace if not single
195
196    *Parameters*
197
198        - **str_typ** : NtvType or String (long_name) - NtvType to aggregate
199        - **def_typ** : NtvType or String (long_name) - default NtvType or Namespace
200        - **single** : Boolean - Ntv entity concerned (True if NtvSingle)'''
201    if isinstance(str_typ, NtvType):
202        str_typ = str_typ.long_name
203    def_type = str_type(def_type, single)
204    if not str_typ and (isinstance(def_type, NtvType) or
205                        (not isinstance(def_type, NtvType) and not single)):
206        return def_type
207    if not str_typ:
208        return NtvType('json')
209    clas = NtvType
210    if str_typ[-1] == '.':
211        clas = Namespace
212    if not def_type:
213        return clas.add(str_typ)
214    if clas == NtvType or clas == Namespace and not single:
215        try:
216            return clas.add(str_typ)
217        except NtvTypeError:
218            return clas.add(_join_type(def_type.long_name, str_typ))
219    raise NtvTypeError(str_typ + 'and' +
220                       def_type.long_name + 'are incompatible')
221
222
223def _join_type(namesp, str_typ):
224    '''join Namespace string and NtvType or Namespace string'''
225    namesp_split = namesp.split('.')[:-1]
226    for name in str_typ.split('.'):
227        if not name in namesp_split:
228            namesp_split.append(name)
229    return '.'.join(namesp_split)
230
231
232def relative_type(str_def, str_typ):
233    '''return relative str_typ string from NtvType or Namespace str_def
234
235    *Parameters*
236
237        - **str_def** : String - long_name of the Namespace or NtvType
238        - **str_type** : String - long_name of Ntvtype to be relative '''
239    if not str_def and not str_typ:
240        return ''
241    if str_def == str_typ:
242        return ''
243    if not str_def or not str_def in str_typ:
244        return str_typ
245    if not str_typ and str_def[-1] != ".":
246        return str_def
247    str_def_split = str_def.split('.')[:-1]
248    str_typ_split = str_typ.split('.')
249    ind = 0
250    for ind, name in enumerate(str_typ_split):
251        if not name in str_def_split:
252            break
253    return '.'.join(str_typ_split[ind:])
254
255
256def str_type(long_name, single):
257    ''' create a NtvType or a Namespace from a string
258
259    *Parameters*
260
261        - **long_name** : String - name of the Namespace or NtvType
262        - **single** : Boolean - If True, default type is 'json', else None'''
263    if not long_name and single:
264        return NtvType('json')
265    if not long_name and not single:
266        return None
267    if isinstance(long_name, (NtvType, Namespace)):
268        return long_name
269    if not isinstance(long_name, str):
270        raise NtvTypeError('the name is not a string')
271    if long_name[-1] == '.':
272        return Namespace.add(long_name)
273    return NtvType.add(long_name)
274
275
276class NtvType():
277    ''' type of NTV entities.
278
279    *Attributes :*
280
281    - **name** : String - name of the type
282    - **nspace** : Namespace - namespace associated
283    - **custom** : boolean - True if not referenced
284
285    The methods defined in this class are :
286
287    *classmethods*
288    - `types`
289    - `add`
290
291    *dynamic values (@property)*
292    - `gen_type`
293    - `long_name`
294
295    *instance methods*
296    - `isin_namespace`
297    '''
298    _types_ = {}
299
300    @classmethod
301    def types(cls):
302        '''return the list of NtvType created'''
303        return [nam.long_name for nam in cls._types_.values()]
304
305    @classmethod
306    def add(cls, long_name, module=False):
307        '''activate and return a valid NtvType defined by the long name
308        
309        *parameters :*
310
311        - **long_name** : String - absolut name of the NtvType
312        - **module** : boolean (default False) - if True search data in the 
313        local .ini file, else in the distant repository
314        '''
315        if long_name == '':
316            return None
317        if long_name in NtvType.types():
318            return cls._types_[long_name]
319        split_name = long_name.rsplit('.', 1)
320        if split_name[-1] == '':
321            raise NtvTypeError(long_name + ' is not a valid NTVtype')
322        if len(split_name) == 1:
323            return cls(split_name[0])
324        if len(split_name) == 2:
325            nspace = Namespace.add(split_name[0]+'.', module=module)
326            return cls(split_name[1], nspace)
327        raise NtvTypeError(long_name + ' is not a valid NTVtype')
328
329    def __init__(self, name, nspace=None):
330        '''NtvType constructor.
331
332        *Parameters*
333
334        - **name** : string - name of the Type
335        - **nspace** : Namespace (default None) - namespace associated'''
336        if isinstance(name, NtvType):
337            self.name = name.name
338            self.nspace = name.nspace
339            self.custom = name.custom
340            return
341        if not name or not isinstance(name, str):
342            raise NtvTypeError('null name is not allowed')
343        if not name and not nspace:
344            name = 'json'
345        if not nspace:
346            nspace = Namespace._namespaces_['']
347        if name[0] != '$' and not nspace.custom and not name in nspace.content['type']:
348            raise NtvTypeError(name + ' is not defined in ' + nspace.long_name)
349        self.name = name
350        self.nspace = nspace
351        self.custom = nspace.custom or name[0] == '$'
352        self._types_[self.long_name] = self
353        return
354
355    def __eq__(self, other):
356        ''' equal if name and nspace are equal'''
357        if self is None and other is None:
358            return True
359        if self is None or other is None:
360            return False
361        if self.__class__.__name__ != other.__class__.__name__:
362            return False
363        return self.name == other.name and self.nspace == other.nspace
364    def __hash__(self):
365        '''return hash(name) + hash(nspace)'''
366        return hash(self.name) + hash(self.nspace)
367
368    def __str__(self):
369        '''return string format'''
370        return self.long_name
371
372    def __repr__(self):
373        '''return classname and long name'''
374        return self.__class__.__name__ + '(' + self.long_name + ')'
375
376    @property
377    def gen_type(self):
378        '''return the generic type of the NtvType'''
379        if self.custom:
380            return ''
381        return self.nspace.content['type'][self.name]
382
383    @property
384    def long_name(self):
385        '''return a string with the absolute name'''
386        return self.nspace.long_name + self.name
387
388    def isin_namespace(self, long_name):
389        '''return the number of level between self and nspace, -1 if None'''
390        return self.nspace.is_child(Namespace.add(long_name))
391
392
393class Namespace():
394    ''' Namespace of NTV entities.
395
396    *Attributes :*
397
398    - **name** : String - name of the namespace
399    - **file** : string - location of the file init
400    - **parent** : Namespace - parent namespace
401    - **custom** : boolean - True if not referenced
402
403    The methods defined in this class are :
404
405    *classmethods*
406    - `namespaces`
407    - `add`
408
409    *dynamic values (@property)*
410    - `long_name`
411    - `content`
412
413    *instance methods*
414    - `is_child`
415    - `is_parent`
416    '''
417    _namespaces_ = {}
418    _pathconfig_ = 'https://raw.githubusercontent.com/loco-philippe/NTV/master/config/'
419    _global_ = "NTV_global_namespace.ini"
420
421    @classmethod
422    def namespaces(cls):
423        '''return the list of Namespace created'''
424        return [nam.long_name for nam in cls._namespaces_.values()]
425
426    @classmethod
427    def add(cls, long_name, module=False):
428        '''activate and return a valid Namespace defined by the long_name.
429                
430        *parameters :*
431
432        - **long_name** : String - absolut name of the Namespace
433        - **module** : boolean (default False) - if True search data in the 
434        local .ini file, else in the distant repository
435        '''
436        if long_name in Namespace.namespaces():
437            return cls._namespaces_[long_name]
438        split_name = long_name.rsplit('.', 2)
439        if len(split_name) == 1 or split_name[-1] != '':
440            raise NtvTypeError(long_name + ' is not a valid classname')
441        if len(split_name) == 2:
442            return cls(split_name[0]+'.', module=module)
443        if len(split_name) == 3:
444            parent = Namespace.add(split_name[0]+'.')
445            return cls(split_name[1]+'.', parent, module=module)
446        raise NtvTypeError(long_name + ' is not a valid classname')
447
448    def __init__(self, name='', parent=None, module=False):
449        '''
450        Namespace constructor.
451
452        *Parameters*
453
454        - **name** : String - name of the namespace
455        - **parent** : Namespace - parent namespace
456        - **module** : boolean (default False) - if True search data in the 
457        local .ini file, else in the distant repository
458        '''
459        if name and parent is None:
460            parent = Namespace._namespaces_['']
461        if name and name[0] != '$' and not parent.custom and \
462          not name in parent.content['namespace']:
463            raise NtvTypeError(name + ' is not defined in ' + parent.long_name)
464        self.name = name
465        self.parent = parent
466        if parent:
467            self.custom = parent.custom or name[0] == '$'
468        else:
469            self.custom = False
470        self.file = Namespace._file(self.parent , self.name, self.custom, module)
471        self.content = Namespace._content(self.file, self.name, self.custom, module)
472        self._namespaces_[self.long_name] = self
473
474    def __eq__(self, other):
475        ''' equal if name and parent are equal'''
476        if self is None and other is None:
477            return True
478        if self is None or other is None:
479            return False
480        if self.__class__.__name__ != other.__class__.__name__:
481            return False
482        return self.name == other.name and self.parent == other.parent
483
484    def __hash__(self):
485        '''return hash(name) + hash(parent)'''
486        return hash(self.name) + hash(self.parent)
487
488    def __str__(self):
489        '''return string format'''
490        return self.long_name
491
492    def __repr__(self):
493        '''return classname and long name'''
494        return self.__class__.__name__ + '(' + self.long_name + ')'
495
496    @staticmethod
497    def _file(parent, name, custom, module):
498        '''return the file name of the Namespace configuration
499                
500        *parameters :*
501
502        - **parent** : Namespace - Parent of the Namespace
503        - **name** : String - name of the Namespace
504        - **custom** : boolean - if True, return None (custom Namespace)
505        - **module** : boolean (default False) - if True search data in the 
506        local .ini file, else in the distant repository
507        '''
508        if custom:
509            return None
510        if parent:
511            config = configparser.ConfigParser()
512            if module:
513                p_file = Path(parent.file).stem + Path(parent.file).suffix
514                config.read(Path(json_ntv.__file__
515                    ).parent.joinpath(p_file))
516            else:
517                config.read_string(requests.get(
518                    parent.file, allow_redirects=True).content.decode())
519            return Namespace._pathconfig_ + json.loads(config['data']['namespace'])[name]
520        return Namespace._pathconfig_ + Namespace._global_
521
522    @staticmethod
523    def _content(file, name, custom, module):
524        '''return the content of the Namespace configuration
525        
526        *parameters :*
527
528        - **file** : string - file name of the parent Namespace
529        - **name** : string - name of the Namespace
530        - **custom** : boolean - if True, return empty dict (custom Namespace)
531        - **module** : boolean (default False) - if True search data in the 
532        local .ini file, else in the distant repository
533        
534        *return :*
535        
536        - dict : {'type': <list of ntv_type names>,  
537                  'namespace': <list of namespace names>}
538        '''
539        if custom:
540            return {'type': {}, 'namespace': {}}
541        config = configparser.ConfigParser()
542        if module:
543            p_file = Path(file).stem + Path(file).suffix
544            config.read(Path(json_ntv.__file__
545                ).parent.joinpath(p_file))
546        else:
547            config.read_string(requests.get(
548                file, allow_redirects=True).content.decode())
549        config_name = config['data']['name']
550        if config_name != name:
551            raise NtvTypeError(file + ' is not correct')
552        return {'type': json.loads(config['data']['type']),
553                'namespace': json.loads(config['data']['namespace'])}
554
555    @property
556    def long_name(self):
557        '''return a string with the absolute name'''
558        if self.parent is None or self.parent.name == '':
559            return self.name
560        return self.parent.long_name + self.name
561
562    def is_child(self, nspace):
563        '''return the number of level between self and nspace, -1 if None'''
564        parent = self.parent
565        if not self.name:
566            return -1
567        if self == nspace:
568            return 0
569        rang = 1
570        while parent.name != '' and parent != nspace:
571            rang += 1
572            parent = parent.parent
573        if parent == nspace:
574            return rang
575        if parent.name == '':
576            return -1
577
578    def is_parent(self, nspace):
579        '''return the number of level between self and nspace, -1 if None'''
580        return nspace.is_child(self)
581
582
583class NtvTypeError(Exception):
584    ''' NtvType or Namespace Exception'''
585    # pass
586
587nroot = Namespace(module=True)
588for root_typ in nroot.content['type'].keys():
589    typ = NtvType.add(root_typ, module=True)
590typ_json = NtvType('json')
def agreg_type(str_typ, def_type, single):
194def agreg_type(str_typ, def_type, single):
195    '''aggregate str_typ and def_type to return an NtvType or a Namespace if not single
196
197    *Parameters*
198
199        - **str_typ** : NtvType or String (long_name) - NtvType to aggregate
200        - **def_typ** : NtvType or String (long_name) - default NtvType or Namespace
201        - **single** : Boolean - Ntv entity concerned (True if NtvSingle)'''
202    if isinstance(str_typ, NtvType):
203        str_typ = str_typ.long_name
204    def_type = str_type(def_type, single)
205    if not str_typ and (isinstance(def_type, NtvType) or
206                        (not isinstance(def_type, NtvType) and not single)):
207        return def_type
208    if not str_typ:
209        return NtvType('json')
210    clas = NtvType
211    if str_typ[-1] == '.':
212        clas = Namespace
213    if not def_type:
214        return clas.add(str_typ)
215    if clas == NtvType or clas == Namespace and not single:
216        try:
217            return clas.add(str_typ)
218        except NtvTypeError:
219            return clas.add(_join_type(def_type.long_name, str_typ))
220    raise NtvTypeError(str_typ + 'and' +
221                       def_type.long_name + 'are incompatible')

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

Parameters

- **str_typ** : NtvType or String (long_name) - NtvType to aggregate
- **def_typ** : NtvType or String (long_name) - default NtvType or Namespace
- **single** : Boolean - Ntv entity concerned (True if NtvSingle)
def relative_type(str_def, str_typ):
233def relative_type(str_def, str_typ):
234    '''return relative str_typ string from NtvType or Namespace str_def
235
236    *Parameters*
237
238        - **str_def** : String - long_name of the Namespace or NtvType
239        - **str_type** : String - long_name of Ntvtype to be relative '''
240    if not str_def and not str_typ:
241        return ''
242    if str_def == str_typ:
243        return ''
244    if not str_def or not str_def in str_typ:
245        return str_typ
246    if not str_typ and str_def[-1] != ".":
247        return str_def
248    str_def_split = str_def.split('.')[:-1]
249    str_typ_split = str_typ.split('.')
250    ind = 0
251    for ind, name in enumerate(str_typ_split):
252        if not name in str_def_split:
253            break
254    return '.'.join(str_typ_split[ind:])

return relative str_typ string from NtvType or Namespace str_def

Parameters

- **str_def** : String - long_name of the Namespace or NtvType
- **str_type** : String - long_name of Ntvtype to be relative
def str_type(long_name, single):
257def str_type(long_name, single):
258    ''' create a NtvType or a Namespace from a string
259
260    *Parameters*
261
262        - **long_name** : String - name of the Namespace or NtvType
263        - **single** : Boolean - If True, default type is 'json', else None'''
264    if not long_name and single:
265        return NtvType('json')
266    if not long_name and not single:
267        return None
268    if isinstance(long_name, (NtvType, Namespace)):
269        return long_name
270    if not isinstance(long_name, str):
271        raise NtvTypeError('the name is not a string')
272    if long_name[-1] == '.':
273        return Namespace.add(long_name)
274    return NtvType.add(long_name)

create a NtvType or a Namespace from a string

Parameters

- **long_name** : String - name of the Namespace or NtvType
- **single** : Boolean - If True, default type is 'json', else None
class NtvType:
277class NtvType():
278    ''' type of NTV entities.
279
280    *Attributes :*
281
282    - **name** : String - name of the type
283    - **nspace** : Namespace - namespace associated
284    - **custom** : boolean - True if not referenced
285
286    The methods defined in this class are :
287
288    *classmethods*
289    - `types`
290    - `add`
291
292    *dynamic values (@property)*
293    - `gen_type`
294    - `long_name`
295
296    *instance methods*
297    - `isin_namespace`
298    '''
299    _types_ = {}
300
301    @classmethod
302    def types(cls):
303        '''return the list of NtvType created'''
304        return [nam.long_name for nam in cls._types_.values()]
305
306    @classmethod
307    def add(cls, long_name, module=False):
308        '''activate and return a valid NtvType defined by the long name
309        
310        *parameters :*
311
312        - **long_name** : String - absolut name of the NtvType
313        - **module** : boolean (default False) - if True search data in the 
314        local .ini file, else in the distant repository
315        '''
316        if long_name == '':
317            return None
318        if long_name in NtvType.types():
319            return cls._types_[long_name]
320        split_name = long_name.rsplit('.', 1)
321        if split_name[-1] == '':
322            raise NtvTypeError(long_name + ' is not a valid NTVtype')
323        if len(split_name) == 1:
324            return cls(split_name[0])
325        if len(split_name) == 2:
326            nspace = Namespace.add(split_name[0]+'.', module=module)
327            return cls(split_name[1], nspace)
328        raise NtvTypeError(long_name + ' is not a valid NTVtype')
329
330    def __init__(self, name, nspace=None):
331        '''NtvType constructor.
332
333        *Parameters*
334
335        - **name** : string - name of the Type
336        - **nspace** : Namespace (default None) - namespace associated'''
337        if isinstance(name, NtvType):
338            self.name = name.name
339            self.nspace = name.nspace
340            self.custom = name.custom
341            return
342        if not name or not isinstance(name, str):
343            raise NtvTypeError('null name is not allowed')
344        if not name and not nspace:
345            name = 'json'
346        if not nspace:
347            nspace = Namespace._namespaces_['']
348        if name[0] != '$' and not nspace.custom and not name in nspace.content['type']:
349            raise NtvTypeError(name + ' is not defined in ' + nspace.long_name)
350        self.name = name
351        self.nspace = nspace
352        self.custom = nspace.custom or name[0] == '$'
353        self._types_[self.long_name] = self
354        return
355
356    def __eq__(self, other):
357        ''' equal if name and nspace are equal'''
358        if self is None and other is None:
359            return True
360        if self is None or other is None:
361            return False
362        if self.__class__.__name__ != other.__class__.__name__:
363            return False
364        return self.name == other.name and self.nspace == other.nspace
365    def __hash__(self):
366        '''return hash(name) + hash(nspace)'''
367        return hash(self.name) + hash(self.nspace)
368
369    def __str__(self):
370        '''return string format'''
371        return self.long_name
372
373    def __repr__(self):
374        '''return classname and long name'''
375        return self.__class__.__name__ + '(' + self.long_name + ')'
376
377    @property
378    def gen_type(self):
379        '''return the generic type of the NtvType'''
380        if self.custom:
381            return ''
382        return self.nspace.content['type'][self.name]
383
384    @property
385    def long_name(self):
386        '''return a string with the absolute name'''
387        return self.nspace.long_name + self.name
388
389    def isin_namespace(self, long_name):
390        '''return the number of level between self and nspace, -1 if None'''
391        return self.nspace.is_child(Namespace.add(long_name))

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 :

classmethods

dynamic values (@property)

instance methods

NtvType(name, nspace=None)
330    def __init__(self, name, nspace=None):
331        '''NtvType constructor.
332
333        *Parameters*
334
335        - **name** : string - name of the Type
336        - **nspace** : Namespace (default None) - namespace associated'''
337        if isinstance(name, NtvType):
338            self.name = name.name
339            self.nspace = name.nspace
340            self.custom = name.custom
341            return
342        if not name or not isinstance(name, str):
343            raise NtvTypeError('null name is not allowed')
344        if not name and not nspace:
345            name = 'json'
346        if not nspace:
347            nspace = Namespace._namespaces_['']
348        if name[0] != '$' and not nspace.custom and not name in nspace.content['type']:
349            raise NtvTypeError(name + ' is not defined in ' + nspace.long_name)
350        self.name = name
351        self.nspace = nspace
352        self.custom = nspace.custom or name[0] == '$'
353        self._types_[self.long_name] = self
354        return

NtvType constructor.

Parameters

  • name : string - name of the Type
  • nspace : Namespace (default None) - namespace associated
@classmethod
def types(cls):
301    @classmethod
302    def types(cls):
303        '''return the list of NtvType created'''
304        return [nam.long_name for nam in cls._types_.values()]

return the list of NtvType created

@classmethod
def add(cls, long_name, module=False):
306    @classmethod
307    def add(cls, long_name, module=False):
308        '''activate and return a valid NtvType defined by the long name
309        
310        *parameters :*
311
312        - **long_name** : String - absolut name of the NtvType
313        - **module** : boolean (default False) - if True search data in the 
314        local .ini file, else in the distant repository
315        '''
316        if long_name == '':
317            return None
318        if long_name in NtvType.types():
319            return cls._types_[long_name]
320        split_name = long_name.rsplit('.', 1)
321        if split_name[-1] == '':
322            raise NtvTypeError(long_name + ' is not a valid NTVtype')
323        if len(split_name) == 1:
324            return cls(split_name[0])
325        if len(split_name) == 2:
326            nspace = Namespace.add(split_name[0]+'.', module=module)
327            return cls(split_name[1], nspace)
328        raise NtvTypeError(long_name + ' is not a valid NTVtype')

activate and return a valid NtvType defined by the long name

parameters :

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

return the generic type of the NtvType

long_name

return a string with the absolute name

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

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

class Namespace:
394class Namespace():
395    ''' Namespace of NTV entities.
396
397    *Attributes :*
398
399    - **name** : String - name of the namespace
400    - **file** : string - location of the file init
401    - **parent** : Namespace - parent namespace
402    - **custom** : boolean - True if not referenced
403
404    The methods defined in this class are :
405
406    *classmethods*
407    - `namespaces`
408    - `add`
409
410    *dynamic values (@property)*
411    - `long_name`
412    - `content`
413
414    *instance methods*
415    - `is_child`
416    - `is_parent`
417    '''
418    _namespaces_ = {}
419    _pathconfig_ = 'https://raw.githubusercontent.com/loco-philippe/NTV/master/config/'
420    _global_ = "NTV_global_namespace.ini"
421
422    @classmethod
423    def namespaces(cls):
424        '''return the list of Namespace created'''
425        return [nam.long_name for nam in cls._namespaces_.values()]
426
427    @classmethod
428    def add(cls, long_name, module=False):
429        '''activate and return a valid Namespace defined by the long_name.
430                
431        *parameters :*
432
433        - **long_name** : String - absolut name of the Namespace
434        - **module** : boolean (default False) - if True search data in the 
435        local .ini file, else in the distant repository
436        '''
437        if long_name in Namespace.namespaces():
438            return cls._namespaces_[long_name]
439        split_name = long_name.rsplit('.', 2)
440        if len(split_name) == 1 or split_name[-1] != '':
441            raise NtvTypeError(long_name + ' is not a valid classname')
442        if len(split_name) == 2:
443            return cls(split_name[0]+'.', module=module)
444        if len(split_name) == 3:
445            parent = Namespace.add(split_name[0]+'.')
446            return cls(split_name[1]+'.', parent, module=module)
447        raise NtvTypeError(long_name + ' is not a valid classname')
448
449    def __init__(self, name='', parent=None, module=False):
450        '''
451        Namespace constructor.
452
453        *Parameters*
454
455        - **name** : String - name of the namespace
456        - **parent** : Namespace - parent namespace
457        - **module** : boolean (default False) - if True search data in the 
458        local .ini file, else in the distant repository
459        '''
460        if name and parent is None:
461            parent = Namespace._namespaces_['']
462        if name and name[0] != '$' and not parent.custom and \
463          not name in parent.content['namespace']:
464            raise NtvTypeError(name + ' is not defined in ' + parent.long_name)
465        self.name = name
466        self.parent = parent
467        if parent:
468            self.custom = parent.custom or name[0] == '$'
469        else:
470            self.custom = False
471        self.file = Namespace._file(self.parent , self.name, self.custom, module)
472        self.content = Namespace._content(self.file, self.name, self.custom, module)
473        self._namespaces_[self.long_name] = self
474
475    def __eq__(self, other):
476        ''' equal if name and parent are equal'''
477        if self is None and other is None:
478            return True
479        if self is None or other is None:
480            return False
481        if self.__class__.__name__ != other.__class__.__name__:
482            return False
483        return self.name == other.name and self.parent == other.parent
484
485    def __hash__(self):
486        '''return hash(name) + hash(parent)'''
487        return hash(self.name) + hash(self.parent)
488
489    def __str__(self):
490        '''return string format'''
491        return self.long_name
492
493    def __repr__(self):
494        '''return classname and long name'''
495        return self.__class__.__name__ + '(' + self.long_name + ')'
496
497    @staticmethod
498    def _file(parent, name, custom, module):
499        '''return the file name of the Namespace configuration
500                
501        *parameters :*
502
503        - **parent** : Namespace - Parent of the Namespace
504        - **name** : String - name of the Namespace
505        - **custom** : boolean - if True, return None (custom Namespace)
506        - **module** : boolean (default False) - if True search data in the 
507        local .ini file, else in the distant repository
508        '''
509        if custom:
510            return None
511        if parent:
512            config = configparser.ConfigParser()
513            if module:
514                p_file = Path(parent.file).stem + Path(parent.file).suffix
515                config.read(Path(json_ntv.__file__
516                    ).parent.joinpath(p_file))
517            else:
518                config.read_string(requests.get(
519                    parent.file, allow_redirects=True).content.decode())
520            return Namespace._pathconfig_ + json.loads(config['data']['namespace'])[name]
521        return Namespace._pathconfig_ + Namespace._global_
522
523    @staticmethod
524    def _content(file, name, custom, module):
525        '''return the content of the Namespace configuration
526        
527        *parameters :*
528
529        - **file** : string - file name of the parent Namespace
530        - **name** : string - name of the Namespace
531        - **custom** : boolean - if True, return empty dict (custom Namespace)
532        - **module** : boolean (default False) - if True search data in the 
533        local .ini file, else in the distant repository
534        
535        *return :*
536        
537        - dict : {'type': <list of ntv_type names>,  
538                  'namespace': <list of namespace names>}
539        '''
540        if custom:
541            return {'type': {}, 'namespace': {}}
542        config = configparser.ConfigParser()
543        if module:
544            p_file = Path(file).stem + Path(file).suffix
545            config.read(Path(json_ntv.__file__
546                ).parent.joinpath(p_file))
547        else:
548            config.read_string(requests.get(
549                file, allow_redirects=True).content.decode())
550        config_name = config['data']['name']
551        if config_name != name:
552            raise NtvTypeError(file + ' is not correct')
553        return {'type': json.loads(config['data']['type']),
554                'namespace': json.loads(config['data']['namespace'])}
555
556    @property
557    def long_name(self):
558        '''return a string with the absolute name'''
559        if self.parent is None or self.parent.name == '':
560            return self.name
561        return self.parent.long_name + self.name
562
563    def is_child(self, nspace):
564        '''return the number of level between self and nspace, -1 if None'''
565        parent = self.parent
566        if not self.name:
567            return -1
568        if self == nspace:
569            return 0
570        rang = 1
571        while parent.name != '' and parent != nspace:
572            rang += 1
573            parent = parent.parent
574        if parent == nspace:
575            return rang
576        if parent.name == '':
577            return -1
578
579    def is_parent(self, nspace):
580        '''return the number of level between self and nspace, -1 if None'''
581        return nspace.is_child(self)

Namespace of NTV entities.

Attributes :

  • name : String - name of the namespace
  • file : string - location of the file init
  • parent : Namespace - parent namespace
  • custom : boolean - True if not referenced

The methods defined in this class are :

classmethods

dynamic values (@property)

instance methods

Namespace(name='', parent=None, module=False)
449    def __init__(self, name='', parent=None, module=False):
450        '''
451        Namespace constructor.
452
453        *Parameters*
454
455        - **name** : String - name of the namespace
456        - **parent** : Namespace - parent namespace
457        - **module** : boolean (default False) - if True search data in the 
458        local .ini file, else in the distant repository
459        '''
460        if name and parent is None:
461            parent = Namespace._namespaces_['']
462        if name and name[0] != '$' and not parent.custom and \
463          not name in parent.content['namespace']:
464            raise NtvTypeError(name + ' is not defined in ' + parent.long_name)
465        self.name = name
466        self.parent = parent
467        if parent:
468            self.custom = parent.custom or name[0] == '$'
469        else:
470            self.custom = False
471        self.file = Namespace._file(self.parent , self.name, self.custom, module)
472        self.content = Namespace._content(self.file, self.name, self.custom, module)
473        self._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
@classmethod
def namespaces(cls):
422    @classmethod
423    def namespaces(cls):
424        '''return the list of Namespace created'''
425        return [nam.long_name for nam in cls._namespaces_.values()]

return the list of Namespace created

@classmethod
def add(cls, long_name, module=False):
427    @classmethod
428    def add(cls, long_name, module=False):
429        '''activate and return a valid Namespace defined by the long_name.
430                
431        *parameters :*
432
433        - **long_name** : String - absolut name of the Namespace
434        - **module** : boolean (default False) - if True search data in the 
435        local .ini file, else in the distant repository
436        '''
437        if long_name in Namespace.namespaces():
438            return cls._namespaces_[long_name]
439        split_name = long_name.rsplit('.', 2)
440        if len(split_name) == 1 or split_name[-1] != '':
441            raise NtvTypeError(long_name + ' is not a valid classname')
442        if len(split_name) == 2:
443            return cls(split_name[0]+'.', module=module)
444        if len(split_name) == 3:
445            parent = Namespace.add(split_name[0]+'.')
446            return cls(split_name[1]+'.', parent, module=module)
447        raise NtvTypeError(long_name + ' is not a valid classname')

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):
563    def is_child(self, nspace):
564        '''return the number of level between self and nspace, -1 if None'''
565        parent = self.parent
566        if not self.name:
567            return -1
568        if self == nspace:
569            return 0
570        rang = 1
571        while parent.name != '' and parent != nspace:
572            rang += 1
573            parent = parent.parent
574        if parent == nspace:
575            return rang
576        if parent.name == '':
577            return -1

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

def is_parent(self, nspace):
579    def is_parent(self, nspace):
580        '''return the number of level between self and nspace, -1 if None'''
581        return nspace.is_child(self)

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

class NtvTypeError(builtins.Exception):
584class NtvTypeError(Exception):
585    ''' NtvType or Namespace Exception'''
586    # pass

NtvType or Namespace Exception

Inherited Members
builtins.Exception
Exception
builtins.BaseException
with_traceback