<commit_before>"""
This module hosts the Connection class that manages the connection to the mysql database,
 and the `conn` function that provides access to a persistent connection in datajoint.
"""
import warnings
from contextlib import contextmanager
import pymysql as client
import logging

from . import config
from . import DataJointError
from .dependencies import Dependencies
from .jobs import JobManager
from pymysql import err

logger = logging.getLogger(__name__)


def conn(host=None, user=None, passwd=None, init_fun=None, reset=False):
    """
    Returns a persistent connection object to be shared by multiple modules.
    If the connection is not yet established or reset=True, a new connection is set up.
    If connection information is not provided, it is taken from config which takes the
    information from dj_local_conf.json. If the password is not specified in that file
    datajoint prompts for the password.

    :param host: hostname
    :param user: mysql user
    :param passwd: <PASSWORD>
    :param init_fun: initialization function
    :param reset: whether the connection should be reseted or not
    """
    if not hasattr(conn, 'connection') or reset:
        host = host if host is not None else config['database.host']
        user = user if user is not None else config['database.user']
        passwd = passwd if passwd is not None else config['database.password']
        if passwd is None:
            passwd = input("Please enter database password: ")
        init_fun = init_fun if init_fun is not None else config['connection.init_function']
        conn.connection = Connection(host, user, passwd, init_fun)
    return conn.connection


class Connection:
    """
    A dj.Connection object manages a connection to a database server.
    It also catalogues modules, schemas, tables, and their dependencies (foreign keys).

    Most of the parameters below should be set in the local configuration file.

    :param host: host name
    :param user: user name
    :param passwd: password
    :param init_fun: initialization function
    """

    def __init__(self, host, user, passwd, init_fun=None):
        if ':' in host:
            host, port = host.split(':')
            port = int(port)
        else:
            port = config['database.port']
        self.conn_info = dict(host=host, port=port, user=user, passwd=<PASSWORD>)
        self.init_fun = init_fun
        self.connect()
        if self.is_connected:
            logger.info("Connected {user}@{host}:{port}".format(**self.conn_info))
        else:
            raise DataJointError('Connection failed.')
        self._conn.autocommit(True)
        self._in_transaction = False
        self.jobs = JobManager(self)
        self.schemas = dict()
        self.dependencies = Dependencies(self)

    def __del__(self):
        logger.info('Disconnecting {user}@{host}:{port}'.format(**self.conn_info))
        self._conn.close()

    def __eq__(self, other):
        return self.conn_info == other.conn_info

    def __repr__(self):
        connected = "connected" if self.is_connected else "disconnected"
        return "DataJoint connection ({connected}) {user}@{host}:{port}".format(
            connected=connected, **self.conn_info)

    def connect(self, init_fun=None):
        """
        Connects to the database server.

        :param init_fun: initialization function passed to pymysql
        """
        self._conn = client.connect(init_command=self.init_fun, **self.conn_info)

    def register(self, schema):
        self.schemas[schema.database] = schema

    @property
    def is_connected(self):
        """
        Returns true if the object is connected to the database server.
        """
        return self._conn.ping()

    def query(self, query, args=(), as_dict=False):
        """
        Execute the specified query and return the tuple generator (cursor).

        :param query: mysql query
        :param args: additional arguments for the client.cursor
        :param as_dict: If as_dict is set to True, the returned cursor objects returns
                        query results as dictionary.
        """
        cursor = client.cursors.DictCursor if as_dict else client.cursors.Cursor
        cur = self._conn.cursor(cursor=cursor)

        # Log the query
        try:
            logger.debug("Executing SQL:" + query[0:300])
            cur.execute(query, args) # TODO insert reconnect
        except err.OperationalError as e:
            if config['database.reconnect']:
                print(e)
                warnings.warn('''Mysql server has gone away.
                    Reconnected to the server. Data from transactions might be lost and referential constraints may
                    be violated. You can switch of this behavior by setting the 'database.reconnect' to False.
                    ''')
                self.connect()
            else:
                raise
        return cur

    # ---------- transaction processing
    @property
    def in_transaction(self):
        """
        :return: True if there is an open transaction.
        """
        self._in_transaction = self._in_transaction and self.is_connected
        return self._in_transaction

    def start_transaction(self):
        """
        Starts a transaction error.

        :raise DataJointError: if there is an ongoing transaction.
        """
        if self.in_transaction:
            raise DataJointError("Nested connections are not supported.")
        self.query('START TRANSACTION WITH CONSISTENT SNAPSHOT')
        self._in_transaction = True
        logger.info("Transaction started")

    def cancel_transaction(self):
        """<commit_msg>fix typo<commit_after>"""
This module hosts the Connection class that manages the connection to the mysql database,
 and the `conn` function that provides access to a persistent connection in datajoint.
"""
import warnings
from contextlib import contextmanager
import pymysql as client
import logging

from . import config
from . import DataJointError
from .dependencies import Dependencies
from .jobs import JobManager
from pymysql import err

logger = logging.getLogger(__name__)


def conn(host=None, user=None, passwd=None, init_fun=None, reset=False):
    """
    Returns a persistent connection object to be shared by multiple modules.
    If the connection is not yet established or reset=True, a new connection is set up.
    If connection information is not provided, it is taken from config which takes the
    information from dj_local_conf.json. If the password is not specified in that file
    datajoint prompts for the password.

    :param host: hostname
    :param user: mysql user
    :param passwd: <PASSWORD>
    :param init_fun: initialization function
    :param reset: whether the connection should be reseted or not
    """
    if not hasattr(conn, 'connection') or reset:
        host = host if host is not None else config['database.host']
        user = user if user is not None else config['database.user']
        passwd = passwd if passwd is not None else config['database.password']
        if passwd is None:
            passwd = input("Please enter database password: ")
        init_fun = init_fun if init_fun is not None else config['connection.init_function']
        conn.connection = Connection(host, user, passwd, init_fun)
    return conn.connection


class Connection:
    """
    A dj.Connection object manages a connection to a database server.
    It also catalogues modules, schemas, tables, and their dependencies (foreign keys).

    Most of the parameters below should be set in the local configuration file.

    :param host: host name
    :param user: user name
    :param passwd: password
    :param init_fun: initialization function
    """

    def __init__(self, host, user, passwd, init_fun=None):
        if ':' in host:
            host, port = host.split(':')
            port = int(port)
        else:
            port = config['database.port']
        self.conn_info = dict(host=host, port=port, user=user, passwd=<PASSWORD>)
        self.init_fun = init_fun
        self.connect()
        if self.is_connected:
            logger.info("Connected {user}@{host}:{port}".format(**self.conn_info))
        else:
            raise DataJointError('Connection failed.')
        self._conn.autocommit(True)
        self._in_transaction = False
        self.jobs = JobManager(self)
        self.schemas = dict()
        self.dependencies = Dependencies(self)

    def __del__(self):
        logger.info('Disconnecting {user}@{host}:{port}'.format(**self.conn_info))
        self._conn.close()

    def __eq__(self, other):
        return self.conn_info == other.conn_info

    def __repr__(self):
        connected = "connected" if self.is_connected else "disconnected"
        return "DataJoint connection ({connected}) {user}@{host}:{port}".format(
            connected=connected, **self.conn_info)

    def connect(self, init_fun=None):
        """
        Connects to the database server.

        :param init_fun: initialization function passed to pymysql
        """
        self._conn = client.connect(init_command=self.init_fun, **self.conn_info)

    def register(self, schema):
        self.schemas[schema.database] = schema

    @property
    def is_connected(self):
        """
        Returns true if the object is connected to the database server.
        """
        return self._conn.ping()

    def query(self, query, args=(), as_dict=False):
        """
        Execute the specified query and return the tuple generator (cursor).

        :param query: mysql query
        :param args: additional arguments for the client.cursor
        :param as_dict: If as_dict is set to True, the returned cursor objects returns
                        query results as dictionary.
        """
        cursor = client.cursors.DictCursor if as_dict else client.cursors.Cursor
        cur = self._conn.cursor(cursor=cursor)

        # Log the query
        try:
            logger.debug("Executing SQL:" + query[0:300])
            cur.execute(query, args) # TODO insert reconnect
        except err.OperationalError as e:
            if config['database.reconnect']:
                print(e)
                warnings.warn('''Mysql server has gone away.
                    Reconnected to the server. Data from transactions might be lost and referential constraints may
                    be violated. You can switch off this behavior by setting the 'database.reconnect' to False.
                    ''')
                self.connect()
            else:
                raise
        return cur

    # ---------- transaction processing
    @property
    def in_transaction(self):
        """
        :return: True if there is an open transaction.
        """
        self._in_transaction = self._in_transaction and self.is_connected
        return self._in_transaction

    def start_transaction(self):
        """
        Starts a transaction error.

        :raise DataJointError: if there is an ongoing transaction.
        """
        if self.in_transaction:
            raise DataJointError("Nested connections are not supported.")
        self.query('START TRANSACTION WITH CONSISTENT SNAPSHOT')
        self._in_transaction = True
        logger.info("Transaction started")

    def cancel_transaction(self):
        """
{'fraction_non_alphanumeric': 0.08579465541490858, 'fraction_numerical': 0.0007032348804500703, 'mean_word_length': 2.5354257302672467, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 7, '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': '3791968', 'n_tokens_mistral': 2985, 'n_tokens_neox': 2880, 'n_words': 1047}