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