# Path: chopsticks/setops.py
# class SetOps:
#     """Mix-in class to provide set-like behaviour.
# 
#     Classes need only provide an _as_group() method to convert them to a
#     group.
# 
#     """
# 
#     def _as_group(self):
#         """Convert to a group object."""
#         raise NotImplementedError()
# 
#     def _set_op(self, ano, op):
#         """Perform a set operation on this group and another."""
#         left = self._as_group()
#         right = ano._as_group()
#         cls = type(left)
# 
#         tunnels = op(set(left.tunnels), set(right.tunnels))
#         grp = cls(tunnels)
#         all_connection_errors = {}
#         for src in [left, right]:
#             all_connection_errors.update(src.connection_errors)
#         for t in tunnels:
#             if t.host in all_connection_errors:
#                 grp.connection_errors[t.host] = all_connection_errors[t.host]
#         return grp
# 
#     def union(self, ano):
#         """Return the union of hosts in this group and another."""
#         return self._set_op(ano, set.union)
# 
#     def intersection(self, ano):
#         """Return the intersection of hosts in this group and another."""
#         return self._set_op(ano, set.intersection)
# 
#     def difference(self, ano):
#         """Return the hosts in this group not in ano."""
#         return self._set_op(ano, set.difference)
# 
#     def symmetric_difference(self, ano):
#         """Return the hosts in this group not in ano."""
#         return self._set_op(ano, set.symmetric_difference)
# 
#     __or__ = __add__ = union
#     __and__ = intersection
#     __sub__ = difference
#     __xor__ = symmetric_difference
# 
# Path: chopsticks/tunnel.py
# PY2 = sys.version_info < (3,)
# OP_CALL = 0
# OP_RET = 1
# OP_EXC = 2
# OP_IMP = 3
# OP_FETCH_BEGIN = 4
# OP_FETCH_DATA = 5
# OP_FETCH_END = 6
# OP_PUT_BEGIN = 7
# OP_PUT_DATA = 8
# OP_PUT_END = 9
# OP_START = 10
# CHOPSTICKS_PREFIX = 'chopsticks://'
#     HIGHEST_PICKLE_PROTOCOL = pickle.HIGHEST_PROTOCOL
#     PYTHON_ARGS = [
#         '-usS',
#         '-c',
#         'import sys, os; _bsz = %d ;' % len(bubble) +
#         'inpipe = os.fdopen(os.dup(0), \'rb\', _bsz); ' +
#         '__bubble = sys.stdin.read(_bsz); ' +
#         'exec(compile(__bubble, \'bubble.py\', \'exec\'))'
#     ]
# def start_errloop():
#     def __init__(self, msg, tb=None):
#     def __repr__(self):
#     def __init__(self):
#     def _reset(self):
#     def __eq__(self, ano):
#     def __ne__(self, ano):
#     def __hash__(self):
#     def __repr__(self):
#     def _as_group(self):
#     def _run_loop(self):
#     def connect(self):
#     def _connect_async(self, callback):
#     def write_msg(self, op, req_id, data=None, **kwargs):
#     def _next_id(self):
#     def _read_source(cls, file):
#     def handle_imp(self, mod):
#     def _get_callback(self, req_id, data):
#     def _pop_callback(self, req_id, data):
#     def on_message(self, msg):
#     def _warn(self, msg):
#     def call(self, callable, *args, **kwargs):
#     def _call_async(self, on_result, callable, *args, **kwargs):
#     def fetch(self, remote_path, local_path=None):
#     def _fetch_async(self, on_result, remote_path, local_path=None):
#     def put(self, local_path, remote_path=None, mode=0o644):
#     def _put_async(
#             self,
#             on_result,
#             local_path,
#             remote_path=None,
#             mode=0o644):
#     def close():
# def iter_chunks(req_id, path):
#     def __init__(self, on_result, local_path=None):
#     def recv(self, data):
#     def __call__(self, result):
#     def __del__(self):
#     def _connect_async(self, callback):
#         def wrapped_callback(res):
#     def on_error(self, err):
#     def _join(self, timeout=5):
#     def close(self):
#     def __del__(self):
#     def __enter__(self):
#     def __exit__(self, exc_type, exc_value, trackback):
#     def connect_pipes(self):
#     def cmd_args(self):
#     def __init__(self, name='localhost'):
#     def __init__(self, user='root', name=None):
#     def cmd_args(self):
#     def close(self):
#     def __init__(self, name, image='python:' + pyver, rm=True):
#     def cmd_args(self):
#     def __init__(self, host, user=None, port=None, sudo=False):
#     def cmd_args(self):
# class ErrorResult:
# class RemoteException(Exception):
# class DepthLimitExceeded(Exception):
# class BaseTunnel(SetOps):
# class Fetch(object):
# class PipeTunnel(BaseTunnel):
# class SubprocessTunnel(PipeTunnel):
# class Local(SubprocessTunnel):
# class Sudo(SubprocessTunnel):
# class Docker(SubprocessTunnel):
# class SSHTunnel(SubprocessTunnel):


from operator import not_
from .setops import SetOps
from .tunnel import SSHTunnel, loop, PY2, ErrorResult, pickle, RemoteException
"""Groups allow running operations on a group of tunnels in parallel.

Groups can also be used merely for representing groups of hosts, which offers
capabilities such as set operations and filtering. For example, they can be
used with Queues to schedule operations asynchronously on a number of hosts.

"""


__metaclass__ = type

if not PY2:
    basestring = str


class GroupResult(dict):
    """The results of a :meth:`Group.call()` operation.

    GroupResult behaves as a dictionary of results, keyed by hostname, although
    failures from individual hosts are represented as :class:`ErrorResult`
    objects.

    Methods are provided to easily process successes and failures separately.

    """

# Path: chopsticks/setops.py
# class SetOps:
#     """Mix-in class to provide set-like behaviour.
# 
#     Classes need only provide an _as_group() method to convert them to a
#     group.
# 
#     """
# 
#     def _as_group(self):
#         """Convert to a group object."""
#         raise NotImplementedError()
# 
#     def _set_op(self, ano, op):
#         """Perform a set operation on this group and another."""
#         left = self._as_group()
#         right = ano._as_group()
#         cls = type(left)
# 
#         tunnels = op(set(left.tunnels), set(right.tunnels))
#         grp = cls(tunnels)
#         all_connection_errors = {}
#         for src in [left, right]:
#             all_connection_errors.update(src.connection_errors)
#         for t in tunnels:
#             if t.host in all_connection_errors:
#                 grp.connection_errors[t.host] = all_connection_errors[t.host]
#         return grp
# 
#     def union(self, ano):
#         """Return the union of hosts in this group and another."""
#         return self._set_op(ano, set.union)
# 
#     def intersection(self, ano):
#         """Return the intersection of hosts in this group and another."""
#         return self._set_op(ano, set.intersection)
# 
#     def difference(self, ano):
#         """Return the hosts in this group not in ano."""
#         return self._set_op(ano, set.difference)
# 
#     def symmetric_difference(self, ano):
#         """Return the hosts in this group not in ano."""
#         return self._set_op(ano, set.symmetric_difference)
# 
#     __or__ = __add__ = union
#     __and__ = intersection
#     __sub__ = difference
#     __xor__ = symmetric_difference
# 
# Path: chopsticks/tunnel.py
# PY2 = sys.version_info < (3,)
# OP_CALL = 0
# OP_RET = 1
# OP_EXC = 2
# OP_IMP = 3
# OP_FETCH_BEGIN = 4
# OP_FETCH_DATA = 5
# OP_FETCH_END = 6
# OP_PUT_BEGIN = 7
# OP_PUT_DATA = 8
# OP_PUT_END = 9
# OP_START = 10
# CHOPSTICKS_PREFIX = 'chopsticks://'
#     HIGHEST_PICKLE_PROTOCOL = pickle.HIGHEST_PROTOCOL
#     PYTHON_ARGS = [
#         '-usS',
#         '-c',
#         'import sys, os; _bsz = %d ;' % len(bubble) +
#         'inpipe = os.fdopen(os.dup(0), \'rb\', _bsz); ' +
#         '__bubble = sys.stdin.read(_bsz); ' +
#         'exec(compile(__bubble, \'bubble.py\', \'exec\'))'
#     ]
# def start_errloop():
#     def __init__(self, msg, tb=None):
#     def __repr__(self):
#     def __init__(self):
#     def _reset(self):
#     def __eq__(self, ano):
#     def __ne__(self, ano):
#     def __hash__(self):
#     def __repr__(self):
#     def _as_group(self):
#     def _run_loop(self):
#     def connect(self):
#     def _connect_async(self, callback):
#     def write_msg(self, op, req_id, data=None, **kwargs):
#     def _next_id(self):
#     def _read_source(cls, file):
#     def handle_imp(self, mod):
#     def _get_callback(self, req_id, data):
#     def _pop_callback(self, req_id, data):
#     def on_message(self, msg):
#     def _warn(self, msg):
#     def call(self, callable, *args, **kwargs):
#     def _call_async(self, on_result, callable, *args, **kwargs):
#     def fetch(self, remote_path, local_path=None):
#     def _fetch_async(self, on_result, remote_path, local_path=None):
#     def put(self, local_path, remote_path=None, mode=0o644):
#     def _put_async(
#             self,
#             on_result,
#             local_path,
#             remote_path=None,
#             mode=0o644):
#     def close():
# def iter_chunks(req_id, path):
#     def __init__(self, on_result, local_path=None):
#     def recv(self, data):
#     def __call__(self, result):
#     def __del__(self):
#     def _connect_async(self, callback):
#         def wrapped_callback(res):
#     def on_error(self, err):
#     def _join(self, timeout=5):
#     def close(self):
#     def __del__(self):
#     def __enter__(self):
#     def __exit__(self, exc_type, exc_value, trackback):
#     def connect_pipes(self):
#     def cmd_args(self):
#     def __init__(self, name='localhost'):
#     def __init__(self, user='root', name=None):
#     def cmd_args(self):
#     def close(self):
#     def __init__(self, name, image='python:' + pyver, rm=True):
#     def cmd_args(self):
#     def __init__(self, host, user=None, port=None, sudo=False):
#     def cmd_args(self):
# class ErrorResult:
# class RemoteException(Exception):
# class DepthLimitExceeded(Exception):
# class BaseTunnel(SetOps):
# class Fetch(object):
# class PipeTunnel(BaseTunnel):
# class SubprocessTunnel(PipeTunnel):
# class Local(SubprocessTunnel):
# class Sudo(SubprocessTunnel):
# class Docker(SubprocessTunnel):
# class SSHTunnel(SubprocessTunnel):


# Path: chopsticks/group.py
from operator import not_
from .setops import SetOps
from .tunnel import SSHTunnel, loop, PY2, ErrorResult, pickle, RemoteException

"""Groups allow running operations on a group of tunnels in parallel.

Groups can also be used merely for representing groups of hosts, which offers
capabilities such as set operations and filtering. For example, they can be
used with Queues to schedule operations asynchronously on a number of hosts.

"""


__metaclass__ = type

if not PY2:
    basestring = str


class GroupResult(dict):
    """The results of a :meth:`Group.call()` operation.

    GroupResult behaves as a dictionary of results, keyed by hostname, although
    failures from individual hosts are represented as :class:`ErrorResult`
    objects.

    Methods are provided to easily process successes and failures separately.

    """

    if not PY2: