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