2
2
# @author: Federico Cerchiari <[email protected] >
3
3
"""Main Tempy classes"""
4
4
import html
5
+ from collections import deque , Iterable
5
6
from copy import copy
6
- from uuid import uuid4
7
- from itertools import chain
8
7
from functools import wraps
9
- from collections import deque
8
+ from itertools import chain
10
9
from types import GeneratorType
10
+ from uuid import uuid4
11
11
12
- from .exceptions import TagError , WrongContentError , DOMModByKeyError , DOMModByIndexError
12
+ from .exceptions import TagError , WrongContentError , WrongArgsError , DOMModByKeyError , DOMModByIndexError
13
13
from .tempyrepr import REPRFinder
14
14
15
15
16
16
class DOMGroup :
17
17
"""Wrapper used to manage element insertion."""
18
+
18
19
def __init__ (self , name , obj ):
19
20
super ().__init__ ()
20
21
if not name and issubclass (obj .__class__ , DOMElement ):
@@ -167,7 +168,7 @@ def __imul__(self, n):
167
168
if n == 0 :
168
169
self .parent .pop (self ._own_index )
169
170
return self
170
- return self .after (self * (n - 1 ))
171
+ return self .after (self * (n - 1 ))
171
172
172
173
def to_code (self , pretty = False ):
173
174
ret = []
@@ -252,6 +253,7 @@ def _iter_child_renders(self, pretty=False):
252
253
# this trick is used to avoid circular imports
253
254
class Patched (tempyREPR_cls , DOMElement ):
254
255
pass
256
+
255
257
child = Patched (child )
256
258
try :
257
259
yield child .render (pretty = pretty )
@@ -272,14 +274,17 @@ def content_receiver(reverse=False):
272
274
Takes args and kwargs and calls the decorated method one time for each argument provided.
273
275
The reverse parameter should be used for prepending (relative to self) methods.
274
276
"""
277
+
275
278
def _receiver (func ):
276
279
@wraps (func )
277
280
def wrapped (inst , * tags , ** kwtags ):
278
281
for i , dom_group in yield_domgroups (tags , kwtags , reverse ):
279
282
inst ._stable = False
280
283
func (inst , i , dom_group )
281
284
return inst
285
+
282
286
return wrapped
287
+
283
288
return _receiver
284
289
285
290
def _insert (self , dom_group , idx = None , prepend = False ):
@@ -298,7 +303,7 @@ def _insert(self, dom_group, idx=None, prepend=False):
298
303
for i_group , elem in enumerate (dom_group ):
299
304
if elem is not None :
300
305
# Element insertion in this DOMElement childs
301
- self .childs .insert (idx + i_group , elem )
306
+ self .childs .insert (idx + i_group , elem )
302
307
# Managing child attributes if needed
303
308
if issubclass (elem .__class__ , DOMElement ):
304
309
elem .parent = self
@@ -393,7 +398,6 @@ def append_to(self, father):
393
398
394
399
def wrap (self , other ):
395
400
"""Wraps this element inside another empty tag."""
396
- # TODO: make multiple with content_receiver
397
401
if other .childs :
398
402
raise TagError (self , 'Wrapping in a non empty Tag is forbidden.' )
399
403
if self .parent :
@@ -402,6 +406,51 @@ def wrap(self, other):
402
406
other .append (self )
403
407
return self
404
408
409
+ def wrap_many (self , * args , strict = False ):
410
+ """Wraps different copies of this element inside all empty tags
411
+ listed in params or param's (non-empty) iterators.
412
+
413
+ Returns list of copies of this element wrapped inside args
414
+ or None if not succeeded, in the same order and same structure,
415
+ i.e. args = (Div(), (Div())) -> value = (A(...), (A(...)))
416
+
417
+ If on some args it must raise TagError, it will only if strict is True,
418
+ otherwise it will do nothing with them and return Nones on their positions"""
419
+
420
+ for arg in args :
421
+ is_elem = arg and isinstance (arg , DOMElement )
422
+ is_elem_iter = (not is_elem and arg and isinstance (arg , Iterable ) and
423
+ isinstance (iter (arg ).__next__ (), DOMElement ))
424
+ if not (is_elem or is_elem_iter ):
425
+ raise WrongArgsError (self , 'Argument {} is not DOMElement nor iterable of DOMElements' .format (arg ))
426
+
427
+ wcopies = []
428
+ failure = []
429
+
430
+ def wrap_next (tag , idx ):
431
+ nonlocal wcopies , failure
432
+ next_copy = self .__copy__ ()
433
+ try :
434
+ return next_copy .wrap (tag )
435
+ except TagError :
436
+ failure .append (idx )
437
+ return next_copy
438
+
439
+ for arg_idx , arg in enumerate (args ):
440
+ if isinstance (arg , DOMElement ):
441
+ wcopies .append (wrap_next (arg , (arg_idx , - 1 )))
442
+ else :
443
+ iter_wcopies = []
444
+ for iter_idx , t in enumerate (arg ):
445
+ iter_wcopies .append (wrap_next (t , (arg_idx , iter_idx )))
446
+ wcopies .append (type (arg )(iter_wcopies ))
447
+
448
+ if failure and strict :
449
+ raise TagError (self , 'Wrapping in a non empty Tag is forbidden, failed on arguments ' +
450
+ ', ' .join (list (map (lambda idx : str (idx [0 ]) if idx [1 ] == - 1 else '[{1}] of {0}' .format (* idx ),
451
+ failure ))))
452
+ return wcopies
453
+
405
454
def wrap_inner (self , other ):
406
455
self .move_childs (other )
407
456
self (other )
@@ -599,7 +648,6 @@ def render(self, *args, **kwargs):
599
648
600
649
601
650
class Escaped (DOMElement ):
602
-
603
651
def __init__ (self , content , ** kwargs ):
604
652
super ().__init__ (** kwargs )
605
653
self ._render = content
0 commit comments