<commit_before># -*- coding: utf-8 -*-
import logging
import operator
from collections import namedtuple
from ipaddress import IPv4Address, AddressValueError

from flask import request, session, current_app
from flask_login import current_user, AnonymousUserMixin
from sqlalchemy.exc import OperationalError
from werkzeug.local import LocalProxy

from . import sample, wu, gerok, hss
from .sqlalchemy import db
from sipa.utils.exceptions import InvalidConfiguration


logger = logging.getLogger(__name__)


AVAILABLE_DATASOURCES = [
    sample.datasource,
    wu.datasource,
    gerok.datasource,
    hss.datasource,
]


def evaluates_uniquely(objects, func):
    """Return true if the return value of `func` is unique among `objects`.

    This can be used to check whether some attribute `obj.attr` is
    unique among a set of such objects, and can thus be used as a key.

    :param objects: the object on which to apply func to
    :param func: the function to be evaluated, given each object as a
        parameter.  Must return something hashable.
    :return: whether the uniqueness holds
    :rtype: bool
    """
    values = [func(obj) for obj in objects]
    return len(values) == len(set(values))


class Backends:
    """The `Backends` flask extension

    This extension lets you initialize some of the available
    datasources and provides some central methods to look things up,
    like for example the user object from some ip.

    `Backends` builds upon the following concept:

    A user is enabled to log in using different backends, called
    *datasources*.  A :py:class:`DataSource` provides information such
    as its name, the email suffix, the user class, the initialization
    method, and so on.

    Originating from the needs of the [AG DSN](github.com/agdsn), the
    user should not select the backend, but the location where he
    lives.  Thus, he selects a :py:class:`Dormitory`, which has not
    only a name, but also a `display_name` and ip subnets.  The latter
    are needed to semi-authenticate a user based on his ip.

    Usage:

    >>> app = Flask('appname')
    >>> backends = Backends()
    >>> backends.init_app(app)
    >>> app.config['BACKENDS'] = ['name1', 'name2']
    >>> # further initialization…
    >>> backends.init_backends()  # call each backend's init method
    >>> app.run()

    This class provides methods concerning:
    * *initialization* of the extension and backends
    * *lists* of the currently (un)supported dormitories and
       datasources
    * *lookup properties* to access the datasources/dormitories/users
      given certain information (ip, name, …)
    * *proxy methods* to access the current datasource/dormitory
      (similiar to current_user)

    """
    def __init__(self, available_datasources=None):
        """Initialize private lookup dicts.

        :param available_datasources: a list of implemented
            datasources to consider activatable.
        """
        if available_datasources is None:
            available_datasources = AVAILABLE_DATASOURCES
        self.available_datasources = available_datasources

        self._datasources = {}
        self._dormitories = {}
        self._premature_dormitories = {}

    def init_app(self, app):
        """Register self to app and initialize datasources

        The datasources will be registered according to the app's
        config.
        """
        app.extensions['backends'] = self
        self.app = app

        backends = app.config.get('BACKENDS')
        if not backends:
            logger.warning('No backends configured')
            return

        for backend_name in backends:
            self._register_backend(backend_name)

    def _register_backend(self, name):
        """Register a datasource by name.

        First, find the name in the available datasources' names, warn
        on inconsistencies.  If found, add the `name: datasource` pair
        to the private dict.

        :param datasource: the datasource to register
        :raises: `ValueError` if a backend with this name is already
            registered
        """
        if name in self._datasources:
            raise InvalidConfiguration('Datasource {} already registered'
                                       .format(name))

        if not evaluates_uniquely(AVAILABLE_DATASOURCES,
                                  func=operator.attrgetter('name')):
            raise ValueError("Implememented datasources have non-unique names")

        for dsrc in self.available_datasources:
            if dsrc.name == name:
                new_datasource = dsrc
                break
        else:
            raise InvalidConfiguration("{} is not an available datasource"
                                       .format(name))

        self._datasources[name] = new_datasource

        # check for name collisions in the updated dormitories
        new_dormitories = new_datasource.dormitories
        if any(dorm.name in self._dormitories.keys()
               for dorm in new_dormitories):
            raise ValueError("Some dormitories of datasource Dormitory "
                             "have a name already registered")
        for dormitory in new_datasource.dormitories:
            self._register_dormitory(dormitory)

    def _register_dormitory(self, dormitory):
        """Register a dormitory by putting it to the dict

        :param dormitory: The dormitory to register
        :raises: `ValueError` if a dormitory with this name is already
            registered.
        """
        name = dormitory.name
        if name in self._dormitories:
            raise ValueError("Dormitory with name {} already exists"
                             .format(name))
        self._dormitories[name] = dormitory

    @staticmethod
    def backends_preinit(app):
        app.config['SQLALCHEMY_BINDS'] = {}
        db.init_app(app)


    def init_backends(self):
        self.backends_preinit(self.app)

        for datasource in self.datasources:
            if datasource.init_context:
                datasource.init_context(self.app)

    # CENTRAL PROPERTIES

    @property
    def datasources(self):
        """A list of the currently registered datasources"""
        return list(self._datasources.values())

    @property<commit_msg>Pep8 fix<commit_after># -*- coding: utf-8 -*-
import logging
import operator
from collections import namedtuple
from ipaddress import IPv4Address, AddressValueError

from flask import request, session, current_app
from flask_login import current_user, AnonymousUserMixin
from sqlalchemy.exc import OperationalError
from werkzeug.local import LocalProxy

from . import sample, wu, gerok, hss
from .sqlalchemy import db
from sipa.utils.exceptions import InvalidConfiguration


logger = logging.getLogger(__name__)


AVAILABLE_DATASOURCES = [
    sample.datasource,
    wu.datasource,
    gerok.datasource,
    hss.datasource,
]


def evaluates_uniquely(objects, func):
    """Return true if the return value of `func` is unique among `objects`.

    This can be used to check whether some attribute `obj.attr` is
    unique among a set of such objects, and can thus be used as a key.

    :param objects: the object on which to apply func to
    :param func: the function to be evaluated, given each object as a
        parameter.  Must return something hashable.
    :return: whether the uniqueness holds
    :rtype: bool
    """
    values = [func(obj) for obj in objects]
    return len(values) == len(set(values))


class Backends:
    """The `Backends` flask extension

    This extension lets you initialize some of the available
    datasources and provides some central methods to look things up,
    like for example the user object from some ip.

    `Backends` builds upon the following concept:

    A user is enabled to log in using different backends, called
    *datasources*.  A :py:class:`DataSource` provides information such
    as its name, the email suffix, the user class, the initialization
    method, and so on.

    Originating from the needs of the [AG DSN](github.com/agdsn), the
    user should not select the backend, but the location where he
    lives.  Thus, he selects a :py:class:`Dormitory`, which has not
    only a name, but also a `display_name` and ip subnets.  The latter
    are needed to semi-authenticate a user based on his ip.

    Usage:

    >>> app = Flask('appname')
    >>> backends = Backends()
    >>> backends.init_app(app)
    >>> app.config['BACKENDS'] = ['name1', 'name2']
    >>> # further initialization…
    >>> backends.init_backends()  # call each backend's init method
    >>> app.run()

    This class provides methods concerning:
    * *initialization* of the extension and backends
    * *lists* of the currently (un)supported dormitories and
       datasources
    * *lookup properties* to access the datasources/dormitories/users
      given certain information (ip, name, …)
    * *proxy methods* to access the current datasource/dormitory
      (similiar to current_user)

    """
    def __init__(self, available_datasources=None):
        """Initialize private lookup dicts.

        :param available_datasources: a list of implemented
            datasources to consider activatable.
        """
        if available_datasources is None:
            available_datasources = AVAILABLE_DATASOURCES
        self.available_datasources = available_datasources

        self._datasources = {}
        self._dormitories = {}
        self._premature_dormitories = {}

    def init_app(self, app):
        """Register self to app and initialize datasources

        The datasources will be registered according to the app's
        config.
        """
        app.extensions['backends'] = self
        self.app = app

        backends = app.config.get('BACKENDS')
        if not backends:
            logger.warning('No backends configured')
            return

        for backend_name in backends:
            self._register_backend(backend_name)

    def _register_backend(self, name):
        """Register a datasource by name.

        First, find the name in the available datasources' names, warn
        on inconsistencies.  If found, add the `name: datasource` pair
        to the private dict.

        :param datasource: the datasource to register
        :raises: `ValueError` if a backend with this name is already
            registered
        """
        if name in self._datasources:
            raise InvalidConfiguration('Datasource {} already registered'
                                       .format(name))

        if not evaluates_uniquely(AVAILABLE_DATASOURCES,
                                  func=operator.attrgetter('name')):
            raise ValueError("Implememented datasources have non-unique names")

        for dsrc in self.available_datasources:
            if dsrc.name == name:
                new_datasource = dsrc
                break
        else:
            raise InvalidConfiguration("{} is not an available datasource"
                                       .format(name))

        self._datasources[name] = new_datasource

        # check for name collisions in the updated dormitories
        new_dormitories = new_datasource.dormitories
        if any(dorm.name in self._dormitories.keys()
               for dorm in new_dormitories):
            raise ValueError("Some dormitories of datasource Dormitory "
                             "have a name already registered")
        for dormitory in new_datasource.dormitories:
            self._register_dormitory(dormitory)

    def _register_dormitory(self, dormitory):
        """Register a dormitory by putting it to the dict

        :param dormitory: The dormitory to register
        :raises: `ValueError` if a dormitory with this name is already
            registered.
        """
        name = dormitory.name
        if name in self._dormitories:
            raise ValueError("Dormitory with name {} already exists"
                             .format(name))
        self._dormitories[name] = dormitory

    @staticmethod
    def backends_preinit(app):
        app.config['SQLALCHEMY_BINDS'] = {}
        db.init_app(app)

    def init_backends(self):
        self.backends_preinit(self.app)

        for datasource in self.datasources:
            if datasource.init_context:
                datasource.init_context(self.app)

    # CENTRAL PROPERTIES

    @property
    def datasources(self):
        """A list of the currently registered datasources"""
        return list(self._datasources.values())

    @property
{'fraction_non_alphanumeric': 0.0730970920069919, 'fraction_numerical': 0.000715080247894486, 'mean_word_length': 2.5188705619233995, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 45, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 6, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1965674', 'n_tokens_mistral': 3455, 'n_tokens_neox': 3239, 'n_words': 1168}