<commit_before>"""
This module contains the client socket API. This API is exposed to the
user throught the backend manager (
:class:`pyqode.core.managers.BackendManager`)

"""
import json
import logging
import socket
import struct
import sys
import uuid
from pyqode.qt import QtCore, QtNetwork
from pyqode.core.backend import NotConnected


def _logger():
    return logging.getLogger(__name__)


#: Dictionary of socket errors messages
SOCKET_ERROR_STRINGS = {
    0: 'the connection was refused by the peer (or timed out).',
    1: 'the remote host closed the connection.',
    2: 'the host address was not found.',
    3: 'the socket operation failed because the application lacked the '
       'required privileges.',
    4: 'the local system ran out of resources (e.g., too many sockets).',
    5: 'the socket operation timed out.',
    6: "the datagram was larger than the operating system's limit (which can "
       "be as low as 8192 bytes).",
    7: 'an error occurred with the network (e.g., the network cable was '
       'accidentally plugged out).',
    # 9 and 10 are UDP only, we only care about TCP.
    # all others erros are unlikely to happen in our case (proxy related
    # errors)
    - 1: 'an unidentified error occurred.',
}

#: Dictionary of process errors messages
PROCESS_ERROR_STRING = {
    0: 'the process failed to start. Either the invoked program is missing, '
       'or you may have insufficient permissions to invoke the program.',
    1: 'the process crashed some time after starting successfully.',
    2: 'the last waitFor...() function timed out. The state of QProcess is '
       'unchanged, and you can try calling waitFor...() again.',
    4: 'an error occurred when attempting to write to the process. '
       'For example, the process may not be running, or it may have closed '
       'its input channel.',
    3: 'an error occurred when attempting to read from the process. '
       'For example, the process may not be running.',
    5: 'an unknown error occurred. This is the default return value of '
       'error().'
}


#: Delay before retrying to connect to server [ms]
TIMEOUT_BEFORE_RETRY = 100
#: Max retry
MAX_RETRY = 100


class JsonTcpClient(QtNetwork.QTcpSocket):
    """
    A json tcp client socket used to start and communicate with the pyqode
    server.

    It uses a simple message protocol. A message is made up of two parts.
    parts:
      - header: contains the length of the payload. (4bytes)
      - payload: data as a json string.

    """

    def __init__(self, parent):
        super(JsonTcpClient, self).__init__(parent)
        self._port = -1
        self.connected.connect(self._on_connected)
        self.error.connect(self._on_error)
        self.disconnected.connect(self._on_disconnected)
        self.readyRead.connect(self._on_ready_read)
        self._header_complete = False
        self._header_buf = bytes()
        self._to_read = 0
        self._data_buf = bytes()
        #: associate request uuid with a callback, popped once executed
        self._callbacks = {}
        self.is_connected = False
        self._process = None
        self._connection_attempts = 0

    def _terminate_server_process(self):
        """ Terminates the server process """
        _logger().debug('terminating backend process')
        self._process.terminate()
        while not self._process.waitForFinished(1):
            pass

    def close(self):
        """ Closes the socket and terminates the server process. """
        try:
            if self._process and self._process.running:
                if self.is_connected:
                    # send shutdown request
                    self.send('shutdown')
                if self._process and self._process.running:
                    self._terminate_server_process()
                    self.is_connected = False
                super(JsonTcpClient, self).close()
                _logger().info('process terminated')
        except (AttributeError, RuntimeError):
            pass

    def start(self, server_script, interpreter=sys.executable, args=None,
              port=None):
        """
        Starts a pyqode server (and connect our client socket when the server
        process has started). The server is started with a random free port
        on local host (the port number is defined by command line args).

        The server is a python script that starts a
        :class:`pyqode.core.backend.server.JsonServer`. You (the user) must
        write the server script so that you can apply your own configuration
        server side.

        The script can be run with a custom interpreter. The default is to use
        sys.executable.

        :param str server_script: Path to the server main script.
        :param str interpreter: The python interpreter to use to run the server
            script. If None, sys.executable is used unless we are in a frozen
            application (cx_Freeze). The executable is not used if the
            executable scripts ends with '.exe' on Windows
        :param list args: list of additional command line args to use to start
            the server process.

        """
        major, minor, build = sys.version_info[:3]
        _logger().debug('running with python %d.%d.%d', major, minor, build)
        self._process = _ServerProcess(self.parent())
        self._process.started.connect(self._on_process_started)
        if not port:
            self._port = self.pick_free_port()
        else:
            self._port = port
        if hasattr(sys, "frozen") and not server_script.endswith('.py'):
            # frozen server script on windows/mac does not need an interpreter
            program = server_script
            pgm_args = [str(self._port)]
        else:
            program = interpreter
            pgm_args = [server_script, str(self._port)]<commit_msg>Make sure the server script path is not a pyc file<commit_after>"""
This module contains the client socket API. This API is exposed to the
user throught the backend manager (
:class:`pyqode.core.managers.BackendManager`)

"""
import json
import logging
import socket
import struct
import sys
import uuid
from pyqode.qt import QtCore, QtNetwork
from pyqode.core.backend import NotConnected


def _logger():
    return logging.getLogger(__name__)


#: Dictionary of socket errors messages
SOCKET_ERROR_STRINGS = {
    0: 'the connection was refused by the peer (or timed out).',
    1: 'the remote host closed the connection.',
    2: 'the host address was not found.',
    3: 'the socket operation failed because the application lacked the '
       'required privileges.',
    4: 'the local system ran out of resources (e.g., too many sockets).',
    5: 'the socket operation timed out.',
    6: "the datagram was larger than the operating system's limit (which can "
       "be as low as 8192 bytes).",
    7: 'an error occurred with the network (e.g., the network cable was '
       'accidentally plugged out).',
    # 9 and 10 are UDP only, we only care about TCP.
    # all others erros are unlikely to happen in our case (proxy related
    # errors)
    - 1: 'an unidentified error occurred.',
}

#: Dictionary of process errors messages
PROCESS_ERROR_STRING = {
    0: 'the process failed to start. Either the invoked program is missing, '
       'or you may have insufficient permissions to invoke the program.',
    1: 'the process crashed some time after starting successfully.',
    2: 'the last waitFor...() function timed out. The state of QProcess is '
       'unchanged, and you can try calling waitFor...() again.',
    4: 'an error occurred when attempting to write to the process. '
       'For example, the process may not be running, or it may have closed '
       'its input channel.',
    3: 'an error occurred when attempting to read from the process. '
       'For example, the process may not be running.',
    5: 'an unknown error occurred. This is the default return value of '
       'error().'
}


#: Delay before retrying to connect to server [ms]
TIMEOUT_BEFORE_RETRY = 100
#: Max retry
MAX_RETRY = 100


class JsonTcpClient(QtNetwork.QTcpSocket):
    """
    A json tcp client socket used to start and communicate with the pyqode
    server.

    It uses a simple message protocol. A message is made up of two parts.
    parts:
      - header: contains the length of the payload. (4bytes)
      - payload: data as a json string.

    """

    def __init__(self, parent):
        super(JsonTcpClient, self).__init__(parent)
        self._port = -1
        self.connected.connect(self._on_connected)
        self.error.connect(self._on_error)
        self.disconnected.connect(self._on_disconnected)
        self.readyRead.connect(self._on_ready_read)
        self._header_complete = False
        self._header_buf = bytes()
        self._to_read = 0
        self._data_buf = bytes()
        #: associate request uuid with a callback, popped once executed
        self._callbacks = {}
        self.is_connected = False
        self._process = None
        self._connection_attempts = 0

    def _terminate_server_process(self):
        """ Terminates the server process """
        _logger().debug('terminating backend process')
        self._process.terminate()
        while not self._process.waitForFinished(1):
            pass

    def close(self):
        """ Closes the socket and terminates the server process. """
        try:
            if self._process and self._process.running:
                if self.is_connected:
                    # send shutdown request
                    self.send('shutdown')
                if self._process and self._process.running:
                    self._terminate_server_process()
                    self.is_connected = False
                super(JsonTcpClient, self).close()
                _logger().info('process terminated')
        except (AttributeError, RuntimeError):
            pass

    def start(self, server_script, interpreter=sys.executable, args=None,
              port=None):
        """
        Starts a pyqode server (and connect our client socket when the server
        process has started). The server is started with a random free port
        on local host (the port number is defined by command line args).

        The server is a python script that starts a
        :class:`pyqode.core.backend.server.JsonServer`. You (the user) must
        write the server script so that you can apply your own configuration
        server side.

        The script can be run with a custom interpreter. The default is to use
        sys.executable.

        :param str server_script: Path to the server main script.
        :param str interpreter: The python interpreter to use to run the server
            script. If None, sys.executable is used unless we are in a frozen
            application (cx_Freeze). The executable is not used if the
            executable scripts ends with '.exe' on Windows
        :param list args: list of additional command line args to use to start
            the server process.

        """
        major, minor, build = sys.version_info[:3]
        _logger().debug('running with python %d.%d.%d', major, minor, build)
        self._process = _ServerProcess(self.parent())
        self._process.started.connect(self._on_process_started)
        server_script = server_script.replace('.pyc', '.py')
        if not port:
            self._port = self.pick_free_port()
        else:
            self._port = port
        if hasattr(sys, "frozen") and not server_script.endswith('.py'):
            # frozen server script on windows/mac does not need an interpreter
            program = server_script
            pgm_args = [str(self._port)]
        else:
            program = interpreter
            pgm_args = [server_script, str(self._port)]
{'fraction_non_alphanumeric': 0.07977376329562721, 'fraction_numerical': 0.005740334290055715, 'mean_word_length': 2.462010520163647, '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': '4979235', 'n_tokens_mistral': 3158, 'n_tokens_neox': 3019, 'n_words': 1264}