from pipes import quote as shell_quote
from pyrsistent import PRecord, field
from effect import Effect


def identity(arg):
    """Return argument untouched."""
    return arg


class RunRemotely(PRecord):
    """
    Run some commands on a remote host.

    :ivar bytes address: The address of the remote host to connect to.
    :ivar bytes username: The user to connect as.
    :ivar Effect commands: The commands to run.
    :ivar int port: The port of the ssh server to connect to.
    :ivar callable log_command_filter: A filter to apply to any logging
        of the executed command.
    """
    username = field(type=bytes, mandatory=True)
    address = field(type=bytes, mandatory=True)
    commands = field(type=Effect, mandatory=True)
    port = field(type=int, initial=22)
    log_command_filter = field(mandatory=True)


def run_remotely(
        username, address, commands, port=22, log_command_filter=identity):
    """
    Run some commands on a remote host.

    :param bytes address: The address of the remote host to connect to.
    :param bytes username: The user to connect as.
    :param Effect commands: The commands to run.
    :param int port: The port of the ssh server to connect to.
    :param callable log_command_filter: A filter to apply to any logging
        of the executed command.

    :return Effect:
    """
    return Effect(RunRemotely(
        username=username, address=address, commands=commands, port=port,
        log_command_filter=log_command_filter))


class Run(PRecord):
    """
    Run a shell command on a remote host.

    :ivar bytes command: The command to run.
    :ivar callable log_command_filter: A filter to apply to any logging
        of the executed command.
    :ivar bytes pre_command: A command to run before the ``command``,
        generally to setup the environment for the command.
    """
    command = field(type=bytes, mandatory=True)
    log_command_filter = field(mandatory=True)
    pre_command = field()

    @classmethod
    def from_args(
            cls, command_args, log_command_filter=identity, pre_command=None):
        return cls(
            command=" ".join(map(shell_quote, command_args)),
            log_command_filter=log_command_filter,
            pre_command=pre_command)


class Sudo(PRecord):
    """
    Run a shell command on a remote host with sudo.

    :ivar bytes command: The command to run.
    :ivar callable log_command_filter: A filter to apply to any logging
        of the executed command.
    """
    command = field(type=bytes, mandatory=True)
    log_command_filter = field(mandatory=True)

    @classmethod
    def from_args(cls, command_args, log_command_filter=identity):
        return cls(
            command=" ".join(map(shell_quote, command_args)),
            log_command_filter=log_command_filter)


class Put(PRecord):
    """
    Create a file with the given content on a remote host.

    :ivar bytes content: The desired contents.
    :ivar bytes path: The remote path to create.
    :ivar callable log_content_filter: A filter to apply to any logging
        of the transferred content.
    """
    content = field(type=bytes, mandatory=True)
    path = field(type=bytes, mandatory=True)
    log_content_filter = field(mandatory=True)


class Comment(PRecord):
    """
    Record a comment to be shown in the documentation corresponding to a task.

    :ivar bytes comment: The desired comment.
    """
    comment = field(type=bytes, mandatory=True)


def run(command, log_command_filter=identity):
    """
    Run a shell command on a remote host.

    :param bytes command: The command to run.
    :param callable log_command_filter: A filter to apply to any logging
        of the executed command.
    """
    return Effect(Run(command=command, log_command_filter=log_command_filter))


def sudo(command, log_command_filter=identity):
    """
    Run a shell command on a remote host with sudo.

    :param bytes command: The command to run.
    :param callable log_command_filter: A filter to apply to any logging
        of the executed command.

    :return Effect:
    """
    return Effect(Sudo(command=command, log_command_filter=log_command_filter))


def put(content, path, log_content_filter=identity):
    """
    Create a file with the given content on a remote host.

    :param bytes content: The desired contents.
    :param bytes path: The remote path to create.
    :param callable log_content_filter: A filter to apply to any logging
        of the transferred content.

    :return Effect:
    """
    return Effect(Put(
        content=content, path=path, log_content_filter=log_content_filter))


def comment(comment):
    """
    Record a comment to be shown in the documentation corresponding to a task.

    :param bytes comment: The desired comment.

    :return Effect:
    """
    return Effect(Comment(comment=comment))


def run_from_args(command, log_command_filter=identity, pre_command=None):
    """
    Run a command on a remote host. This quotes the provided arguments, so they
    are not interpreted by the shell.

    :param list command: The command to run.
    :param callable log_command_filter: A filter to apply to any logging
        of the executed command.
    :param bytes pre_command: A command to run before the ``command``,
        generally to setup the environment for the command.

    :return Effect:
    """
    return Effect(
        Run.from_args(
            command, log_command_filter=log_command_filter,
            pre_command=pre_command))


def sudo_from_args(command, log_command_filter=identity):
    """
    Run a command on a remote host with sudo. This quotes the provided
    arguments, so they are not interpreted by the shell.

    :param list command: The command to run.
    :param callable log_command_filter: A filter to apply to any logging
        of the executed command.

    :return Effect:
    """
    return Effect(
        Sudo.from_args(command, log_command_filter=log_command_filter))

@@ -3512,32 +3512,50 @@
 _filter=identity
+, pre_command=None
 ):%0A    %22%22%22%0A    R
@@ -3736,32 +3736,163 @@
 ecuted command.%0A
+    :param bytes pre_command: A command to run before the %60%60command%60%60,%0A        generally to setup the environment for the command.%0A
     %22%22%22%0A    retu
@@ -3905,16 +3905,25 @@
 ect(Run(
+%0A        
 command=
@@ -3960,32 +3960,65 @@
 g_command_filter
+,%0A        pre_command=pre_command
 ))%0A%0A%0Adef sudo(co

Add default for pre_command to run function