NTV.json_ntv.ntv

@author: Philippe@loco-labs.io

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

It contains the classes NtvSingle, NtvList, Ntv(abstract) for NTV entities.

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

   1# -*- coding: utf-8 -*-
   2"""
   3@author: Philippe@loco-labs.io
   4
   5The `ntv` 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 classes `NtvSingle`, `NtvList`, `Ntv`(abstract) for NTV entities.
   9
  10For more information, see the
  11[user guide](https://loco-philippe.github.io/NTV/documentation/user_guide.html)
  12or the [github repository](https://github.com/loco-philippe/NTV).
  13
  14"""
  15import copy
  16from abc import ABC, abstractmethod
  17from numbers import Number
  18import json
  19
  20from json_ntv.namespace import Datatype, Namespace, str_type, relative_type, agreg_type
  21from json_ntv.ntv_util import NtvError, NtvJsonEncoder, NtvConnector, NtvTree, NtvUtil
  22from json_ntv.ntv_patch import NtvPointer
  23
  24NAME = 'N'
  25TYPE = 'T'
  26VALUE = 'V'
  27ENTITY = 'E'
  28NTVSINGLE = 'S'
  29NTVLIST = 'L'
  30
  31
  32class Ntv(ABC, NtvUtil):
  33    ''' The Ntv class is an abstract class used by `NtvSingle`and `NtvList` classes.
  34
  35    *Attributes :*
  36    - **ntv_name** :  String - name of the NTV entity
  37    - **ntv_type**:   Datatype or Namespace - type of the entity
  38    - **ntv_value**:  value of the entity
  39
  40    *Internal attributes :*
  41    - **parent**:     parent NtvList entity
  42    - **is_json**:    True if ntv_value is a json_value
  43
  44    *dynamic values (@property)*
  45    - `code_ntv`
  46    - `json_name_str`
  47    - `json_array` (abstract method)
  48    - `max_len`
  49    - `name`
  50    - `tree`
  51    - `type_str`
  52    - `val`
  53
  54    The methods defined in this class are :
  55
  56    *Ntv constructor (staticmethod)*
  57    - `fast`
  58    - `obj`
  59    - `from_obj`
  60    - `from_att`
  61
  62    *NTV conversion (instance methods)*
  63    - `alike`
  64    - `to_json_ntv`
  65    - `to_obj_ntv`
  66    - `validate` (validation)
  67
  68    *export - conversion (instance methods)*
  69    - `expand`
  70    - `no_type`
  71    - `no_name`
  72    - `reduce`
  73    - `to_fast`
  74    - `to_name`
  75    - `to_obj`
  76    - `to_repr`
  77    - `to_mermaid`
  78    - `to_tuple`
  79    - `to_ntvsingle`
  80    - `to_ntvlist`
  81
  82    *tree methods (instance methods)*
  83    - `childs`
  84    - `pointer`
  85    - `replace`
  86    - `remove`
  87    - `append` (NtvList only)
  88    - `insert` (NtvList only)
  89
  90    *other instance methods*
  91    - `from_value`
  92    - `json_name`
  93    - `set_name`
  94    - `set_type`
  95    - `set_value`
  96    - `obj_value` (abstract method)
  97
  98    *utility methods*
  99    - `decode_json` *(staticmethod)*
 100    - `obj_ntv` *(staticmethod)*
 101    '''
 102
 103    def __init__(self, ntv_value, ntv_name, ntv_type):
 104        '''Ntv constructor.
 105
 106        *Parameters*
 107
 108        - **ntv_value**: Json entity - value of the entity
 109        - **ntv_name** : String (default None) - name of the NTV entity
 110        - **ntv_type**: String or Datatype or Namespace (default None) - type of the entity
 111        '''
 112        if ntv_type.__class__.__name__ in ['Datatype', 'Namespace']:
 113            self.ntv_type = ntv_type
 114        elif ntv_type and ntv_type[-1] != '.':
 115            self.ntv_type = Datatype.add(ntv_type)
 116        elif ntv_type and ntv_type[-1] == '.':
 117            self.ntv_type = Namespace.add(ntv_type)
 118        else:
 119            self.ntv_type = None
 120        if not isinstance(ntv_name, str):
 121            ntv_name = ''
 122        self.ntv_name = ntv_name
 123        self.ntv_value = ntv_value
 124        self.is_json = NtvConnector.is_json(ntv_value)
 125        self.parent = None
 126
 127    @staticmethod
 128    def fast(data, no_typ=False, typ_auto=False):
 129        ''' return an Ntv entity from data without conversion.
 130
 131        *Parameters* : see `obj` method'''
 132        return Ntv.obj(data, no_typ=no_typ, typ_auto=typ_auto, fast=True)
 133
 134    @staticmethod
 135    def obj(data, no_typ=False, decode_str=False, typ_auto=False, fast=False):
 136        ''' return an Ntv entity from data.
 137
 138        *Parameters*
 139
 140        - **Data** can be :
 141            - a tuple with value, name, typ and cat (see `from_att` method)
 142            - a value to decode (see `from_obj`method)
 143        - **no_typ** : boolean (default False) - if True, NtvList is with None type
 144        - **type_auto**: boolean (default False) - if True, default type for NtvList
 145        is the ntv_type of the first Ntv in the ntv_value
 146        - **fast** : boolean (default False) - if True, Ntv entity is created without conversion
 147        - **decode_str**: boolean (default False) - if True, string are loaded in json data'''
 148        #print('obj : ', Namespace.namespaces(), '\n')
 149
 150        if isinstance(data, tuple):
 151            return Ntv.from_att(*data, decode_str=decode_str, fast=fast)
 152        # if isinstance(data, str) and data.lstrip() and data.lstrip()[0] in '{[':
 153        if isinstance(data, str):
 154            try:
 155                data = json.loads(data)
 156            except json.JSONDecodeError:
 157                pass
 158        return Ntv.from_obj(data, no_typ=no_typ, decode_str=decode_str,
 159                            typ_auto=typ_auto, fast=fast)
 160
 161    @staticmethod
 162    def from_att(value, name, typ, cat, decode_str=False, fast=False):
 163        ''' return an Ntv entity.
 164
 165        *Parameters*
 166
 167        - **value**: Ntv entity or value to convert in an Ntv entity
 168        - **name** : string - name of the Ntv entity
 169        - **typ** : string or NtvType - type of the NTV entity
 170        - **cat**: string - NTV category ('single', 'list')
 171        - **fast** : boolean (default False) - if True, Ntv entity is created without conversion
 172        - **decode_str**: boolean (default False) - if True, string are loaded as json data'''
 173
 174        value = Ntv._from_value(value, decode_str)
 175        if value.__class__.__name__ in ['NtvSingle', 'NtvList']:
 176            return value
 177        if isinstance(value, list) and cat == 'list':
 178            return NtvList(value, name, typ, fast=fast)
 179        if cat == 'single':
 180            return NtvSingle(value, name, typ, fast=fast)
 181        return Ntv.from_obj(value, def_type=typ, fast=fast)
 182
 183    @staticmethod
 184    def from_obj(value, def_type=None, def_sep=None, no_typ=False, decode_str=False,
 185                 typ_auto=False, fast=False):
 186        ''' return an Ntv entity from an object value.
 187
 188        *Parameters*
 189
 190        - **value**: Ntv value to convert in an Ntv entity
 191        - **no_typ** : boolean (default None) - if True, NtvList is with None type
 192        - **def_type** : Datatype or Namespace (default None) - default type of the value
 193        - **def_sep**: ':', '::' or None (default None) - default separator of the value
 194        - **decode_str**: boolean (default False) - if True, string are loaded as json data
 195        - **type_auto**: boolean (default False) - if True, default type for NtvList
 196        is the ntv_type of the first Ntv in the ntv_value
 197        - **fast** : boolean (default False) - if True, Ntv entity is created without conversion'''
 198        value = Ntv._from_value(value, decode_str)
 199        if value.__class__.__name__ in ['NtvSingle', 'NtvList']:
 200            return value
 201        ntv_value, ntv_name, str_typ, sep, is_json = Ntv.decode_json(value)
 202        sep = def_sep if not sep else sep
 203        sep = None if str_typ and str_typ[-1] == '.' and sep == ':' else sep
 204        if isinstance(ntv_value, (list, dict)) and sep in (None, '::'):
 205            return Ntv._create_ntvlist(str_typ, def_type, sep, ntv_value,
 206                                       typ_auto, no_typ, ntv_name, fast)
 207        if sep == ':' or (sep is None and isinstance(ntv_value, dict)):
 208            ntv_type = agreg_type(str_typ, def_type, False)
 209            return NtvSingle(ntv_value, ntv_name, ntv_type, fast=fast)
 210        if sep is None and not isinstance(ntv_value, dict):
 211            #is_single_json = isinstance(value, (int, str, float, bool))
 212            is_single_json = isinstance(ntv_value, (int, str, float, bool))
 213            ntv_type = agreg_type(str_typ, def_type, is_single_json)
 214            return NtvSingle(ntv_value, ntv_name, ntv_type, fast=fast)
 215        raise NtvError('separator ":" is not compatible with value')
 216
 217    def __len__(self):
 218        ''' len of ntv_value'''
 219        if isinstance(self.ntv_value, list):
 220            return len(self.ntv_value)
 221        return 1
 222
 223    def __str__(self):
 224        '''return string format'''
 225        return self.to_obj(encoded=True)
 226
 227    def __repr__(self):
 228        '''return classname and code'''
 229        # return json.dumps(self.to_repr(False, False, False, 10), cls=NtvJsonEncoder)
 230        return self.reduce(obj=False, level=3, maxi=6).to_obj(encoded=True)
 231
 232    def __contains__(self, item):
 233        ''' item of Ntv entities'''
 234        if isinstance(self.val, list):
 235            return item in self.ntv_value
 236        return item == self.ntv_value
 237
 238    def __iter__(self):
 239        ''' iterator for Ntv entities'''
 240        if isinstance(self, NtvSingle):
 241            # return iter([self.val])
 242            return iter([self])
 243        return iter(self.val)
 244
 245    def __getitem__(self, selec):
 246        ''' return ntv_value item with selec:
 247            - String beginning with "#" : json-pointer,
 248            - string : name of the ntv,
 249            - list : recursive selector
 250            - tuple : list of name or index '''
 251        if selec is None or selec == '':
 252            return self
 253        if isinstance(selec, NtvPointer) or isinstance(selec, str) and selec[0] == '#':
 254            selec = self._pointer_to_list(selec)
 255        if selec in ([], ()):
 256            return self
 257        if isinstance(selec, (list, tuple)) and len(selec) == 1:
 258            selec = selec[0]
 259        if (selec == 0 or selec == self.ntv_name) and isinstance(self, NtvSingle):
 260            return self.ntv_value
 261        if isinstance(self, NtvSingle):
 262            raise NtvError('item not present')
 263        if isinstance(selec, tuple):
 264            return [self[i] for i in selec]
 265        if isinstance(selec, str):
 266            return self.ntv_value[self._string_to_ind(selec)]
 267        if isinstance(selec, list):
 268            return self[selec[0]][selec[1:]]
 269        return self.ntv_value[selec]
 270
 271    def _pointer_to_list(self, pointer):
 272        '''return a list of child pointers from a string or a NtvPointer'''
 273        if isinstance(pointer, str):
 274            selec = list(NtvPointer(pointer[1:]))
 275        elif isinstance(pointer, NtvPointer):
 276            selec = list(pointer)
 277        else:
 278            raise NtvError('pointer is not a valid pointer')
 279        if not (selec[0] in [self.json_name(string=True), self.ntv_name]
 280                or (isinstance(selec[0], int) and selec[0] == 0)):
 281            raise NtvError(
 282                str(selec[0]) + 'is not the root json_name : ' + self.ntv_name)
 283        return selec[1:]
 284
 285    def _string_to_ind(self, json_name):
 286        '''return the index of a name or a json_name'''
 287        if NtvUtil.from_obj_name(json_name)[1]:
 288            return [ntv.json_name(def_type=self.type_str, string=True)
 289                    for ntv in self.ntv_value].index(json_name)
 290        return [ntv.ntv_name for ntv in self.ntv_value].index(json_name)
 291
 292    def __lt__(self, other):
 293        ''' return a comparison between two ntv_value'''
 294        # order: number > string > None
 295        res = Ntv.lower(self, other)
 296        if res is None:
 297            res = None if len(self) == len(other) else len(self) < len(other)
 298        res = self.to_obj(encoded=True) < other.to_obj(
 299            encoded=True) if res is None else res
 300        return res
 301
 302    @property
 303    def code_ntv(self):
 304        '''return a string with the NTV code composed with 1 to 3 letters:
 305        - 'l' (NtvList), 's' (NtvSingle / json_value) or 'o' (NtvSingle / obj_value)
 306        - 'N' if ntv_name is present else none
 307        - 'T' if ntv_type is present else none'''
 308        dic = {'NtvList': 'l', 'NtvSingle': 's'}
 309        code = dic[self.__class__.__name__]
 310        if isinstance(self, NtvSingle) and not self.is_json:
 311            code = 'o'
 312        if self.ntv_name:
 313            code += 'N'
 314        if self.ntv_type and self.ntv_type.long_name != 'json':
 315            code += 'T'
 316        return code
 317
 318    @property
 319    def json_name_str(self):
 320        '''return the JSON name of the NTV entity'''
 321        return self.json_name(def_type=self.parent.type_str, string=True)
 322
 323    @property
 324    def max_len(self):
 325        '''return the highest len of Ntv entity included'''
 326        maxi = len(self)
 327        if isinstance(self.ntv_value, (list, set)):
 328            maxi = max(maxi, max(ntv.max_len for ntv in self.ntv_value))
 329        return maxi
 330
 331    @property
 332    def name(self):
 333        '''return the ntv_name of the entity'''
 334        return self.ntv_name
 335
 336    @property
 337    def tree(self):
 338        '''return a tree with included entities (NtvTree object)'''
 339        return NtvTree(self)
 340
 341    @property
 342    def type_str(self):
 343        '''return a string with the value of the NtvType of the entity'''
 344        if not self.ntv_type:
 345            return ''
 346        return self.ntv_type.long_name
 347
 348    @property
 349    def val(self):
 350        '''return the ntv_value of the entity'''
 351        return self.ntv_value
 352
 353    def alike(self, ntv_value):
 354        ''' return a Ntv entity with same name and type.
 355
 356        *Parameters*
 357
 358        - **ntv_value**: list of ntv values'''
 359        return self.__class__(ntv_value, self.ntv_name, self.ntv_type)
 360
 361    def childs(self, obj=False, nam=False, typ=False):
 362        ''' return a list of child Ntv entities or child data
 363
 364        *parameters*
 365
 366        - **obj**: boolean (default False) - return json-value
 367        - **nam**: boolean (default False) - return name (with or without type)
 368        - **typ**: boolean (default False) - return type (with or without name)
 369        '''
 370        if isinstance(self, NtvSingle):
 371            return []
 372        if not (obj or nam or typ):
 373            return self.val
 374        if obj:
 375            return [ntv.to_obj() for ntv in self.val]
 376        return [(ntv.name if nam else '') + (' - ' if nam and typ else '') +
 377                (ntv.type_str if typ else '') for ntv in self.val]
 378
 379    def expand(self, full=True, entity=True):
 380        '''return a json representation of the triplet (name, type, value)
 381
 382
 383        *Parameters*
 384
 385        - **full**: Boolean (default True) - If False only members with non empty values are present
 386        - **entity**: Boolean (default True) - If True, member with entity name is added
 387        '''
 388        exp = {ENTITY: self.__class__.__name__} if entity else {}
 389        if isinstance(self, NtvList) and full:
 390            return exp | {NAME: self.name, TYPE: self.type_str,
 391                          VALUE: [ntv.expand(full) for ntv in self.val]}
 392        if isinstance(self, NtvSingle) and full:
 393            return exp | {NAME: self.name, TYPE: self.type_str, VALUE: self.val}
 394        exp |= {} if not self.name else {NAME: self.name}
 395        if not self.type_str in ['json', '']:
 396            exp[TYPE] = self.type_str
 397        if isinstance(self, NtvList):
 398            exp[VALUE] = [ntv.expand(full) for ntv in self.val]
 399        else:
 400            exp[VALUE] = self.val
 401        return exp
 402
 403    def from_value(self):
 404        '''return a Ntv entity from ntv_value'''
 405        if isinstance(self.ntv_value, list):
 406            return NtvList(self.ntv_value)
 407        return Ntv.from_obj(self.ntv_value)
 408
 409    def json_name(self, def_type=None, string=False, explicit=False):
 410        '''return the JSON name of the NTV entity (json-ntv format)
 411
 412        *Parameters*
 413
 414        - **def_typ** : Datatype or Namespace (default None) - type of the parent entity
 415        - **string** : boolean (default False) - If True, return a string else a tuple
 416        - **explicit** : boolean (default False) - If True, type is always included'''
 417        if def_type is None:
 418            def_type = ''
 419        elif isinstance(def_type, (Datatype, Namespace)):
 420            def_type = def_type.long_name
 421        json_name = self.ntv_name if self.ntv_name else ''
 422        json_type = relative_type(
 423            def_type, self.type_str) if self.ntv_type else ''
 424        implicit = isinstance(self, NtvSingle) and (json_type == 'json'
 425                                                    and (not def_type or def_type == 'json' or def_type[-1] == '.')
 426                                                    or not NtvConnector.is_json_class(self.val))
 427        if implicit and not explicit:
 428            json_type = ''
 429        json_sep = self._obj_sep(json_name, json_type, def_type)
 430        if string:
 431            return json_name + json_sep + json_type
 432        return [json_name, json_sep, json_type]
 433
 434    @staticmethod
 435    def lower(val1, val2):
 436        ''' compare two ntv_value and return True if val1 < val2, False if val1 > val2 and
 437        None in the other cases'''
 438        res = None
 439        for v1, v2 in zip(Ntv.obj(val1).tree.leaf_nodes, Ntv.obj(val2).tree.leaf_nodes):
 440            if v1.val is None:
 441                res = True
 442            elif isinstance(v1.val, (dict, list)):
 443                res = Ntv.lower(v1.val, v2)
 444            elif isinstance(v2.val, (dict, list)):
 445                res = Ntv.lower(v1, v2.val)
 446            elif isinstance(v1.val, Number):
 447                if isinstance(v2.val, Number):
 448                    res = None if v1.val == v2.val else v1.val < v2.val
 449                else:
 450                    res = False
 451            elif isinstance(v1.val, str):
 452                if isinstance(v2.val, Number):
 453                    res = True
 454                elif isinstance(v2.val, str):
 455                    res = None if v1.val == v2.val else v1.val < v2.val
 456                else:
 457                    res = False
 458            if not res is None:
 459                break
 460        return res
 461
 462    def no_type(self):
 463        '''convert NTV entity in a NV entity (in which ntv_type is 'json' or None')'''
 464        no_typ = copy.copy(self)
 465        for ntv in NtvTree(no_typ).leaf_nodes:
 466            ntv.set_type('json')
 467        for ntv in NtvTree(no_typ).inner_nodes:
 468            ntv.set_type()
 469        return no_typ
 470
 471    def no_name(self):
 472        '''convert NTV entity in a TV entity (in which ntv_name is None)'''
 473        no_nam = copy.copy(self)
 474        for ntv in NtvTree(no_nam):
 475            ntv.ntv_name = None
 476        return no_nam
 477
 478    def no_value(self):
 479        '''convert NTV entity in a NV entity (in which ntv_value of leaf nodes is ntv_type )'''
 480        no_val = copy.copy(self)
 481        for ntv in NtvTree(no_val).leaf_nodes:
 482            ntv.ntv_value = ntv.type_str
 483            ntv.set_type('json')
 484        return no_val
 485
 486    def only_type(self):
 487        '''convert NTV entity in a V entity (in which ntv_value of leaf nodes
 488        is ntv_type )'''
 489        only_typ = copy.copy(self)
 490        for ntv in NtvTree(only_typ).leaf_nodes:
 491            ntv.ntv_value = ntv.type_str
 492            ntv.set_type('json')
 493            ntv.set_name()
 494        for ntv in NtvTree(only_typ).inner_nodes:
 495            ntv.set_name()
 496            ntv.set_type()
 497        return only_typ
 498
 499    def only_name(self):
 500        '''convert NTV entity in a V entity (in which ntv_value of leaf nodes
 501        is ntv_name )'''
 502        only_nam = copy.copy(self)
 503        for ntv in NtvTree(only_nam).leaf_nodes:
 504            ntv.ntv_value = ntv.name
 505            ntv.set_type('json')
 506            ntv.set_name()
 507        for ntv in NtvTree(only_nam).inner_nodes:
 508            ntv.set_type()
 509        return only_nam
 510
 511    def only_value(self):
 512        '''convert NTV entity in a V entity'''
 513        only_val = copy.copy(self)
 514        for ntv in NtvTree(only_val).leaf_nodes:
 515            ntv.set_type('json')
 516            ntv.set_name()
 517        for ntv in NtvTree(only_val).inner_nodes:
 518            ntv.set_type()
 519            ntv.set_name()
 520        return only_val
 521
 522    @staticmethod
 523    def obj_ntv(value, name='', typ='', single=False):
 524        '''return a json-ntv representation without using Ntv structure.
 525
 526        *Parameters*
 527
 528        - **value** : ntv-value of the json-ntv
 529        - **name** : string (default '') - ntv-name of the json-ntv
 530        - **typ** : string (default '') - ntv_type of the json-ntv
 531        - **single** : boolean (default False) - if True, value is a single object
 532        else value is a set of objetcs.
 533        '''
 534        value = {} if not value else value
 535        name = '' if not name else name
 536        typ = '' if not typ else typ
 537        ntv_list = isinstance(value, (list, dict))
 538        if not single and not ntv_list:
 539            raise NtvError('value is not compatible with not single NTV data')
 540        sep = ':' if single else '::'
 541        sep = '' if not typ and (
 542            not single or single and not ntv_list) else sep
 543        name += sep + typ
 544        value = [value] if not name and isinstance(
 545            value, dict) and len(value) == 1 else value
 546        return {name: value} if name else value
 547
 548    def pointer(self, index=False, item_idx=None):
 549        '''return a nested list of pointer from root
 550
 551        *Parameters*
 552
 553        - **index**: Boolean (default False) - use index instead of name
 554        - **item_idx**: Integer (default None) - index value for the pointer
 555        (useful with duplicate data)'''
 556        if not self.parent:
 557            root_pointer = 0 if index else self.json_name(string=True)
 558            return NtvPointer([root_pointer])
 559        idx = item_idx if item_idx else self.parent.ntv_value.index(self)
 560        num = index or self.parent.json_array
 561        pointer = self.parent.pointer(index)
 562        pointer.append(idx if num else self.json_name_str)
 563        return pointer
 564
 565    def reduce(self, obj=True, maxi=6, level=3):
 566        '''reduce the length and the level of the entity
 567
 568        *Parameters*
 569
 570        - **obj**: boolean (default True) - If True return jsonNTV else NTV entity
 571        - **maxi**: integer (default 6) - Number of returned entities in an NtvList
 572        - **level**: integer (default 6) - returned entities in an NtvList at this level is None
 573
 574        *return*
 575
 576        - **NTV entity** or **jsonNTV**
 577        '''
 578        ntv = copy.copy(self)
 579        cont = Ntv.obj('___') if self.json_array else Ntv.obj({'___': ''})
 580        if isinstance(self, NtvSingle):
 581            return ntv
 582        if level == 0:
 583            ntv.ntv_value = [Ntv.obj('___', no_typ=True)]
 584        if len(self) <= maxi:
 585            ntv.ntv_value = [child.reduce(False, maxi, level-1)
 586                             for child in ntv]
 587            return ntv
 588        mid = maxi // 2
 589        cont.set_type(ntv.type_str)
 590        start = [child.reduce(False, maxi, level-1) for child in ntv[:mid]]
 591        middle = [cont]
 592        end = [child.reduce(False, maxi, level-1) for child in ntv[-mid:]]
 593        ntv.ntv_value = start + middle + end
 594        if obj:
 595            return ntv.to_obj()
 596        return ntv
 597
 598    def remove(self, first=True, index=None):
 599        '''remove self from its parent entity.
 600
 601        *parameters*
 602
 603        - **first** : boolean (default True) - if True only the first instance
 604        else all
 605        - **index** : integer (default None) - index of self in its parent
 606        '''
 607        parent = self.parent
 608        if not parent:
 609            return
 610        idx = parent.ntv_value.index(self) if index is None else index
 611        if not parent[idx] == self:
 612            raise NtvError('the entity is not present at the index')
 613        del parent.ntv_value[idx]
 614        if not first and index is None:
 615            while self in parent:
 616                idx = parent.ntv_value.index(self)
 617                del parent.ntv_value[idx]
 618        if not self in parent:
 619            self.parent = None
 620        return
 621
 622    def replace(self, ntv):
 623        '''replace self by ntv in the tree'''
 624        parent = self.parent
 625        if parent:
 626            idx = parent.ntv_value.index(self)
 627            parent.insert(idx, ntv)
 628            del parent[idx+1]
 629            if not self in parent:
 630                self.parent = None
 631        else:
 632            self = ntv
 633
 634    def set_name(self, name='', nodes='simple'):
 635        '''set new names to the entity
 636
 637        *Parameters*
 638
 639        - **name**: list or string (default '') - New name values
 640        - **nodes**: string (default 'simple') - nodes to be changed
 641            'simple': current entity
 642            'leaves': NtvSingle entities
 643            'inner': NtvList entities
 644            'all': all entities  '''
 645        name = '' if name is None else name
 646        match nodes:
 647            case 'simple':
 648                self.ntv_name = str(name)
 649            case 'leaves':
 650                if not isinstance(name, list):
 651                    name = [str(name)] * NtvTree(self).breadth
 652                for nam, ntv in zip(name, NtvTree(self).leaf_nodes):
 653                    ntv.ntv_name = nam
 654            case 'inner':
 655                if not isinstance(name, list):
 656                    name = [str(name)] * len(NtvTree(self).inner_nodes)
 657                for nam, ntv in zip(name, NtvTree(self).inner_nodes):
 658                    ntv.ntv_name = nam
 659            case 'all':
 660                if not isinstance(name, list):
 661                    name = [str(name)] * NtvTree(self).size
 662                for nam, ntv in zip(name, NtvTree(self).nodes):
 663                    ntv.ntv_name = nam
 664            case _:
 665                raise NtvError('the nodes option is not valid')
 666
 667    def set_type(self, typ=None):
 668        '''set a new type to the entity
 669
 670        *Parameters*
 671
 672        - **typ**: string, Datatype, Namespace (default None)'''
 673        if typ and not isinstance(typ, (str, Datatype, Namespace)):
 674            raise NtvError('the type is not a valid type')
 675        self.ntv_type = str_type(typ, self.__class__.__name__ == 'NtvSingle')
 676
 677    def set_value(self, value=None, fast=False):
 678        '''set new ntv_value of a single entity or of a list of entities included
 679
 680        *Parameters*
 681
 682        - **value**: list or single value
 683        - **fast** : boolean (default False) - if True, value is not converted'''
 684        if isinstance(self, NtvSingle):
 685            self.ntv_value = NtvSingle(value, ntv_type=self.ntv_type,
 686                                       fast=fast).val
 687            return
 688        if not isinstance(value, list):
 689            value = [value] * NtvTree(self).breadth
 690        ntv_val = NtvList(value, fast=fast)
 691        for val, ntv in zip(ntv_val, NtvTree(self).leaf_nodes):
 692            ntv.ntv_value = val.val
 693        return
 694
 695    def to_ntvsingle(self, name=None, typ=None, def_type=None, **kwargs):
 696        '''convert NtvList entity to NtvSingle entity
 697
 698        *Parameters*
 699
 700        - **ntv_name** : String (default None) - name of the NTV entity
 701        - **ntv_type**: String (default None) - type of the entity
 702        - **value**: value of the entity
 703        - **fast**: boolean (default False) - Ntv is created with a list of json values
 704        without control
 705        '''
 706        return NtvSingle(self.obj_value(def_type=def_type, **kwargs),
 707                         self.name if self.name else name,
 708                         self.type_str if self.type_str else typ)
 709
 710    def to_ntvlist(self, def_type=None, def_sep=None, no_typ=False, decode_str=False,
 711                   typ_auto=False, fast=False):
 712        '''convert NtvSingle entity to NtvList entity
 713
 714        *Parameters*
 715
 716        - **value**: Ntv value to convert in an Ntv entity
 717        - **no_typ** : boolean (default None) - if True, NtvList is with 'json' type
 718        - **def_type** : Datatype or Namespace (default None) - default type of the value
 719        - **def_sep**: ':', '::' or None (default None) - default separator of the value
 720        - **decode_str**: boolean (default False) - if True, string are loaded as json data
 721        - **type_auto**: boolean (default False) - if True, default type for NtvList
 722        is the ntv_type of the first Ntv in the ntv_value
 723        - **fast** : boolean (default False) - if True, Ntv entity is created without conversion
 724        '''
 725        ntv = Ntv.from_obj(self.ntv_value, def_type, def_sep, no_typ, decode_str,
 726                           typ_auto, fast)
 727        if ntv.__class__.__name__ == 'NtvSingle':
 728            return NtvList([self])
 729        if self.ntv_name:
 730            ntv.set_name(self.ntv_name)
 731        return ntv
 732
 733    def to_mermaid(self, title='', disp=False, row=False, leaves=False):
 734        '''return a mermaid flowchart.
 735
 736        *Parameters*
 737
 738        - **title**: String (default '') - title of the flowchart
 739        - **disp**: Boolean (default False) - if true, return a display else return
 740        a mermaid text diagram
 741        - **row**: Boolean (default False) - if True, add the node row
 742        - **leaves**: Boolean (default False) - if True, add the leaf row
 743        '''
 744        option = {'title': title, 'disp': disp, 'row': row, 'leaves': leaves}
 745        if disp:
 746            Ntv.obj({':$mermaid': self.to_obj()}).to_obj(
 747                format='obj', **option)
 748            return None
 749        return Ntv.obj({':$mermaid': self.to_obj()}).to_obj(format='obj', **option)
 750
 751    def to_repr(self, nam=True, typ=True, val=True, jsn=False, maxi=10):
 752        '''return a simple json representation of the Ntv entity.
 753
 754        *Parameters*
 755
 756        - **nam**: Boolean(default True) - if true, the names are included
 757        - **typ**: Boolean(default True) - if true, the types are included
 758        - **val**: Boolean(default True) - if true, the values are included
 759        - **jsn**: Boolean(default False) - if false, the 'json' type is not included
 760        - **maxi**: Integer (default 10) - number of values to include for NtvList
 761        entities. If maxi < 1 all the values are included.
 762        '''
 763        ntv = self.code_ntv
 764        if nam and typ:
 765            ntv = ntv[0]
 766        if self.ntv_name and nam:
 767            ntv += '-' + self.ntv_name
 768        if self.ntv_type and typ and (jsn or self.ntv_type.long_name != 'json'):
 769            ntv += '-' + self.ntv_type.long_name
 770        clas = self.__class__.__name__
 771        clas_val = self.ntv_value.__class__.__name__
 772        if clas == 'NtvSingle' and clas_val != 'NtvSingle':
 773            if val:
 774                if ntv:
 775                    ntv += '-'
 776                ntv += json.dumps(self.ntv_value, cls=NtvJsonEncoder)
 777            return ntv
 778        if clas == 'NtvSingle' and clas_val == 'NtvSingle':
 779            return {ntv:  self.ntv_value.to_repr(nam, typ, val, jsn, maxi)}
 780        if clas == 'NtvList':
 781            maxv = len(self.ntv_value) if maxi < 1 else maxi
 782            return {ntv:  [ntvi.to_repr(nam, typ, val, jsn, maxi)
 783                           for ntvi in self.ntv_value[:maxv]]}
 784        raise NtvError('the ntv entity is not consistent')
 785
 786    def to_name(self, default=''):
 787        '''return the name of the NTV entity
 788
 789        *Parameters*
 790
 791        - **default**: string (default ''): returned value if name is not present '''
 792        if self.ntv_name == '':
 793            return default
 794        return self.ntv_name
 795
 796    def to_fast(self, def_type=None, **kwargs):
 797        '''return the JSON representation of the NTV entity (json-ntv format)
 798        without value conversion.
 799
 800        *Parameters*
 801
 802        - **def_type** : Datatype or Namespace (default None) - default type to apply
 803        to the NTV entity
 804        - **encoded** : boolean (default False) - choice for return format
 805        (string/bytes if True, dict/list/tuple else)
 806        - **format**  : string (default 'json')- choice for return format
 807        (json, cbor, obj)
 808        - **simpleval** : boolean (default False) - if True, only value (without
 809        name and type) is included
 810        - **name** : boolean (default true) - if False, name is not included
 811        - **json_array** : boolean (default false) - if True, Json-object is not used for NtvList
 812        - **maxi**: Integer (default -1) - number of values to include for NtvList
 813        entities. If maxi < 1 all the values are included.
 814        '''
 815        option = kwargs | {'fast': True}
 816        return self.to_obj(def_type=def_type, **option)
 817
 818    def to_obj(self, def_type=None, **kwargs):
 819        '''return the JSON representation of the NTV entity (json-ntv format).
 820
 821        *Parameters*
 822
 823        - **def_type** : Datatype or Namespace (default None) - default type to apply
 824        to the NTV entity
 825        - **encoded** : boolean (default False) - choice for return format
 826        (string/bytes if True, dict/list/tuple else)
 827        - **format**  : string (default 'json')- choice for return format
 828        (json, cbor, obj)
 829        - **simpleval** : boolean (default False) - if True, only value (without
 830        name and type) is included
 831        - **name** : boolean (default true) - if False, name is not included
 832        - **json_array** : boolean (default false) - if True, Json-object is not used for NtvList
 833        - **fast** : boolean (default False) - if True, json is created without conversion
 834        - **maxi**: Integer (default -1) - number of values to include for NtvList
 835        entities. If maxi < 1 all the values are included.
 836        '''
 837        option = {'encoded': False, 'format': 'json', 'fast': False, 'maxi': -1,
 838                  'simpleval': False, 'name': True, 'json_array': False} | kwargs
 839        value = self.obj_value(def_type=def_type, **option)
 840        obj_name = self.json_name(def_type)
 841        if not option['name']:
 842            obj_name[0] = ''
 843        if option['simpleval']:
 844            name = ''
 845        elif option['format'] in ('cbor', 'obj') and not NtvConnector.is_json_class(value):
 846            name = obj_name[0]
 847        else:
 848            name = obj_name[0] + obj_name[1] + obj_name[2]
 849        value = [value] if not name and isinstance(
 850            value, dict) and len(value) == 1 else value
 851        json_obj = {name: value} if name else value
 852        if option['encoded'] and option['format'] == 'json':
 853            return json.dumps(json_obj, cls=NtvJsonEncoder)
 854        if option['encoded'] and option['format'] == 'cbor':
 855            return NtvConnector.connector()['CborConnec'].to_obj_ntv(json_obj)
 856        return json_obj
 857
 858    def to_json_ntv(self):
 859        ''' create a copy where ntv-value of the self-tree nodes is converted
 860        in json-value'''
 861        ntv = copy.copy(self)
 862        for leaf in ntv.tree.leaf_nodes:
 863            if isinstance(leaf.ntv_value, (NtvSingle, NtvList)):
 864                leaf.ntv_value = leaf.ntv_value.to_obj()
 865                leaf.ntv_type = Datatype('ntv')
 866            elif not leaf.is_json:
 867                leaf.ntv_value, leaf.ntv_name, type_str = NtvConnector.cast(
 868                    leaf.ntv_value, leaf.ntv_name, leaf.type_str)
 869                leaf.ntv_type = Datatype.add(type_str)
 870                leaf.is_json = True
 871        return ntv
 872
 873    def to_obj_ntv(self, **kwargs):
 874        ''' create a copy where ntv-value of the self-tree nodes is converted
 875        in object-value
 876
 877        *Parameters*
 878
 879        - **kwargs** : parameters used in NtvConnector class (specific for each Connector)'''
 880        ntv = copy.copy(self)
 881        for leaf in ntv.tree.leaf_nodes:
 882            if (leaf.is_json and leaf.type_str in set(NtvConnector.dic_type.values())
 883                    or leaf.ntv_type is None):
 884                leaf.ntv_value, leaf.ntv_name, type_str = NtvConnector.uncast(
 885                    leaf, **kwargs)
 886                leaf.ntv_type = Datatype.add(type_str) if type_str else None
 887                leaf.is_json = NtvConnector.is_json(leaf.ntv_value)
 888        return ntv
 889
 890    def to_tuple(self, maxi=10):
 891        '''return a nested tuple representation of the NTV entity
 892        (NtvList/NtvSingle, ntv_name, ntv_type, ntv_value).
 893
 894        *Parameters*
 895
 896        - **maxi**: Integer (default 10) - number of values to include for NtvList
 897        entities. If maxi < 1 all the values are included.
 898        '''
 899        clas = self.__class__.__name__
 900        val = self.ntv_value
 901        name = self.ntv_name
 902        typ = None
 903        if self.ntv_type:
 904            typ = self.ntv_type.long_name
 905        if isinstance(self, NtvSingle) and not isinstance(val, NtvSingle):
 906            return (clas, name, typ, val)
 907        if isinstance(self, NtvSingle) and isinstance(val, NtvSingle):
 908            return (clas, name, typ, val.to_tuple(maxi=maxi))
 909        if isinstance(self, NtvList):
 910            maxv = len(self.ntv_value) if maxi < 1 else maxi
 911            return (clas, name, typ, [ntv.to_tuple(maxi=maxi) for ntv in val[:maxv]])
 912        raise NtvError('the ntv entity is not consistent')
 913
 914    def validate(self, unique=False):
 915        '''check the ntv_type validity and return a boolean and a list of errors
 916
 917        *Parameters*
 918
 919        - **unique**: boolean (default False) - if True, stop validation at the
 920        first error'''
 921        errors = []
 922        for ntv in self.tree.leaf_nodes:
 923            valid = ntv.ntv_type.validate(ntv.ntv_value)
 924            if not valid:
 925                errors.append(str(ntv.pointer()))
 926                if unique:
 927                    return (False, errors)
 928        return (not errors, errors)
 929
 930    @abstractmethod
 931    def obj_value(self):
 932        '''return the ntv_value with different formats defined by kwargs (abstract method)'''
 933
 934    @property
 935    @abstractmethod
 936    def json_array(self):
 937        ''' return the json_array dynamic attribute (abstract method)'''
 938
 939    @abstractmethod
 940    def _obj_sep(self, json_type, def_type):
 941        ''' return separator to include in json_name (abstract method)'''
 942
 943    @staticmethod
 944    def _from_value(value, decode_str=False):
 945        '''return a decoded value
 946
 947        *Parameters*
 948
 949        - **decode_str**: boolean (default False) - if True, string are loaded as json data
 950        '''
 951        if isinstance(value, bytes):
 952            value = Ntv.from_obj({'$cbor': value}).ntv_value
 953        elif decode_str and isinstance(value, str) and value.lstrip() and\
 954                value.lstrip()[0] in '"-{[0123456789tfn':
 955            try:
 956                value = json.loads(value)
 957            except json.JSONDecodeError:
 958                pass
 959        return value
 960
 961    @staticmethod
 962    def decode_json(json_value):
 963        '''return (value, name, type, separator, isjson) of a json object'''
 964        if isinstance(json_value, dict) and len(json_value) == 1:
 965            json_name = list(json_value.keys())[0]
 966            val = json_value[json_name]
 967            return (val, *NtvUtil.from_obj_name(json_name), NtvConnector.is_json(val))
 968        return (json_value, None, None, None, NtvConnector.is_json(json_value))
 969
 970    @staticmethod
 971    def _create_ntvlist(str_typ, def_type, sep, ntv_value, typ_auto, no_typ, ntv_name, fast):
 972        '''return a NtvList with parameters from Ntv.from_obj method'''
 973        def_type = agreg_type(str_typ, def_type, False)
 974        sep_val = ':' if sep and def_type else None
 975        if isinstance(ntv_value, dict):
 976            keys = list(ntv_value.keys())
 977            values = list(ntv_value.values())
 978            ntv_list = [Ntv.from_obj({key: val}, def_type, sep_val, fast=fast)
 979                        for key, val in zip(keys, values)]
 980        else:
 981            ntv_list = [Ntv.from_obj(val, def_type, sep_val, fast=fast)
 982                        for val in ntv_value]
 983        if typ_auto and not def_type and ntv_list:
 984            def_type = ntv_list[0].ntv_type
 985        def_type = None if no_typ else def_type
 986        return NtvList(ntv_list, ntv_name, def_type, typ_auto, fast=fast)
 987
 988    @staticmethod
 989    def _listed(idx):
 990        '''transform a tuple of tuple object in a list of list object'''
 991        return [val if not isinstance(val, tuple) else Ntv._listed(val) for val in idx]
 992
 993
 994class NtvSingle(Ntv):
 995    ''' A NTV-single entity is a Ntv entity not composed with other entities.
 996
 997    *Attributes :*
 998    - no additional attributes to those of parent class `Ntv`
 999
1000    *dynamic values (@property)*
1001    - `json_array`
1002
1003    The additional methods defined in this class are :
1004
1005    *instance methods*
1006    - `obj_value`
1007    '''
1008
1009    def __init__(self, value, ntv_name=None, ntv_type=None, fast=False):
1010        '''NtvSingle constructor.
1011
1012        *Parameters*
1013
1014        - **ntv_name** : String (default None) - name of the NTV entity
1015        - **ntv_type**: String (default None) - type of the entity
1016        - **value**: value of the entity
1017        - **fast**: boolean (default False) - Ntv is created with a list of json values
1018        without control
1019        '''
1020        if not fast:
1021            value, ntv_name, ntv_type = NtvSingle._decode_s(
1022                value, ntv_name, ntv_type)
1023            if ntv_type and isinstance(ntv_type, str) and ntv_type[-1] == '.':
1024                raise NtvError('the ntv_type is not valid')
1025        super().__init__(value, ntv_name, ntv_type)
1026
1027    def __eq__(self, other):
1028        ''' equal if name type and value are equal'''
1029        return self.__class__.__name__ == other.__class__.__name__ and\
1030            self.ntv_name == other.ntv_name and self.ntv_type == other.ntv_type and\
1031            self.ntv_value == other.ntv_value
1032
1033    def __hash__(self):
1034        '''return hash(name) + hash(type) + hash(value)'''
1035        return hash(self.ntv_name) + hash(self.ntv_type) + \
1036            hash(json.dumps(self.ntv_value, cls=NtvJsonEncoder))
1037
1038    def __copy__(self):
1039        ''' Copy all the Ntv tree '''
1040        return self.__class__(copy.copy(self.ntv_value), self.ntv_name,
1041                              self.ntv_type, fast=True)
1042
1043    @property
1044    def json_array(self):
1045        ''' return the json_array dynamic attribute (always False)'''
1046        return False
1047
1048    def obj_value(self, def_type=None, **kwargs):
1049        '''return the ntv_value with different formats defined by kwargs'''
1050        option = {'encoded': False, 'format': 'json',
1051                  'simpleval': False, 'fast': False} | kwargs
1052        if option['fast'] or option['format'] in ('json', 'tuple'):
1053            return self.ntv_value
1054        if option['format'] == 'obj' and self.ntv_value == 'null':
1055            return None
1056        return NtvConnector.uncast(self, **option)[0]
1057
1058    def _obj_sep(self, json_name, json_type, def_type=None):  # REQ5
1059        ''' return separator to include in json_name'''
1060        if json_type or not def_type and isinstance(self.ntv_value, (list, dict)):
1061            return ':'
1062        return ''
1063
1064    @staticmethod
1065    def _decode_s(ntv_value, ntv_name, ntv_type_str):
1066        '''return adjusted ntv_value, ntv_name, ntv_type(str)'''
1067        is_json = NtvConnector.is_json(ntv_value)
1068        if is_json:
1069            if isinstance(ntv_value, (list)):
1070                ntv_value = [NtvSingle._decode_s(val, '', ntv_type_str)[
1071                    0] for val in ntv_value]
1072            elif isinstance(ntv_value, (dict)):
1073                ntv_value = {key: NtvSingle._decode_s(val, '', ntv_type_str)[
1074                    0] for key, val in ntv_value.items()}
1075        elif isinstance(ntv_value, NtvSingle):
1076            ntv_value = ntv_value.to_obj()
1077            return (ntv_value, ntv_name, 'ntv')
1078        else:
1079            ntv_value, name, typ_str = NtvConnector.cast(ntv_value, ntv_name)
1080            ntv_type_str = Datatype.add(
1081                typ_str).name if typ_str else ntv_type_str
1082        if not ntv_type_str:
1083            if is_json:
1084                ntv_type_str = 'json'
1085            else:
1086                ntv_type_str = typ_str
1087                if not ntv_name:
1088                    ntv_name = name
1089        elif not is_json and ntv_type_str != typ_str:
1090            raise NtvError('ntv_value is not compatible with ntv_type')
1091        return (ntv_value, ntv_name, ntv_type_str)
1092
1093
1094class NtvList(Ntv):
1095    '''A NTV-list entity is a Ntv entity where:
1096
1097    - ntv_value is a list of NTV entities,
1098    - ntv_type is a default type available for included NTV entities
1099
1100    *Attributes :*
1101    - no additional attributes to those of parent class `Ntv`
1102
1103    *dynamic values (@property)*
1104    - `json_array`
1105
1106    The additional methods defined in this class are :
1107
1108    *instance methods*
1109    - `obj_value`
1110    '''
1111
1112    def __init__(self, list_ntv, ntv_name=None, ntv_type=None, typ_auto=False, fast=False):
1113        '''NtvList constructor.
1114
1115        *Parameters*
1116
1117        - **ntv_name** : String (default None) - name of the NTV entity
1118        - **ntv_type**: String (default None) - default type or namespace of
1119        the included entities
1120        - **list_ntv**: list - list of Ntv objects or obj_value of Ntv objects
1121        - **fast**: boolean (default False) - if True, Ntv is created with a list
1122        of json values without control
1123        - **type_auto**: boolean (default False) - if True, default type for NtvList
1124        is the ntv_type of the first Ntv in the ntv_value'''
1125        if isinstance(list_ntv, NtvList):
1126            ntv_value = [copy.copy(ntv) for ntv in list_ntv.ntv_value]
1127            ntv_type = list_ntv.ntv_type
1128            ntv_name = list_ntv.ntv_name
1129        elif isinstance(list_ntv, list):
1130            ntv_value = [Ntv.from_obj(ntv, ntv_type, ':', fast=fast)
1131                         for ntv in list_ntv]
1132        elif isinstance(list_ntv, dict):
1133            ntv_value = [Ntv.from_obj({key: val}, ntv_type, ':', fast=fast)
1134                         for key, val in list_ntv.items()]
1135        else:
1136            raise NtvError('ntv_value is not a list')
1137        if typ_auto and not ntv_type and len(ntv_value) > 0 and ntv_value[0].ntv_type:
1138            ntv_type = ntv_value[0].ntv_type
1139        super().__init__(ntv_value, ntv_name, ntv_type)
1140        for ntv in self:
1141            ntv.parent = self
1142
1143    @property
1144    def json_array(self):
1145        ''' return the json_array dynamic attribute'''
1146        set_name = {ntv.json_name(
1147            def_type=self.type_str, string=True) for ntv in self}
1148        return '' in set_name or len(set_name) != len(self)
1149
1150    def __eq__(self, other):
1151        ''' equal if name and value are equal'''
1152        return self.__class__.__name__ == other.__class__.__name__ and\
1153            self.ntv_name == other.ntv_name and self.ntv_value == other.ntv_value
1154
1155    def __hash__(self):
1156        '''return hash(name) + hash(value)'''
1157        return hash(self.ntv_name) + hash(tuple(self.ntv_value))
1158
1159    def __copy__(self):
1160        ''' Copy all the data '''
1161        cop = self.__class__(self)
1162        cop.parent = None
1163        return cop
1164
1165    def __setitem__(self, ind, value):
1166        ''' replace ntv_value item at the `ind` row with `value`'''
1167        if ind < 0 or ind >= len(self):
1168            raise NtvError("out of bounds")
1169        self.ntv_value[ind] = value
1170        if isinstance(value, (NtvSingle, NtvList)):
1171            value.parent = self
1172
1173    def __delitem__(self, ind):
1174        '''remove ntv_value item at the `ind` row'''
1175        if isinstance(ind, int):
1176            self.ntv_value.pop(ind)
1177        else:
1178            self.ntv_value.pop(self.ntv_value.index(self[ind]))
1179
1180    def append(self, ntv):
1181        ''' add ntv at the end of the list of Ntv entities included'''
1182        old_parent = ntv.parent
1183        if old_parent:
1184            del(old_parent[old_parent.ntv_value.index(ntv)])
1185        self.ntv_value.append(ntv)
1186        ntv.parent = self
1187
1188    def insert(self, idx, ntv):
1189        ''' add ntv at the index idx of the list of Ntv entities included'''
1190        old_parent = ntv.parent
1191        if old_parent:
1192            del(old_parent[old_parent.ntv_value.index(ntv)])
1193        self.ntv_value.insert(idx, ntv)
1194        ntv.parent = self
1195
1196    def _obj_sep(self, json_name, json_type, def_type=None):
1197        ''' return separator to include in json_name'''
1198        sep = ':' if (json_type and json_type[-1] == '.') else '::'
1199        return sep if (json_type and json_type[-1] != '.') or (json_type and json_name) else ''
1200
1201    def obj_value(self, def_type=None, **kwargs):
1202        '''return the ntv_value with different formats defined by kwargs
1203        '''
1204        option = {'encoded': False, 'format': 'json', 'simpleval': False,
1205                  'json_array': False, 'fast': False, 'maxi': -1} | kwargs
1206        opt2 = option | {'encoded': False}
1207        maxv = len(self.ntv_value) if option['maxi'] < 1 else option['maxi']
1208        def_type = self.ntv_type.long_name if self.ntv_type else def_type
1209        values = [ntv.to_obj(def_type=def_type, **opt2)
1210                  for ntv in self.ntv_value[:maxv]]
1211        if len(self) == 1 and isinstance(self[0], NtvSingle) and isinstance(values[0], dict):
1212            return values[0]
1213        if self.json_array or option['simpleval'] or option['json_array']:
1214            return values
1215        return {list(val.items())[0][0]: list(val.items())[0][1] for val in values}
class Ntv(abc.ABC, json_ntv.ntv_util.NtvUtil):
 33class Ntv(ABC, NtvUtil):
 34    ''' The Ntv class is an abstract class used by `NtvSingle`and `NtvList` classes.
 35
 36    *Attributes :*
 37    - **ntv_name** :  String - name of the NTV entity
 38    - **ntv_type**:   Datatype or Namespace - type of the entity
 39    - **ntv_value**:  value of the entity
 40
 41    *Internal attributes :*
 42    - **parent**:     parent NtvList entity
 43    - **is_json**:    True if ntv_value is a json_value
 44
 45    *dynamic values (@property)*
 46    - `code_ntv`
 47    - `json_name_str`
 48    - `json_array` (abstract method)
 49    - `max_len`
 50    - `name`
 51    - `tree`
 52    - `type_str`
 53    - `val`
 54
 55    The methods defined in this class are :
 56
 57    *Ntv constructor (staticmethod)*
 58    - `fast`
 59    - `obj`
 60    - `from_obj`
 61    - `from_att`
 62
 63    *NTV conversion (instance methods)*
 64    - `alike`
 65    - `to_json_ntv`
 66    - `to_obj_ntv`
 67    - `validate` (validation)
 68
 69    *export - conversion (instance methods)*
 70    - `expand`
 71    - `no_type`
 72    - `no_name`
 73    - `reduce`
 74    - `to_fast`
 75    - `to_name`
 76    - `to_obj`
 77    - `to_repr`
 78    - `to_mermaid`
 79    - `to_tuple`
 80    - `to_ntvsingle`
 81    - `to_ntvlist`
 82
 83    *tree methods (instance methods)*
 84    - `childs`
 85    - `pointer`
 86    - `replace`
 87    - `remove`
 88    - `append` (NtvList only)
 89    - `insert` (NtvList only)
 90
 91    *other instance methods*
 92    - `from_value`
 93    - `json_name`
 94    - `set_name`
 95    - `set_type`
 96    - `set_value`
 97    - `obj_value` (abstract method)
 98
 99    *utility methods*
100    - `decode_json` *(staticmethod)*
101    - `obj_ntv` *(staticmethod)*
102    '''
103
104    def __init__(self, ntv_value, ntv_name, ntv_type):
105        '''Ntv constructor.
106
107        *Parameters*
108
109        - **ntv_value**: Json entity - value of the entity
110        - **ntv_name** : String (default None) - name of the NTV entity
111        - **ntv_type**: String or Datatype or Namespace (default None) - type of the entity
112        '''
113        if ntv_type.__class__.__name__ in ['Datatype', 'Namespace']:
114            self.ntv_type = ntv_type
115        elif ntv_type and ntv_type[-1] != '.':
116            self.ntv_type = Datatype.add(ntv_type)
117        elif ntv_type and ntv_type[-1] == '.':
118            self.ntv_type = Namespace.add(ntv_type)
119        else:
120            self.ntv_type = None
121        if not isinstance(ntv_name, str):
122            ntv_name = ''
123        self.ntv_name = ntv_name
124        self.ntv_value = ntv_value
125        self.is_json = NtvConnector.is_json(ntv_value)
126        self.parent = None
127
128    @staticmethod
129    def fast(data, no_typ=False, typ_auto=False):
130        ''' return an Ntv entity from data without conversion.
131
132        *Parameters* : see `obj` method'''
133        return Ntv.obj(data, no_typ=no_typ, typ_auto=typ_auto, fast=True)
134
135    @staticmethod
136    def obj(data, no_typ=False, decode_str=False, typ_auto=False, fast=False):
137        ''' return an Ntv entity from data.
138
139        *Parameters*
140
141        - **Data** can be :
142            - a tuple with value, name, typ and cat (see `from_att` method)
143            - a value to decode (see `from_obj`method)
144        - **no_typ** : boolean (default False) - if True, NtvList is with None type
145        - **type_auto**: boolean (default False) - if True, default type for NtvList
146        is the ntv_type of the first Ntv in the ntv_value
147        - **fast** : boolean (default False) - if True, Ntv entity is created without conversion
148        - **decode_str**: boolean (default False) - if True, string are loaded in json data'''
149        #print('obj : ', Namespace.namespaces(), '\n')
150
151        if isinstance(data, tuple):
152            return Ntv.from_att(*data, decode_str=decode_str, fast=fast)
153        # if isinstance(data, str) and data.lstrip() and data.lstrip()[0] in '{[':
154        if isinstance(data, str):
155            try:
156                data = json.loads(data)
157            except json.JSONDecodeError:
158                pass
159        return Ntv.from_obj(data, no_typ=no_typ, decode_str=decode_str,
160                            typ_auto=typ_auto, fast=fast)
161
162    @staticmethod
163    def from_att(value, name, typ, cat, decode_str=False, fast=False):
164        ''' return an Ntv entity.
165
166        *Parameters*
167
168        - **value**: Ntv entity or value to convert in an Ntv entity
169        - **name** : string - name of the Ntv entity
170        - **typ** : string or NtvType - type of the NTV entity
171        - **cat**: string - NTV category ('single', 'list')
172        - **fast** : boolean (default False) - if True, Ntv entity is created without conversion
173        - **decode_str**: boolean (default False) - if True, string are loaded as json data'''
174
175        value = Ntv._from_value(value, decode_str)
176        if value.__class__.__name__ in ['NtvSingle', 'NtvList']:
177            return value
178        if isinstance(value, list) and cat == 'list':
179            return NtvList(value, name, typ, fast=fast)
180        if cat == 'single':
181            return NtvSingle(value, name, typ, fast=fast)
182        return Ntv.from_obj(value, def_type=typ, fast=fast)
183
184    @staticmethod
185    def from_obj(value, def_type=None, def_sep=None, no_typ=False, decode_str=False,
186                 typ_auto=False, fast=False):
187        ''' return an Ntv entity from an object value.
188
189        *Parameters*
190
191        - **value**: Ntv value to convert in an Ntv entity
192        - **no_typ** : boolean (default None) - if True, NtvList is with None type
193        - **def_type** : Datatype or Namespace (default None) - default type of the value
194        - **def_sep**: ':', '::' or None (default None) - default separator of the value
195        - **decode_str**: boolean (default False) - if True, string are loaded as json data
196        - **type_auto**: boolean (default False) - if True, default type for NtvList
197        is the ntv_type of the first Ntv in the ntv_value
198        - **fast** : boolean (default False) - if True, Ntv entity is created without conversion'''
199        value = Ntv._from_value(value, decode_str)
200        if value.__class__.__name__ in ['NtvSingle', 'NtvList']:
201            return value
202        ntv_value, ntv_name, str_typ, sep, is_json = Ntv.decode_json(value)
203        sep = def_sep if not sep else sep
204        sep = None if str_typ and str_typ[-1] == '.' and sep == ':' else sep
205        if isinstance(ntv_value, (list, dict)) and sep in (None, '::'):
206            return Ntv._create_ntvlist(str_typ, def_type, sep, ntv_value,
207                                       typ_auto, no_typ, ntv_name, fast)
208        if sep == ':' or (sep is None and isinstance(ntv_value, dict)):
209            ntv_type = agreg_type(str_typ, def_type, False)
210            return NtvSingle(ntv_value, ntv_name, ntv_type, fast=fast)
211        if sep is None and not isinstance(ntv_value, dict):
212            #is_single_json = isinstance(value, (int, str, float, bool))
213            is_single_json = isinstance(ntv_value, (int, str, float, bool))
214            ntv_type = agreg_type(str_typ, def_type, is_single_json)
215            return NtvSingle(ntv_value, ntv_name, ntv_type, fast=fast)
216        raise NtvError('separator ":" is not compatible with value')
217
218    def __len__(self):
219        ''' len of ntv_value'''
220        if isinstance(self.ntv_value, list):
221            return len(self.ntv_value)
222        return 1
223
224    def __str__(self):
225        '''return string format'''
226        return self.to_obj(encoded=True)
227
228    def __repr__(self):
229        '''return classname and code'''
230        # return json.dumps(self.to_repr(False, False, False, 10), cls=NtvJsonEncoder)
231        return self.reduce(obj=False, level=3, maxi=6).to_obj(encoded=True)
232
233    def __contains__(self, item):
234        ''' item of Ntv entities'''
235        if isinstance(self.val, list):
236            return item in self.ntv_value
237        return item == self.ntv_value
238
239    def __iter__(self):
240        ''' iterator for Ntv entities'''
241        if isinstance(self, NtvSingle):
242            # return iter([self.val])
243            return iter([self])
244        return iter(self.val)
245
246    def __getitem__(self, selec):
247        ''' return ntv_value item with selec:
248            - String beginning with "#" : json-pointer,
249            - string : name of the ntv,
250            - list : recursive selector
251            - tuple : list of name or index '''
252        if selec is None or selec == '':
253            return self
254        if isinstance(selec, NtvPointer) or isinstance(selec, str) and selec[0] == '#':
255            selec = self._pointer_to_list(selec)
256        if selec in ([], ()):
257            return self
258        if isinstance(selec, (list, tuple)) and len(selec) == 1:
259            selec = selec[0]
260        if (selec == 0 or selec == self.ntv_name) and isinstance(self, NtvSingle):
261            return self.ntv_value
262        if isinstance(self, NtvSingle):
263            raise NtvError('item not present')
264        if isinstance(selec, tuple):
265            return [self[i] for i in selec]
266        if isinstance(selec, str):
267            return self.ntv_value[self._string_to_ind(selec)]
268        if isinstance(selec, list):
269            return self[selec[0]][selec[1:]]
270        return self.ntv_value[selec]
271
272    def _pointer_to_list(self, pointer):
273        '''return a list of child pointers from a string or a NtvPointer'''
274        if isinstance(pointer, str):
275            selec = list(NtvPointer(pointer[1:]))
276        elif isinstance(pointer, NtvPointer):
277            selec = list(pointer)
278        else:
279            raise NtvError('pointer is not a valid pointer')
280        if not (selec[0] in [self.json_name(string=True), self.ntv_name]
281                or (isinstance(selec[0], int) and selec[0] == 0)):
282            raise NtvError(
283                str(selec[0]) + 'is not the root json_name : ' + self.ntv_name)
284        return selec[1:]
285
286    def _string_to_ind(self, json_name):
287        '''return the index of a name or a json_name'''
288        if NtvUtil.from_obj_name(json_name)[1]:
289            return [ntv.json_name(def_type=self.type_str, string=True)
290                    for ntv in self.ntv_value].index(json_name)
291        return [ntv.ntv_name for ntv in self.ntv_value].index(json_name)
292
293    def __lt__(self, other):
294        ''' return a comparison between two ntv_value'''
295        # order: number > string > None
296        res = Ntv.lower(self, other)
297        if res is None:
298            res = None if len(self) == len(other) else len(self) < len(other)
299        res = self.to_obj(encoded=True) < other.to_obj(
300            encoded=True) if res is None else res
301        return res
302
303    @property
304    def code_ntv(self):
305        '''return a string with the NTV code composed with 1 to 3 letters:
306        - 'l' (NtvList), 's' (NtvSingle / json_value) or 'o' (NtvSingle / obj_value)
307        - 'N' if ntv_name is present else none
308        - 'T' if ntv_type is present else none'''
309        dic = {'NtvList': 'l', 'NtvSingle': 's'}
310        code = dic[self.__class__.__name__]
311        if isinstance(self, NtvSingle) and not self.is_json:
312            code = 'o'
313        if self.ntv_name:
314            code += 'N'
315        if self.ntv_type and self.ntv_type.long_name != 'json':
316            code += 'T'
317        return code
318
319    @property
320    def json_name_str(self):
321        '''return the JSON name of the NTV entity'''
322        return self.json_name(def_type=self.parent.type_str, string=True)
323
324    @property
325    def max_len(self):
326        '''return the highest len of Ntv entity included'''
327        maxi = len(self)
328        if isinstance(self.ntv_value, (list, set)):
329            maxi = max(maxi, max(ntv.max_len for ntv in self.ntv_value))
330        return maxi
331
332    @property
333    def name(self):
334        '''return the ntv_name of the entity'''
335        return self.ntv_name
336
337    @property
338    def tree(self):
339        '''return a tree with included entities (NtvTree object)'''
340        return NtvTree(self)
341
342    @property
343    def type_str(self):
344        '''return a string with the value of the NtvType of the entity'''
345        if not self.ntv_type:
346            return ''
347        return self.ntv_type.long_name
348
349    @property
350    def val(self):
351        '''return the ntv_value of the entity'''
352        return self.ntv_value
353
354    def alike(self, ntv_value):
355        ''' return a Ntv entity with same name and type.
356
357        *Parameters*
358
359        - **ntv_value**: list of ntv values'''
360        return self.__class__(ntv_value, self.ntv_name, self.ntv_type)
361
362    def childs(self, obj=False, nam=False, typ=False):
363        ''' return a list of child Ntv entities or child data
364
365        *parameters*
366
367        - **obj**: boolean (default False) - return json-value
368        - **nam**: boolean (default False) - return name (with or without type)
369        - **typ**: boolean (default False) - return type (with or without name)
370        '''
371        if isinstance(self, NtvSingle):
372            return []
373        if not (obj or nam or typ):
374            return self.val
375        if obj:
376            return [ntv.to_obj() for ntv in self.val]
377        return [(ntv.name if nam else '') + (' - ' if nam and typ else '') +
378                (ntv.type_str if typ else '') for ntv in self.val]
379
380    def expand(self, full=True, entity=True):
381        '''return a json representation of the triplet (name, type, value)
382
383
384        *Parameters*
385
386        - **full**: Boolean (default True) - If False only members with non empty values are present
387        - **entity**: Boolean (default True) - If True, member with entity name is added
388        '''
389        exp = {ENTITY: self.__class__.__name__} if entity else {}
390        if isinstance(self, NtvList) and full:
391            return exp | {NAME: self.name, TYPE: self.type_str,
392                          VALUE: [ntv.expand(full) for ntv in self.val]}
393        if isinstance(self, NtvSingle) and full:
394            return exp | {NAME: self.name, TYPE: self.type_str, VALUE: self.val}
395        exp |= {} if not self.name else {NAME: self.name}
396        if not self.type_str in ['json', '']:
397            exp[TYPE] = self.type_str
398        if isinstance(self, NtvList):
399            exp[VALUE] = [ntv.expand(full) for ntv in self.val]
400        else:
401            exp[VALUE] = self.val
402        return exp
403
404    def from_value(self):
405        '''return a Ntv entity from ntv_value'''
406        if isinstance(self.ntv_value, list):
407            return NtvList(self.ntv_value)
408        return Ntv.from_obj(self.ntv_value)
409
410    def json_name(self, def_type=None, string=False, explicit=False):
411        '''return the JSON name of the NTV entity (json-ntv format)
412
413        *Parameters*
414
415        - **def_typ** : Datatype or Namespace (default None) - type of the parent entity
416        - **string** : boolean (default False) - If True, return a string else a tuple
417        - **explicit** : boolean (default False) - If True, type is always included'''
418        if def_type is None:
419            def_type = ''
420        elif isinstance(def_type, (Datatype, Namespace)):
421            def_type = def_type.long_name
422        json_name = self.ntv_name if self.ntv_name else ''
423        json_type = relative_type(
424            def_type, self.type_str) if self.ntv_type else ''
425        implicit = isinstance(self, NtvSingle) and (json_type == 'json'
426                                                    and (not def_type or def_type == 'json' or def_type[-1] == '.')
427                                                    or not NtvConnector.is_json_class(self.val))
428        if implicit and not explicit:
429            json_type = ''
430        json_sep = self._obj_sep(json_name, json_type, def_type)
431        if string:
432            return json_name + json_sep + json_type
433        return [json_name, json_sep, json_type]
434
435    @staticmethod
436    def lower(val1, val2):
437        ''' compare two ntv_value and return True if val1 < val2, False if val1 > val2 and
438        None in the other cases'''
439        res = None
440        for v1, v2 in zip(Ntv.obj(val1).tree.leaf_nodes, Ntv.obj(val2).tree.leaf_nodes):
441            if v1.val is None:
442                res = True
443            elif isinstance(v1.val, (dict, list)):
444                res = Ntv.lower(v1.val, v2)
445            elif isinstance(v2.val, (dict, list)):
446                res = Ntv.lower(v1, v2.val)
447            elif isinstance(v1.val, Number):
448                if isinstance(v2.val, Number):
449                    res = None if v1.val == v2.val else v1.val < v2.val
450                else:
451                    res = False
452            elif isinstance(v1.val, str):
453                if isinstance(v2.val, Number):
454                    res = True
455                elif isinstance(v2.val, str):
456                    res = None if v1.val == v2.val else v1.val < v2.val
457                else:
458                    res = False
459            if not res is None:
460                break
461        return res
462
463    def no_type(self):
464        '''convert NTV entity in a NV entity (in which ntv_type is 'json' or None')'''
465        no_typ = copy.copy(self)
466        for ntv in NtvTree(no_typ).leaf_nodes:
467            ntv.set_type('json')
468        for ntv in NtvTree(no_typ).inner_nodes:
469            ntv.set_type()
470        return no_typ
471
472    def no_name(self):
473        '''convert NTV entity in a TV entity (in which ntv_name is None)'''
474        no_nam = copy.copy(self)
475        for ntv in NtvTree(no_nam):
476            ntv.ntv_name = None
477        return no_nam
478
479    def no_value(self):
480        '''convert NTV entity in a NV entity (in which ntv_value of leaf nodes is ntv_type )'''
481        no_val = copy.copy(self)
482        for ntv in NtvTree(no_val).leaf_nodes:
483            ntv.ntv_value = ntv.type_str
484            ntv.set_type('json')
485        return no_val
486
487    def only_type(self):
488        '''convert NTV entity in a V entity (in which ntv_value of leaf nodes
489        is ntv_type )'''
490        only_typ = copy.copy(self)
491        for ntv in NtvTree(only_typ).leaf_nodes:
492            ntv.ntv_value = ntv.type_str
493            ntv.set_type('json')
494            ntv.set_name()
495        for ntv in NtvTree(only_typ).inner_nodes:
496            ntv.set_name()
497            ntv.set_type()
498        return only_typ
499
500    def only_name(self):
501        '''convert NTV entity in a V entity (in which ntv_value of leaf nodes
502        is ntv_name )'''
503        only_nam = copy.copy(self)
504        for ntv in NtvTree(only_nam).leaf_nodes:
505            ntv.ntv_value = ntv.name
506            ntv.set_type('json')
507            ntv.set_name()
508        for ntv in NtvTree(only_nam).inner_nodes:
509            ntv.set_type()
510        return only_nam
511
512    def only_value(self):
513        '''convert NTV entity in a V entity'''
514        only_val = copy.copy(self)
515        for ntv in NtvTree(only_val).leaf_nodes:
516            ntv.set_type('json')
517            ntv.set_name()
518        for ntv in NtvTree(only_val).inner_nodes:
519            ntv.set_type()
520            ntv.set_name()
521        return only_val
522
523    @staticmethod
524    def obj_ntv(value, name='', typ='', single=False):
525        '''return a json-ntv representation without using Ntv structure.
526
527        *Parameters*
528
529        - **value** : ntv-value of the json-ntv
530        - **name** : string (default '') - ntv-name of the json-ntv
531        - **typ** : string (default '') - ntv_type of the json-ntv
532        - **single** : boolean (default False) - if True, value is a single object
533        else value is a set of objetcs.
534        '''
535        value = {} if not value else value
536        name = '' if not name else name
537        typ = '' if not typ else typ
538        ntv_list = isinstance(value, (list, dict))
539        if not single and not ntv_list:
540            raise NtvError('value is not compatible with not single NTV data')
541        sep = ':' if single else '::'
542        sep = '' if not typ and (
543            not single or single and not ntv_list) else sep
544        name += sep + typ
545        value = [value] if not name and isinstance(
546            value, dict) and len(value) == 1 else value
547        return {name: value} if name else value
548
549    def pointer(self, index=False, item_idx=None):
550        '''return a nested list of pointer from root
551
552        *Parameters*
553
554        - **index**: Boolean (default False) - use index instead of name
555        - **item_idx**: Integer (default None) - index value for the pointer
556        (useful with duplicate data)'''
557        if not self.parent:
558            root_pointer = 0 if index else self.json_name(string=True)
559            return NtvPointer([root_pointer])
560        idx = item_idx if item_idx else self.parent.ntv_value.index(self)
561        num = index or self.parent.json_array
562        pointer = self.parent.pointer(index)
563        pointer.append(idx if num else self.json_name_str)
564        return pointer
565
566    def reduce(self, obj=True, maxi=6, level=3):
567        '''reduce the length and the level of the entity
568
569        *Parameters*
570
571        - **obj**: boolean (default True) - If True return jsonNTV else NTV entity
572        - **maxi**: integer (default 6) - Number of returned entities in an NtvList
573        - **level**: integer (default 6) - returned entities in an NtvList at this level is None
574
575        *return*
576
577        - **NTV entity** or **jsonNTV**
578        '''
579        ntv = copy.copy(self)
580        cont = Ntv.obj('___') if self.json_array else Ntv.obj({'___': ''})
581        if isinstance(self, NtvSingle):
582            return ntv
583        if level == 0:
584            ntv.ntv_value = [Ntv.obj('___', no_typ=True)]
585        if len(self) <= maxi:
586            ntv.ntv_value = [child.reduce(False, maxi, level-1)
587                             for child in ntv]
588            return ntv
589        mid = maxi // 2
590        cont.set_type(ntv.type_str)
591        start = [child.reduce(False, maxi, level-1) for child in ntv[:mid]]
592        middle = [cont]
593        end = [child.reduce(False, maxi, level-1) for child in ntv[-mid:]]
594        ntv.ntv_value = start + middle + end
595        if obj:
596            return ntv.to_obj()
597        return ntv
598
599    def remove(self, first=True, index=None):
600        '''remove self from its parent entity.
601
602        *parameters*
603
604        - **first** : boolean (default True) - if True only the first instance
605        else all
606        - **index** : integer (default None) - index of self in its parent
607        '''
608        parent = self.parent
609        if not parent:
610            return
611        idx = parent.ntv_value.index(self) if index is None else index
612        if not parent[idx] == self:
613            raise NtvError('the entity is not present at the index')
614        del parent.ntv_value[idx]
615        if not first and index is None:
616            while self in parent:
617                idx = parent.ntv_value.index(self)
618                del parent.ntv_value[idx]
619        if not self in parent:
620            self.parent = None
621        return
622
623    def replace(self, ntv):
624        '''replace self by ntv in the tree'''
625        parent = self.parent
626        if parent:
627            idx = parent.ntv_value.index(self)
628            parent.insert(idx, ntv)
629            del parent[idx+1]
630            if not self in parent:
631                self.parent = None
632        else:
633            self = ntv
634
635    def set_name(self, name='', nodes='simple'):
636        '''set new names to the entity
637
638        *Parameters*
639
640        - **name**: list or string (default '') - New name values
641        - **nodes**: string (default 'simple') - nodes to be changed
642            'simple': current entity
643            'leaves': NtvSingle entities
644            'inner': NtvList entities
645            'all': all entities  '''
646        name = '' if name is None else name
647        match nodes:
648            case 'simple':
649                self.ntv_name = str(name)
650            case 'leaves':
651                if not isinstance(name, list):
652                    name = [str(name)] * NtvTree(self).breadth
653                for nam, ntv in zip(name, NtvTree(self).leaf_nodes):
654                    ntv.ntv_name = nam
655            case 'inner':
656                if not isinstance(name, list):
657                    name = [str(name)] * len(NtvTree(self).inner_nodes)
658                for nam, ntv in zip(name, NtvTree(self).inner_nodes):
659                    ntv.ntv_name = nam
660            case 'all':
661                if not isinstance(name, list):
662                    name = [str(name)] * NtvTree(self).size
663                for nam, ntv in zip(name, NtvTree(self).nodes):
664                    ntv.ntv_name = nam
665            case _:
666                raise NtvError('the nodes option is not valid')
667
668    def set_type(self, typ=None):
669        '''set a new type to the entity
670
671        *Parameters*
672
673        - **typ**: string, Datatype, Namespace (default None)'''
674        if typ and not isinstance(typ, (str, Datatype, Namespace)):
675            raise NtvError('the type is not a valid type')
676        self.ntv_type = str_type(typ, self.__class__.__name__ == 'NtvSingle')
677
678    def set_value(self, value=None, fast=False):
679        '''set new ntv_value of a single entity or of a list of entities included
680
681        *Parameters*
682
683        - **value**: list or single value
684        - **fast** : boolean (default False) - if True, value is not converted'''
685        if isinstance(self, NtvSingle):
686            self.ntv_value = NtvSingle(value, ntv_type=self.ntv_type,
687                                       fast=fast).val
688            return
689        if not isinstance(value, list):
690            value = [value] * NtvTree(self).breadth
691        ntv_val = NtvList(value, fast=fast)
692        for val, ntv in zip(ntv_val, NtvTree(self).leaf_nodes):
693            ntv.ntv_value = val.val
694        return
695
696    def to_ntvsingle(self, name=None, typ=None, def_type=None, **kwargs):
697        '''convert NtvList entity to NtvSingle entity
698
699        *Parameters*
700
701        - **ntv_name** : String (default None) - name of the NTV entity
702        - **ntv_type**: String (default None) - type of the entity
703        - **value**: value of the entity
704        - **fast**: boolean (default False) - Ntv is created with a list of json values
705        without control
706        '''
707        return NtvSingle(self.obj_value(def_type=def_type, **kwargs),
708                         self.name if self.name else name,
709                         self.type_str if self.type_str else typ)
710
711    def to_ntvlist(self, def_type=None, def_sep=None, no_typ=False, decode_str=False,
712                   typ_auto=False, fast=False):
713        '''convert NtvSingle entity to NtvList entity
714
715        *Parameters*
716
717        - **value**: Ntv value to convert in an Ntv entity
718        - **no_typ** : boolean (default None) - if True, NtvList is with 'json' type
719        - **def_type** : Datatype or Namespace (default None) - default type of the value
720        - **def_sep**: ':', '::' or None (default None) - default separator of the value
721        - **decode_str**: boolean (default False) - if True, string are loaded as json data
722        - **type_auto**: boolean (default False) - if True, default type for NtvList
723        is the ntv_type of the first Ntv in the ntv_value
724        - **fast** : boolean (default False) - if True, Ntv entity is created without conversion
725        '''
726        ntv = Ntv.from_obj(self.ntv_value, def_type, def_sep, no_typ, decode_str,
727                           typ_auto, fast)
728        if ntv.__class__.__name__ == 'NtvSingle':
729            return NtvList([self])
730        if self.ntv_name:
731            ntv.set_name(self.ntv_name)
732        return ntv
733
734    def to_mermaid(self, title='', disp=False, row=False, leaves=False):
735        '''return a mermaid flowchart.
736
737        *Parameters*
738
739        - **title**: String (default '') - title of the flowchart
740        - **disp**: Boolean (default False) - if true, return a display else return
741        a mermaid text diagram
742        - **row**: Boolean (default False) - if True, add the node row
743        - **leaves**: Boolean (default False) - if True, add the leaf row
744        '''
745        option = {'title': title, 'disp': disp, 'row': row, 'leaves': leaves}
746        if disp:
747            Ntv.obj({':$mermaid': self.to_obj()}).to_obj(
748                format='obj', **option)
749            return None
750        return Ntv.obj({':$mermaid': self.to_obj()}).to_obj(format='obj', **option)
751
752    def to_repr(self, nam=True, typ=True, val=True, jsn=False, maxi=10):
753        '''return a simple json representation of the Ntv entity.
754
755        *Parameters*
756
757        - **nam**: Boolean(default True) - if true, the names are included
758        - **typ**: Boolean(default True) - if true, the types are included
759        - **val**: Boolean(default True) - if true, the values are included
760        - **jsn**: Boolean(default False) - if false, the 'json' type is not included
761        - **maxi**: Integer (default 10) - number of values to include for NtvList
762        entities. If maxi < 1 all the values are included.
763        '''
764        ntv = self.code_ntv
765        if nam and typ:
766            ntv = ntv[0]
767        if self.ntv_name and nam:
768            ntv += '-' + self.ntv_name
769        if self.ntv_type and typ and (jsn or self.ntv_type.long_name != 'json'):
770            ntv += '-' + self.ntv_type.long_name
771        clas = self.__class__.__name__
772        clas_val = self.ntv_value.__class__.__name__
773        if clas == 'NtvSingle' and clas_val != 'NtvSingle':
774            if val:
775                if ntv:
776                    ntv += '-'
777                ntv += json.dumps(self.ntv_value, cls=NtvJsonEncoder)
778            return ntv
779        if clas == 'NtvSingle' and clas_val == 'NtvSingle':
780            return {ntv:  self.ntv_value.to_repr(nam, typ, val, jsn, maxi)}
781        if clas == 'NtvList':
782            maxv = len(self.ntv_value) if maxi < 1 else maxi
783            return {ntv:  [ntvi.to_repr(nam, typ, val, jsn, maxi)
784                           for ntvi in self.ntv_value[:maxv]]}
785        raise NtvError('the ntv entity is not consistent')
786
787    def to_name(self, default=''):
788        '''return the name of the NTV entity
789
790        *Parameters*
791
792        - **default**: string (default ''): returned value if name is not present '''
793        if self.ntv_name == '':
794            return default
795        return self.ntv_name
796
797    def to_fast(self, def_type=None, **kwargs):
798        '''return the JSON representation of the NTV entity (json-ntv format)
799        without value conversion.
800
801        *Parameters*
802
803        - **def_type** : Datatype or Namespace (default None) - default type to apply
804        to the NTV entity
805        - **encoded** : boolean (default False) - choice for return format
806        (string/bytes if True, dict/list/tuple else)
807        - **format**  : string (default 'json')- choice for return format
808        (json, cbor, obj)
809        - **simpleval** : boolean (default False) - if True, only value (without
810        name and type) is included
811        - **name** : boolean (default true) - if False, name is not included
812        - **json_array** : boolean (default false) - if True, Json-object is not used for NtvList
813        - **maxi**: Integer (default -1) - number of values to include for NtvList
814        entities. If maxi < 1 all the values are included.
815        '''
816        option = kwargs | {'fast': True}
817        return self.to_obj(def_type=def_type, **option)
818
819    def to_obj(self, def_type=None, **kwargs):
820        '''return the JSON representation of the NTV entity (json-ntv format).
821
822        *Parameters*
823
824        - **def_type** : Datatype or Namespace (default None) - default type to apply
825        to the NTV entity
826        - **encoded** : boolean (default False) - choice for return format
827        (string/bytes if True, dict/list/tuple else)
828        - **format**  : string (default 'json')- choice for return format
829        (json, cbor, obj)
830        - **simpleval** : boolean (default False) - if True, only value (without
831        name and type) is included
832        - **name** : boolean (default true) - if False, name is not included
833        - **json_array** : boolean (default false) - if True, Json-object is not used for NtvList
834        - **fast** : boolean (default False) - if True, json is created without conversion
835        - **maxi**: Integer (default -1) - number of values to include for NtvList
836        entities. If maxi < 1 all the values are included.
837        '''
838        option = {'encoded': False, 'format': 'json', 'fast': False, 'maxi': -1,
839                  'simpleval': False, 'name': True, 'json_array': False} | kwargs
840        value = self.obj_value(def_type=def_type, **option)
841        obj_name = self.json_name(def_type)
842        if not option['name']:
843            obj_name[0] = ''
844        if option['simpleval']:
845            name = ''
846        elif option['format'] in ('cbor', 'obj') and not NtvConnector.is_json_class(value):
847            name = obj_name[0]
848        else:
849            name = obj_name[0] + obj_name[1] + obj_name[2]
850        value = [value] if not name and isinstance(
851            value, dict) and len(value) == 1 else value
852        json_obj = {name: value} if name else value
853        if option['encoded'] and option['format'] == 'json':
854            return json.dumps(json_obj, cls=NtvJsonEncoder)
855        if option['encoded'] and option['format'] == 'cbor':
856            return NtvConnector.connector()['CborConnec'].to_obj_ntv(json_obj)
857        return json_obj
858
859    def to_json_ntv(self):
860        ''' create a copy where ntv-value of the self-tree nodes is converted
861        in json-value'''
862        ntv = copy.copy(self)
863        for leaf in ntv.tree.leaf_nodes:
864            if isinstance(leaf.ntv_value, (NtvSingle, NtvList)):
865                leaf.ntv_value = leaf.ntv_value.to_obj()
866                leaf.ntv_type = Datatype('ntv')
867            elif not leaf.is_json:
868                leaf.ntv_value, leaf.ntv_name, type_str = NtvConnector.cast(
869                    leaf.ntv_value, leaf.ntv_name, leaf.type_str)
870                leaf.ntv_type = Datatype.add(type_str)
871                leaf.is_json = True
872        return ntv
873
874    def to_obj_ntv(self, **kwargs):
875        ''' create a copy where ntv-value of the self-tree nodes is converted
876        in object-value
877
878        *Parameters*
879
880        - **kwargs** : parameters used in NtvConnector class (specific for each Connector)'''
881        ntv = copy.copy(self)
882        for leaf in ntv.tree.leaf_nodes:
883            if (leaf.is_json and leaf.type_str in set(NtvConnector.dic_type.values())
884                    or leaf.ntv_type is None):
885                leaf.ntv_value, leaf.ntv_name, type_str = NtvConnector.uncast(
886                    leaf, **kwargs)
887                leaf.ntv_type = Datatype.add(type_str) if type_str else None
888                leaf.is_json = NtvConnector.is_json(leaf.ntv_value)
889        return ntv
890
891    def to_tuple(self, maxi=10):
892        '''return a nested tuple representation of the NTV entity
893        (NtvList/NtvSingle, ntv_name, ntv_type, ntv_value).
894
895        *Parameters*
896
897        - **maxi**: Integer (default 10) - number of values to include for NtvList
898        entities. If maxi < 1 all the values are included.
899        '''
900        clas = self.__class__.__name__
901        val = self.ntv_value
902        name = self.ntv_name
903        typ = None
904        if self.ntv_type:
905            typ = self.ntv_type.long_name
906        if isinstance(self, NtvSingle) and not isinstance(val, NtvSingle):
907            return (clas, name, typ, val)
908        if isinstance(self, NtvSingle) and isinstance(val, NtvSingle):
909            return (clas, name, typ, val.to_tuple(maxi=maxi))
910        if isinstance(self, NtvList):
911            maxv = len(self.ntv_value) if maxi < 1 else maxi
912            return (clas, name, typ, [ntv.to_tuple(maxi=maxi) for ntv in val[:maxv]])
913        raise NtvError('the ntv entity is not consistent')
914
915    def validate(self, unique=False):
916        '''check the ntv_type validity and return a boolean and a list of errors
917
918        *Parameters*
919
920        - **unique**: boolean (default False) - if True, stop validation at the
921        first error'''
922        errors = []
923        for ntv in self.tree.leaf_nodes:
924            valid = ntv.ntv_type.validate(ntv.ntv_value)
925            if not valid:
926                errors.append(str(ntv.pointer()))
927                if unique:
928                    return (False, errors)
929        return (not errors, errors)
930
931    @abstractmethod
932    def obj_value(self):
933        '''return the ntv_value with different formats defined by kwargs (abstract method)'''
934
935    @property
936    @abstractmethod
937    def json_array(self):
938        ''' return the json_array dynamic attribute (abstract method)'''
939
940    @abstractmethod
941    def _obj_sep(self, json_type, def_type):
942        ''' return separator to include in json_name (abstract method)'''
943
944    @staticmethod
945    def _from_value(value, decode_str=False):
946        '''return a decoded value
947
948        *Parameters*
949
950        - **decode_str**: boolean (default False) - if True, string are loaded as json data
951        '''
952        if isinstance(value, bytes):
953            value = Ntv.from_obj({'$cbor': value}).ntv_value
954        elif decode_str and isinstance(value, str) and value.lstrip() and\
955                value.lstrip()[0] in '"-{[0123456789tfn':
956            try:
957                value = json.loads(value)
958            except json.JSONDecodeError:
959                pass
960        return value
961
962    @staticmethod
963    def decode_json(json_value):
964        '''return (value, name, type, separator, isjson) of a json object'''
965        if isinstance(json_value, dict) and len(json_value) == 1:
966            json_name = list(json_value.keys())[0]
967            val = json_value[json_name]
968            return (val, *NtvUtil.from_obj_name(json_name), NtvConnector.is_json(val))
969        return (json_value, None, None, None, NtvConnector.is_json(json_value))
970
971    @staticmethod
972    def _create_ntvlist(str_typ, def_type, sep, ntv_value, typ_auto, no_typ, ntv_name, fast):
973        '''return a NtvList with parameters from Ntv.from_obj method'''
974        def_type = agreg_type(str_typ, def_type, False)
975        sep_val = ':' if sep and def_type else None
976        if isinstance(ntv_value, dict):
977            keys = list(ntv_value.keys())
978            values = list(ntv_value.values())
979            ntv_list = [Ntv.from_obj({key: val}, def_type, sep_val, fast=fast)
980                        for key, val in zip(keys, values)]
981        else:
982            ntv_list = [Ntv.from_obj(val, def_type, sep_val, fast=fast)
983                        for val in ntv_value]
984        if typ_auto and not def_type and ntv_list:
985            def_type = ntv_list[0].ntv_type
986        def_type = None if no_typ else def_type
987        return NtvList(ntv_list, ntv_name, def_type, typ_auto, fast=fast)
988
989    @staticmethod
990    def _listed(idx):
991        '''transform a tuple of tuple object in a list of list object'''
992        return [val if not isinstance(val, tuple) else Ntv._listed(val) for val in idx]

The Ntv class is an abstract class used by NtvSingleand NtvList classes.

Attributes :

  • ntv_name : String - name of the NTV entity
  • ntv_type: Datatype or Namespace - type of the entity
  • ntv_value: value of the entity

Internal attributes :

  • parent: parent NtvList entity
  • is_json: True if ntv_value is a json_value

dynamic values (@property)

The methods defined in this class are :

Ntv constructor (staticmethod)

NTV conversion (instance methods)

export - conversion (instance methods)

tree methods (instance methods)

other instance methods

utility methods

Ntv(ntv_value, ntv_name, ntv_type)
104    def __init__(self, ntv_value, ntv_name, ntv_type):
105        '''Ntv constructor.
106
107        *Parameters*
108
109        - **ntv_value**: Json entity - value of the entity
110        - **ntv_name** : String (default None) - name of the NTV entity
111        - **ntv_type**: String or Datatype or Namespace (default None) - type of the entity
112        '''
113        if ntv_type.__class__.__name__ in ['Datatype', 'Namespace']:
114            self.ntv_type = ntv_type
115        elif ntv_type and ntv_type[-1] != '.':
116            self.ntv_type = Datatype.add(ntv_type)
117        elif ntv_type and ntv_type[-1] == '.':
118            self.ntv_type = Namespace.add(ntv_type)
119        else:
120            self.ntv_type = None
121        if not isinstance(ntv_name, str):
122            ntv_name = ''
123        self.ntv_name = ntv_name
124        self.ntv_value = ntv_value
125        self.is_json = NtvConnector.is_json(ntv_value)
126        self.parent = None

Ntv constructor.

Parameters

  • ntv_value: Json entity - value of the entity
  • ntv_name : String (default None) - name of the NTV entity
  • ntv_type: String or Datatype or Namespace (default None) - type of the entity
@staticmethod
def fast(data, no_typ=False, typ_auto=False):
128    @staticmethod
129    def fast(data, no_typ=False, typ_auto=False):
130        ''' return an Ntv entity from data without conversion.
131
132        *Parameters* : see `obj` method'''
133        return Ntv.obj(data, no_typ=no_typ, typ_auto=typ_auto, fast=True)

return an Ntv entity from data without conversion.

Parameters : see obj method

@staticmethod
def obj(data, no_typ=False, decode_str=False, typ_auto=False, fast=False):
135    @staticmethod
136    def obj(data, no_typ=False, decode_str=False, typ_auto=False, fast=False):
137        ''' return an Ntv entity from data.
138
139        *Parameters*
140
141        - **Data** can be :
142            - a tuple with value, name, typ and cat (see `from_att` method)
143            - a value to decode (see `from_obj`method)
144        - **no_typ** : boolean (default False) - if True, NtvList is with None type
145        - **type_auto**: boolean (default False) - if True, default type for NtvList
146        is the ntv_type of the first Ntv in the ntv_value
147        - **fast** : boolean (default False) - if True, Ntv entity is created without conversion
148        - **decode_str**: boolean (default False) - if True, string are loaded in json data'''
149        #print('obj : ', Namespace.namespaces(), '\n')
150
151        if isinstance(data, tuple):
152            return Ntv.from_att(*data, decode_str=decode_str, fast=fast)
153        # if isinstance(data, str) and data.lstrip() and data.lstrip()[0] in '{[':
154        if isinstance(data, str):
155            try:
156                data = json.loads(data)
157            except json.JSONDecodeError:
158                pass
159        return Ntv.from_obj(data, no_typ=no_typ, decode_str=decode_str,
160                            typ_auto=typ_auto, fast=fast)

return an Ntv entity from data.

Parameters

  • Data can be :
    • a tuple with value, name, typ and cat (see from_att method)
    • a value to decode (see from_objmethod)
  • no_typ : boolean (default False) - if True, NtvList is with None type
  • type_auto: boolean (default False) - if True, default type for NtvList is the ntv_type of the first Ntv in the ntv_value
  • fast : boolean (default False) - if True, Ntv entity is created without conversion
  • decode_str: boolean (default False) - if True, string are loaded in json data
@staticmethod
def from_att(value, name, typ, cat, decode_str=False, fast=False):
162    @staticmethod
163    def from_att(value, name, typ, cat, decode_str=False, fast=False):
164        ''' return an Ntv entity.
165
166        *Parameters*
167
168        - **value**: Ntv entity or value to convert in an Ntv entity
169        - **name** : string - name of the Ntv entity
170        - **typ** : string or NtvType - type of the NTV entity
171        - **cat**: string - NTV category ('single', 'list')
172        - **fast** : boolean (default False) - if True, Ntv entity is created without conversion
173        - **decode_str**: boolean (default False) - if True, string are loaded as json data'''
174
175        value = Ntv._from_value(value, decode_str)
176        if value.__class__.__name__ in ['NtvSingle', 'NtvList']:
177            return value
178        if isinstance(value, list) and cat == 'list':
179            return NtvList(value, name, typ, fast=fast)
180        if cat == 'single':
181            return NtvSingle(value, name, typ, fast=fast)
182        return Ntv.from_obj(value, def_type=typ, fast=fast)

return an Ntv entity.

Parameters

  • value: Ntv entity or value to convert in an Ntv entity
  • name : string - name of the Ntv entity
  • typ : string or NtvType - type of the NTV entity
  • cat: string - NTV category ('single', 'list')
  • fast : boolean (default False) - if True, Ntv entity is created without conversion
  • decode_str: boolean (default False) - if True, string are loaded as json data
@staticmethod
def from_obj( value, def_type=None, def_sep=None, no_typ=False, decode_str=False, typ_auto=False, fast=False):
184    @staticmethod
185    def from_obj(value, def_type=None, def_sep=None, no_typ=False, decode_str=False,
186                 typ_auto=False, fast=False):
187        ''' return an Ntv entity from an object value.
188
189        *Parameters*
190
191        - **value**: Ntv value to convert in an Ntv entity
192        - **no_typ** : boolean (default None) - if True, NtvList is with None type
193        - **def_type** : Datatype or Namespace (default None) - default type of the value
194        - **def_sep**: ':', '::' or None (default None) - default separator of the value
195        - **decode_str**: boolean (default False) - if True, string are loaded as json data
196        - **type_auto**: boolean (default False) - if True, default type for NtvList
197        is the ntv_type of the first Ntv in the ntv_value
198        - **fast** : boolean (default False) - if True, Ntv entity is created without conversion'''
199        value = Ntv._from_value(value, decode_str)
200        if value.__class__.__name__ in ['NtvSingle', 'NtvList']:
201            return value
202        ntv_value, ntv_name, str_typ, sep, is_json = Ntv.decode_json(value)
203        sep = def_sep if not sep else sep
204        sep = None if str_typ and str_typ[-1] == '.' and sep == ':' else sep
205        if isinstance(ntv_value, (list, dict)) and sep in (None, '::'):
206            return Ntv._create_ntvlist(str_typ, def_type, sep, ntv_value,
207                                       typ_auto, no_typ, ntv_name, fast)
208        if sep == ':' or (sep is None and isinstance(ntv_value, dict)):
209            ntv_type = agreg_type(str_typ, def_type, False)
210            return NtvSingle(ntv_value, ntv_name, ntv_type, fast=fast)
211        if sep is None and not isinstance(ntv_value, dict):
212            #is_single_json = isinstance(value, (int, str, float, bool))
213            is_single_json = isinstance(ntv_value, (int, str, float, bool))
214            ntv_type = agreg_type(str_typ, def_type, is_single_json)
215            return NtvSingle(ntv_value, ntv_name, ntv_type, fast=fast)
216        raise NtvError('separator ":" is not compatible with value')

return an Ntv entity from an object value.

Parameters

  • value: Ntv value to convert in an Ntv entity
  • no_typ : boolean (default None) - if True, NtvList is with None type
  • def_type : Datatype or Namespace (default None) - default type of the value
  • def_sep: ':', '::' or None (default None) - default separator of the value
  • decode_str: boolean (default False) - if True, string are loaded as json data
  • type_auto: boolean (default False) - if True, default type for NtvList is the ntv_type of the first Ntv in the ntv_value
  • fast : boolean (default False) - if True, Ntv entity is created without conversion
code_ntv

return a string with the NTV code composed with 1 to 3 letters:

  • 'l' (NtvList), 's' (NtvSingle / json_value) or 'o' (NtvSingle / obj_value)
  • 'N' if ntv_name is present else none
  • 'T' if ntv_type is present else none
json_name_str

return the JSON name of the NTV entity

max_len

return the highest len of Ntv entity included

name

return the ntv_name of the entity

tree

return a tree with included entities (NtvTree object)

type_str

return a string with the value of the NtvType of the entity

val

return the ntv_value of the entity

def alike(self, ntv_value):
354    def alike(self, ntv_value):
355        ''' return a Ntv entity with same name and type.
356
357        *Parameters*
358
359        - **ntv_value**: list of ntv values'''
360        return self.__class__(ntv_value, self.ntv_name, self.ntv_type)

return a Ntv entity with same name and type.

Parameters

  • ntv_value: list of ntv values
def childs(self, obj=False, nam=False, typ=False):
362    def childs(self, obj=False, nam=False, typ=False):
363        ''' return a list of child Ntv entities or child data
364
365        *parameters*
366
367        - **obj**: boolean (default False) - return json-value
368        - **nam**: boolean (default False) - return name (with or without type)
369        - **typ**: boolean (default False) - return type (with or without name)
370        '''
371        if isinstance(self, NtvSingle):
372            return []
373        if not (obj or nam or typ):
374            return self.val
375        if obj:
376            return [ntv.to_obj() for ntv in self.val]
377        return [(ntv.name if nam else '') + (' - ' if nam and typ else '') +
378                (ntv.type_str if typ else '') for ntv in self.val]

return a list of child Ntv entities or child data

parameters

  • obj: boolean (default False) - return json-value
  • nam: boolean (default False) - return name (with or without type)
  • typ: boolean (default False) - return type (with or without name)
def expand(self, full=True, entity=True):
380    def expand(self, full=True, entity=True):
381        '''return a json representation of the triplet (name, type, value)
382
383
384        *Parameters*
385
386        - **full**: Boolean (default True) - If False only members with non empty values are present
387        - **entity**: Boolean (default True) - If True, member with entity name is added
388        '''
389        exp = {ENTITY: self.__class__.__name__} if entity else {}
390        if isinstance(self, NtvList) and full:
391            return exp | {NAME: self.name, TYPE: self.type_str,
392                          VALUE: [ntv.expand(full) for ntv in self.val]}
393        if isinstance(self, NtvSingle) and full:
394            return exp | {NAME: self.name, TYPE: self.type_str, VALUE: self.val}
395        exp |= {} if not self.name else {NAME: self.name}
396        if not self.type_str in ['json', '']:
397            exp[TYPE] = self.type_str
398        if isinstance(self, NtvList):
399            exp[VALUE] = [ntv.expand(full) for ntv in self.val]
400        else:
401            exp[VALUE] = self.val
402        return exp

return a json representation of the triplet (name, type, value)

Parameters

  • full: Boolean (default True) - If False only members with non empty values are present
  • entity: Boolean (default True) - If True, member with entity name is added
def from_value(self):
404    def from_value(self):
405        '''return a Ntv entity from ntv_value'''
406        if isinstance(self.ntv_value, list):
407            return NtvList(self.ntv_value)
408        return Ntv.from_obj(self.ntv_value)

return a Ntv entity from ntv_value

def json_name(self, def_type=None, string=False, explicit=False):
410    def json_name(self, def_type=None, string=False, explicit=False):
411        '''return the JSON name of the NTV entity (json-ntv format)
412
413        *Parameters*
414
415        - **def_typ** : Datatype or Namespace (default None) - type of the parent entity
416        - **string** : boolean (default False) - If True, return a string else a tuple
417        - **explicit** : boolean (default False) - If True, type is always included'''
418        if def_type is None:
419            def_type = ''
420        elif isinstance(def_type, (Datatype, Namespace)):
421            def_type = def_type.long_name
422        json_name = self.ntv_name if self.ntv_name else ''
423        json_type = relative_type(
424            def_type, self.type_str) if self.ntv_type else ''
425        implicit = isinstance(self, NtvSingle) and (json_type == 'json'
426                                                    and (not def_type or def_type == 'json' or def_type[-1] == '.')
427                                                    or not NtvConnector.is_json_class(self.val))
428        if implicit and not explicit:
429            json_type = ''
430        json_sep = self._obj_sep(json_name, json_type, def_type)
431        if string:
432            return json_name + json_sep + json_type
433        return [json_name, json_sep, json_type]

return the JSON name of the NTV entity (json-ntv format)

Parameters

  • def_typ : Datatype or Namespace (default None) - type of the parent entity
  • string : boolean (default False) - If True, return a string else a tuple
  • explicit : boolean (default False) - If True, type is always included
@staticmethod
def lower(val1, val2):
435    @staticmethod
436    def lower(val1, val2):
437        ''' compare two ntv_value and return True if val1 < val2, False if val1 > val2 and
438        None in the other cases'''
439        res = None
440        for v1, v2 in zip(Ntv.obj(val1).tree.leaf_nodes, Ntv.obj(val2).tree.leaf_nodes):
441            if v1.val is None:
442                res = True
443            elif isinstance(v1.val, (dict, list)):
444                res = Ntv.lower(v1.val, v2)
445            elif isinstance(v2.val, (dict, list)):
446                res = Ntv.lower(v1, v2.val)
447            elif isinstance(v1.val, Number):
448                if isinstance(v2.val, Number):
449                    res = None if v1.val == v2.val else v1.val < v2.val
450                else:
451                    res = False
452            elif isinstance(v1.val, str):
453                if isinstance(v2.val, Number):
454                    res = True
455                elif isinstance(v2.val, str):
456                    res = None if v1.val == v2.val else v1.val < v2.val
457                else:
458                    res = False
459            if not res is None:
460                break
461        return res

compare two ntv_value and return True if val1 < val2, False if val1 > val2 and None in the other cases

def no_type(self):
463    def no_type(self):
464        '''convert NTV entity in a NV entity (in which ntv_type is 'json' or None')'''
465        no_typ = copy.copy(self)
466        for ntv in NtvTree(no_typ).leaf_nodes:
467            ntv.set_type('json')
468        for ntv in NtvTree(no_typ).inner_nodes:
469            ntv.set_type()
470        return no_typ

convert NTV entity in a NV entity (in which ntv_type is 'json' or None')

def no_name(self):
472    def no_name(self):
473        '''convert NTV entity in a TV entity (in which ntv_name is None)'''
474        no_nam = copy.copy(self)
475        for ntv in NtvTree(no_nam):
476            ntv.ntv_name = None
477        return no_nam

convert NTV entity in a TV entity (in which ntv_name is None)

def no_value(self):
479    def no_value(self):
480        '''convert NTV entity in a NV entity (in which ntv_value of leaf nodes is ntv_type )'''
481        no_val = copy.copy(self)
482        for ntv in NtvTree(no_val).leaf_nodes:
483            ntv.ntv_value = ntv.type_str
484            ntv.set_type('json')
485        return no_val

convert NTV entity in a NV entity (in which ntv_value of leaf nodes is ntv_type )

def only_type(self):
487    def only_type(self):
488        '''convert NTV entity in a V entity (in which ntv_value of leaf nodes
489        is ntv_type )'''
490        only_typ = copy.copy(self)
491        for ntv in NtvTree(only_typ).leaf_nodes:
492            ntv.ntv_value = ntv.type_str
493            ntv.set_type('json')
494            ntv.set_name()
495        for ntv in NtvTree(only_typ).inner_nodes:
496            ntv.set_name()
497            ntv.set_type()
498        return only_typ

convert NTV entity in a V entity (in which ntv_value of leaf nodes is ntv_type )

def only_name(self):
500    def only_name(self):
501        '''convert NTV entity in a V entity (in which ntv_value of leaf nodes
502        is ntv_name )'''
503        only_nam = copy.copy(self)
504        for ntv in NtvTree(only_nam).leaf_nodes:
505            ntv.ntv_value = ntv.name
506            ntv.set_type('json')
507            ntv.set_name()
508        for ntv in NtvTree(only_nam).inner_nodes:
509            ntv.set_type()
510        return only_nam

convert NTV entity in a V entity (in which ntv_value of leaf nodes is ntv_name )

def only_value(self):
512    def only_value(self):
513        '''convert NTV entity in a V entity'''
514        only_val = copy.copy(self)
515        for ntv in NtvTree(only_val).leaf_nodes:
516            ntv.set_type('json')
517            ntv.set_name()
518        for ntv in NtvTree(only_val).inner_nodes:
519            ntv.set_type()
520            ntv.set_name()
521        return only_val

convert NTV entity in a V entity

@staticmethod
def obj_ntv(value, name='', typ='', single=False):
523    @staticmethod
524    def obj_ntv(value, name='', typ='', single=False):
525        '''return a json-ntv representation without using Ntv structure.
526
527        *Parameters*
528
529        - **value** : ntv-value of the json-ntv
530        - **name** : string (default '') - ntv-name of the json-ntv
531        - **typ** : string (default '') - ntv_type of the json-ntv
532        - **single** : boolean (default False) - if True, value is a single object
533        else value is a set of objetcs.
534        '''
535        value = {} if not value else value
536        name = '' if not name else name
537        typ = '' if not typ else typ
538        ntv_list = isinstance(value, (list, dict))
539        if not single and not ntv_list:
540            raise NtvError('value is not compatible with not single NTV data')
541        sep = ':' if single else '::'
542        sep = '' if not typ and (
543            not single or single and not ntv_list) else sep
544        name += sep + typ
545        value = [value] if not name and isinstance(
546            value, dict) and len(value) == 1 else value
547        return {name: value} if name else value

return a json-ntv representation without using Ntv structure.

Parameters

  • value : ntv-value of the json-ntv
  • name : string (default '') - ntv-name of the json-ntv
  • typ : string (default '') - ntv_type of the json-ntv
  • single : boolean (default False) - if True, value is a single object else value is a set of objetcs.
def pointer(self, index=False, item_idx=None):
549    def pointer(self, index=False, item_idx=None):
550        '''return a nested list of pointer from root
551
552        *Parameters*
553
554        - **index**: Boolean (default False) - use index instead of name
555        - **item_idx**: Integer (default None) - index value for the pointer
556        (useful with duplicate data)'''
557        if not self.parent:
558            root_pointer = 0 if index else self.json_name(string=True)
559            return NtvPointer([root_pointer])
560        idx = item_idx if item_idx else self.parent.ntv_value.index(self)
561        num = index or self.parent.json_array
562        pointer = self.parent.pointer(index)
563        pointer.append(idx if num else self.json_name_str)
564        return pointer

return a nested list of pointer from root

Parameters

  • index: Boolean (default False) - use index instead of name
  • item_idx: Integer (default None) - index value for the pointer (useful with duplicate data)
def reduce(self, obj=True, maxi=6, level=3):
566    def reduce(self, obj=True, maxi=6, level=3):
567        '''reduce the length and the level of the entity
568
569        *Parameters*
570
571        - **obj**: boolean (default True) - If True return jsonNTV else NTV entity
572        - **maxi**: integer (default 6) - Number of returned entities in an NtvList
573        - **level**: integer (default 6) - returned entities in an NtvList at this level is None
574
575        *return*
576
577        - **NTV entity** or **jsonNTV**
578        '''
579        ntv = copy.copy(self)
580        cont = Ntv.obj('___') if self.json_array else Ntv.obj({'___': ''})
581        if isinstance(self, NtvSingle):
582            return ntv
583        if level == 0:
584            ntv.ntv_value = [Ntv.obj('___', no_typ=True)]
585        if len(self) <= maxi:
586            ntv.ntv_value = [child.reduce(False, maxi, level-1)
587                             for child in ntv]
588            return ntv
589        mid = maxi // 2
590        cont.set_type(ntv.type_str)
591        start = [child.reduce(False, maxi, level-1) for child in ntv[:mid]]
592        middle = [cont]
593        end = [child.reduce(False, maxi, level-1) for child in ntv[-mid:]]
594        ntv.ntv_value = start + middle + end
595        if obj:
596            return ntv.to_obj()
597        return ntv

reduce the length and the level of the entity

Parameters

  • obj: boolean (default True) - If True return jsonNTV else NTV entity
  • maxi: integer (default 6) - Number of returned entities in an NtvList
  • level: integer (default 6) - returned entities in an NtvList at this level is None

return

  • NTV entity or jsonNTV
def remove(self, first=True, index=None):
599    def remove(self, first=True, index=None):
600        '''remove self from its parent entity.
601
602        *parameters*
603
604        - **first** : boolean (default True) - if True only the first instance
605        else all
606        - **index** : integer (default None) - index of self in its parent
607        '''
608        parent = self.parent
609        if not parent:
610            return
611        idx = parent.ntv_value.index(self) if index is None else index
612        if not parent[idx] == self:
613            raise NtvError('the entity is not present at the index')
614        del parent.ntv_value[idx]
615        if not first and index is None:
616            while self in parent:
617                idx = parent.ntv_value.index(self)
618                del parent.ntv_value[idx]
619        if not self in parent:
620            self.parent = None
621        return

remove self from its parent entity.

parameters

  • first : boolean (default True) - if True only the first instance else all
  • index : integer (default None) - index of self in its parent
def replace(self, ntv):
623    def replace(self, ntv):
624        '''replace self by ntv in the tree'''
625        parent = self.parent
626        if parent:
627            idx = parent.ntv_value.index(self)
628            parent.insert(idx, ntv)
629            del parent[idx+1]
630            if not self in parent:
631                self.parent = None
632        else:
633            self = ntv

replace self by ntv in the tree

def set_name(self, name='', nodes='simple'):
635    def set_name(self, name='', nodes='simple'):
636        '''set new names to the entity
637
638        *Parameters*
639
640        - **name**: list or string (default '') - New name values
641        - **nodes**: string (default 'simple') - nodes to be changed
642            'simple': current entity
643            'leaves': NtvSingle entities
644            'inner': NtvList entities
645            'all': all entities  '''
646        name = '' if name is None else name
647        match nodes:
648            case 'simple':
649                self.ntv_name = str(name)
650            case 'leaves':
651                if not isinstance(name, list):
652                    name = [str(name)] * NtvTree(self).breadth
653                for nam, ntv in zip(name, NtvTree(self).leaf_nodes):
654                    ntv.ntv_name = nam
655            case 'inner':
656                if not isinstance(name, list):
657                    name = [str(name)] * len(NtvTree(self).inner_nodes)
658                for nam, ntv in zip(name, NtvTree(self).inner_nodes):
659                    ntv.ntv_name = nam
660            case 'all':
661                if not isinstance(name, list):
662                    name = [str(name)] * NtvTree(self).size
663                for nam, ntv in zip(name, NtvTree(self).nodes):
664                    ntv.ntv_name = nam
665            case _:
666                raise NtvError('the nodes option is not valid')

set new names to the entity

Parameters

  • name: list or string (default '') - New name values
  • nodes: string (default 'simple') - nodes to be changed 'simple': current entity 'leaves': NtvSingle entities 'inner': NtvList entities 'all': all entities
def set_type(self, typ=None):
668    def set_type(self, typ=None):
669        '''set a new type to the entity
670
671        *Parameters*
672
673        - **typ**: string, Datatype, Namespace (default None)'''
674        if typ and not isinstance(typ, (str, Datatype, Namespace)):
675            raise NtvError('the type is not a valid type')
676        self.ntv_type = str_type(typ, self.__class__.__name__ == 'NtvSingle')

set a new type to the entity

Parameters

  • typ: string, Datatype, Namespace (default None)
def set_value(self, value=None, fast=False):
678    def set_value(self, value=None, fast=False):
679        '''set new ntv_value of a single entity or of a list of entities included
680
681        *Parameters*
682
683        - **value**: list or single value
684        - **fast** : boolean (default False) - if True, value is not converted'''
685        if isinstance(self, NtvSingle):
686            self.ntv_value = NtvSingle(value, ntv_type=self.ntv_type,
687                                       fast=fast).val
688            return
689        if not isinstance(value, list):
690            value = [value] * NtvTree(self).breadth
691        ntv_val = NtvList(value, fast=fast)
692        for val, ntv in zip(ntv_val, NtvTree(self).leaf_nodes):
693            ntv.ntv_value = val.val
694        return

set new ntv_value of a single entity or of a list of entities included

Parameters

  • value: list or single value
  • fast : boolean (default False) - if True, value is not converted
def to_ntvsingle(self, name=None, typ=None, def_type=None, **kwargs):
696    def to_ntvsingle(self, name=None, typ=None, def_type=None, **kwargs):
697        '''convert NtvList entity to NtvSingle entity
698
699        *Parameters*
700
701        - **ntv_name** : String (default None) - name of the NTV entity
702        - **ntv_type**: String (default None) - type of the entity
703        - **value**: value of the entity
704        - **fast**: boolean (default False) - Ntv is created with a list of json values
705        without control
706        '''
707        return NtvSingle(self.obj_value(def_type=def_type, **kwargs),
708                         self.name if self.name else name,
709                         self.type_str if self.type_str else typ)

convert NtvList entity to NtvSingle entity

Parameters

  • ntv_name : String (default None) - name of the NTV entity
  • ntv_type: String (default None) - type of the entity
  • value: value of the entity
  • fast: boolean (default False) - Ntv is created with a list of json values without control
def to_ntvlist( self, def_type=None, def_sep=None, no_typ=False, decode_str=False, typ_auto=False, fast=False):
711    def to_ntvlist(self, def_type=None, def_sep=None, no_typ=False, decode_str=False,
712                   typ_auto=False, fast=False):
713        '''convert NtvSingle entity to NtvList entity
714
715        *Parameters*
716
717        - **value**: Ntv value to convert in an Ntv entity
718        - **no_typ** : boolean (default None) - if True, NtvList is with 'json' type
719        - **def_type** : Datatype or Namespace (default None) - default type of the value
720        - **def_sep**: ':', '::' or None (default None) - default separator of the value
721        - **decode_str**: boolean (default False) - if True, string are loaded as json data
722        - **type_auto**: boolean (default False) - if True, default type for NtvList
723        is the ntv_type of the first Ntv in the ntv_value
724        - **fast** : boolean (default False) - if True, Ntv entity is created without conversion
725        '''
726        ntv = Ntv.from_obj(self.ntv_value, def_type, def_sep, no_typ, decode_str,
727                           typ_auto, fast)
728        if ntv.__class__.__name__ == 'NtvSingle':
729            return NtvList([self])
730        if self.ntv_name:
731            ntv.set_name(self.ntv_name)
732        return ntv

convert NtvSingle entity to NtvList entity

Parameters

  • value: Ntv value to convert in an Ntv entity
  • no_typ : boolean (default None) - if True, NtvList is with 'json' type
  • def_type : Datatype or Namespace (default None) - default type of the value
  • def_sep: ':', '::' or None (default None) - default separator of the value
  • decode_str: boolean (default False) - if True, string are loaded as json data
  • type_auto: boolean (default False) - if True, default type for NtvList is the ntv_type of the first Ntv in the ntv_value
  • fast : boolean (default False) - if True, Ntv entity is created without conversion
def to_mermaid(self, title='', disp=False, row=False, leaves=False):
734    def to_mermaid(self, title='', disp=False, row=False, leaves=False):
735        '''return a mermaid flowchart.
736
737        *Parameters*
738
739        - **title**: String (default '') - title of the flowchart
740        - **disp**: Boolean (default False) - if true, return a display else return
741        a mermaid text diagram
742        - **row**: Boolean (default False) - if True, add the node row
743        - **leaves**: Boolean (default False) - if True, add the leaf row
744        '''
745        option = {'title': title, 'disp': disp, 'row': row, 'leaves': leaves}
746        if disp:
747            Ntv.obj({':$mermaid': self.to_obj()}).to_obj(
748                format='obj', **option)
749            return None
750        return Ntv.obj({':$mermaid': self.to_obj()}).to_obj(format='obj', **option)

return a mermaid flowchart.

Parameters

  • title: String (default '') - title of the flowchart
  • disp: Boolean (default False) - if true, return a display else return a mermaid text diagram
  • row: Boolean (default False) - if True, add the node row
  • leaves: Boolean (default False) - if True, add the leaf row
def to_repr(self, nam=True, typ=True, val=True, jsn=False, maxi=10):
752    def to_repr(self, nam=True, typ=True, val=True, jsn=False, maxi=10):
753        '''return a simple json representation of the Ntv entity.
754
755        *Parameters*
756
757        - **nam**: Boolean(default True) - if true, the names are included
758        - **typ**: Boolean(default True) - if true, the types are included
759        - **val**: Boolean(default True) - if true, the values are included
760        - **jsn**: Boolean(default False) - if false, the 'json' type is not included
761        - **maxi**: Integer (default 10) - number of values to include for NtvList
762        entities. If maxi < 1 all the values are included.
763        '''
764        ntv = self.code_ntv
765        if nam and typ:
766            ntv = ntv[0]
767        if self.ntv_name and nam:
768            ntv += '-' + self.ntv_name
769        if self.ntv_type and typ and (jsn or self.ntv_type.long_name != 'json'):
770            ntv += '-' + self.ntv_type.long_name
771        clas = self.__class__.__name__
772        clas_val = self.ntv_value.__class__.__name__
773        if clas == 'NtvSingle' and clas_val != 'NtvSingle':
774            if val:
775                if ntv:
776                    ntv += '-'
777                ntv += json.dumps(self.ntv_value, cls=NtvJsonEncoder)
778            return ntv
779        if clas == 'NtvSingle' and clas_val == 'NtvSingle':
780            return {ntv:  self.ntv_value.to_repr(nam, typ, val, jsn, maxi)}
781        if clas == 'NtvList':
782            maxv = len(self.ntv_value) if maxi < 1 else maxi
783            return {ntv:  [ntvi.to_repr(nam, typ, val, jsn, maxi)
784                           for ntvi in self.ntv_value[:maxv]]}
785        raise NtvError('the ntv entity is not consistent')

return a simple json representation of the Ntv entity.

Parameters

  • nam: Boolean(default True) - if true, the names are included
  • typ: Boolean(default True) - if true, the types are included
  • val: Boolean(default True) - if true, the values are included
  • jsn: Boolean(default False) - if false, the 'json' type is not included
  • maxi: Integer (default 10) - number of values to include for NtvList entities. If maxi < 1 all the values are included.
def to_name(self, default=''):
787    def to_name(self, default=''):
788        '''return the name of the NTV entity
789
790        *Parameters*
791
792        - **default**: string (default ''): returned value if name is not present '''
793        if self.ntv_name == '':
794            return default
795        return self.ntv_name

return the name of the NTV entity

Parameters

  • default: string (default ''): returned value if name is not present
def to_fast(self, def_type=None, **kwargs):
797    def to_fast(self, def_type=None, **kwargs):
798        '''return the JSON representation of the NTV entity (json-ntv format)
799        without value conversion.
800
801        *Parameters*
802
803        - **def_type** : Datatype or Namespace (default None) - default type to apply
804        to the NTV entity
805        - **encoded** : boolean (default False) - choice for return format
806        (string/bytes if True, dict/list/tuple else)
807        - **format**  : string (default 'json')- choice for return format
808        (json, cbor, obj)
809        - **simpleval** : boolean (default False) - if True, only value (without
810        name and type) is included
811        - **name** : boolean (default true) - if False, name is not included
812        - **json_array** : boolean (default false) - if True, Json-object is not used for NtvList
813        - **maxi**: Integer (default -1) - number of values to include for NtvList
814        entities. If maxi < 1 all the values are included.
815        '''
816        option = kwargs | {'fast': True}
817        return self.to_obj(def_type=def_type, **option)

return the JSON representation of the NTV entity (json-ntv format) without value conversion.

Parameters

  • def_type : Datatype or Namespace (default None) - default type to apply to the NTV entity
  • encoded : boolean (default False) - choice for return format (string/bytes if True, dict/list/tuple else)
  • format : string (default 'json')- choice for return format (json, cbor, obj)
  • simpleval : boolean (default False) - if True, only value (without name and type) is included
  • name : boolean (default true) - if False, name is not included
  • json_array : boolean (default false) - if True, Json-object is not used for NtvList
  • maxi: Integer (default -1) - number of values to include for NtvList entities. If maxi < 1 all the values are included.
def to_obj(self, def_type=None, **kwargs):
819    def to_obj(self, def_type=None, **kwargs):
820        '''return the JSON representation of the NTV entity (json-ntv format).
821
822        *Parameters*
823
824        - **def_type** : Datatype or Namespace (default None) - default type to apply
825        to the NTV entity
826        - **encoded** : boolean (default False) - choice for return format
827        (string/bytes if True, dict/list/tuple else)
828        - **format**  : string (default 'json')- choice for return format
829        (json, cbor, obj)
830        - **simpleval** : boolean (default False) - if True, only value (without
831        name and type) is included
832        - **name** : boolean (default true) - if False, name is not included
833        - **json_array** : boolean (default false) - if True, Json-object is not used for NtvList
834        - **fast** : boolean (default False) - if True, json is created without conversion
835        - **maxi**: Integer (default -1) - number of values to include for NtvList
836        entities. If maxi < 1 all the values are included.
837        '''
838        option = {'encoded': False, 'format': 'json', 'fast': False, 'maxi': -1,
839                  'simpleval': False, 'name': True, 'json_array': False} | kwargs
840        value = self.obj_value(def_type=def_type, **option)
841        obj_name = self.json_name(def_type)
842        if not option['name']:
843            obj_name[0] = ''
844        if option['simpleval']:
845            name = ''
846        elif option['format'] in ('cbor', 'obj') and not NtvConnector.is_json_class(value):
847            name = obj_name[0]
848        else:
849            name = obj_name[0] + obj_name[1] + obj_name[2]
850        value = [value] if not name and isinstance(
851            value, dict) and len(value) == 1 else value
852        json_obj = {name: value} if name else value
853        if option['encoded'] and option['format'] == 'json':
854            return json.dumps(json_obj, cls=NtvJsonEncoder)
855        if option['encoded'] and option['format'] == 'cbor':
856            return NtvConnector.connector()['CborConnec'].to_obj_ntv(json_obj)
857        return json_obj

return the JSON representation of the NTV entity (json-ntv format).

Parameters

  • def_type : Datatype or Namespace (default None) - default type to apply to the NTV entity
  • encoded : boolean (default False) - choice for return format (string/bytes if True, dict/list/tuple else)
  • format : string (default 'json')- choice for return format (json, cbor, obj)
  • simpleval : boolean (default False) - if True, only value (without name and type) is included
  • name : boolean (default true) - if False, name is not included
  • json_array : boolean (default false) - if True, Json-object is not used for NtvList
  • fast : boolean (default False) - if True, json is created without conversion
  • maxi: Integer (default -1) - number of values to include for NtvList entities. If maxi < 1 all the values are included.
def to_json_ntv(self):
859    def to_json_ntv(self):
860        ''' create a copy where ntv-value of the self-tree nodes is converted
861        in json-value'''
862        ntv = copy.copy(self)
863        for leaf in ntv.tree.leaf_nodes:
864            if isinstance(leaf.ntv_value, (NtvSingle, NtvList)):
865                leaf.ntv_value = leaf.ntv_value.to_obj()
866                leaf.ntv_type = Datatype('ntv')
867            elif not leaf.is_json:
868                leaf.ntv_value, leaf.ntv_name, type_str = NtvConnector.cast(
869                    leaf.ntv_value, leaf.ntv_name, leaf.type_str)
870                leaf.ntv_type = Datatype.add(type_str)
871                leaf.is_json = True
872        return ntv

create a copy where ntv-value of the self-tree nodes is converted in json-value

def to_obj_ntv(self, **kwargs):
874    def to_obj_ntv(self, **kwargs):
875        ''' create a copy where ntv-value of the self-tree nodes is converted
876        in object-value
877
878        *Parameters*
879
880        - **kwargs** : parameters used in NtvConnector class (specific for each Connector)'''
881        ntv = copy.copy(self)
882        for leaf in ntv.tree.leaf_nodes:
883            if (leaf.is_json and leaf.type_str in set(NtvConnector.dic_type.values())
884                    or leaf.ntv_type is None):
885                leaf.ntv_value, leaf.ntv_name, type_str = NtvConnector.uncast(
886                    leaf, **kwargs)
887                leaf.ntv_type = Datatype.add(type_str) if type_str else None
888                leaf.is_json = NtvConnector.is_json(leaf.ntv_value)
889        return ntv

create a copy where ntv-value of the self-tree nodes is converted in object-value

Parameters

  • kwargs : parameters used in NtvConnector class (specific for each Connector)
def to_tuple(self, maxi=10):
891    def to_tuple(self, maxi=10):
892        '''return a nested tuple representation of the NTV entity
893        (NtvList/NtvSingle, ntv_name, ntv_type, ntv_value).
894
895        *Parameters*
896
897        - **maxi**: Integer (default 10) - number of values to include for NtvList
898        entities. If maxi < 1 all the values are included.
899        '''
900        clas = self.__class__.__name__
901        val = self.ntv_value
902        name = self.ntv_name
903        typ = None
904        if self.ntv_type:
905            typ = self.ntv_type.long_name
906        if isinstance(self, NtvSingle) and not isinstance(val, NtvSingle):
907            return (clas, name, typ, val)
908        if isinstance(self, NtvSingle) and isinstance(val, NtvSingle):
909            return (clas, name, typ, val.to_tuple(maxi=maxi))
910        if isinstance(self, NtvList):
911            maxv = len(self.ntv_value) if maxi < 1 else maxi
912            return (clas, name, typ, [ntv.to_tuple(maxi=maxi) for ntv in val[:maxv]])
913        raise NtvError('the ntv entity is not consistent')

return a nested tuple representation of the NTV entity (NtvList/NtvSingle, ntv_name, ntv_type, ntv_value).

Parameters

  • maxi: Integer (default 10) - number of values to include for NtvList entities. If maxi < 1 all the values are included.
def validate(self, unique=False):
915    def validate(self, unique=False):
916        '''check the ntv_type validity and return a boolean and a list of errors
917
918        *Parameters*
919
920        - **unique**: boolean (default False) - if True, stop validation at the
921        first error'''
922        errors = []
923        for ntv in self.tree.leaf_nodes:
924            valid = ntv.ntv_type.validate(ntv.ntv_value)
925            if not valid:
926                errors.append(str(ntv.pointer()))
927                if unique:
928                    return (False, errors)
929        return (not errors, errors)

check the ntv_type validity and return a boolean and a list of errors

Parameters

  • unique: boolean (default False) - if True, stop validation at the first error
@abstractmethod
def obj_value(self):
931    @abstractmethod
932    def obj_value(self):
933        '''return the ntv_value with different formats defined by kwargs (abstract method)'''

return the ntv_value with different formats defined by kwargs (abstract method)

json_array

return the json_array dynamic attribute (abstract method)

@staticmethod
def decode_json(json_value):
962    @staticmethod
963    def decode_json(json_value):
964        '''return (value, name, type, separator, isjson) of a json object'''
965        if isinstance(json_value, dict) and len(json_value) == 1:
966            json_name = list(json_value.keys())[0]
967            val = json_value[json_name]
968            return (val, *NtvUtil.from_obj_name(json_name), NtvConnector.is_json(val))
969        return (json_value, None, None, None, NtvConnector.is_json(json_value))

return (value, name, type, separator, isjson) of a json object

Inherited Members
json_ntv.ntv_util.NtvUtil
from_obj_name
decode_ntv_tab
to_ntvpointer
class NtvSingle(Ntv):
 995class NtvSingle(Ntv):
 996    ''' A NTV-single entity is a Ntv entity not composed with other entities.
 997
 998    *Attributes :*
 999    - no additional attributes to those of parent class `Ntv`
1000
1001    *dynamic values (@property)*
1002    - `json_array`
1003
1004    The additional methods defined in this class are :
1005
1006    *instance methods*
1007    - `obj_value`
1008    '''
1009
1010    def __init__(self, value, ntv_name=None, ntv_type=None, fast=False):
1011        '''NtvSingle constructor.
1012
1013        *Parameters*
1014
1015        - **ntv_name** : String (default None) - name of the NTV entity
1016        - **ntv_type**: String (default None) - type of the entity
1017        - **value**: value of the entity
1018        - **fast**: boolean (default False) - Ntv is created with a list of json values
1019        without control
1020        '''
1021        if not fast:
1022            value, ntv_name, ntv_type = NtvSingle._decode_s(
1023                value, ntv_name, ntv_type)
1024            if ntv_type and isinstance(ntv_type, str) and ntv_type[-1] == '.':
1025                raise NtvError('the ntv_type is not valid')
1026        super().__init__(value, ntv_name, ntv_type)
1027
1028    def __eq__(self, other):
1029        ''' equal if name type and value are equal'''
1030        return self.__class__.__name__ == other.__class__.__name__ and\
1031            self.ntv_name == other.ntv_name and self.ntv_type == other.ntv_type and\
1032            self.ntv_value == other.ntv_value
1033
1034    def __hash__(self):
1035        '''return hash(name) + hash(type) + hash(value)'''
1036        return hash(self.ntv_name) + hash(self.ntv_type) + \
1037            hash(json.dumps(self.ntv_value, cls=NtvJsonEncoder))
1038
1039    def __copy__(self):
1040        ''' Copy all the Ntv tree '''
1041        return self.__class__(copy.copy(self.ntv_value), self.ntv_name,
1042                              self.ntv_type, fast=True)
1043
1044    @property
1045    def json_array(self):
1046        ''' return the json_array dynamic attribute (always False)'''
1047        return False
1048
1049    def obj_value(self, def_type=None, **kwargs):
1050        '''return the ntv_value with different formats defined by kwargs'''
1051        option = {'encoded': False, 'format': 'json',
1052                  'simpleval': False, 'fast': False} | kwargs
1053        if option['fast'] or option['format'] in ('json', 'tuple'):
1054            return self.ntv_value
1055        if option['format'] == 'obj' and self.ntv_value == 'null':
1056            return None
1057        return NtvConnector.uncast(self, **option)[0]
1058
1059    def _obj_sep(self, json_name, json_type, def_type=None):  # REQ5
1060        ''' return separator to include in json_name'''
1061        if json_type or not def_type and isinstance(self.ntv_value, (list, dict)):
1062            return ':'
1063        return ''
1064
1065    @staticmethod
1066    def _decode_s(ntv_value, ntv_name, ntv_type_str):
1067        '''return adjusted ntv_value, ntv_name, ntv_type(str)'''
1068        is_json = NtvConnector.is_json(ntv_value)
1069        if is_json:
1070            if isinstance(ntv_value, (list)):
1071                ntv_value = [NtvSingle._decode_s(val, '', ntv_type_str)[
1072                    0] for val in ntv_value]
1073            elif isinstance(ntv_value, (dict)):
1074                ntv_value = {key: NtvSingle._decode_s(val, '', ntv_type_str)[
1075                    0] for key, val in ntv_value.items()}
1076        elif isinstance(ntv_value, NtvSingle):
1077            ntv_value = ntv_value.to_obj()
1078            return (ntv_value, ntv_name, 'ntv')
1079        else:
1080            ntv_value, name, typ_str = NtvConnector.cast(ntv_value, ntv_name)
1081            ntv_type_str = Datatype.add(
1082                typ_str).name if typ_str else ntv_type_str
1083        if not ntv_type_str:
1084            if is_json:
1085                ntv_type_str = 'json'
1086            else:
1087                ntv_type_str = typ_str
1088                if not ntv_name:
1089                    ntv_name = name
1090        elif not is_json and ntv_type_str != typ_str:
1091            raise NtvError('ntv_value is not compatible with ntv_type')
1092        return (ntv_value, ntv_name, ntv_type_str)

A NTV-single entity is a Ntv entity not composed with other entities.

Attributes :

  • no additional attributes to those of parent class Ntv

dynamic values (@property)

The additional methods defined in this class are :

instance methods

NtvSingle(value, ntv_name=None, ntv_type=None, fast=False)
1010    def __init__(self, value, ntv_name=None, ntv_type=None, fast=False):
1011        '''NtvSingle constructor.
1012
1013        *Parameters*
1014
1015        - **ntv_name** : String (default None) - name of the NTV entity
1016        - **ntv_type**: String (default None) - type of the entity
1017        - **value**: value of the entity
1018        - **fast**: boolean (default False) - Ntv is created with a list of json values
1019        without control
1020        '''
1021        if not fast:
1022            value, ntv_name, ntv_type = NtvSingle._decode_s(
1023                value, ntv_name, ntv_type)
1024            if ntv_type and isinstance(ntv_type, str) and ntv_type[-1] == '.':
1025                raise NtvError('the ntv_type is not valid')
1026        super().__init__(value, ntv_name, ntv_type)

NtvSingle constructor.

Parameters

  • ntv_name : String (default None) - name of the NTV entity
  • ntv_type: String (default None) - type of the entity
  • value: value of the entity
  • fast: boolean (default False) - Ntv is created with a list of json values without control
json_array

return the json_array dynamic attribute (always False)

def obj_value(self, def_type=None, **kwargs):
1049    def obj_value(self, def_type=None, **kwargs):
1050        '''return the ntv_value with different formats defined by kwargs'''
1051        option = {'encoded': False, 'format': 'json',
1052                  'simpleval': False, 'fast': False} | kwargs
1053        if option['fast'] or option['format'] in ('json', 'tuple'):
1054            return self.ntv_value
1055        if option['format'] == 'obj' and self.ntv_value == 'null':
1056            return None
1057        return NtvConnector.uncast(self, **option)[0]

return the ntv_value with different formats defined by kwargs

class NtvList(Ntv):
1095class NtvList(Ntv):
1096    '''A NTV-list entity is a Ntv entity where:
1097
1098    - ntv_value is a list of NTV entities,
1099    - ntv_type is a default type available for included NTV entities
1100
1101    *Attributes :*
1102    - no additional attributes to those of parent class `Ntv`
1103
1104    *dynamic values (@property)*
1105    - `json_array`
1106
1107    The additional methods defined in this class are :
1108
1109    *instance methods*
1110    - `obj_value`
1111    '''
1112
1113    def __init__(self, list_ntv, ntv_name=None, ntv_type=None, typ_auto=False, fast=False):
1114        '''NtvList constructor.
1115
1116        *Parameters*
1117
1118        - **ntv_name** : String (default None) - name of the NTV entity
1119        - **ntv_type**: String (default None) - default type or namespace of
1120        the included entities
1121        - **list_ntv**: list - list of Ntv objects or obj_value of Ntv objects
1122        - **fast**: boolean (default False) - if True, Ntv is created with a list
1123        of json values without control
1124        - **type_auto**: boolean (default False) - if True, default type for NtvList
1125        is the ntv_type of the first Ntv in the ntv_value'''
1126        if isinstance(list_ntv, NtvList):
1127            ntv_value = [copy.copy(ntv) for ntv in list_ntv.ntv_value]
1128            ntv_type = list_ntv.ntv_type
1129            ntv_name = list_ntv.ntv_name
1130        elif isinstance(list_ntv, list):
1131            ntv_value = [Ntv.from_obj(ntv, ntv_type, ':', fast=fast)
1132                         for ntv in list_ntv]
1133        elif isinstance(list_ntv, dict):
1134            ntv_value = [Ntv.from_obj({key: val}, ntv_type, ':', fast=fast)
1135                         for key, val in list_ntv.items()]
1136        else:
1137            raise NtvError('ntv_value is not a list')
1138        if typ_auto and not ntv_type and len(ntv_value) > 0 and ntv_value[0].ntv_type:
1139            ntv_type = ntv_value[0].ntv_type
1140        super().__init__(ntv_value, ntv_name, ntv_type)
1141        for ntv in self:
1142            ntv.parent = self
1143
1144    @property
1145    def json_array(self):
1146        ''' return the json_array dynamic attribute'''
1147        set_name = {ntv.json_name(
1148            def_type=self.type_str, string=True) for ntv in self}
1149        return '' in set_name or len(set_name) != len(self)
1150
1151    def __eq__(self, other):
1152        ''' equal if name and value are equal'''
1153        return self.__class__.__name__ == other.__class__.__name__ and\
1154            self.ntv_name == other.ntv_name and self.ntv_value == other.ntv_value
1155
1156    def __hash__(self):
1157        '''return hash(name) + hash(value)'''
1158        return hash(self.ntv_name) + hash(tuple(self.ntv_value))
1159
1160    def __copy__(self):
1161        ''' Copy all the data '''
1162        cop = self.__class__(self)
1163        cop.parent = None
1164        return cop
1165
1166    def __setitem__(self, ind, value):
1167        ''' replace ntv_value item at the `ind` row with `value`'''
1168        if ind < 0 or ind >= len(self):
1169            raise NtvError("out of bounds")
1170        self.ntv_value[ind] = value
1171        if isinstance(value, (NtvSingle, NtvList)):
1172            value.parent = self
1173
1174    def __delitem__(self, ind):
1175        '''remove ntv_value item at the `ind` row'''
1176        if isinstance(ind, int):
1177            self.ntv_value.pop(ind)
1178        else:
1179            self.ntv_value.pop(self.ntv_value.index(self[ind]))
1180
1181    def append(self, ntv):
1182        ''' add ntv at the end of the list of Ntv entities included'''
1183        old_parent = ntv.parent
1184        if old_parent:
1185            del(old_parent[old_parent.ntv_value.index(ntv)])
1186        self.ntv_value.append(ntv)
1187        ntv.parent = self
1188
1189    def insert(self, idx, ntv):
1190        ''' add ntv at the index idx of the list of Ntv entities included'''
1191        old_parent = ntv.parent
1192        if old_parent:
1193            del(old_parent[old_parent.ntv_value.index(ntv)])
1194        self.ntv_value.insert(idx, ntv)
1195        ntv.parent = self
1196
1197    def _obj_sep(self, json_name, json_type, def_type=None):
1198        ''' return separator to include in json_name'''
1199        sep = ':' if (json_type and json_type[-1] == '.') else '::'
1200        return sep if (json_type and json_type[-1] != '.') or (json_type and json_name) else ''
1201
1202    def obj_value(self, def_type=None, **kwargs):
1203        '''return the ntv_value with different formats defined by kwargs
1204        '''
1205        option = {'encoded': False, 'format': 'json', 'simpleval': False,
1206                  'json_array': False, 'fast': False, 'maxi': -1} | kwargs
1207        opt2 = option | {'encoded': False}
1208        maxv = len(self.ntv_value) if option['maxi'] < 1 else option['maxi']
1209        def_type = self.ntv_type.long_name if self.ntv_type else def_type
1210        values = [ntv.to_obj(def_type=def_type, **opt2)
1211                  for ntv in self.ntv_value[:maxv]]
1212        if len(self) == 1 and isinstance(self[0], NtvSingle) and isinstance(values[0], dict):
1213            return values[0]
1214        if self.json_array or option['simpleval'] or option['json_array']:
1215            return values
1216        return {list(val.items())[0][0]: list(val.items())[0][1] for val in values}

A NTV-list entity is a Ntv entity where:

  • ntv_value is a list of NTV entities,
  • ntv_type is a default type available for included NTV entities

Attributes :

  • no additional attributes to those of parent class Ntv

dynamic values (@property)

The additional methods defined in this class are :

instance methods

NtvList(list_ntv, ntv_name=None, ntv_type=None, typ_auto=False, fast=False)
1113    def __init__(self, list_ntv, ntv_name=None, ntv_type=None, typ_auto=False, fast=False):
1114        '''NtvList constructor.
1115
1116        *Parameters*
1117
1118        - **ntv_name** : String (default None) - name of the NTV entity
1119        - **ntv_type**: String (default None) - default type or namespace of
1120        the included entities
1121        - **list_ntv**: list - list of Ntv objects or obj_value of Ntv objects
1122        - **fast**: boolean (default False) - if True, Ntv is created with a list
1123        of json values without control
1124        - **type_auto**: boolean (default False) - if True, default type for NtvList
1125        is the ntv_type of the first Ntv in the ntv_value'''
1126        if isinstance(list_ntv, NtvList):
1127            ntv_value = [copy.copy(ntv) for ntv in list_ntv.ntv_value]
1128            ntv_type = list_ntv.ntv_type
1129            ntv_name = list_ntv.ntv_name
1130        elif isinstance(list_ntv, list):
1131            ntv_value = [Ntv.from_obj(ntv, ntv_type, ':', fast=fast)
1132                         for ntv in list_ntv]
1133        elif isinstance(list_ntv, dict):
1134            ntv_value = [Ntv.from_obj({key: val}, ntv_type, ':', fast=fast)
1135                         for key, val in list_ntv.items()]
1136        else:
1137            raise NtvError('ntv_value is not a list')
1138        if typ_auto and not ntv_type and len(ntv_value) > 0 and ntv_value[0].ntv_type:
1139            ntv_type = ntv_value[0].ntv_type
1140        super().__init__(ntv_value, ntv_name, ntv_type)
1141        for ntv in self:
1142            ntv.parent = self

NtvList constructor.

Parameters

  • ntv_name : String (default None) - name of the NTV entity
  • ntv_type: String (default None) - default type or namespace of the included entities
  • list_ntv: list - list of Ntv objects or obj_value of Ntv objects
  • fast: boolean (default False) - if True, Ntv is created with a list of json values without control
  • type_auto: boolean (default False) - if True, default type for NtvList is the ntv_type of the first Ntv in the ntv_value
json_array

return the json_array dynamic attribute

def append(self, ntv):
1181    def append(self, ntv):
1182        ''' add ntv at the end of the list of Ntv entities included'''
1183        old_parent = ntv.parent
1184        if old_parent:
1185            del(old_parent[old_parent.ntv_value.index(ntv)])
1186        self.ntv_value.append(ntv)
1187        ntv.parent = self

add ntv at the end of the list of Ntv entities included

def insert(self, idx, ntv):
1189    def insert(self, idx, ntv):
1190        ''' add ntv at the index idx of the list of Ntv entities included'''
1191        old_parent = ntv.parent
1192        if old_parent:
1193            del(old_parent[old_parent.ntv_value.index(ntv)])
1194        self.ntv_value.insert(idx, ntv)
1195        ntv.parent = self

add ntv at the index idx of the list of Ntv entities included

def obj_value(self, def_type=None, **kwargs):
1202    def obj_value(self, def_type=None, **kwargs):
1203        '''return the ntv_value with different formats defined by kwargs
1204        '''
1205        option = {'encoded': False, 'format': 'json', 'simpleval': False,
1206                  'json_array': False, 'fast': False, 'maxi': -1} | kwargs
1207        opt2 = option | {'encoded': False}
1208        maxv = len(self.ntv_value) if option['maxi'] < 1 else option['maxi']
1209        def_type = self.ntv_type.long_name if self.ntv_type else def_type
1210        values = [ntv.to_obj(def_type=def_type, **opt2)
1211                  for ntv in self.ntv_value[:maxv]]
1212        if len(self) == 1 and isinstance(self[0], NtvSingle) and isinstance(values[0], dict):
1213            return values[0]
1214        if self.json_array or option['simpleval'] or option['json_array']:
1215            return values
1216        return {list(val.items())[0][0]: list(val.items())[0][1] for val in values}

return the ntv_value with different formats defined by kwargs