Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.11/site-packages/toolz/functoolz.py: 40%

Shortcuts on this page

r m x   toggle line displays

j k   next/prev highlighted chunk

0   (zero) top of page

1   (one) first highlighted chunk

459 statements  

1from functools import reduce, partial 

2import inspect 

3import sys 

4from operator import attrgetter, not_ 

5from importlib import import_module 

6from types import MethodType 

7 

8from .utils import no_default 

9 

10if sys.version_info >= (3, 14): 

11 import annotationlib # already imported by inspect 

12 

13 

14__all__ = ('identity', 'apply', 'thread_first', 'thread_last', 'memoize', 

15 'compose', 'compose_left', 'pipe', 'complement', 'juxt', 'do', 

16 'curry', 'flip', 'excepts') 

17 

18PYPY = hasattr(sys, 'pypy_version_info') 

19 

20 

21def identity(x): 

22 """ Identity function. Return x 

23 

24 >>> identity(3) 

25 3 

26 """ 

27 return x 

28 

29 

30def apply(*func_and_args, **kwargs): 

31 """ Applies a function and returns the results 

32 

33 >>> def double(x): return 2*x 

34 >>> def inc(x): return x + 1 

35 >>> apply(double, 5) 

36 10 

37 

38 >>> tuple(map(apply, [double, inc, double], [10, 500, 8000])) 

39 (20, 501, 16000) 

40 """ 

41 if not func_and_args: 

42 raise TypeError('func argument is required') 

43 func, args = func_and_args[0], func_and_args[1:] 

44 return func(*args, **kwargs) 

45 

46 

47def thread_first(val, *forms): 

48 """ Thread value through a sequence of functions/forms 

49 

50 >>> def double(x): return 2*x 

51 >>> def inc(x): return x + 1 

52 >>> thread_first(1, inc, double) 

53 4 

54 

55 If the function expects more than one input you can specify those inputs 

56 in a tuple. The value is used as the first input. 

57 

58 >>> def add(x, y): return x + y 

59 >>> def pow(x, y): return x**y 

60 >>> thread_first(1, (add, 4), (pow, 2)) # pow(add(1, 4), 2) 

61 25 

62 

63 So in general 

64 thread_first(x, f, (g, y, z)) 

65 expands to 

66 g(f(x), y, z) 

67 

68 See Also: 

69 thread_last 

70 """ 

71 def evalform_front(val, form): 

72 if callable(form): 

73 return form(val) 

74 if isinstance(form, tuple): 

75 func, args = form[0], form[1:] 

76 args = (val,) + args 

77 return func(*args) 

78 return reduce(evalform_front, forms, val) 

79 

80 

81def thread_last(val, *forms): 

82 """ Thread value through a sequence of functions/forms 

83 

84 >>> def double(x): return 2*x 

85 >>> def inc(x): return x + 1 

86 >>> thread_last(1, inc, double) 

87 4 

88 

89 If the function expects more than one input you can specify those inputs 

90 in a tuple. The value is used as the last input. 

91 

92 >>> def add(x, y): return x + y 

93 >>> def pow(x, y): return x**y 

94 >>> thread_last(1, (add, 4), (pow, 2)) # pow(2, add(4, 1)) 

95 32 

96 

97 So in general 

98 thread_last(x, f, (g, y, z)) 

99 expands to 

100 g(y, z, f(x)) 

101 

102 >>> def iseven(x): 

103 ... return x % 2 == 0 

104 >>> list(thread_last([1, 2, 3], (map, inc), (filter, iseven))) 

105 [2, 4] 

106 

107 See Also: 

108 thread_first 

109 """ 

110 def evalform_back(val, form): 

111 if callable(form): 

112 return form(val) 

113 if isinstance(form, tuple): 

114 func, args = form[0], form[1:] 

115 args = args + (val,) 

116 return func(*args) 

117 return reduce(evalform_back, forms, val) 

118 

119 

120def instanceproperty(fget=None, fset=None, fdel=None, doc=None, classval=None): 

121 """ Like @property, but returns ``classval`` when used as a class attribute 

122 

123 >>> class MyClass(object): 

124 ... '''The class docstring''' 

125 ... @instanceproperty(classval=__doc__) 

126 ... def __doc__(self): 

127 ... return 'An object docstring' 

128 ... @instanceproperty 

129 ... def val(self): 

130 ... return 42 

131 ... 

132 >>> MyClass.__doc__ 

133 'The class docstring' 

134 >>> MyClass.val is None 

135 True 

136 >>> obj = MyClass() 

137 >>> obj.__doc__ 

138 'An object docstring' 

139 >>> obj.val 

140 42 

141 """ 

142 if fget is None: 

143 return partial(instanceproperty, fset=fset, fdel=fdel, doc=doc, 

144 classval=classval) 

145 return InstanceProperty(fget=fget, fset=fset, fdel=fdel, doc=doc, 

146 classval=classval) 

147 

148 

149class InstanceProperty(property): 

150 """ Like @property, but returns ``classval`` when used as a class attribute 

151 

152 Should not be used directly. Use ``instanceproperty`` instead. 

153 """ 

154 def __init__(self, fget=None, fset=None, fdel=None, doc=None, 

155 classval=None): 

156 self.classval = classval 

157 property.__init__(self, fget=fget, fset=fset, fdel=fdel, doc=doc) 

158 

159 def __get__(self, obj, type=None): 

160 if obj is None: 

161 return self.classval 

162 return property.__get__(self, obj, type) 

163 

164 def __reduce__(self): 

165 state = (self.fget, self.fset, self.fdel, self.__doc__, self.classval) 

166 return InstanceProperty, state 

167 

168 

169class curry: 

170 """ Curry a callable function 

171 

172 Enables partial application of arguments through calling a function with an 

173 incomplete set of arguments. 

174 

175 >>> def mul(x, y): 

176 ... return x * y 

177 >>> mul = curry(mul) 

178 

179 >>> double = mul(2) 

180 >>> double(10) 

181 20 

182 

183 Also supports keyword arguments 

184 

185 >>> @curry # Can use curry as a decorator 

186 ... def f(x, y, a=10): 

187 ... return a * (x + y) 

188 

189 >>> add = f(a=1) 

190 >>> add(2, 3) 

191 5 

192 

193 See Also: 

194 toolz.curried - namespace of curried functions 

195 https://toolz.readthedocs.io/en/latest/curry.html 

196 """ 

197 def __init__(self, *args, **kwargs): 

198 if not args: 

199 raise TypeError('__init__() takes at least 2 arguments (1 given)') 

200 func, args = args[0], args[1:] 

201 if not callable(func): 

202 raise TypeError("Input must be callable") 

203 

204 # curry- or functools.partial-like object? Unpack and merge arguments 

205 if ( 

206 hasattr(func, 'func') 

207 and hasattr(func, 'args') 

208 and hasattr(func, 'keywords') 

209 and isinstance(func.args, tuple) 

210 ): 

211 _kwargs = {} 

212 if func.keywords: 

213 _kwargs.update(func.keywords) 

214 _kwargs.update(kwargs) 

215 kwargs = _kwargs 

216 args = func.args + args 

217 func = func.func 

218 

219 if kwargs: 

220 self._partial = partial(func, *args, **kwargs) 

221 else: 

222 self._partial = partial(func, *args) 

223 

224 self.__doc__ = getattr(func, '__doc__', None) 

225 self.__name__ = getattr(func, '__name__', '<curry>') 

226 self.__module__ = getattr(func, '__module__', None) 

227 self.__qualname__ = getattr(func, '__qualname__', None) 

228 self._sigspec = None 

229 self._has_unknown_args = None 

230 

231 @instanceproperty 

232 def func(self): 

233 return self._partial.func 

234 

235 @instanceproperty 

236 def __signature__(self): 

237 sig = inspect.signature(self.func) 

238 args = self.args or () 

239 keywords = self.keywords or {} 

240 if is_partial_args(self.func, args, keywords, sig) is False: 

241 raise TypeError('curry object has incorrect arguments') 

242 

243 params = list(sig.parameters.values()) 

244 skip = 0 

245 for param in params[:len(args)]: 

246 if param.kind == param.VAR_POSITIONAL: 

247 break 

248 skip += 1 

249 

250 kwonly = False 

251 newparams = [] 

252 for param in params[skip:]: 

253 kind = param.kind 

254 default = param.default 

255 if kind == param.VAR_KEYWORD: 

256 pass 

257 elif kind == param.VAR_POSITIONAL: 

258 if kwonly: 

259 continue 

260 elif param.name in keywords: 

261 default = keywords[param.name] 

262 kind = param.KEYWORD_ONLY 

263 kwonly = True 

264 else: 

265 if kwonly: 

266 kind = param.KEYWORD_ONLY 

267 if default is param.empty: 

268 default = no_default 

269 newparams.append(param.replace(default=default, kind=kind)) 

270 

271 return sig.replace(parameters=newparams) 

272 

273 @instanceproperty 

274 def args(self): 

275 return self._partial.args 

276 

277 @instanceproperty 

278 def keywords(self): 

279 return self._partial.keywords 

280 

281 @instanceproperty 

282 def func_name(self): 

283 return self.__name__ 

284 

285 def __str__(self): 

286 return str(self.func) 

287 

288 def __repr__(self): 

289 return repr(self.func) 

290 

291 def __hash__(self): 

292 return hash((self.func, self.args, 

293 frozenset(self.keywords.items()) if self.keywords 

294 else None)) 

295 

296 def __eq__(self, other): 

297 return (isinstance(other, curry) and self.func == other.func and 

298 self.args == other.args and self.keywords == other.keywords) 

299 

300 def __ne__(self, other): 

301 return not self.__eq__(other) 

302 

303 def __call__(self, *args, **kwargs): 

304 try: 

305 return self._partial(*args, **kwargs) 

306 except TypeError as exc: 

307 if self._should_curry(args, kwargs, exc): 

308 return self.bind(*args, **kwargs) 

309 raise 

310 

311 def _should_curry(self, args, kwargs, exc=None): 

312 func = self.func 

313 args = self.args + args 

314 if self.keywords: 

315 kwargs = dict(self.keywords, **kwargs) 

316 if self._sigspec is None: 

317 sigspec = self._sigspec = _sigs.signature_or_spec(func) 

318 self._has_unknown_args = has_varargs(func, sigspec) is not False 

319 else: 

320 sigspec = self._sigspec 

321 

322 if is_partial_args(func, args, kwargs, sigspec) is False: 

323 # Nothing can make the call valid 

324 return False 

325 elif self._has_unknown_args: 

326 # The call may be valid and raised a TypeError, but we curry 

327 # anyway because the function may have `*args`. This is useful 

328 # for decorators with signature `func(*args, **kwargs)`. 

329 return True 

330 elif not is_valid_args(func, args, kwargs, sigspec): 

331 # Adding more arguments may make the call valid 

332 return True 

333 else: 

334 # There was a genuine TypeError 

335 return False 

336 

337 def bind(self, *args, **kwargs): 

338 return type(self)(self, *args, **kwargs) 

339 

340 def call(self, *args, **kwargs): 

341 return self._partial(*args, **kwargs) 

342 

343 def __get__(self, instance, owner): 

344 if instance is None: 

345 return self 

346 return curry(self, instance) 

347 

348 def __reduce__(self): 

349 func = self.func 

350 modname = getattr(func, '__module__', None) 

351 qualname = getattr(func, '__qualname__', None) 

352 if qualname is None: # pragma: no cover 

353 qualname = getattr(func, '__name__', None) 

354 is_decorated = None 

355 if modname and qualname: 

356 attrs = [] 

357 obj = import_module(modname) 

358 for attr in qualname.split('.'): 

359 if isinstance(obj, curry): 

360 attrs.append('func') 

361 obj = obj.func 

362 obj = getattr(obj, attr, None) 

363 if obj is None: 

364 break 

365 attrs.append(attr) 

366 if isinstance(obj, curry) and obj.func is func: 

367 is_decorated = obj is self 

368 qualname = '.'.join(attrs) 

369 func = f'{modname}:{qualname}' 

370 

371 # functools.partial objects can't be pickled 

372 userdict = tuple((k, v) for k, v in self.__dict__.items() 

373 if k not in ('_partial', '_sigspec')) 

374 state = (type(self), func, self.args, self.keywords, userdict, 

375 is_decorated) 

376 return _restore_curry, state 

377 

378 

379def _restore_curry(cls, func, args, kwargs, userdict, is_decorated): 

380 if isinstance(func, str): 

381 modname, qualname = func.rsplit(':', 1) 

382 obj = import_module(modname) 

383 for attr in qualname.split('.'): 

384 obj = getattr(obj, attr) 

385 if is_decorated: 

386 return obj 

387 func = obj.func 

388 obj = cls(func, *args, **(kwargs or {})) 

389 obj.__dict__.update(userdict) 

390 return obj 

391 

392 

393@curry 

394def memoize(func, cache=None, key=None): 

395 """ Cache a function's result for speedy future evaluation 

396 

397 Considerations: 

398 Trades memory for speed. 

399 Only use on pure functions. 

400 

401 >>> def add(x, y): return x + y 

402 >>> add = memoize(add) 

403 

404 Or use as a decorator 

405 

406 >>> @memoize 

407 ... def add(x, y): 

408 ... return x + y 

409 

410 Use the ``cache`` keyword to provide a dict-like object as an initial cache 

411 

412 >>> @memoize(cache={(1, 2): 3}) 

413 ... def add(x, y): 

414 ... return x + y 

415 

416 Note that the above works as a decorator because ``memoize`` is curried. 

417 

418 It is also possible to provide a ``key(args, kwargs)`` function that 

419 calculates keys used for the cache, which receives an ``args`` tuple and 

420 ``kwargs`` dict as input, and must return a hashable value. However, 

421 the default key function should be sufficient most of the time. 

422 

423 >>> # Use key function that ignores extraneous keyword arguments 

424 >>> @memoize(key=lambda args, kwargs: args) 

425 ... def add(x, y, verbose=False): 

426 ... if verbose: 

427 ... print('Calculating %s + %s' % (x, y)) 

428 ... return x + y 

429 """ 

430 if cache is None: 

431 cache = {} 

432 

433 try: 

434 may_have_kwargs = has_keywords(func) is not False 

435 # Is unary function (single arg, no variadic argument or keywords)? 

436 is_unary = is_arity(1, func) 

437 except TypeError: # pragma: no cover 

438 may_have_kwargs = True 

439 is_unary = False 

440 

441 if key is None: 

442 if is_unary: 

443 def key(args, kwargs): 

444 return args[0] 

445 elif may_have_kwargs: 

446 def key(args, kwargs): 

447 return ( 

448 args or None, 

449 frozenset(kwargs.items()) if kwargs else None, 

450 ) 

451 else: 

452 def key(args, kwargs): 

453 return args 

454 

455 def memof(*args, **kwargs): 

456 k = key(args, kwargs) 

457 try: 

458 return cache[k] 

459 except TypeError: 

460 raise TypeError("Arguments to memoized function must be hashable") 

461 except KeyError: 

462 cache[k] = result = func(*args, **kwargs) 

463 return result 

464 

465 try: 

466 memof.__name__ = func.__name__ 

467 except AttributeError: 

468 pass 

469 memof.__doc__ = func.__doc__ 

470 memof.__wrapped__ = func 

471 return memof 

472 

473 

474class _InstanceAnnotations(dict): 

475 """ Descriptor for an ``__annotations__`` computed by ``fget`` on instances 

476 

477 Accessed on the class, this is an empty dict, because ``typing`` and 

478 ``inspect`` require a class's ``__annotations__`` to be a dict (before 

479 Python 3.14 they read it directly from the class ``__dict__``). 

480 """ 

481 __slots__ = ('fget',) 

482 

483 def __init__(self, fget): 

484 self.fget = fget 

485 

486 def __get__(self, obj, type=None): 

487 return self if obj is None else self.fget(obj) 

488 

489 

490class Compose: 

491 """ A composition of functions 

492 

493 See Also: 

494 compose 

495 """ 

496 __slots__ = 'first', 'funcs' 

497 

498 def __init__(self, funcs): 

499 funcs = tuple(reversed(funcs)) 

500 self.first = funcs[0] 

501 self.funcs = funcs[1:] 

502 

503 def __call__(self, *args, **kwargs): 

504 ret = self.first(*args, **kwargs) 

505 for f in self.funcs: 

506 ret = f(ret) 

507 return ret 

508 

509 def __getstate__(self): 

510 return self.first, self.funcs 

511 

512 def __setstate__(self, state): 

513 self.first, self.funcs = state 

514 

515 @instanceproperty(classval=__doc__) 

516 def __doc__(self): 

517 def composed_doc(*fs): 

518 """Generate a docstring for the composition of fs. 

519 """ 

520 if not fs: 

521 # Argument name for the docstring. 

522 return '*args, **kwargs' 

523 

524 return f'{fs[0].__name__}({composed_doc(*fs[1:])})' 

525 

526 try: 

527 return ( 

528 'lambda *args, **kwargs: ' + 

529 composed_doc(*reversed((self.first,) + self.funcs)) 

530 ) 

531 except AttributeError: 

532 # One of our callables does not have a `__name__`, whatever. 

533 return 'A composition of functions' 

534 

535 @property 

536 def __name__(self): 

537 try: 

538 return '_of_'.join( 

539 f.__name__ for f in reversed((self.first,) + self.funcs) 

540 ) 

541 except AttributeError: 

542 return type(self).__name__ 

543 

544 def __repr__(self): 

545 return '{.__class__.__name__}{!r}'.format( 

546 self, tuple(reversed((self.first, ) + self.funcs))) 

547 

548 def __eq__(self, other): 

549 if isinstance(other, Compose): 

550 return other.first == self.first and other.funcs == self.funcs 

551 return NotImplemented 

552 

553 def __ne__(self, other): 

554 equality = self.__eq__(other) 

555 return NotImplemented if equality is NotImplemented else not equality 

556 

557 def __hash__(self): 

558 return hash(self.first) ^ hash(self.funcs) 

559 

560 # Mimic the descriptor behavior of python functions. 

561 # i.e. let Compose be called as a method when bound to a class. 

562 # adapted from 

563 # docs.python.org/3/howto/descriptor.html#functions-and-methods 

564 def __get__(self, obj, objtype=None): 

565 return self if obj is None else MethodType(self, obj) 

566 

567 # introspection with Signature is only possible from py3.3+ 

568 @instanceproperty 

569 def __signature__(self): 

570 base = inspect.signature(self.first) 

571 last = inspect.signature(self.funcs[-1]) 

572 return base.replace(return_annotation=last.return_annotation) 

573 

574 __wrapped__ = instanceproperty(attrgetter('first')) 

575 

576 def _combined_annotations(self, annotation_format=None): 

577 """ Combined type annotations for the composed callable. 

578 

579 Parameter annotations come from the first function applied in the 

580 composition, and the return annotation comes from the last function 

581 applied, as ``inspect.signature`` reports them (so this agrees with 

582 ``__signature__``). Functions without a signature contribute nothing. 

583 

584 ``annotation_format`` (Python 3.14+) is passed to 

585 ``inspect.signature``. With ``FORWARDREF`` or ``STRING``, which must 

586 not raise ``NameError``, a function whose ``__signature__`` evaluates 

587 annotations eagerly (e.g. ``curry``) and fails also contributes 

588 nothing. 

589 

590 String annotations are returned as-is; ``typing.get_type_hints`` 

591 evaluates them in the namespace of the first function. 

592 """ 

593 if annotation_format is None: 

594 kwargs = {} 

595 lazy = False 

596 else: 

597 kwargs = {'annotation_format': annotation_format} 

598 lazy = annotation_format != annotationlib.Format.VALUE 

599 

600 def annotations_of(func): 

601 if isinstance(func, Compose): 

602 return func._combined_annotations(annotation_format) 

603 try: 

604 sig = inspect.signature(func, **kwargs) 

605 except (TypeError, ValueError): 

606 return {} 

607 except NameError: 

608 if not lazy: 

609 raise 

610 return {} 

611 rv = {name: param.annotation 

612 for name, param in sig.parameters.items() 

613 if param.annotation is not param.empty} 

614 if sig.return_annotation is not sig.empty: 

615 rv['return'] = sig.return_annotation 

616 return rv 

617 

618 annotations = annotations_of(self.first) 

619 annotations.pop('return', None) 

620 last = self.funcs[-1] if self.funcs else self.first 

621 last_annotations = annotations_of(last) 

622 if 'return' in last_annotations: 

623 annotations['return'] = last_annotations['return'] 

624 return annotations 

625 

626 # Lets ``typing.get_type_hints`` and ``inspect.get_annotations`` see the 

627 # same types as ``__signature__``. 

628 __annotations__ = _InstanceAnnotations(_combined_annotations) 

629 

630 if sys.version_info >= (3, 14): 

631 def _get_annotate(self): 

632 def annotate(format): 

633 Format = annotationlib.Format 

634 if format == Format.STRING: 

635 # Some ``__signature__`` (e.g. ``curry``) ignore the 

636 # format and give values, so stringify what we got. 

637 return annotationlib.annotations_to_string( 

638 self._combined_annotations(format) 

639 ) 

640 if format in (Format.VALUE, Format.FORWARDREF): 

641 return self._combined_annotations(format) 

642 raise NotImplementedError(format) 

643 return annotate 

644 

645 # PEP 649/749: support the other annotation formats (``FORWARDREF``, 

646 # ``STRING``) and let ``functools.wraps`` copy the annotations. 

647 # Not ``def __annotate__``: coverage.py never traces functions with 

648 # that name, since it assumes they are compiler-generated. 

649 __annotate__ = instanceproperty(_get_annotate) 

650 

651 

652def compose(*funcs): 

653 """ Compose functions to operate in series. 

654 

655 Returns a function that applies other functions in sequence. 

656 

657 Functions are applied from right to left so that 

658 ``compose(f, g, h)(x, y)`` is the same as ``f(g(h(x, y)))``. 

659 

660 If no arguments are provided, the identity function (f(x) = x) is returned. 

661 

662 >>> inc = lambda i: i + 1 

663 >>> compose(str, inc)(3) 

664 '4' 

665 

666 See Also: 

667 compose_left 

668 pipe 

669 """ 

670 if not funcs: 

671 return identity 

672 if len(funcs) == 1: 

673 return funcs[0] 

674 else: 

675 return Compose(funcs) 

676 

677 

678def compose_left(*funcs): 

679 """ Compose functions to operate in series. 

680 

681 Returns a function that applies other functions in sequence. 

682 

683 Functions are applied from left to right so that 

684 ``compose_left(f, g, h)(x, y)`` is the same as ``h(g(f(x, y)))``. 

685 

686 If no arguments are provided, the identity function (f(x) = x) is returned. 

687 

688 >>> inc = lambda i: i + 1 

689 >>> compose_left(inc, str)(3) 

690 '4' 

691 

692 See Also: 

693 compose 

694 pipe 

695 """ 

696 return compose(*reversed(funcs)) 

697 

698 

699def pipe(data, *funcs): 

700 """ Pipe a value through a sequence of functions 

701 

702 I.e. ``pipe(data, f, g, h)`` is equivalent to ``h(g(f(data)))`` 

703 

704 We think of the value as progressing through a pipe of several 

705 transformations, much like pipes in UNIX 

706 

707 ``$ cat data | f | g | h`` 

708 

709 >>> double = lambda i: 2 * i 

710 >>> pipe(3, double, str) 

711 '6' 

712 

713 See Also: 

714 compose 

715 compose_left 

716 thread_first 

717 thread_last 

718 """ 

719 for func in funcs: 

720 data = func(data) 

721 return data 

722 

723 

724def complement(func): 

725 """ Convert a predicate function to its logical complement. 

726 

727 In other words, return a function that, for inputs that normally 

728 yield True, yields False, and vice-versa. 

729 

730 >>> def iseven(n): return n % 2 == 0 

731 >>> isodd = complement(iseven) 

732 >>> iseven(2) 

733 True 

734 >>> isodd(2) 

735 False 

736 """ 

737 return compose(not_, func) 

738 

739 

740class juxt: 

741 """ Creates a function that calls several functions with the same arguments 

742 

743 Takes several functions and returns a function that applies its arguments 

744 to each of those functions then returns a tuple of the results. 

745 

746 Name comes from juxtaposition: the fact of two things being seen or placed 

747 close together with contrasting effect. 

748 

749 >>> inc = lambda x: x + 1 

750 >>> double = lambda x: x * 2 

751 >>> juxt(inc, double)(10) 

752 (11, 20) 

753 >>> juxt([inc, double])(10) 

754 (11, 20) 

755 """ 

756 __slots__ = ['funcs'] 

757 

758 def __init__(self, *funcs): 

759 if len(funcs) == 1 and not callable(funcs[0]): 

760 funcs = funcs[0] 

761 self.funcs = tuple(funcs) 

762 

763 def __call__(self, *args, **kwargs): 

764 return tuple(func(*args, **kwargs) for func in self.funcs) 

765 

766 def __getstate__(self): 

767 return self.funcs 

768 

769 def __setstate__(self, state): 

770 self.funcs = state 

771 

772 

773def do(func, x): 

774 """ Runs ``func`` on ``x``, returns ``x`` 

775 

776 Because the results of ``func`` are not returned, only the side 

777 effects of ``func`` are relevant. 

778 

779 Logging functions can be made by composing ``do`` with a storage function 

780 like ``list.append`` or ``file.write`` 

781 

782 >>> from toolz import compose 

783 >>> from toolz.curried import do 

784 

785 >>> log = [] 

786 >>> inc = lambda x: x + 1 

787 >>> inc = compose(inc, do(log.append)) 

788 >>> inc(1) 

789 2 

790 >>> inc(11) 

791 12 

792 >>> log 

793 [1, 11] 

794 """ 

795 func(x) 

796 return x 

797 

798 

799@curry 

800def flip(func, a, b): 

801 """ Call the function call with the arguments flipped 

802 

803 This function is curried. 

804 

805 >>> def div(a, b): 

806 ... return a // b 

807 ... 

808 >>> flip(div, 2, 6) 

809 3 

810 >>> div_by_two = flip(div, 2) 

811 >>> div_by_two(4) 

812 2 

813 

814 This is particularly useful for built in functions and functions defined 

815 in C extensions that accept positional only arguments. For example: 

816 isinstance, issubclass. 

817 

818 >>> data = [1, 'a', 'b', 2, 1.5, object(), 3] 

819 >>> only_ints = list(filter(flip(isinstance, int), data)) 

820 >>> only_ints 

821 [1, 2, 3] 

822 """ 

823 return func(b, a) 

824 

825 

826def return_none(exc): 

827 """ Returns None. 

828 """ 

829 return None 

830 

831 

832class excepts: 

833 """A wrapper around a function to catch exceptions and 

834 dispatch to a handler. 

835 

836 This is like a functional try/except block, in the same way that 

837 ifexprs are functional if/else blocks. 

838 

839 Examples 

840 -------- 

841 >>> excepting = excepts( 

842 ... ValueError, 

843 ... lambda a: [1, 2].index(a), 

844 ... lambda _: -1, 

845 ... ) 

846 >>> excepting(1) 

847 0 

848 >>> excepting(3) 

849 -1 

850 

851 Multiple exceptions and default except clause. 

852 

853 >>> excepting = excepts((IndexError, KeyError), lambda a: a[0]) 

854 >>> excepting([]) 

855 >>> excepting([1]) 

856 1 

857 >>> excepting({}) 

858 >>> excepting({0: 1}) 

859 1 

860 """ 

861 def __init__(self, exc, func, handler=return_none): 

862 self.exc = exc 

863 self.func = func 

864 self.handler = handler 

865 

866 def __call__(self, *args, **kwargs): 

867 try: 

868 return self.func(*args, **kwargs) 

869 except self.exc as e: 

870 return self.handler(e) 

871 

872 @instanceproperty(classval=__doc__) 

873 def __doc__(self): 

874 from textwrap import dedent 

875 

876 exc = self.exc 

877 try: 

878 if isinstance(exc, tuple): 

879 exc_name = '(%s)' % ', '.join( 

880 map(attrgetter('__name__'), exc), 

881 ) 

882 else: 

883 exc_name = exc.__name__ 

884 

885 return dedent( 

886 """\ 

887 A wrapper around {inst.func.__name__!r} that will except: 

888 {exc} 

889 and handle any exceptions with {inst.handler.__name__!r}. 

890 

891 Docs for {inst.func.__name__!r}: 

892 {inst.func.__doc__} 

893 

894 Docs for {inst.handler.__name__!r}: 

895 {inst.handler.__doc__} 

896 """ 

897 ).format( 

898 inst=self, 

899 exc=exc_name, 

900 ) 

901 except AttributeError: 

902 return type(self).__doc__ 

903 

904 @property 

905 def __name__(self): 

906 exc = self.exc 

907 try: 

908 if isinstance(exc, tuple): 

909 exc_name = '_or_'.join(map(attrgetter('__name__'), exc)) 

910 else: 

911 exc_name = exc.__name__ 

912 return f'{self.func.__name__}_excepting_{exc_name}' 

913 except AttributeError: 

914 return 'excepting' 

915 

916 

917def _check_sigspec(sigspec, func, builtin_func, *builtin_args): 

918 if sigspec is None: 

919 try: 

920 sigspec = inspect.signature(func) 

921 except (ValueError, TypeError) as e: 

922 sigspec = e 

923 if isinstance(sigspec, ValueError): 

924 return None, builtin_func(*builtin_args) 

925 elif not isinstance(sigspec, inspect.Signature): 

926 if ( 

927 func in _sigs.signatures 

928 and ( 

929 hasattr(func, '__signature__') 

930 and hasattr(func.__signature__, '__get__') 

931 ) 

932 ): 

933 val = builtin_func(*builtin_args) 

934 return None, val 

935 return None, False 

936 return sigspec, None 

937 

938 

939if PYPY: # pragma: no cover 

940 _check_sigspec_orig = _check_sigspec 

941 

942 def _check_sigspec(sigspec, func, builtin_func, *builtin_args): 

943 # PyPy may lie, so use our registry for builtins instead 

944 if func in _sigs.signatures: 

945 val = builtin_func(*builtin_args) 

946 return None, val 

947 return _check_sigspec_orig(sigspec, func, builtin_func, *builtin_args) 

948 

949 

950_check_sigspec.__doc__ = """ \ 

951Private function to aid in introspection compatibly across Python versions. 

952 

953If a callable doesn't have a signature (Python 3) or an argspec (Python 2), 

954the signature registry in toolz._signatures is used. 

955""" 

956 

957 

958def num_required_args(func, sigspec=None): 

959 sigspec, rv = _check_sigspec(sigspec, func, _sigs._num_required_args, 

960 func) 

961 if sigspec is None: 

962 return rv 

963 return sum(1 for p in sigspec.parameters.values() 

964 if p.default is p.empty 

965 and p.kind in (p.POSITIONAL_OR_KEYWORD, p.POSITIONAL_ONLY)) 

966 

967 

968def has_varargs(func, sigspec=None): 

969 sigspec, rv = _check_sigspec(sigspec, func, _sigs._has_varargs, func) 

970 if sigspec is None: 

971 return rv 

972 return any(p.kind == p.VAR_POSITIONAL 

973 for p in sigspec.parameters.values()) 

974 

975 

976def has_keywords(func, sigspec=None): 

977 sigspec, rv = _check_sigspec(sigspec, func, _sigs._has_keywords, func) 

978 if sigspec is None: 

979 return rv 

980 return any(p.default is not p.empty 

981 or p.kind in (p.KEYWORD_ONLY, p.VAR_KEYWORD) 

982 for p in sigspec.parameters.values()) 

983 

984 

985def is_valid_args(func, args, kwargs, sigspec=None): 

986 sigspec, rv = _check_sigspec(sigspec, func, _sigs._is_valid_args, 

987 func, args, kwargs) 

988 if sigspec is None: 

989 return rv 

990 try: 

991 sigspec.bind(*args, **kwargs) 

992 except TypeError: 

993 return False 

994 return True 

995 

996 

997def is_partial_args(func, args, kwargs, sigspec=None): 

998 sigspec, rv = _check_sigspec(sigspec, func, _sigs._is_partial_args, 

999 func, args, kwargs) 

1000 if sigspec is None: 

1001 return rv 

1002 try: 

1003 sigspec.bind_partial(*args, **kwargs) 

1004 except TypeError: 

1005 return False 

1006 return True 

1007 

1008 

1009def is_arity(n, func, sigspec=None): 

1010 """ Does a function have only n positional arguments? 

1011 

1012 This function relies on introspection and does not call the function. 

1013 Returns None if validity can't be determined. 

1014 

1015 >>> def f(x): 

1016 ... return x 

1017 >>> is_arity(1, f) 

1018 True 

1019 >>> def g(x, y=1): 

1020 ... return x + y 

1021 >>> is_arity(1, g) 

1022 False 

1023 """ 

1024 sigspec, rv = _check_sigspec(sigspec, func, _sigs._is_arity, n, func) 

1025 if sigspec is None: 

1026 return rv 

1027 num = num_required_args(func, sigspec) 

1028 if num is not None: 

1029 num = num == n 

1030 if not num: 

1031 return False 

1032 varargs = has_varargs(func, sigspec) 

1033 if varargs: 

1034 return False 

1035 keywords = has_keywords(func, sigspec) 

1036 if keywords: 

1037 return False 

1038 if num is None or varargs is None or keywords is None: # pragma: no cover 

1039 return None 

1040 return True 

1041 

1042 

1043num_required_args.__doc__ = """ \ 

1044Number of required positional arguments 

1045 

1046 This function relies on introspection and does not call the function. 

1047 Returns None if validity can't be determined. 

1048 

1049 >>> def f(x, y, z=3): 

1050 ... return x + y + z 

1051 >>> num_required_args(f) 

1052 2 

1053 >>> def g(*args, **kwargs): 

1054 ... pass 

1055 >>> num_required_args(g) 

1056 0 

1057 """ 

1058 

1059has_varargs.__doc__ = """ \ 

1060Does a function have variadic positional arguments? 

1061 

1062 This function relies on introspection and does not call the function. 

1063 Returns None if validity can't be determined. 

1064 

1065 >>> def f(*args): 

1066 ... return args 

1067 >>> has_varargs(f) 

1068 True 

1069 >>> def g(**kwargs): 

1070 ... return kwargs 

1071 >>> has_varargs(g) 

1072 False 

1073 """ 

1074 

1075has_keywords.__doc__ = """ \ 

1076Does a function have keyword arguments? 

1077 

1078 This function relies on introspection and does not call the function. 

1079 Returns None if validity can't be determined. 

1080 

1081 >>> def f(x, y=0): 

1082 ... return x + y 

1083 

1084 >>> has_keywords(f) 

1085 True 

1086 """ 

1087 

1088is_valid_args.__doc__ = """ \ 

1089Is ``func(*args, **kwargs)`` a valid function call? 

1090 

1091 This function relies on introspection and does not call the function. 

1092 Returns None if validity can't be determined. 

1093 

1094 >>> def add(x, y): 

1095 ... return x + y 

1096 

1097 >>> is_valid_args(add, (1,), {}) 

1098 False 

1099 >>> is_valid_args(add, (1, 2), {}) 

1100 True 

1101 >>> is_valid_args(map, (), {}) 

1102 False 

1103 

1104 **Implementation notes** 

1105 Python 2 relies on ``inspect.getargspec``, which only works for 

1106 user-defined functions. Python 3 uses ``inspect.signature``, which 

1107 works for many more types of callables. 

1108 

1109 Many builtins in the standard library are also supported. 

1110 """ 

1111 

1112is_partial_args.__doc__ = """ \ 

1113Can partial(func, *args, **kwargs)(*args2, **kwargs2) be a valid call? 

1114 

1115 Returns True *only* if the call is valid or if it is possible for the 

1116 call to become valid by adding more positional or keyword arguments. 

1117 

1118 This function relies on introspection and does not call the function. 

1119 Returns None if validity can't be determined. 

1120 

1121 >>> def add(x, y): 

1122 ... return x + y 

1123 

1124 >>> is_partial_args(add, (1,), {}) 

1125 True 

1126 >>> is_partial_args(add, (1, 2), {}) 

1127 True 

1128 >>> is_partial_args(add, (1, 2, 3), {}) 

1129 False 

1130 >>> is_partial_args(map, (), {}) 

1131 True 

1132 

1133 **Implementation notes** 

1134 Python 2 relies on ``inspect.getargspec``, which only works for 

1135 user-defined functions. Python 3 uses ``inspect.signature``, which 

1136 works for many more types of callables. 

1137 

1138 Many builtins in the standard library are also supported. 

1139 """ 

1140 

1141from . import _signatures as _sigs