<commit_before>#!/usr/bin/env python
# -*- coding: utf8 -*-
# *****************************************************************
# **       PTS -- Python Toolkit for working with SKIRT          **
# **       © Astronomical Observatory, Ghent University          **
# *****************************************************************

## \package pts.core.basics.remote Contains the Remote class.

# -----------------------------------------------------------------

# Import standard modules
import os
import re
import sys
import pexpect
from pexpect import pxssh
import tempfile

# Import astronomical modules
from astropy.utils import lazyproperty

# Import the relevant PTS classes and modules
from .host import Host
from .vpn import VPN
from ..tools.logging import log
from ..tools import parsing
from ..tools import filesystem as fs
from ..tools import time
from .task import Task
from ..tools import introspection

# -----------------------------------------------------------------

connected_remotes = dict()

# -----------------------------------------------------------------

class Remote(object):

    """
    This function ...
    """

    def __init__(self):

        """
        The constructor ...
        :return:
        """

        # Call the constructor of the base class
        super(Remote, self).__init__()

        # -- Attributes --

        # The SSH interface, an instance of the pxssh class
        self.ssh = pxssh.pxssh()

        # The host instance
        self.host = None

        # The VPN service
        self.vpn = None

        # A flag indicating whether the connection with the remote has been established
        self.connected = False

        # A regular expression object that strips away special unicode characters, used on the remote console output
        self.ansi_escape = re.compile(r'\x1b[^m]*m')

        # Flag that says whether we are in a remote python session
        self.in_python_session = False

    # -----------------------------------------------------------------

    def setup(self, host_id, cluster=None):

        """
        This function ...
        :param host_id:
        :param cluster:
        :return:
        """

        # Create the host object
        self.host = Host(host_id, cluster)

        # If a VPN connection is required for the remote host
        if self.host.requires_vpn: self.connect_to_vpn()

        # Make the connection
        self.login()

        # TODO: swap to cluster here?

        # Load the necessary modules
        if self.host.modules is not None:

            log.info("Loading necessary modules...")
            self.execute("module load " + " ".join(self.host.modules), output=False)

        # Check whether the output directory exists
        if not self.is_directory(self.host.output_path): raise ValueError("The specified output path does not exist")

    # -----------------------------------------------------------------

    def __del__(self):

        """
        The destructor ...
        :return:
        """

        # Disconnect from the remote host
        self.logout()

    # -----------------------------------------------------------------

    def connect_to_vpn(self):

        """
        This function ...
        :return:
        """

        # Inform the user
        log.info("Connecting to vpn service '" + self.host.vpn.service + "' ...")

        # Connect to the VPN service
        self.vpn = VPN(self.host.vpn.service)
        self.vpn.connect(self.host.vpn.user, self.host.vpn.password, self.host.vpn.secret, self.host.vpn.prompt_time_delay)

    # -----------------------------------------------------------------

    def login(self):

        """
        This function ...
        :return:
        """

        # Inform the user
        log.info("Logging in to the remote environment on host '" + self.host.id + "' ...")

        # Connect to the remote host
        self.connected = self.ssh.login(self.host.name, self.host.user, self.host.password)

        # Check whether connection was succesful
        if not self.connected: raise RuntimeError("Connection failed")

        # If the connection was succesful, add the remote to the dictionary of currently connected remotes
        if self.connected: connected_remotes[self.host.id] = self

    # -----------------------------------------------------------------

    def logout(self):

        """
        This function ...
        :return:
        """

        # Inform the user
        log.info("Logging out from the remote environment ...")

        # Disconnect
        if self.connected:

            self.ssh.logout()
            connected_remotes[self.host.id] = None
            self.connected = False

        # Disconnect from the VPN service if necessary
        if self.vpn is not None: self.vpn.disconnect()

    # -----------------------------------------------------------------

    def start_screen(self, name, local_script_path, script_destination, screen_output_path=None, keep_remote_script=False):

        """
        This function ...<commit_msg>Fixed a small issue that caused a weird Error message at the end of a script that uses Remote instances<commit_after>#!/usr/bin/env python
# -*- coding: utf8 -*-
# *****************************************************************
# **       PTS -- Python Toolkit for working with SKIRT          **
# **       © Astronomical Observatory, Ghent University          **
# *****************************************************************

## \package pts.core.basics.remote Contains the Remote class.

# -----------------------------------------------------------------

# Import standard modules
import os
import re
import sys
import pexpect
from pexpect import pxssh
import tempfile

# Import astronomical modules
from astropy.utils import lazyproperty

# Import the relevant PTS classes and modules
from .host import Host
from .vpn import VPN
from ..tools.logging import log
from ..tools import parsing
from ..tools import filesystem as fs
from ..tools import time
from .task import Task
from ..tools import introspection

# -----------------------------------------------------------------

connected_remotes = dict()

# -----------------------------------------------------------------

class Remote(object):

    """
    This function ...
    """

    def __init__(self):

        """
        The constructor ...
        :return:
        """

        # Call the constructor of the base class
        super(Remote, self).__init__()

        # -- Attributes --

        # The SSH interface, an instance of the pxssh class
        self.ssh = pxssh.pxssh()

        # The host instance
        self.host = None

        # The VPN service
        self.vpn = None

        # A flag indicating whether the connection with the remote has been established
        self.connected = False

        # A regular expression object that strips away special unicode characters, used on the remote console output
        self.ansi_escape = re.compile(r'\x1b[^m]*m')

        # Flag that says whether we are in a remote python session
        self.in_python_session = False

    # -----------------------------------------------------------------

    def setup(self, host_id, cluster=None):

        """
        This function ...
        :param host_id:
        :param cluster:
        :return:
        """

        # Create the host object
        self.host = Host(host_id, cluster)

        # If a VPN connection is required for the remote host
        if self.host.requires_vpn: self.connect_to_vpn()

        # Make the connection
        self.login()

        # TODO: swap to cluster here?

        # Load the necessary modules
        if self.host.modules is not None:

            log.info("Loading necessary modules...")
            self.execute("module load " + " ".join(self.host.modules), output=False)

        # Check whether the output directory exists
        if not self.is_directory(self.host.output_path): raise ValueError("The specified output path does not exist")

    # -----------------------------------------------------------------

    def __del__(self):

        """
        The destructor ...
        :return:
        """

        # Disconnect from the remote host
        self.logout()

    # -----------------------------------------------------------------

    def connect_to_vpn(self):

        """
        This function ...
        :return:
        """

        # Inform the user
        log.info("Connecting to vpn service '" + self.host.vpn.service + "' ...")

        # Connect to the VPN service
        self.vpn = VPN(self.host.vpn.service)
        self.vpn.connect(self.host.vpn.user, self.host.vpn.password, self.host.vpn.secret, self.host.vpn.prompt_time_delay)

    # -----------------------------------------------------------------

    def login(self):

        """
        This function ...
        :return:
        """

        # Inform the user
        log.info("Logging in to the remote environment on host '" + self.host.id + "' ...")

        # Connect to the remote host
        self.connected = self.ssh.login(self.host.name, self.host.user, self.host.password)

        # Check whether connection was succesful
        if not self.connected: raise RuntimeError("Connection failed")

        # If the connection was succesful, add the remote to the dictionary of currently connected remotes
        if self.connected: connected_remotes[self.host.id] = self

    # -----------------------------------------------------------------

    def logout(self):

        """
        This function ...
        :return:
        """

        # Inform the user
        if log is not None: log.info("Logging out from the remote environment ...")

        # Disconnect
        if self.connected:

            self.ssh.logout()
            #connected_remotes[self.host.id] = None
            if connected_remotes is not None: del connected_remotes[self.host.id]
            self.connected = False

        # Disconnect from the VPN service if necessary
        if self.vpn is not None: self.vpn.disconnect()

    # -----------------------------------------------------------------

    def start_screen(self, name, local_script_path, script_destination, screen_output_path=None, keep_remote_script=False):

        """
        This function ...
{'fraction_non_alphanumeric': 0.21976215798124335, 'fraction_numerical': 0.0003867349898482065, 'mean_word_length': 1.5180136319376825, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 3, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '3179171', 'n_tokens_mistral': 2556, 'n_tokens_neox': 2409, 'n_words': 795}