Coveralls logob
Coveralls logo
  • Home
  • Features
  • Pricing
  • Docs
  • Sign In

suavecode / SUAVE / 43602228

19 May 2022 - 21:17 coverage decreased (-0.1%) to 90.042%
43602228

Pull #588

appveyor

GitHub
Merge 931e5745c into 6585b32f0
Pull Request #588: Jax data

28855 of 32046 relevant lines covered (90.04%)

0.9 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

68.71
/trunk/SUAVE/Core/Data.py
1
## @ingroup Core
2
# Data.py
3
#
4
# Created:  Jun 2016, E. Botero
5
# Modified: Jan 2020, M. Clarke
6
#           May 2020, E. Botero
7
#           Jul 2021, E. Botero
8
#           Oct 2021, E. Botero
9

10

11

12
# ----------------------------------------------------------------------
13
#   Imports
14
# ----------------------------------------------------------------------
15

16
import numpy as np
1×
17
from .Arrays import atleast_2d_col, array_type, matrix_type, append_array
1×
18

19
from copy import copy
1×
20

21
from jax.tree_util import register_pytree_node_class
1×
22

23
# for enforcing attribute style access names
24
import string
1×
25
from warnings import warn
1×
26
chars = string.punctuation + string.whitespace
1×
27
t_table = str.maketrans( chars          + string.ascii_uppercase , 
1×
28
                            '_'*len(chars) + string.ascii_lowercase )
29

30
dictgetitem = dict.__getitem__
1×
31
objgetattrib = object.__getattribute__
1×
32

33
# ----------------------------------------------------------------------
34
#   Data
35
# ----------------------------------------------------------------------        
36

37
## @ingroup Core
38
@register_pytree_node_class
1×
39
class Data(dict):
1×
40
    """ An extension of the Python dict which allows for both tag and '.' usage.
41
        This is an unordered dictionary. So indexing it will not produce deterministic results.
42
        This has less overhead than ordering. If ordering is needed use DataOrdered().
43
       
44
        Assumptions:
45
        N/A
46
        
47
        Source:
48
        N/A
49
    """
50
    
51
    def tree_flatten(self):
1×
52
        """ Returns a list of objects for tree operations.
53
    
54
            Assumptions:
55
            N/A
56
    
57
            Source:
58
            N/A
59
    
60
            Inputs:
61
            method  - name of the method to access
62
    
63
            Outputs:
64
            Result  - the results of the method
65
    
66
            Properties Used:
67
            N/A    
68
        """          
69
            
70
        #Get all keys and values from the data class
71
        keys   = list(self.keys())
!
UNCOV
72
        values = self.values()         
!
73

74
        # Make a dictionary of the strings in self
UNCOV
75
        aux_dict = dict()
!
UNCOV
76
        for key, value in zip(keys,values):
!
UNCOV
77
            if type(value) is str:
!
UNCOV
78
                aux_dict[key] = self.pop(key)
!
UNCOV
79
            elif isinstance(value,dict):
!
UNCOV
80
                if not bool(value):
!
UNCOV
81
                    aux_dict[key] = self.pop(key)
!
UNCOV
82
            elif isinstance(value,bool):
!
UNCOV
83
                aux_dict[key] = self.pop(key)
!
84

85
        # Some children classes might have "static_keys" that are marked as immutable
UNCOV
86
        if hasattr(self,'static_keys'):
!
UNCOV
87
            for k in self.static_keys:
!
UNCOV
88
                aux_dict[k] = self.pop(k)
!
89
                
90
            # static_keys is also a static key...
UNCOV
91
            aux_dict['static_keys'] = self.pop('static_keys')            
!
92

93
        #Get all keys and values from the data class, now that they don't have strings or static_keys
94
        stringless_keys = list(self.keys())
!
95
        children        = self.values()   
!
96
        
97
        # Put back what I have taken away
98
        self.update(aux_dict)
!
99
        
100
        # Pack up the results
UNCOV
101
        aux_data        = [stringless_keys,aux_dict] 
!
102

UNCOV
103
        return (children, aux_data)
!
104
  
105
    @classmethod
1×
106
    def tree_unflatten(cls, aux_data, children):
1×
107
        """ Rebuilds the Data class.
108
    
109
            Assumptions:
110
            N/A
111
    
112
            Source:
113
            N/A
114
    
115
            Inputs:
116
            method  - name of the method to access
117
    
118
            Outputs:
119
            Result  - the results of the method
120
    
121
            Properties Used:
122
            N/A    
123
        """
124
        # Create the class
UNCOV
125
        recreated = cls()
!
126
        
127
        # add an string data
UNCOV
128
        recreated.update(aux_data[1])
!
129
        
130
        # keys
UNCOV
131
        keys      = aux_data[0]
!
UNCOV
132
        length    = len(keys)
!
UNCOV
133
        keys      = list(keys)
!
UNCOV
134
        for ii in range(length):
!
UNCOV
135
            recreated[keys[ii]] = children[ii]
!
136

UNCOV
137
        return recreated
!
138
        
139
        
140
    
141
    def __getattribute__(self, k):
1×
142
        """ Retrieves an attribute set by a key k
143
    
144
            Assumptions:
145
            Does a try if it is a dict, but if that fails treats it as an object
146
    
147
            Source:
148
            N/A
149
    
150
            Inputs:
151
            k
152
    
153
            Outputs:
154
            whatever is found by k
155
    
156
            Properties Used:
157
            N/A
158
            """         
159
        try:
1×
160
            return dictgetitem(self,k)
1×
161
        except:
1×
162
            return objgetattrib(self,k)
1×
163

164
    def __setattr__(self, k, v):
1×
165
        """ An override of the standard __setattr_ in Python.
166
            
167
            Assumptions:
168
            This one tries to treat k as an object, if that fails it treats it as a key.
169
    
170
            Source:
171
            N/A
172
    
173
            Inputs:
174
            k        [key]
175
            v        [value]
176
    
177
            Outputs:
178
            N/A
179
    
180
            Properties Used:
181
            N/A    
182
        """
183
        try:
1×
184
            objgetattrib(self, k)
1×
185
        except:
1×
186
            self[k] = v
1×
187
        else:          
188
            object.__setattr__(self, k, v) 
1×
189
            
190
    def __delattr__(self, k):
1×
191
        """ An override of the standard __delattr_ in Python. This deletes whatever is called by k
192
            
193
            Assumptions:
194
            This one tries to treat k as an object, if that fails it treats it as a key.
195
    
196
            Source:
197
            N/A
198
    
199
            Inputs:
200
            N/A
201
    
202
            Outputs:
203
            N/A
204
    
205
            Properties Used:
206
            N/A    
207
        """        
UNCOV
208
        try:
!
UNCOV
209
            objgetattrib(self, k)
!
UNCOV
210
        except:
!
UNCOV
211
            del self[k]
!
212
        else:
UNCOV
213
            object.__delattr__(self, k)
!
214
    
215
    def __defaults__(self):
1×
216
        """ A stub for all classes that come later
217
            
218
            Assumptions:
219
            N/A
220
    
221
            Source:
222
            N/A
223
    
224
            Inputs:
225
            N/A
226
    
227
            Outputs:
228
            N/A
229
    
230
            Properties Used:
231
            N/A    
232
        """              
233
        pass      
1×
234
    
235
    def __new__(cls,*args,**kwarg):
1×
236
        """ Creates a new Data() class
237
    
238
            Assumptions:
239
            N/A
240
    
241
            Source:
242
            N/A
243
    
244
            Inputs:
245
            N/A
246
    
247
            Outputs:
248
            N/A
249
    
250
            Properties Used:
251
            N/A    
252
        """         
253
        
254
        
255
        # initialize data, no inputs
256
        self = super(Data,cls).__new__(cls)
1×
257
        super(Data,self).__init__() 
1×
258
        
259
        # get base class list
260
        klasses = self.get_bases()
1×
261
                
262
        # fill in defaults trunk to leaf
263
        for klass in klasses[::-1]:
1×
264
            try:
1×
265
                klass.__defaults__(self)
1×
UNCOV
266
            except:
!
UNCOV
267
                pass
!
268
            
269
        return self
1×
270
    
271
    def typestring(self):
1×
272
        """ This function makes the .key.key structure in string form of Data()
273
    
274
            Assumptions:
275
            N/A
276
    
277
            Source:
278
            N/A
279
    
280
            Inputs:
281
            N/A
282
    
283
            Outputs:
284
            N/A
285
    
286
            Properties Used:
287
            N/A    
288
        """         
289
        
290
        # build typestring
291
        typestring = str(type(self)).split("'")[1]
1×
292
        typestring = typestring.split('.')
1×
293
        if typestring[-1] == typestring[-2]:
1×
294
            del typestring[-1]
1×
295
        typestring = '.'.join(typestring) 
1×
296
        return typestring    
1×
297
    
298
    def dataname(self):
1×
299
        """ This function is used for printing the class
300
    
301
            Assumptions:
302
            N/A
303
    
304
            Source:
305
            N/A
306
    
307
            Inputs:
308
            N/A
309
    
310
            Outputs:
311
            N/A
312
    
313
            Properties Used:
314
            N/A    
315
        """          
316
        return "<data object '" + self.typestring() + "'>"     
1×
317
    
318
    
319
    def __str__(self,indent=''):
1×
320
        """ This function is used for printing the class. This starts the first line of printing.
321
    
322
            Assumptions:
323
            N/A
324
    
325
            Source:
326
            N/A
327
    
328
            Inputs:
329
            N/A
330
    
331
            Outputs:
332
            N/A
333
    
334
            Properties Used:
335
            N/A    
336
        """         
337
        
338
        new_indent = '  '
1×
339
        args = ''
1×
340
        
341
        # trunk data name
342
        if not indent:
1×
343
            args += self.dataname()  + '\n'
1×
344
        else:
345
            args += ''
1×
346
            
347
        args += self.__str2(indent)
1×
348
        
349
        return args    
1×
350
    
351
    
352
    def __str2(self,indent=''):
1×
353
        """ This regresses through and does the rest of printing that __str__ missed
354
    
355
            Assumptions:
356
            N/A
357
    
358
            Source:
359
            N/A
360
    
361
            Inputs:
362
            N/A
363
    
364
            Outputs:
365
            N/A
366
    
367
            Properties Used:
368
            N/A    
369
        """     
370
        
371
        new_indent = '  '
1×
372
        args = ''
1×
373
        
374
        # trunk data name
375
        if indent: args += '\n'
1×
376
        
377
        # print values   
378
        for key,value in self.items():
1×
379
            
380
            # skip 'hidden' items
381
            if isinstance(key,str) and key.startswith('_'):
1×
UNCOV
382
                continue
!
383
            
384
            # recurse into other dict types
385
            if isinstance(value,dict):
1×
386
                if not value:
1×
387
                    val = '\n'
1×
388
                else:
389
                    try:
1×
390
                        val = value.__str2(indent+new_indent)
1×
391
                    except RuntimeError: # recursion limit
1×
UNCOV
392
                        val = ''
!
393
                    except:
1×
394
                        val = value.__str__(indent+new_indent)
1×
395
                                                
396
            # everything else
397
            else:
398
                val = str(value) + '\n'
1×
399
                
400
            # this key-value, indented
401
            args+= indent + str(key) + ' : ' + val
1×
402
            
403
        return args        
1×
404
    
405
    def __init__(self,*args,**kwarg):
1×
406
        """ Initializes a new Data() class
407
    
408
            Assumptions:
409
            N/A
410
    
411
            Source:
412
            N/A
413
    
414
            Inputs:
415
            N/A
416
    
417
            Outputs:
418
            N/A
419
    
420
            Properties Used:
421
            N/A    
422
        """           
423

424
        # handle input data (ala class factory)
425
        input_data = Data.__base__(*args,**kwarg)
1×
426
        
427
        # update this data with inputs
428
        self.update(input_data)          
1×
429
            
430

431
    def __iter__(self):
1×
432
        """ Returns all the iterable values. Can be used in a for loop.
433
    
434
            Assumptions:
435
            N/A
436
    
437
            Source:
438
            N/A
439
    
440
            Inputs:
441
            N/A
442
    
443
            Outputs:
444
            N/A
445
    
446
            Properties Used:
447
            N/A    
448
        """           
449
        return iter(self.values())
1×
450
    
451
    def itervalues(self):
1×
452
        """ Finds all the values that can be iterated over.
453
    
454
            Assumptions:
455
            N/A
456
    
457
            Source:
458
            N/A
459
    
460
            Inputs:
461
            N/A
462
    
463
            Outputs:
464
            N/A
465
    
466
            Properties Used:
467
            N/A    
468
        """             
UNCOV
469
        for k in super(Data,self).__iter__():
!
UNCOV
470
            yield self[k]   
!
471
    
472
    def values(self):
1×
473
        """ Returns all values inside the Data() class.
474
    
475
            Assumptions:
476
            N/A
477
    
478
            Source:
479
            N/A
480
    
481
            Inputs:
482
            N/A
483
    
484
            Outputs:
485
            values
486
    
487
            Properties Used:
488
            N/A    
489
        """          
490
        return self.__values()          
1×
491
            
492
    def __values(self):
1×
493
        """ Iterates over all keys to find all the data values.
494
    
495
            Assumptions:
496
            N/A
497
    
498
            Source:
499
            N/A
500
    
501
            Inputs:
502
            N/A
503
    
504
            Outputs:
505
            values
506
    
507
            Properties Used:
508
            N/A    
509
        """           
510
        return [self[key] for key in super(Data,self).__iter__()]    
1×
511
    
512
    def update(self,other):
1×
513
        """ Updates the internal values of a dictionary with given data
514
    
515
            Assumptions:
516
            N/A
517
    
518
            Source:
519
            N/A
520
    
521
            Inputs:
522
            other
523
    
524
            Outputs:
525
            N/A
526
    
527
            Properties Used:
528
            N/A    
529
        """           
530
        if not isinstance(other,dict):
1×
UNCOV
531
            raise TypeError('input is not a dictionary type')
!
532
        for k,v in other.items():
1×
533
            # recurse only if self's value is a Dict()
534
            if k.startswith('_'):
1×
535
                continue
1×
536
        
537
            try:
1×
538
                self[k].update(v)
1×
539
            except:
1×
540
                self[k] = v
1×
541
        return
1×
542
    
543
    def append_or_update(self,other):
1×
544
        """ Appends an array or updates the internal values of a dictionary with given data
545
    
546
            Assumptions:
547
            N/A
548
    
549
            Source:
550
            N/A
551
    
552
            Inputs:
553
            other
554
    
555
            Outputs:
556
            N/A
557
    
558
            Properties Used:
559
            N/A    
560
        """           
561
        if not isinstance(other,dict):
1×
562
            raise TypeError('input is not a dictionary type')
!
563
        for k,v in other.items():
1×
564
            # recurse only if self's value is a Dict()
565
            if k.startswith('_'):
1×
UNCOV
566
                continue
!
567
            
568
            # Check if v is an array and if k is a key in self
569
            if isinstance(v,array_type) and hasattr(self,k):
1×
570
                self[k] = append_array(self[k],v)
1×
571
            else:
572
                try:
1×
573
                    self[k].append_or_update(v)
1×
574
                except:
1×
575
                    self[k] = copy(v)
1×
576
        return          
1×
577
    
578
    def get_bases(self):
1×
579
        """ Finds the higher classes that may be built off of data
580
    
581
            Assumptions:
582
            N/A
583
    
584
            Source:
585
            N/A
586
    
587
            Inputs:
588
            N/A
589
    
590
            Outputs:
591
            klasses
592
    
593
            Properties Used:
594
            N/A    
595
        """          
596
        # Get the Method Resolution Order, i.e. the ancestor tree
597
        klasses = list(self.__class__.__mro__)
1×
598
        
599
        # Make sure that this is a Data object, otherwise throw an error.
600
        if Data not in klasses:
1×
UNCOV
601
            raise TypeError('class %s is not of type Data()' % self.__class__)    
!
602
        
603
        # Remove the last two items, dict and object. Since the line before ensures this is a data objectt this won't break
604
        klasses = klasses[:-2]
1×
605

606
        return klasses    
1×
607
    
608
    def append(self,value,key=None):
1×
609
        """ Adds new values to the classes. Can also change an already appended key
610
    
611
            Assumptions:
612
            N/A
613
    
614
            Source:
615
            N/A
616
    
617
            Inputs:
618
            value
619
            key
620
    
621
            Outputs:
622
            N/A
623
    
624
            Properties Used:
625
            N/A    
626
        """          
627
        if key is None: key = value.tag
1×
628
        key = key.translate(t_table)
1×
629
        if key in self: raise KeyError('key "%s" already exists' % key)
1×
630
        self[key] = value        
1×
631
    
632
    def deep_set(self,keys,val):
1×
633
        """ Regresses through a list of keys the same value in various places in a dictionary.
634
    
635
            Assumptions:
636
            N/A
637
    
638
            Source:
639
            N/A
640
    
641
            Inputs:
642
            keys  - The keys to iterate over
643
            val   - The value to be set
644
    
645
            Outputs:
646
            N/A
647
    
648
            Properties Used:
649
            N/A    
650
        """               
651
        
652
        if isinstance(keys,str):
1×
653
            keys = keys.split('.')
1×
654
        
655
        data = self
1×
656
         
657
        if len(keys) > 1:
1×
658
            for k in keys[:-1]:
1×
659
                data = data[k]
1×
660
        
661
        if keys[-1][-1] ==']':
1×
UNCOV
662
            splitkey = keys[-1].split('[')
!
UNCOV
663
            thing = data[splitkey[0]]
!
UNCOV
664
            for ii in range(1,len(splitkey)-1):
!
UNCOV
665
                index    = int(splitkey[ii][:-1])
!
UNCOV
666
                thing = thing[index]
!
UNCOV
667
            index    = int(splitkey[-1][:-1])
!
UNCOV
668
            thing[index] = val
!
669
        else:
670
            data[ keys[-1] ] = val
1×
671
            
672
        return data
1×
673

674
    def deep_get(self,keys):
1×
675
        """ Regresses through a list of keys to pull a specific value out
676
    
677
            Assumptions:
678
            N/A
679
    
680
            Source:
681
            N/A
682
    
683
            Inputs:
684
            keys  - The keys to iterate over
685
            
686
            Outputs:
687
            value - The value to be retrieved
688
    
689
            Properties Used:
690
            N/A    
691
        """          
692
        
693
        if isinstance(keys,str):
1×
694
            keys = keys.split('.')
1×
695
        
696
        data = self
1×
697
         
698
        if len(keys) > 1:
1×
699
            for k in keys[:-1]:
1×
700
                data = data[k]
1×
701
        
702
        value = data[ keys[-1] ]
1×
703
        
704
        return value
1×
705
        
706
    def pack_array(self,output='vector'):
1×
707
        """ maps the data dict to a 1D vector or 2D column array
708
        
709
            Assumptions:
710
                will only pack int, float, np.array and np.matrix (max rank 2)
711
                if using output = 'matrix', all data values must have 
712
                same length (if 1D) or number of rows (if 2D), otherwise is skipped
713
    
714
            Source:
715
            N/A
716
    
717
            Inputs:
718
                output - either 'vector' (default), or 'array'
719
                         chooses whether to output a 1d vector or 
720
                         2d column array
721
            
722
            Outputs:
723
                array - the packed array
724
    
725
            Properties Used:
726
            N/A  
727
        
728
        """
729
        
730
        
731
        # check output type
732
        if not output in ('vector','array'): raise Exception('output type must be "vector" or "array"')        
1×
733
        vector = output == 'vector'
1×
734
        
735
        # list to pre-dump array elements
736
        M = []
1×
737
        
738
        # valid types for output
739
        valid_types = ( int, float,
1×
740
                        array_type,
741
                        matrix_type )
742
        
743
        # initialize array row size (for array output)
744
        size = [False]
1×
745
        
746
        # the packing function
747
        def do_pack(D):
1×
748
            for v in D.values(): 
1×
749
                try:
1×
750
                    rank = v.ndim
1×
751
                except:
1×
752
                    rank = 0
1×
753
                    
754
                # type checking
755
                if isinstance( v, dict ): 
1×
756
                    do_pack(v) # recursion!
1×
757
                    continue
1×
758
                elif not isinstance( v, valid_types ): continue
1×
759
                elif rank > 2: continue
1×
760
                # make column vectors
761
                v = atleast_2d_col(v)
1×
762
                # handle output type
763
                if vector:
1×
764
                    # unravel into 1d vector
765
                    v = v.ravel(order='F')
1×
766
                else:
767
                    # check array size
UNCOV
768
                    size[0] = size[0] or v.shape[0] # updates size once on first array
!
UNCOV
769
                    if v.shape[0] != size[0]: 
!
770
                        #warn ('array size mismatch, skipping. all values in data must have same number of rows for array packing',RuntimeWarning)
UNCOV
771
                        continue
!
772
                # dump to list
773
                M.append(v)
1×
774
            #: for each value
775
        
776
        # do the packing
777
        do_pack(self)
1×
778
        
779
        # pack into final array
780
        if M:
1×
781
            M = np.hstack(M)
1×
782
        else:
783
            # empty result
UNCOV
784
            if vector:
!
UNCOV
785
                M = np.array([])
!
786
            else:
UNCOV
787
                M = np.array([[]])
!
788
        
789
        # done!
790
        return M
1×
791
    
792
    def unpack_array(self,M):
1×
793
        """ unpacks an input 1d vector or 2d column array into the data dictionary
794
            important that the structure of the data dictionary, and the shapes
795
            of the contained values are the same as the data from which the array was packed
796
    
797
            Assumptions:
798
            N/A
799
    
800
            Source:
801
            N/A
802
    
803
            Inputs:
804
            M - either a 1D vector or 2D column array
805
            
806
            Outputs:
807
            a reference to self, updates self in place
808
    
809
            Properties Used:
810
            N/A    
811
        """           
812

813
        
814
        # dont require dict to have numpy
815
        import numpy as np
1×
816
        from .Arrays import atleast_2d_col, array_type, matrix_type
1×
817
        
818
        # check input type
819
        vector = M.ndim  == 1
1×
820
        
821
        # valid types for output
822
        valid_types = ( int, float,
1×
823
                        array_type,
824
                        matrix_type )
825
        
826
        # counter for unpacking
827
        _index = [0]
1×
828
        
829
        # the unpacking function
830
        def do_unpack(D):
1×
831
            for k,v in D.items():
1×
832
                try:
1×
833
                    rank = v.ndim
1×
834
                except:
1×
835
                    rank = 0
1×
836
                # type checking
837
                if isinstance(v, dict): 
1×
UNCOV
838
                    do_unpack(v) # recursion!
!
UNCOV
839
                    continue
!
840
                elif not isinstance(v,valid_types): continue
1×
841
                
842
                # get unpack index
843
                index = _index[0]                
1×
844
                
845
                # skip if too big
846
                if rank > 2: 
1×
UNCOV
847
                    continue
!
848
                
849
                # scalars
850
                elif rank == 0:
1×
851
                    if vector:
1×
852
                        D[k] = M[index]
1×
853
                        index += 1
1×
854
                    else:#array
UNCOV
855
                        continue
!
856
                        #raise RuntimeError , 'array size mismatch, all values in data must have same number of rows for array unpacking'
857
                    
858
                # 1d vectors
859
                elif rank == 1:
1×
860
                    n = len(v)
1×
861
                    if vector:
1×
862
                        D[k][:] = M[index:(index+n)]
1×
863
                        index += n
1×
864
                    else:#array
UNCOV
865
                        D[k][:] = M[:,index]
!
UNCOV
866
                        index += 1
!
867
                    
868
                # 2d arrays
869
                elif rank == 2:
1×
870
                    n,m = v.shape
1×
871
                    if vector:
1×
872
                        D[k][:,:] = np.reshape( M[index:(index+(n*m))] ,[n,m], order='F')
1×
873
                        index += n*m 
1×
874
                    else:#array
875
                        D[k][:,:] = M[:,index:(index+m)]
!
876
                        index += m
!
877
                
878
                #: switch rank
879
                
880
                _index[0] = index
1×
881

882
            #: for each itme
883
        
884
        # do the unpack
885
        do_unpack(self)
1×
886
         
887
        # check
888
        if not M.shape[-1] == _index[0]: warn('did not unpack all values',RuntimeWarning)
1×
889
         
890
        # done!
891
        return self     
1×
892
    
893
    def do_recursive(self,method,other=None,default=None):
1×
894
        """ Recursively applies a method of the class.
895
    
896
            Assumptions:
897
            N/A
898
    
899
            Source:
900
            N/A
901
    
902
            Inputs:
903
            method  - name of the method to access
904
    
905
            Outputs:
906
            Result  - the results of the method
907
    
908
            Properties Used:
909
            N/A    
910
        """          
911
    
912
        # result data structure
UNCOV
913
        klass = self.__class__
!
UNCOV
914
        if isinstance(klass,Data):
!
UNCOV
915
            klass = Data
!
UNCOV
916
        result = klass()
!
917
    
918
        # the update function
UNCOV
919
        def do_operation(A,B,C):
!
UNCOV
920
            for k,a in A.items():
!
UNCOV
921
                if isinstance(B,Data):
!
UNCOV
922
                    if k in B:
!
UNCOV
923
                        b = B[k]
!
924
                    else: 
UNCOV
925
                        C[k] = a
!
UNCOV
926
                        continue
!
927
                else:
UNCOV
928
                    b = B
!
929
                # recursion
UNCOV
930
                if isinstance(a,Data):
!
UNCOV
931
                    c = klass()
!
UNCOV
932
                    C[k] = c
!
UNCOV
933
                    do_operation(a,b,c)
!
934
                # method
935
                else:
UNCOV
936
                    if b is None:
!
UNCOV
937
                        c = method(a)
!
938
                    else:
UNCOV
939
                        c = method(a,b)
!
UNCOV
940
                    if not c is None:
!
UNCOV
941
                        C[k] = c
!
942
                #: if type
943
            #: for each key,value
944
    
945
        # do the update!
UNCOV
946
        do_operation(self,other,result)    
!
947
    
UNCOV
948
        return result
!
949

950
    
Troubleshooting · Open an Issue · Sales · Support · ENTERPRISE · CAREERS · STATUS
BLOG · TWITTER · Legal & Privacy · Supported CI Services · What's a CI service? · Automated Testing

© 2022 Coveralls, Inc