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

412 statements  

« prev     ^ index     » next       coverage.py v7.2.2, created at 2023-03-26 06:47 +0000

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 

10PYPY = hasattr(sys, 'pypy_version_info') and sys.version_info[0] > 2 

11 

12 

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

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

15 'curry', 'flip', 'excepts') 

16 

17PYPY = hasattr(sys, 'pypy_version_info') 

18 

19 

20def identity(x): 

21 """ Identity function. Return x 

22 

23 >>> identity(3) 

24 3 

25 """ 

26 return x 

27 

28 

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

30 """ Applies a function and returns the results 

31 

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

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

34 >>> apply(double, 5) 

35 10 

36 

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

38 (20, 501, 16000) 

39 """ 

40 if not func_and_args: 

41 raise TypeError('func argument is required') 

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

43 return func(*args, **kwargs) 

44 

45 

46def thread_first(val, *forms): 

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

48 

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

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

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

52 4 

53 

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

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

56 

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

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

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

60 25 

61 

62 So in general 

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

64 expands to 

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

66 

67 See Also: 

68 thread_last 

69 """ 

70 def evalform_front(val, form): 

71 if callable(form): 

72 return form(val) 

73 if isinstance(form, tuple): 

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

75 args = (val,) + args 

76 return func(*args) 

77 return reduce(evalform_front, forms, val) 

78 

79 

80def thread_last(val, *forms): 

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

82 

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

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

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

86 4 

87 

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

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

90 

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

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

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

94 32 

95 

96 So in general 

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

98 expands to 

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

100 

101 >>> def iseven(x): 

102 ... return x % 2 == 0 

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

104 [2, 4] 

105 

106 See Also: 

107 thread_first 

108 """ 

109 def evalform_back(val, form): 

110 if callable(form): 

111 return form(val) 

112 if isinstance(form, tuple): 

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

114 args = args + (val,) 

115 return func(*args) 

116 return reduce(evalform_back, forms, val) 

117 

118 

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

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

121 

122 >>> class MyClass(object): 

123 ... '''The class docstring''' 

124 ... @instanceproperty(classval=__doc__) 

125 ... def __doc__(self): 

126 ... return 'An object docstring' 

127 ... @instanceproperty 

128 ... def val(self): 

129 ... return 42 

130 ... 

131 >>> MyClass.__doc__ 

132 'The class docstring' 

133 >>> MyClass.val is None 

134 True 

135 >>> obj = MyClass() 

136 >>> obj.__doc__ 

137 'An object docstring' 

138 >>> obj.val 

139 42 

140 """ 

141 if fget is None: 

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

143 classval=classval) 

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

145 classval=classval) 

146 

147 

148class InstanceProperty(property): 

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

150 

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

152 """ 

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

154 classval=None): 

155 self.classval = classval 

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

157 

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

159 if obj is None: 

160 return self.classval 

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

162 

163 def __reduce__(self): 

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

165 return InstanceProperty, state 

166 

167 

168class curry(object): 

169 """ Curry a callable function 

170 

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

172 incomplete set of arguments. 

173 

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

175 ... return x * y 

176 >>> mul = curry(mul) 

177 

178 >>> double = mul(2) 

179 >>> double(10) 

180 20 

181 

182 Also supports keyword arguments 

183 

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

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

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

187 

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

189 >>> add(2, 3) 

190 5 

191 

192 See Also: 

193 toolz.curried - namespace of curried functions 

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

195 """ 

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

197 if not args: 

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

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

200 if not callable(func): 

201 raise TypeError("Input must be callable") 

202 

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

204 if ( 

205 hasattr(func, 'func') 

206 and hasattr(func, 'args') 

207 and hasattr(func, 'keywords') 

208 and isinstance(func.args, tuple) 

209 ): 

210 _kwargs = {} 

211 if func.keywords: 

212 _kwargs.update(func.keywords) 

213 _kwargs.update(kwargs) 

214 kwargs = _kwargs 

215 args = func.args + args 

216 func = func.func 

217 

218 if kwargs: 

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

220 else: 

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

222 

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

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

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

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

227 self._sigspec = None 

228 self._has_unknown_args = None 

229 

230 @instanceproperty 

231 def func(self): 

232 return self._partial.func 

233 

234 @instanceproperty 

235 def __signature__(self): 

236 sig = inspect.signature(self.func) 

237 args = self.args or () 

238 keywords = self.keywords or {} 

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

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

241 

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

243 skip = 0 

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

245 if param.kind == param.VAR_POSITIONAL: 

246 break 

247 skip += 1 

248 

249 kwonly = False 

250 newparams = [] 

251 for param in params[skip:]: 

252 kind = param.kind 

253 default = param.default 

254 if kind == param.VAR_KEYWORD: 

255 pass 

256 elif kind == param.VAR_POSITIONAL: 

257 if kwonly: 

258 continue 

259 elif param.name in keywords: 

260 default = keywords[param.name] 

261 kind = param.KEYWORD_ONLY 

262 kwonly = True 

263 else: 

264 if kwonly: 

265 kind = param.KEYWORD_ONLY 

266 if default is param.empty: 

267 default = no_default 

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

269 

270 return sig.replace(parameters=newparams) 

271 

272 @instanceproperty 

273 def args(self): 

274 return self._partial.args 

275 

276 @instanceproperty 

277 def keywords(self): 

278 return self._partial.keywords 

279 

280 @instanceproperty 

281 def func_name(self): 

282 return self.__name__ 

283 

284 def __str__(self): 

285 return str(self.func) 

286 

287 def __repr__(self): 

288 return repr(self.func) 

289 

290 def __hash__(self): 

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

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

293 else None)) 

294 

295 def __eq__(self, other): 

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

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

298 

299 def __ne__(self, other): 

300 return not self.__eq__(other) 

301 

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

303 try: 

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

305 except TypeError as exc: 

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

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

308 raise 

309 

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

311 func = self.func 

312 args = self.args + args 

313 if self.keywords: 

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

315 if self._sigspec is None: 

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

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

318 else: 

319 sigspec = self._sigspec 

320 

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

322 # Nothing can make the call valid 

323 return False 

324 elif self._has_unknown_args: 

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

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

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

328 return True 

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

330 # Adding more arguments may make the call valid 

331 return True 

332 else: 

333 # There was a genuine TypeError 

334 return False 

335 

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

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

338 

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

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

341 

342 def __get__(self, instance, owner): 

343 if instance is None: 

344 return self 

345 return curry(self, instance) 

346 

347 def __reduce__(self): 

348 func = self.func 

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

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

351 if qualname is None: # pragma: no cover 

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

353 is_decorated = None 

354 if modname and qualname: 

355 attrs = [] 

356 obj = import_module(modname) 

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

358 if isinstance(obj, curry): 

359 attrs.append('func') 

360 obj = obj.func 

361 obj = getattr(obj, attr, None) 

362 if obj is None: 

363 break 

364 attrs.append(attr) 

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

366 is_decorated = obj is self 

367 qualname = '.'.join(attrs) 

368 func = '%s:%s' % (modname, qualname) 

369 

370 # functools.partial objects can't be pickled 

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

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

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

374 is_decorated) 

375 return _restore_curry, state 

376 

377 

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

379 if isinstance(func, str): 

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

381 obj = import_module(modname) 

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

383 obj = getattr(obj, attr) 

384 if is_decorated: 

385 return obj 

386 func = obj.func 

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

388 obj.__dict__.update(userdict) 

389 return obj 

390 

391 

392@curry 

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

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

395 

396 Considerations: 

397 Trades memory for speed. 

398 Only use on pure functions. 

399 

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

401 >>> add = memoize(add) 

402 

403 Or use as a decorator 

404 

405 >>> @memoize 

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

407 ... return x + y 

408 

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

410 

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

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

413 ... return x + y 

414 

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

416 

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

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

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

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

421 

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

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

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

425 ... if verbose: 

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

427 ... return x + y 

428 """ 

429 if cache is None: 

430 cache = {} 

431 

432 try: 

433 may_have_kwargs = has_keywords(func) is not False 

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

435 is_unary = is_arity(1, func) 

436 except TypeError: # pragma: no cover 

437 may_have_kwargs = True 

438 is_unary = False 

439 

440 if key is None: 

441 if is_unary: 

442 def key(args, kwargs): 

443 return args[0] 

444 elif may_have_kwargs: 

445 def key(args, kwargs): 

446 return ( 

447 args or None, 

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

449 ) 

450 else: 

451 def key(args, kwargs): 

452 return args 

453 

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

455 k = key(args, kwargs) 

456 try: 

457 return cache[k] 

458 except TypeError: 

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

460 except KeyError: 

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

462 return result 

463 

464 try: 

465 memof.__name__ = func.__name__ 

466 except AttributeError: 

467 pass 

468 memof.__doc__ = func.__doc__ 

469 memof.__wrapped__ = func 

470 return memof 

471 

472 

473class Compose(object): 

474 """ A composition of functions 

475 

476 See Also: 

477 compose 

478 """ 

479 __slots__ = 'first', 'funcs' 

480 

481 def __init__(self, funcs): 

482 funcs = tuple(reversed(funcs)) 

483 self.first = funcs[0] 

484 self.funcs = funcs[1:] 

485 

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

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

488 for f in self.funcs: 

489 ret = f(ret) 

490 return ret 

491 

492 def __getstate__(self): 

493 return self.first, self.funcs 

494 

495 def __setstate__(self, state): 

496 self.first, self.funcs = state 

497 

498 @instanceproperty(classval=__doc__) 

499 def __doc__(self): 

500 def composed_doc(*fs): 

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

502 """ 

503 if not fs: 

504 # Argument name for the docstring. 

505 return '*args, **kwargs' 

506 

507 return '{f}({g})'.format(f=fs[0].__name__, g=composed_doc(*fs[1:])) 

508 

509 try: 

510 return ( 

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

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

513 ) 

514 except AttributeError: 

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

516 return 'A composition of functions' 

517 

518 @property 

519 def __name__(self): 

520 try: 

521 return '_of_'.join( 

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

523 ) 

524 except AttributeError: 

525 return type(self).__name__ 

526 

527 def __repr__(self): 

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

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

530 

531 def __eq__(self, other): 

532 if isinstance(other, Compose): 

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

534 return NotImplemented 

535 

536 def __ne__(self, other): 

537 equality = self.__eq__(other) 

538 return NotImplemented if equality is NotImplemented else not equality 

539 

540 def __hash__(self): 

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

542 

543 # Mimic the descriptor behavior of python functions. 

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

545 # adapted from 

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

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

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

549 

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

551 @instanceproperty 

552 def __signature__(self): 

553 base = inspect.signature(self.first) 

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

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

556 

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

558 

559 

560def compose(*funcs): 

561 """ Compose functions to operate in series. 

562 

563 Returns a function that applies other functions in sequence. 

564 

565 Functions are applied from right to left so that 

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

567 

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

569 

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

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

572 '4' 

573 

574 See Also: 

575 compose_left 

576 pipe 

577 """ 

578 if not funcs: 

579 return identity 

580 if len(funcs) == 1: 

581 return funcs[0] 

582 else: 

583 return Compose(funcs) 

584 

585 

586def compose_left(*funcs): 

587 """ Compose functions to operate in series. 

588 

589 Returns a function that applies other functions in sequence. 

590 

591 Functions are applied from left to right so that 

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

593 

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

595 

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

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

598 '4' 

599 

600 See Also: 

601 compose 

602 pipe 

603 """ 

604 return compose(*reversed(funcs)) 

605 

606 

607def pipe(data, *funcs): 

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

609 

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

611 

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

613 transformations, much like pipes in UNIX 

614 

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

616 

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

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

619 '6' 

620 

621 See Also: 

622 compose 

623 compose_left 

624 thread_first 

625 thread_last 

626 """ 

627 for func in funcs: 

628 data = func(data) 

629 return data 

630 

631 

632def complement(func): 

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

634 

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

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

637 

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

639 >>> isodd = complement(iseven) 

640 >>> iseven(2) 

641 True 

642 >>> isodd(2) 

643 False 

644 """ 

645 return compose(not_, func) 

646 

647 

648class juxt(object): 

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

650 

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

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

653 

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

655 close together with contrasting effect. 

656 

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

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

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

660 (11, 20) 

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

662 (11, 20) 

663 """ 

664 __slots__ = ['funcs'] 

665 

666 def __init__(self, *funcs): 

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

668 funcs = funcs[0] 

669 self.funcs = tuple(funcs) 

670 

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

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

673 

674 def __getstate__(self): 

675 return self.funcs 

676 

677 def __setstate__(self, state): 

678 self.funcs = state 

679 

680 

681def do(func, x): 

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

683 

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

685 effects of ``func`` are relevant. 

686 

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

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

689 

690 >>> from toolz import compose 

691 >>> from toolz.curried import do 

692 

693 >>> log = [] 

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

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

696 >>> inc(1) 

697 2 

698 >>> inc(11) 

699 12 

700 >>> log 

701 [1, 11] 

702 """ 

703 func(x) 

704 return x 

705 

706 

707@curry 

708def flip(func, a, b): 

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

710 

711 This function is curried. 

712 

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

714 ... return a // b 

715 ... 

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

717 3 

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

719 >>> div_by_two(4) 

720 2 

721 

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

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

724 isinstance, issubclass. 

725 

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

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

728 >>> only_ints 

729 [1, 2, 3] 

730 """ 

731 return func(b, a) 

732 

733 

734def return_none(exc): 

735 """ Returns None. 

736 """ 

737 return None 

738 

739 

740class excepts(object): 

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

742 dispatch to a handler. 

743 

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

745 ifexprs are functional if/else blocks. 

746 

747 Examples 

748 -------- 

749 >>> excepting = excepts( 

750 ... ValueError, 

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

752 ... lambda _: -1, 

753 ... ) 

754 >>> excepting(1) 

755 0 

756 >>> excepting(3) 

757 -1 

758 

759 Multiple exceptions and default except clause. 

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

761 >>> excepting([]) 

762 >>> excepting([1]) 

763 1 

764 >>> excepting({}) 

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

766 1 

767 """ 

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

769 self.exc = exc 

770 self.func = func 

771 self.handler = handler 

772 

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

774 try: 

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

776 except self.exc as e: 

777 return self.handler(e) 

778 

779 @instanceproperty(classval=__doc__) 

780 def __doc__(self): 

781 from textwrap import dedent 

782 

783 exc = self.exc 

784 try: 

785 if isinstance(exc, tuple): 

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

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

788 ) 

789 else: 

790 exc_name = exc.__name__ 

791 

792 return dedent( 

793 """\ 

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

795 {exc} 

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

797 

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

799 {inst.func.__doc__} 

800 

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

802 {inst.handler.__doc__} 

803 """ 

804 ).format( 

805 inst=self, 

806 exc=exc_name, 

807 ) 

808 except AttributeError: 

809 return type(self).__doc__ 

810 

811 @property 

812 def __name__(self): 

813 exc = self.exc 

814 try: 

815 if isinstance(exc, tuple): 

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

817 else: 

818 exc_name = exc.__name__ 

819 return '%s_excepting_%s' % (self.func.__name__, exc_name) 

820 except AttributeError: 

821 return 'excepting' 

822 

823 

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

825 if sigspec is None: 

826 try: 

827 sigspec = inspect.signature(func) 

828 except (ValueError, TypeError) as e: 

829 sigspec = e 

830 if isinstance(sigspec, ValueError): 

831 return None, builtin_func(*builtin_args) 

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

833 if ( 

834 func in _sigs.signatures 

835 and (( 

836 hasattr(func, '__signature__') 

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

838 )) 

839 ): 

840 val = builtin_func(*builtin_args) 

841 return None, val 

842 return None, False 

843 return sigspec, None 

844 

845 

846if PYPY: # pragma: no cover 

847 _check_sigspec_orig = _check_sigspec 

848 

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

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

851 if func in _sigs.signatures: 

852 val = builtin_func(*builtin_args) 

853 return None, val 

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

855 

856 

857_check_sigspec.__doc__ = """ \ 

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

859 

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

861the signature registry in toolz._signatures is used. 

862""" 

863 

864 

865def num_required_args(func, sigspec=None): 

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

867 func) 

868 if sigspec is None: 

869 return rv 

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

871 if p.default is p.empty 

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

873 

874 

875def has_varargs(func, sigspec=None): 

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

877 if sigspec is None: 

878 return rv 

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

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

881 

882 

883def has_keywords(func, sigspec=None): 

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

885 if sigspec is None: 

886 return rv 

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

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

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

890 

891 

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

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

894 func, args, kwargs) 

895 if sigspec is None: 

896 return rv 

897 try: 

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

899 except TypeError: 

900 return False 

901 return True 

902 

903 

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

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

906 func, args, kwargs) 

907 if sigspec is None: 

908 return rv 

909 try: 

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

911 except TypeError: 

912 return False 

913 return True 

914 

915 

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

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

918 

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

920 Returns None if validity can't be determined. 

921 

922 >>> def f(x): 

923 ... return x 

924 >>> is_arity(1, f) 

925 True 

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

927 ... return x + y 

928 >>> is_arity(1, g) 

929 False 

930 """ 

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

932 if sigspec is None: 

933 return rv 

934 num = num_required_args(func, sigspec) 

935 if num is not None: 

936 num = num == n 

937 if not num: 

938 return False 

939 varargs = has_varargs(func, sigspec) 

940 if varargs: 

941 return False 

942 keywords = has_keywords(func, sigspec) 

943 if keywords: 

944 return False 

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

946 return None 

947 return True 

948 

949 

950num_required_args.__doc__ = """ \ 

951Number of required positional arguments 

952 

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

954 Returns None if validity can't be determined. 

955 

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

957 ... return x + y + z 

958 >>> num_required_args(f) 

959 2 

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

961 ... pass 

962 >>> num_required_args(g) 

963 0 

964 """ 

965 

966has_varargs.__doc__ = """ \ 

967Does a function have variadic positional arguments? 

968 

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

970 Returns None if validity can't be determined. 

971 

972 >>> def f(*args): 

973 ... return args 

974 >>> has_varargs(f) 

975 True 

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

977 ... return kwargs 

978 >>> has_varargs(g) 

979 False 

980 """ 

981 

982has_keywords.__doc__ = """ \ 

983Does a function have keyword arguments? 

984 

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

986 Returns None if validity can't be determined. 

987 

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

989 ... return x + y 

990 

991 >>> has_keywords(f) 

992 True 

993 """ 

994 

995is_valid_args.__doc__ = """ \ 

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

997 

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

999 Returns None if validity can't be determined. 

1000 

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

1002 ... return x + y 

1003 

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

1005 False 

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

1007 True 

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

1009 False 

1010 

1011 **Implementation notes** 

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

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

1014 works for many more types of callables. 

1015 

1016 Many builtins in the standard library are also supported. 

1017 """ 

1018 

1019is_partial_args.__doc__ = """ \ 

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

1021 

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

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

1024 

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

1026 Returns None if validity can't be determined. 

1027 

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

1029 ... return x + y 

1030 

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

1032 True 

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

1034 True 

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

1036 False 

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

1038 True 

1039 

1040 **Implementation notes** 

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

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

1043 works for many more types of callables. 

1044 

1045 Many builtins in the standard library are also supported. 

1046 """ 

1047 

1048from . import _signatures as _sigs