PageRenderTime 50ms CodeModel.GetById 17ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/matplotlib/cbook.py

https://github.com/tris-sondon/matplotlib
Python | 1704 lines | 1603 code | 37 blank | 64 comment | 38 complexity | fd626b42bc2d93ed661162212bdffc6b MD5 | raw file
  1. """
  2. A collection of utility functions and classes. Originally, many
  3. (but not all) were from the Python Cookbook -- hence the name cbook.
  4. This module is safe to import from anywhere within matplotlib;
  5. it imports matplotlib only at runtime.
  6. """
  7. from __future__ import (absolute_import, division, print_function,
  8. unicode_literals)
  9. import six
  10. from six.moves import xrange
  11. import datetime
  12. import errno
  13. from functools import reduce
  14. import glob
  15. import gzip
  16. import io
  17. import locale
  18. import os
  19. import re
  20. import sys
  21. import threading
  22. import time
  23. import traceback
  24. import types
  25. import warnings
  26. from weakref import ref, WeakKeyDictionary
  27. import numpy as np
  28. import numpy.ma as ma
  29. class MatplotlibDeprecationWarning(UserWarning):
  30. """
  31. A class for issuing deprecation warnings for Matplotlib users.
  32. In light of the fact that Python builtin DeprecationWarnings are ignored
  33. by default as of Python 2.7 (see link below), this class was put in to
  34. allow for the signaling of deprecation, but via UserWarnings which are not
  35. ignored by default.
  36. http://docs.python.org/dev/whatsnew/2.7.html#the-future-for-python-2-x
  37. """
  38. pass
  39. mplDeprecation = MatplotlibDeprecationWarning
  40. def _generate_deprecation_message(since, message='', name='',
  41. alternative='', pending=False,
  42. obj_type='attribute'):
  43. if not message:
  44. altmessage = ''
  45. if pending:
  46. message = (
  47. 'The %(func)s %(obj_type)s will be deprecated in a '
  48. 'future version.')
  49. else:
  50. message = (
  51. 'The %(func)s %(obj_type)s was deprecated in version '
  52. '%(since)s.')
  53. if alternative:
  54. altmessage = ' Use %s instead.' % alternative
  55. message = ((message % {
  56. 'func': name,
  57. 'name': name,
  58. 'alternative': alternative,
  59. 'obj_type': obj_type,
  60. 'since': since}) +
  61. altmessage)
  62. return message
  63. def warn_deprecated(
  64. since, message='', name='', alternative='', pending=False,
  65. obj_type='attribute'):
  66. """
  67. Used to display deprecation warning in a standard way.
  68. Parameters
  69. ------------
  70. since : str
  71. The release at which this API became deprecated.
  72. message : str, optional
  73. Override the default deprecation message. The format
  74. specifier `%(func)s` may be used for the name of the function,
  75. and `%(alternative)s` may be used in the deprecation message
  76. to insert the name of an alternative to the deprecated
  77. function. `%(obj_type)` may be used to insert a friendly name
  78. for the type of object being deprecated.
  79. name : str, optional
  80. The name of the deprecated function; if not provided the name
  81. is automatically determined from the passed in function,
  82. though this is useful in the case of renamed functions, where
  83. the new function is just assigned to the name of the
  84. deprecated function. For example::
  85. def new_function():
  86. ...
  87. oldFunction = new_function
  88. alternative : str, optional
  89. An alternative function that the user may use in place of the
  90. deprecated function. The deprecation warning will tell the user about
  91. this alternative if provided.
  92. pending : bool, optional
  93. If True, uses a PendingDeprecationWarning instead of a
  94. DeprecationWarning.
  95. obj_type : str, optional
  96. The object type being deprecated.
  97. Example
  98. -------
  99. # To warn of the deprecation of "matplotlib.name_of_module"
  100. warn_deprecated('1.4.0', name='matplotlib.name_of_module',
  101. obj_type='module')
  102. """
  103. message = _generate_deprecation_message(
  104. since, message, name, alternative, pending, obj_type)
  105. warnings.warn(message, mplDeprecation, stacklevel=1)
  106. def deprecated(since, message='', name='', alternative='', pending=False,
  107. obj_type='function'):
  108. """
  109. Decorator to mark a function as deprecated.
  110. Parameters
  111. ------------
  112. since : str
  113. The release at which this API became deprecated. This is
  114. required.
  115. message : str, optional
  116. Override the default deprecation message. The format
  117. specifier `%(func)s` may be used for the name of the function,
  118. and `%(alternative)s` may be used in the deprecation message
  119. to insert the name of an alternative to the deprecated
  120. function. `%(obj_type)` may be used to insert a friendly name
  121. for the type of object being deprecated.
  122. name : str, optional
  123. The name of the deprecated function; if not provided the name
  124. is automatically determined from the passed in function,
  125. though this is useful in the case of renamed functions, where
  126. the new function is just assigned to the name of the
  127. deprecated function. For example::
  128. def new_function():
  129. ...
  130. oldFunction = new_function
  131. alternative : str, optional
  132. An alternative function that the user may use in place of the
  133. deprecated function. The deprecation warning will tell the user about
  134. this alternative if provided.
  135. pending : bool, optional
  136. If True, uses a PendingDeprecationWarning instead of a
  137. DeprecationWarning.
  138. Example
  139. -------
  140. @deprecated('1.4.0')
  141. def the_function_to_deprecate():
  142. pass
  143. """
  144. def deprecate(func, message=message, name=name, alternative=alternative,
  145. pending=pending):
  146. import functools
  147. import textwrap
  148. if isinstance(func, classmethod):
  149. try:
  150. func = func.__func__
  151. except AttributeError:
  152. # classmethods in Python2.6 and below lack the __func__
  153. # attribute so we need to hack around to get it
  154. method = func.__get__(None, object)
  155. if hasattr(method, '__func__'):
  156. func = method.__func__
  157. elif hasattr(method, 'im_func'):
  158. func = method.im_func
  159. else:
  160. # Nothing we can do really... just return the original
  161. # classmethod
  162. return func
  163. is_classmethod = True
  164. else:
  165. is_classmethod = False
  166. if not name:
  167. name = func.__name__
  168. message = _generate_deprecation_message(
  169. since, message, name, alternative, pending, obj_type)
  170. @functools.wraps(func)
  171. def deprecated_func(*args, **kwargs):
  172. warnings.warn(message, mplDeprecation, stacklevel=2)
  173. return func(*args, **kwargs)
  174. old_doc = deprecated_func.__doc__
  175. if not old_doc:
  176. old_doc = ''
  177. old_doc = textwrap.dedent(old_doc).strip('\n')
  178. message = message.strip()
  179. new_doc = (('\n.. deprecated:: %(since)s'
  180. '\n %(message)s\n\n' %
  181. {'since': since, 'message': message}) + old_doc)
  182. if not old_doc:
  183. # This is to prevent a spurious 'unexected unindent' warning from
  184. # docutils when the original docstring was blank.
  185. new_doc += r'\ '
  186. deprecated_func.__doc__ = new_doc
  187. if is_classmethod:
  188. deprecated_func = classmethod(deprecated_func)
  189. return deprecated_func
  190. return deprecate
  191. # On some systems, locale.getpreferredencoding returns None,
  192. # which can break unicode; and the sage project reports that
  193. # some systems have incorrect locale specifications, e.g.,
  194. # an encoding instead of a valid locale name. Another
  195. # pathological case that has been reported is an empty string.
  196. # On some systems, getpreferredencoding sets the locale, which has
  197. # side effects. Passing False eliminates those side effects.
  198. def unicode_safe(s):
  199. import matplotlib
  200. if isinstance(s, bytes):
  201. try:
  202. preferredencoding = locale.getpreferredencoding(
  203. matplotlib.rcParams['axes.formatter.use_locale']).strip()
  204. if not preferredencoding:
  205. preferredencoding = None
  206. except (ValueError, ImportError, AttributeError):
  207. preferredencoding = None
  208. if preferredencoding is None:
  209. return six.text_type(s)
  210. else:
  211. return six.text_type(s, preferredencoding)
  212. return s
  213. class converter(object):
  214. """
  215. Base class for handling string -> python type with support for
  216. missing values
  217. """
  218. def __init__(self, missing='Null', missingval=None):
  219. self.missing = missing
  220. self.missingval = missingval
  221. def __call__(self, s):
  222. if s == self.missing:
  223. return self.missingval
  224. return s
  225. def is_missing(self, s):
  226. return not s.strip() or s == self.missing
  227. class tostr(converter):
  228. 'convert to string or None'
  229. def __init__(self, missing='Null', missingval=''):
  230. converter.__init__(self, missing=missing, missingval=missingval)
  231. class todatetime(converter):
  232. 'convert to a datetime or None'
  233. def __init__(self, fmt='%Y-%m-%d', missing='Null', missingval=None):
  234. 'use a :func:`time.strptime` format string for conversion'
  235. converter.__init__(self, missing, missingval)
  236. self.fmt = fmt
  237. def __call__(self, s):
  238. if self.is_missing(s):
  239. return self.missingval
  240. tup = time.strptime(s, self.fmt)
  241. return datetime.datetime(*tup[:6])
  242. class todate(converter):
  243. 'convert to a date or None'
  244. def __init__(self, fmt='%Y-%m-%d', missing='Null', missingval=None):
  245. 'use a :func:`time.strptime` format string for conversion'
  246. converter.__init__(self, missing, missingval)
  247. self.fmt = fmt
  248. def __call__(self, s):
  249. if self.is_missing(s):
  250. return self.missingval
  251. tup = time.strptime(s, self.fmt)
  252. return datetime.date(*tup[:3])
  253. class tofloat(converter):
  254. 'convert to a float or None'
  255. def __init__(self, missing='Null', missingval=None):
  256. converter.__init__(self, missing)
  257. self.missingval = missingval
  258. def __call__(self, s):
  259. if self.is_missing(s):
  260. return self.missingval
  261. return float(s)
  262. class toint(converter):
  263. 'convert to an int or None'
  264. def __init__(self, missing='Null', missingval=None):
  265. converter.__init__(self, missing)
  266. def __call__(self, s):
  267. if self.is_missing(s):
  268. return self.missingval
  269. return int(s)
  270. class _BoundMethodProxy(object):
  271. '''
  272. Our own proxy object which enables weak references to bound and unbound
  273. methods and arbitrary callables. Pulls information about the function,
  274. class, and instance out of a bound method. Stores a weak reference to the
  275. instance to support garbage collection.
  276. @organization: IBM Corporation
  277. @copyright: Copyright (c) 2005, 2006 IBM Corporation
  278. @license: The BSD License
  279. Minor bugfixes by Michael Droettboom
  280. '''
  281. def __init__(self, cb):
  282. try:
  283. try:
  284. self.inst = ref(cb.im_self)
  285. except TypeError:
  286. self.inst = None
  287. if six.PY3:
  288. self.func = cb.__func__
  289. self.klass = cb.__self__.__class__
  290. else:
  291. self.func = cb.im_func
  292. self.klass = cb.im_class
  293. except AttributeError:
  294. self.inst = None
  295. self.func = cb
  296. self.klass = None
  297. def __getstate__(self):
  298. d = self.__dict__.copy()
  299. # de-weak reference inst
  300. inst = d['inst']
  301. if inst is not None:
  302. d['inst'] = inst()
  303. return d
  304. def __setstate__(self, statedict):
  305. self.__dict__ = statedict
  306. inst = statedict['inst']
  307. # turn inst back into a weakref
  308. if inst is not None:
  309. self.inst = ref(inst)
  310. def __call__(self, *args, **kwargs):
  311. '''
  312. Proxy for a call to the weak referenced object. Take
  313. arbitrary params to pass to the callable.
  314. Raises `ReferenceError`: When the weak reference refers to
  315. a dead object
  316. '''
  317. if self.inst is not None and self.inst() is None:
  318. raise ReferenceError
  319. elif self.inst is not None:
  320. # build a new instance method with a strong reference to the
  321. # instance
  322. mtd = types.MethodType(self.func, self.inst())
  323. else:
  324. # not a bound method, just return the func
  325. mtd = self.func
  326. # invoke the callable and return the result
  327. return mtd(*args, **kwargs)
  328. def __eq__(self, other):
  329. '''
  330. Compare the held function and instance with that held by
  331. another proxy.
  332. '''
  333. try:
  334. if self.inst is None:
  335. return self.func == other.func and other.inst is None
  336. else:
  337. return self.func == other.func and self.inst() == other.inst()
  338. except Exception:
  339. return False
  340. def __ne__(self, other):
  341. '''
  342. Inverse of __eq__.
  343. '''
  344. return not self.__eq__(other)
  345. class CallbackRegistry:
  346. """
  347. Handle registering and disconnecting for a set of signals and
  348. callbacks:
  349. >>> def oneat(x):
  350. ... print('eat', x)
  351. >>> def ondrink(x):
  352. ... print('drink', x)
  353. >>> from matplotlib.cbook import CallbackRegistry
  354. >>> callbacks = CallbackRegistry()
  355. >>> id_eat = callbacks.connect('eat', oneat)
  356. >>> id_drink = callbacks.connect('drink', ondrink)
  357. >>> callbacks.process('drink', 123)
  358. drink 123
  359. >>> callbacks.process('eat', 456)
  360. eat 456
  361. >>> callbacks.process('be merry', 456) # nothing will be called
  362. >>> callbacks.disconnect(id_eat)
  363. >>> callbacks.process('eat', 456) # nothing will be called
  364. In practice, one should always disconnect all callbacks when they
  365. are no longer needed to avoid dangling references (and thus memory
  366. leaks). However, real code in matplotlib rarely does so, and due
  367. to its design, it is rather difficult to place this kind of code.
  368. To get around this, and prevent this class of memory leaks, we
  369. instead store weak references to bound methods only, so when the
  370. destination object needs to die, the CallbackRegistry won't keep
  371. it alive. The Python stdlib weakref module can not create weak
  372. references to bound methods directly, so we need to create a proxy
  373. object to handle weak references to bound methods (or regular free
  374. functions). This technique was shared by Peter Parente on his
  375. `"Mindtrove" blog
  376. <http://mindtrove.info/articles/python-weak-references/>`_.
  377. """
  378. def __init__(self, *args):
  379. if len(args):
  380. warn_deprecated(
  381. '1.3',
  382. message="CallbackRegistry no longer requires a list of "
  383. "callback types. Ignoring arguments. *args will "
  384. "be removed in 1.5")
  385. self.callbacks = dict()
  386. self._cid = 0
  387. self._func_cid_map = {}
  388. def __getstate__(self):
  389. # We cannot currently pickle the callables in the registry, so
  390. # return an empty dictionary.
  391. return {}
  392. def __setstate__(self, state):
  393. # re-initialise an empty callback registry
  394. self.__init__()
  395. def connect(self, s, func):
  396. """
  397. register *func* to be called when a signal *s* is generated
  398. func will be called
  399. """
  400. self._func_cid_map.setdefault(s, WeakKeyDictionary())
  401. if func in self._func_cid_map[s]:
  402. return self._func_cid_map[s][func]
  403. self._cid += 1
  404. cid = self._cid
  405. self._func_cid_map[s][func] = cid
  406. self.callbacks.setdefault(s, dict())
  407. proxy = _BoundMethodProxy(func)
  408. self.callbacks[s][cid] = proxy
  409. return cid
  410. def disconnect(self, cid):
  411. """
  412. disconnect the callback registered with callback id *cid*
  413. """
  414. for eventname, callbackd in list(six.iteritems(self.callbacks)):
  415. try:
  416. del callbackd[cid]
  417. except KeyError:
  418. continue
  419. else:
  420. for category, functions in list(
  421. six.iteritems(self._func_cid_map)):
  422. for function, value in list(six.iteritems(functions)):
  423. if value == cid:
  424. del functions[function]
  425. return
  426. def process(self, s, *args, **kwargs):
  427. """
  428. process signal *s*. All of the functions registered to receive
  429. callbacks on *s* will be called with *\*args* and *\*\*kwargs*
  430. """
  431. if s in self.callbacks:
  432. for cid, proxy in list(six.iteritems(self.callbacks[s])):
  433. # Clean out dead references
  434. if proxy.inst is not None and proxy.inst() is None:
  435. del self.callbacks[s][cid]
  436. else:
  437. proxy(*args, **kwargs)
  438. class Scheduler(threading.Thread):
  439. """
  440. Base class for timeout and idle scheduling
  441. """
  442. idlelock = threading.Lock()
  443. id = 0
  444. def __init__(self):
  445. threading.Thread.__init__(self)
  446. self.id = Scheduler.id
  447. self._stopped = False
  448. Scheduler.id += 1
  449. self._stopevent = threading.Event()
  450. def stop(self):
  451. if self._stopped:
  452. return
  453. self._stopevent.set()
  454. self.join()
  455. self._stopped = True
  456. class Timeout(Scheduler):
  457. """
  458. Schedule recurring events with a wait time in seconds
  459. """
  460. def __init__(self, wait, func):
  461. Scheduler.__init__(self)
  462. self.wait = wait
  463. self.func = func
  464. def run(self):
  465. while not self._stopevent.isSet():
  466. self._stopevent.wait(self.wait)
  467. Scheduler.idlelock.acquire()
  468. b = self.func(self)
  469. Scheduler.idlelock.release()
  470. if not b:
  471. break
  472. class Idle(Scheduler):
  473. """
  474. Schedule callbacks when scheduler is idle
  475. """
  476. # the prototype impl is a bit of a poor man's idle handler. It
  477. # just implements a short wait time. But it will provide a
  478. # placeholder for a proper impl ater
  479. waittime = 0.05
  480. def __init__(self, func):
  481. Scheduler.__init__(self)
  482. self.func = func
  483. def run(self):
  484. while not self._stopevent.isSet():
  485. self._stopevent.wait(Idle.waittime)
  486. Scheduler.idlelock.acquire()
  487. b = self.func(self)
  488. Scheduler.idlelock.release()
  489. if not b:
  490. break
  491. class silent_list(list):
  492. """
  493. override repr when returning a list of matplotlib artists to
  494. prevent long, meaningless output. This is meant to be used for a
  495. homogeneous list of a given type
  496. """
  497. def __init__(self, type, seq=None):
  498. self.type = type
  499. if seq is not None:
  500. self.extend(seq)
  501. def __repr__(self):
  502. return '<a list of %d %s objects>' % (len(self), self.type)
  503. def __str__(self):
  504. return repr(self)
  505. def __getstate__(self):
  506. # store a dictionary of this SilentList's state
  507. return {'type': self.type, 'seq': self[:]}
  508. def __setstate__(self, state):
  509. self.type = state['type']
  510. self.extend(state['seq'])
  511. def strip_math(s):
  512. 'remove latex formatting from mathtext'
  513. remove = (r'\mathdefault', r'\rm', r'\cal', r'\tt', r'\it', '\\', '{', '}')
  514. s = s[1:-1]
  515. for r in remove:
  516. s = s.replace(r, '')
  517. return s
  518. class Bunch:
  519. """
  520. Often we want to just collect a bunch of stuff together, naming each
  521. item of the bunch; a dictionary's OK for that, but a small do- nothing
  522. class is even handier, and prettier to use. Whenever you want to
  523. group a few variables::
  524. >>> point = Bunch(datum=2, squared=4, coord=12)
  525. >>> point.datum
  526. By: Alex Martelli
  527. From: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/52308
  528. """
  529. def __init__(self, **kwds):
  530. self.__dict__.update(kwds)
  531. def __repr__(self):
  532. keys = six.iterkeys(self.__dict__)
  533. return 'Bunch(%s)' % ', '.join(['%s=%s' % (k, self.__dict__[k])
  534. for k
  535. in keys])
  536. def unique(x):
  537. 'Return a list of unique elements of *x*'
  538. return list(six.iterkeys(dict([(val, 1) for val in x])))
  539. def iterable(obj):
  540. 'return true if *obj* is iterable'
  541. try:
  542. iter(obj)
  543. except TypeError:
  544. return False
  545. return True
  546. def is_string_like(obj):
  547. 'Return True if *obj* looks like a string'
  548. if isinstance(obj, six.string_types):
  549. return True
  550. # numpy strings are subclass of str, ma strings are not
  551. if ma.isMaskedArray(obj):
  552. if obj.ndim == 0 and obj.dtype.kind in 'SU':
  553. return True
  554. else:
  555. return False
  556. try:
  557. obj + ''
  558. except:
  559. return False
  560. return True
  561. def is_sequence_of_strings(obj):
  562. """
  563. Returns true if *obj* is iterable and contains strings
  564. """
  565. if not iterable(obj):
  566. return False
  567. if is_string_like(obj):
  568. return False
  569. for o in obj:
  570. if not is_string_like(o):
  571. return False
  572. return True
  573. def is_writable_file_like(obj):
  574. 'return true if *obj* looks like a file object with a *write* method'
  575. return hasattr(obj, 'write') and six.callable(obj.write)
  576. def file_requires_unicode(x):
  577. """
  578. Returns `True` if the given writable file-like object requires Unicode
  579. to be written to it.
  580. """
  581. try:
  582. x.write(b'')
  583. except TypeError:
  584. return True
  585. else:
  586. return False
  587. def is_scalar(obj):
  588. 'return true if *obj* is not string like and is not iterable'
  589. return not is_string_like(obj) and not iterable(obj)
  590. def is_numlike(obj):
  591. 'return true if *obj* looks like a number'
  592. try:
  593. obj + 1
  594. except:
  595. return False
  596. else:
  597. return True
  598. def to_filehandle(fname, flag='rU', return_opened=False):
  599. """
  600. *fname* can be a filename or a file handle. Support for gzipped
  601. files is automatic, if the filename ends in .gz. *flag* is a
  602. read/write flag for :func:`file`
  603. """
  604. if is_string_like(fname):
  605. if fname.endswith('.gz'):
  606. # get rid of 'U' in flag for gzipped files.
  607. flag = flag.replace('U', '')
  608. fh = gzip.open(fname, flag)
  609. elif fname.endswith('.bz2'):
  610. # get rid of 'U' in flag for bz2 files
  611. flag = flag.replace('U', '')
  612. import bz2
  613. fh = bz2.BZ2File(fname, flag)
  614. else:
  615. fh = open(fname, flag)
  616. opened = True
  617. elif hasattr(fname, 'seek'):
  618. fh = fname
  619. opened = False
  620. else:
  621. raise ValueError('fname must be a string or file handle')
  622. if return_opened:
  623. return fh, opened
  624. return fh
  625. def is_scalar_or_string(val):
  626. """Return whether the given object is a scalar or string like."""
  627. return is_string_like(val) or not iterable(val)
  628. def _string_to_bool(s):
  629. if not is_string_like(s):
  630. return s
  631. if s == 'on':
  632. return True
  633. if s == 'off':
  634. return False
  635. raise ValueError("string argument must be either 'on' or 'off'")
  636. def get_sample_data(fname, asfileobj=True):
  637. """
  638. Return a sample data file. *fname* is a path relative to the
  639. `mpl-data/sample_data` directory. If *asfileobj* is `True`
  640. return a file object, otherwise just a file path.
  641. Set the rc parameter examples.directory to the directory where we should
  642. look, if sample_data files are stored in a location different than
  643. default (which is 'mpl-data/sample_data` at the same level of 'matplotlib`
  644. Python module files).
  645. If the filename ends in .gz, the file is implicitly ungzipped.
  646. """
  647. import matplotlib
  648. if matplotlib.rcParams['examples.directory']:
  649. root = matplotlib.rcParams['examples.directory']
  650. else:
  651. root = os.path.join(os.path.dirname(__file__),
  652. "mpl-data", "sample_data")
  653. path = os.path.join(root, fname)
  654. if asfileobj:
  655. if (os.path.splitext(fname)[-1].lower() in
  656. ('.csv', '.xrc', '.txt')):
  657. mode = 'r'
  658. else:
  659. mode = 'rb'
  660. base, ext = os.path.splitext(fname)
  661. if ext == '.gz':
  662. return gzip.open(path, mode)
  663. else:
  664. return open(path, mode)
  665. else:
  666. return path
  667. def flatten(seq, scalarp=is_scalar_or_string):
  668. """
  669. Returns a generator of flattened nested containers
  670. For example:
  671. >>> from matplotlib.cbook import flatten
  672. >>> l = (('John', ['Hunter']), (1, 23), [[([42, (5, 23)], )]])
  673. >>> print(list(flatten(l)))
  674. ['John', 'Hunter', 1, 23, 42, 5, 23]
  675. By: Composite of Holger Krekel and Luther Blissett
  676. From: http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/121294
  677. and Recipe 1.12 in cookbook
  678. """
  679. for item in seq:
  680. if scalarp(item):
  681. yield item
  682. else:
  683. for subitem in flatten(item, scalarp):
  684. yield subitem
  685. class Sorter:
  686. """
  687. Sort by attribute or item
  688. Example usage::
  689. sort = Sorter()
  690. list = [(1, 2), (4, 8), (0, 3)]
  691. dict = [{'a': 3, 'b': 4}, {'a': 5, 'b': 2}, {'a': 0, 'b': 0},
  692. {'a': 9, 'b': 9}]
  693. sort(list) # default sort
  694. sort(list, 1) # sort by index 1
  695. sort(dict, 'a') # sort a list of dicts by key 'a'
  696. """
  697. def _helper(self, data, aux, inplace):
  698. aux.sort()
  699. result = [data[i] for junk, i in aux]
  700. if inplace:
  701. data[:] = result
  702. return result
  703. def byItem(self, data, itemindex=None, inplace=1):
  704. if itemindex is None:
  705. if inplace:
  706. data.sort()
  707. result = data
  708. else:
  709. result = data[:]
  710. result.sort()
  711. return result
  712. else:
  713. aux = [(data[i][itemindex], i) for i in range(len(data))]
  714. return self._helper(data, aux, inplace)
  715. def byAttribute(self, data, attributename, inplace=1):
  716. aux = [(getattr(data[i], attributename), i) for i in range(len(data))]
  717. return self._helper(data, aux, inplace)
  718. # a couple of handy synonyms
  719. sort = byItem
  720. __call__ = byItem
  721. class Xlator(dict):
  722. """
  723. All-in-one multiple-string-substitution class
  724. Example usage::
  725. text = "Larry Wall is the creator of Perl"
  726. adict = {
  727. "Larry Wall" : "Guido van Rossum",
  728. "creator" : "Benevolent Dictator for Life",
  729. "Perl" : "Python",
  730. }
  731. print multiple_replace(adict, text)
  732. xlat = Xlator(adict)
  733. print xlat.xlat(text)
  734. """
  735. def _make_regex(self):
  736. """ Build re object based on the keys of the current dictionary """
  737. return re.compile("|".join(map(re.escape, list(six.iterkeys(self)))))
  738. def __call__(self, match):
  739. """ Handler invoked for each regex *match* """
  740. return self[match.group(0)]
  741. def xlat(self, text):
  742. """ Translate *text*, returns the modified text. """
  743. return self._make_regex().sub(self, text)
  744. def soundex(name, len=4):
  745. """ soundex module conforming to Odell-Russell algorithm """
  746. # digits holds the soundex values for the alphabet
  747. soundex_digits = '01230120022455012623010202'
  748. sndx = ''
  749. fc = ''
  750. # Translate letters in name to soundex digits
  751. for c in name.upper():
  752. if c.isalpha():
  753. if not fc:
  754. fc = c # Remember first letter
  755. d = soundex_digits[ord(c) - ord('A')]
  756. # Duplicate consecutive soundex digits are skipped
  757. if not sndx or (d != sndx[-1]):
  758. sndx += d
  759. # Replace first digit with first letter
  760. sndx = fc + sndx[1:]
  761. # Remove all 0s from the soundex code
  762. sndx = sndx.replace('0', '')
  763. # Return soundex code truncated or 0-padded to len characters
  764. return (sndx + (len * '0'))[:len]
  765. class Null:
  766. """ Null objects always and reliably "do nothing." """
  767. def __init__(self, *args, **kwargs):
  768. pass
  769. def __call__(self, *args, **kwargs):
  770. return self
  771. def __str__(self):
  772. return "Null()"
  773. def __repr__(self):
  774. return "Null()"
  775. if six.PY3:
  776. def __bool__(self):
  777. return 0
  778. else:
  779. def __nonzero__(self):
  780. return 0
  781. def __getattr__(self, name):
  782. return self
  783. def __setattr__(self, name, value):
  784. return self
  785. def __delattr__(self, name):
  786. return self
  787. def mkdirs(newdir, mode=0o777):
  788. """
  789. make directory *newdir* recursively, and set *mode*. Equivalent to ::
  790. > mkdir -p NEWDIR
  791. > chmod MODE NEWDIR
  792. """
  793. try:
  794. if not os.path.exists(newdir):
  795. parts = os.path.split(newdir)
  796. for i in range(1, len(parts) + 1):
  797. thispart = os.path.join(*parts[:i])
  798. if not os.path.exists(thispart):
  799. os.makedirs(thispart, mode)
  800. except OSError as err:
  801. # Reraise the error unless it's about an already existing directory
  802. if err.errno != errno.EEXIST or not os.path.isdir(newdir):
  803. raise
  804. class GetRealpathAndStat:
  805. def __init__(self):
  806. self._cache = {}
  807. def __call__(self, path):
  808. result = self._cache.get(path)
  809. if result is None:
  810. realpath = os.path.realpath(path)
  811. if sys.platform == 'win32':
  812. stat_key = realpath
  813. else:
  814. stat = os.stat(realpath)
  815. stat_key = (stat.st_ino, stat.st_dev)
  816. result = realpath, stat_key
  817. self._cache[path] = result
  818. return result
  819. get_realpath_and_stat = GetRealpathAndStat()
  820. def dict_delall(d, keys):
  821. 'delete all of the *keys* from the :class:`dict` *d*'
  822. for key in keys:
  823. try:
  824. del d[key]
  825. except KeyError:
  826. pass
  827. class RingBuffer:
  828. """ class that implements a not-yet-full buffer """
  829. def __init__(self, size_max):
  830. self.max = size_max
  831. self.data = []
  832. class __Full:
  833. """ class that implements a full buffer """
  834. def append(self, x):
  835. """ Append an element overwriting the oldest one. """
  836. self.data[self.cur] = x
  837. self.cur = (self.cur + 1) % self.max
  838. def get(self):
  839. """ return list of elements in correct order """
  840. return self.data[self.cur:] + self.data[:self.cur]
  841. def append(self, x):
  842. """append an element at the end of the buffer"""
  843. self.data.append(x)
  844. if len(self.data) == self.max:
  845. self.cur = 0
  846. # Permanently change self's class from non-full to full
  847. self.__class__ = __Full
  848. def get(self):
  849. """ Return a list of elements from the oldest to the newest. """
  850. return self.data
  851. def __get_item__(self, i):
  852. return self.data[i % len(self.data)]
  853. def get_split_ind(seq, N):
  854. """
  855. *seq* is a list of words. Return the index into seq such that::
  856. len(' '.join(seq[:ind])<=N
  857. .
  858. """
  859. sLen = 0
  860. # todo: use Alex's xrange pattern from the cbook for efficiency
  861. for (word, ind) in zip(seq, xrange(len(seq))):
  862. sLen += len(word) + 1 # +1 to account for the len(' ')
  863. if sLen >= N:
  864. return ind
  865. return len(seq)
  866. def wrap(prefix, text, cols):
  867. 'wrap *text* with *prefix* at length *cols*'
  868. pad = ' ' * len(prefix.expandtabs())
  869. available = cols - len(pad)
  870. seq = text.split(' ')
  871. Nseq = len(seq)
  872. ind = 0
  873. lines = []
  874. while ind < Nseq:
  875. lastInd = ind
  876. ind += get_split_ind(seq[ind:], available)
  877. lines.append(seq[lastInd:ind])
  878. # add the prefix to the first line, pad with spaces otherwise
  879. ret = prefix + ' '.join(lines[0]) + '\n'
  880. for line in lines[1:]:
  881. ret += pad + ' '.join(line) + '\n'
  882. return ret
  883. # A regular expression used to determine the amount of space to
  884. # remove. It looks for the first sequence of spaces immediately
  885. # following the first newline, or at the beginning of the string.
  886. _find_dedent_regex = re.compile("(?:(?:\n\r?)|^)( *)\S")
  887. # A cache to hold the regexs that actually remove the indent.
  888. _dedent_regex = {}
  889. def dedent(s):
  890. """
  891. Remove excess indentation from docstring *s*.
  892. Discards any leading blank lines, then removes up to n whitespace
  893. characters from each line, where n is the number of leading
  894. whitespace characters in the first line. It differs from
  895. textwrap.dedent in its deletion of leading blank lines and its use
  896. of the first non-blank line to determine the indentation.
  897. It is also faster in most cases.
  898. """
  899. # This implementation has a somewhat obtuse use of regular
  900. # expressions. However, this function accounted for almost 30% of
  901. # matplotlib startup time, so it is worthy of optimization at all
  902. # costs.
  903. if not s: # includes case of s is None
  904. return ''
  905. match = _find_dedent_regex.match(s)
  906. if match is None:
  907. return s
  908. # This is the number of spaces to remove from the left-hand side.
  909. nshift = match.end(1) - match.start(1)
  910. if nshift == 0:
  911. return s
  912. # Get a regex that will remove *up to* nshift spaces from the
  913. # beginning of each line. If it isn't in the cache, generate it.
  914. unindent = _dedent_regex.get(nshift, None)
  915. if unindent is None:
  916. unindent = re.compile("\n\r? {0,%d}" % nshift)
  917. _dedent_regex[nshift] = unindent
  918. result = unindent.sub("\n", s).strip()
  919. return result
  920. def listFiles(root, patterns='*', recurse=1, return_folders=0):
  921. """
  922. Recursively list files
  923. from Parmar and Martelli in the Python Cookbook
  924. """
  925. import os.path
  926. import fnmatch
  927. # Expand patterns from semicolon-separated string to list
  928. pattern_list = patterns.split(';')
  929. results = []
  930. for dirname, dirs, files in os.walk(root):
  931. # Append to results all relevant files (and perhaps folders)
  932. for name in files:
  933. fullname = os.path.normpath(os.path.join(dirname, name))
  934. if return_folders or os.path.isfile(fullname):
  935. for pattern in pattern_list:
  936. if fnmatch.fnmatch(name, pattern):
  937. results.append(fullname)
  938. break
  939. # Block recursion if recursion was disallowed
  940. if not recurse:
  941. break
  942. return results
  943. def get_recursive_filelist(args):
  944. """
  945. Recurse all the files and dirs in *args* ignoring symbolic links
  946. and return the files as a list of strings
  947. """
  948. files = []
  949. for arg in args:
  950. if os.path.isfile(arg):
  951. files.append(arg)
  952. continue
  953. if os.path.isdir(arg):
  954. newfiles = listFiles(arg, recurse=1, return_folders=1)
  955. files.extend(newfiles)
  956. return [f for f in files if not os.path.islink(f)]
  957. def pieces(seq, num=2):
  958. "Break up the *seq* into *num* tuples"
  959. start = 0
  960. while 1:
  961. item = seq[start:start + num]
  962. if not len(item):
  963. break
  964. yield item
  965. start += num
  966. def exception_to_str(s=None):
  967. if six.PY3:
  968. sh = io.StringIO()
  969. else:
  970. sh = io.BytesIO()
  971. if s is not None:
  972. print(s, file=sh)
  973. traceback.print_exc(file=sh)
  974. return sh.getvalue()
  975. def allequal(seq):
  976. """
  977. Return *True* if all elements of *seq* compare equal. If *seq* is
  978. 0 or 1 length, return *True*
  979. """
  980. if len(seq) < 2:
  981. return True
  982. val = seq[0]
  983. for i in xrange(1, len(seq)):
  984. thisval = seq[i]
  985. if thisval != val:
  986. return False
  987. return True
  988. def alltrue(seq):
  989. """
  990. Return *True* if all elements of *seq* evaluate to *True*. If
  991. *seq* is empty, return *False*.
  992. """
  993. if not len(seq):
  994. return False
  995. for val in seq:
  996. if not val:
  997. return False
  998. return True
  999. def onetrue(seq):
  1000. """
  1001. Return *True* if one element of *seq* is *True*. It *seq* is
  1002. empty, return *False*.
  1003. """
  1004. if not len(seq):
  1005. return False
  1006. for val in seq:
  1007. if val:
  1008. return True
  1009. return False
  1010. def allpairs(x):
  1011. """
  1012. return all possible pairs in sequence *x*
  1013. Condensed by Alex Martelli from this thread_ on c.l.python
  1014. .. _thread: http://groups.google.com/groups?q=all+pairs+group:*python*&hl=en&lr=&ie=UTF-8&selm=mailman.4028.1096403649.5135.python-list%40python.org&rnum=1
  1015. """
  1016. return [(s, f) for i, f in enumerate(x) for s in x[i + 1:]]
  1017. class maxdict(dict):
  1018. """
  1019. A dictionary with a maximum size; this doesn't override all the
  1020. relevant methods to contrain size, just setitem, so use with
  1021. caution
  1022. """
  1023. def __init__(self, maxsize):
  1024. dict.__init__(self)
  1025. self.maxsize = maxsize
  1026. self._killkeys = []
  1027. def __setitem__(self, k, v):
  1028. if k not in self:
  1029. if len(self) >= self.maxsize:
  1030. del self[self._killkeys[0]]
  1031. del self._killkeys[0]
  1032. self._killkeys.append(k)
  1033. dict.__setitem__(self, k, v)
  1034. class Stack(object):
  1035. """
  1036. Implement a stack where elements can be pushed on and you can move
  1037. back and forth. But no pop. Should mimic home / back / forward
  1038. in a browser
  1039. """
  1040. def __init__(self, default=None):
  1041. self.clear()
  1042. self._default = default
  1043. def __call__(self):
  1044. 'return the current element, or None'
  1045. if not len(self._elements):
  1046. return self._default
  1047. else:
  1048. return self._elements[self._pos]
  1049. def __len__(self):
  1050. return self._elements.__len__()
  1051. def __getitem__(self, ind):
  1052. return self._elements.__getitem__(ind)
  1053. def forward(self):
  1054. 'move the position forward and return the current element'
  1055. N = len(self._elements)
  1056. if self._pos < N - 1:
  1057. self._pos += 1
  1058. return self()
  1059. def back(self):
  1060. 'move the position back and return the current element'
  1061. if self._pos > 0:
  1062. self._pos -= 1
  1063. return self()
  1064. def push(self, o):
  1065. """
  1066. push object onto stack at current position - all elements
  1067. occurring later than the current position are discarded
  1068. """
  1069. self._elements = self._elements[:self._pos + 1]
  1070. self._elements.append(o)
  1071. self._pos = len(self._elements) - 1
  1072. return self()
  1073. def home(self):
  1074. 'push the first element onto the top of the stack'
  1075. if not len(self._elements):
  1076. return
  1077. self.push(self._elements[0])
  1078. return self()
  1079. def empty(self):
  1080. return len(self._elements) == 0
  1081. def clear(self):
  1082. 'empty the stack'
  1083. self._pos = -1
  1084. self._elements = []
  1085. def bubble(self, o):
  1086. """
  1087. raise *o* to the top of the stack and return *o*. *o* must be
  1088. in the stack
  1089. """
  1090. if o not in self._elements:
  1091. raise ValueError('Unknown element o')
  1092. old = self._elements[:]
  1093. self.clear()
  1094. bubbles = []
  1095. for thiso in old:
  1096. if thiso == o:
  1097. bubbles.append(thiso)
  1098. else:
  1099. self.push(thiso)
  1100. for thiso in bubbles:
  1101. self.push(o)
  1102. return o
  1103. def remove(self, o):
  1104. 'remove element *o* from the stack'
  1105. if o not in self._elements:
  1106. raise ValueError('Unknown element o')
  1107. old = self._elements[:]
  1108. self.clear()
  1109. for thiso in old:
  1110. if thiso == o:
  1111. continue
  1112. else:
  1113. self.push(thiso)
  1114. def popall(seq):
  1115. 'empty a list'
  1116. for i in xrange(len(seq)):
  1117. seq.pop()
  1118. def finddir(o, match, case=False):
  1119. """
  1120. return all attributes of *o* which match string in match. if case
  1121. is True require an exact case match.
  1122. """
  1123. if case:
  1124. names = [(name, name) for name in dir(o) if is_string_like(name)]
  1125. else:
  1126. names = [(name.lower(), name) for name in dir(o)
  1127. if is_string_like(name)]
  1128. match = match.lower()
  1129. return [orig for name, orig in names if name.find(match) >= 0]
  1130. def reverse_dict(d):
  1131. 'reverse the dictionary -- may lose data if values are not unique!'
  1132. return dict([(v, k) for k, v in six.iteritems(d)])
  1133. def restrict_dict(d, keys):
  1134. """
  1135. Return a dictionary that contains those keys that appear in both
  1136. d and keys, with values from d.
  1137. """
  1138. return dict([(k, v) for (k, v) in six.iteritems(d) if k in keys])
  1139. def report_memory(i=0): # argument may go away
  1140. 'return the memory consumed by process'
  1141. from matplotlib.compat.subprocess import Popen, PIPE
  1142. pid = os.getpid()
  1143. if sys.platform == 'sunos5':
  1144. try:
  1145. a2 = Popen('ps -p %d -o osz' % pid, shell=True,
  1146. stdout=PIPE).stdout.readlines()
  1147. except OSError:
  1148. raise NotImplementedError(
  1149. "report_memory works on Sun OS only if "
  1150. "the 'ps' program is found")
  1151. mem = int(a2[-1].strip())
  1152. elif sys.platform.startswith('linux'):
  1153. try:
  1154. a2 = Popen('ps -p %d -o rss,sz' % pid, shell=True,
  1155. stdout=PIPE).stdout.readlines()
  1156. except OSError:
  1157. raise NotImplementedError(
  1158. "report_memory works on Linux only if "
  1159. "the 'ps' program is found")
  1160. mem = int(a2[1].split()[1])
  1161. elif sys.platform.startswith('darwin'):
  1162. try:
  1163. a2 = Popen('ps -p %d -o rss,vsz' % pid, shell=True,
  1164. stdout=PIPE).stdout.readlines()
  1165. except OSError:
  1166. raise NotImplementedError(
  1167. "report_memory works on Mac OS only if "
  1168. "the 'ps' program is found")
  1169. mem = int(a2[1].split()[0])
  1170. elif sys.platform.startswith('win'):
  1171. try:
  1172. a2 = Popen(["tasklist", "/nh", "/fi", "pid eq %d" % pid],
  1173. stdout=PIPE).stdout.read()
  1174. except OSError:
  1175. raise NotImplementedError(
  1176. "report_memory works on Windows only if "
  1177. "the 'tasklist' program is found")
  1178. mem = int(a2.strip().split()[-2].replace(',', ''))
  1179. else:
  1180. raise NotImplementedError(
  1181. "We don't have a memory monitor for %s" % sys.platform)
  1182. return mem
  1183. _safezip_msg = 'In safezip, len(args[0])=%d but len(args[%d])=%d'
  1184. def safezip(*args):
  1185. 'make sure *args* are equal len before zipping'
  1186. Nx = len(args[0])
  1187. for i, arg in enumerate(args[1:]):
  1188. if len(arg) != Nx:
  1189. raise ValueError(_safezip_msg % (Nx, i + 1, len(arg)))
  1190. return list(zip(*args))
  1191. def issubclass_safe(x, klass):
  1192. 'return issubclass(x, klass) and return False on a TypeError'
  1193. try:
  1194. return issubclass(x, klass)
  1195. except TypeError:
  1196. return False
  1197. def safe_masked_invalid(x):
  1198. x = np.asanyarray(x)
  1199. try:
  1200. xm = np.ma.masked_invalid(x, copy=False)
  1201. xm.shrink_mask()
  1202. except TypeError:
  1203. return x
  1204. return xm
  1205. class MemoryMonitor:
  1206. def __init__(self, nmax=20000):
  1207. self._nmax = nmax
  1208. self._mem = np.zeros((self._nmax,), np.int32)
  1209. self.clear()
  1210. def clear(self):
  1211. self._n = 0
  1212. self._overflow = False
  1213. def __call__(self):
  1214. mem = report_memory()
  1215. if self._n < self._nmax:
  1216. self._mem[self._n] = mem
  1217. self._n += 1
  1218. else:
  1219. self._overflow = True
  1220. return mem
  1221. def report(self, segments=4):
  1222. n = self._n
  1223. segments = min(n, segments)
  1224. dn = int(n / segments)
  1225. ii = list(xrange(0, n, dn))
  1226. ii[-1] = n - 1
  1227. print()
  1228. print('memory report: i, mem, dmem, dmem/nloops')
  1229. print(0, self._mem[0])
  1230. for i in range(1, len(ii)):
  1231. di = ii[i] - ii[i - 1]
  1232. if di == 0:
  1233. continue
  1234. dm = self._mem[ii[i]] - self._mem[ii[i - 1]]
  1235. print('%5d %5d %3d %8.3f' % (ii[i], self._mem[ii[i]],
  1236. dm, dm / float(di)))
  1237. if self._overflow:
  1238. print("Warning: array size was too small for the number of calls.")
  1239. def xy(self, i0=0, isub=1):
  1240. x = np.arange(i0, self._n, isub)
  1241. return x, self._mem[i0:self._n:isub]
  1242. def plot(self, i0=0, isub=1, fig=None):
  1243. if fig is None:
  1244. from .pylab import figure
  1245. fig = figure()
  1246. ax = fig.add_subplot(111)
  1247. ax.plot(*self.xy(i0, isub))
  1248. fig.canvas.draw()
  1249. def print_cycles(objects, outstream=sys.stdout, show_progress=False):
  1250. """
  1251. *objects*
  1252. A list of objects to find cycles in. It is often useful to
  1253. pass in gc.garbage to find the cycles that are preventing some
  1254. objects from being garbage collected.
  1255. *outstream*
  1256. The stream for output.
  1257. *show_progress*
  1258. If True, print the number of objects reached as they are found.
  1259. """
  1260. import gc
  1261. from types import FrameType
  1262. def print_path(path):
  1263. for i, step in enumerate(path):
  1264. # next "wraps around"
  1265. next = path[(i + 1) % len(path)]
  1266. outstream.write(" %s -- " % str(type(step)))
  1267. if isinstance(step, dict):
  1268. for key, val in six.iteritems(step):
  1269. if val is next:
  1270. outstream.write("[%s]" % repr(key))
  1271. break
  1272. if key is next:
  1273. outstream.write("[key] = %s" % repr(val))
  1274. break
  1275. elif isinstance(step, list):
  1276. outstream.write("[%d]" % step.index(next))
  1277. elif isinstance(step, tuple):
  1278. outstream.write("( tuple )")
  1279. else:
  1280. outstream.write(repr(step))
  1281. outstream.write(" ->\n")
  1282. outstream.write("\n")
  1283. def recurse(obj, start, all, current_path):
  1284. if show_progress:
  1285. outstream.write("%d\r" % len(all))
  1286. all[id(obj)] = None
  1287. referents = gc.get_referents(obj)
  1288. for referent in referents:
  1289. # If we've found our way back to the start, this is
  1290. # a cycle, so print it out
  1291. if referent is start:
  1292. print_path(current_path)
  1293. # Don't go back through the original list of objects, or
  1294. # through temporary references to the object, since those
  1295. # are just an artifact of the cycle detector itself.
  1296. elif referent is objects or isinstance(referent, FrameType):
  1297. continue
  1298. # We haven't seen this object before, so recurse
  1299. elif id(referent) not in all:
  1300. recurse(referent, start, all, current_path + [obj])
  1301. for obj in objects:
  1302. outstream.write("Examining: %r\n" % (obj,))
  1303. recurse(obj, obj, {}, [])
  1304. class Grouper(object):
  1305. """
  1306. This class provides a lightweight way to group arbitrary objects
  1307. together into disjoint sets when a full-blown graph data structure
  1308. would be overkill.
  1309. Objects can be joined using :meth:`join`, tested for connectedness
  1310. using :meth:`joined`, and all disjoint sets can be retreived by
  1311. using the object as an iterator.
  1312. The objects being joined must be hashable and weak-referenceable.
  1313. For example:
  1314. >>> from matplotlib.cbook import Grouper
  1315. >>> class Foo(object):
  1316. ... def __init__(self, s):
  1317. ... self.s = s
  1318. ... def __repr__(self):
  1319. ... return self.s
  1320. ...
  1321. >>> a, b, c, d, e, f = [Foo(x) for x in 'abcdef']
  1322. >>> grp = Grouper()
  1323. >>> grp.join(a, b)
  1324. >>> grp.join(b, c)
  1325. >>> grp.join(d, e)
  1326. >>> sorted(map(tuple, grp))
  1327. [(a, b, c), (d, e)]
  1328. >>> grp.joined(a, b)
  1329. True
  1330. >>> grp.joined(a, c)
  1331. True
  1332. >>> grp.joined(a, d)
  1333. False
  1334. """
  1335. def __init__(self, init=[]):
  1336. mapping = self._mapping = {}
  1337. for x in init:
  1338. mapping[ref(x)] = [ref(x)]
  1339. def __contains__(self, item):
  1340. return ref(item) in self._mapping
  1341. def clean(self):
  1342. """
  1343. Clean dead weak references from the dictionary
  1344. """
  1345. mapping = self._mapping
  1346. to_drop = [key for key in mapping if key() is None]
  1347. for key in to_drop:
  1348. val = mapping.pop(key)
  1349. val.remove(key)
  1350. def join(self, a, *args):
  1351. """
  1352. Join given arguments into the same set. Accepts one or more
  1353. arguments.
  1354. """
  1355. mapping = self._mapping
  1356. set_a = mapping.setdefault(ref(a), [ref(a)])
  1357. for arg in args:
  1358. set_b = mapping.get(ref(arg))
  1359. if set_b is None:
  1360. set_a.append(ref(arg))
  1361. mapping[ref(arg)] = set_a
  1362. elif set_b is not set_a:
  1363. if len(set_b) > len(set_a):
  1364. set_a, set_b = set_b, set_a
  1365. set_a.extend(set_b)
  1366. for elem in set_b:
  1367. mapping[elem] = set_a
  1368. self.clean()
  1369. def joined(self, a, b):
  1370. """
  1371. Returns True if *a* and *b* are members of the same set.
  1372. """
  1373. self.clean()
  1374. mapping = self._mapping
  1375. try:
  1376. return mapping[ref(a)] is mapping[ref(b)]
  1377. except KeyError:
  1378. return False
  1379. def __iter__(self):
  1380. """
  1381. Iterate over each of th