<commit_before># Copyright (C) 2014 Universidad Politecnica de Madrid
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

import abc
import six

from keystone.common import dependency
from keystone import exception
from keystone.common import extension
from keystone.common import manager

from keystone.openstack.common import log

LOG = log.getLogger(__name__)

EXTENSION_DATA = {
    'name': 'UPM-FIWARE Roles API',
    'namespace': 'https://github.com/ging/keystone/'
                 'OS-ROLES/v1.0',
    'alias': 'OS-ROLES',
    'updated': '2014-11-3T12:00:0-00:00',
    'description': 'UPM\'s Roles provider for applications in the FIWARE GE \
                    Identity Manager implementation',
    'links': [
        {
            'rel': 'describedby',
            # TODO(garcianavalon): needs a description
            'type': 'text/html',
            'href': 'https://github.com/ging/keystone/wiki',
        }
    ]}
extension.register_admin_extension(EXTENSION_DATA['alias'], EXTENSION_DATA)
extension.register_public_extension(EXTENSION_DATA['alias'], EXTENSION_DATA)

ASSIGN_ALL_PUBLIC_ROLES_PERMISSION = 'Get and assign all public application roles'
ASSIGN_OWNED_PUBLIC_ROLES_PERMISSION = 'Get and assign only public owned roles'
ASSIGN_INTERNAL_ROLES_PERMISSION = 'Get and assign all internal application roles'
MANAGE_APPLICATION_PERMISSION = 'Manage the application'
MANAGE_ROLES_PERMISSION = 'Manage roles'

@dependency.requires('assignment_api')
@dependency.provider('roles_api')
class RolesManager(manager.Manager):
    """Roles and Permissions Manager.

    See :mod:`keystone.common.manager.Manager` for more details on
    how this dynamically calls the backend.

    """

    def __init__(self):
        super(RolesManager, self).__init__(
            'keystone.contrib.roles.backends.sql.Roles')

    def get_authorized_organizations(self, user,
                                    application_id,
                                    remove_default_organization=False):
        # roles associated with this user in the application
        assignments = self.driver.list_role_user_assignments(
            user_id=user['id'], application_id=application_id)

        # organizations the user is in
        organizations = self.assignment_api.list_projects_for_user(user['id'])

        # filter to only organizations with roles
        organizations = [org for org in organizations
            if org['id'] in [a['organization_id'] for a in assignments]]

        for organization in organizations:
            role_ids = [a['role_id'] for a in assignments
                        if a['organization_id'] == organization['id']]
            # Load roles' names
            organization['roles'] = [dict(id=r['id'], name=r['name']) for r
                    in [self.driver.get_role(id) for id in role_ids]]

        if remove_default_organization:
            # always remove the default org
            organizations = [org for org in organizations
                if not org['id'] == user['default_project_id']]

        return organizations

    def list_applications_user_allowed_to_manage_roles(self, user_id,
                                                       organization_id):
        """List all the applications in which the user has at least
        one role with the permission 'Manage the application' permission.
        """
        assignments = self.driver.list_role_user_assignments(
            user_id, organization_id)
        return self._get_allowed_applications_manage_roles(assignments)

    def list_applications_organization_allowed_to_manage_roles(self,
                                                               organization_id):
        """List all the applications in which the organization has at least
        one role with the permission 'Manage the application' permission.
        """
        assignments = self.driver.list_role_organization_assignments(
            organization_id)
        return self._get_allowed_applications_manage_roles(assignments)

    def list_applications_user_allowed_to_manage(self, user_id,
                                                 organization_id):
        """List all the applications in which the user has at least
        one role with the permission 'Manage the application' permission.
        """
        assignments = self.driver.list_role_user_assignments(
            user_id, organization_id)
        return self._get_allowed_applications(assignments)

    def list_applications_organization_allowed_to_manage(self,
                                                         organization_id):
        """List all the applications in which the organization has at least
        one role with the permission 'Manage the application' permission.
        """
        assignments = self.driver.list_role_organization_assignments(
            organization_id)
        return self._get_allowed_applications(assignments)

    def list_roles_user_allowed_to_assign(self, user_id, organization_id):
        """List the roles that a given user can assign. To be able to assign roles
        a user needs a certain permission. It can be the 'get and assign all
        application's roles' or the 'get and assign owned roles'

        :param user_id: user with roles
        :type user_id: string
        :param organization_id: organization-scope
        :type organization_id: string
        :returns: dictionary with application ids as keys and list
            of role ids as values
        """
        assignments = self.driver.list_role_user_assignments(
            user_id, organization_id)
        return self._get_allowed_roles(assignments)

    def list_roles_organization_allowed_to_assign(self, organization_id):
        """List the roles that a given organization can assign. To be able to assign roles
        a organization needs a certain permission. It can be the 'get and assign all
        application's roles' or the 'get and assign owned roles'

        :param organization_id: organization with roles
        :type organization_id: string
        :returns: dictionary with application ids as keys and list
            of role ids as values
        """
        assignments = self.driver.list_role_organization_assignments(
            organization_id)
        return self._get_allowed_roles(assignments)

    def _get_all_internal_permissions(self, current_assignments):
        applications = set([a['application_id'] for a in current_assignments])
        permissions = {}
        for application_id in applications:
            owned_roles = [a['role_id'] for a in current_assignments
                           if a['application_id'] == application_id]
            permissions[application_id] = []
            for role_id in owned_roles:
                permissions[application_id] += \
                    [p['name'] for p in
                     self.driver.list_permissions_for_role(role_id)
                     if p['is_internal'] == True]
        return permissions

    def _get_allowed_applications_manage_roles(self, current_assignments):
        application_permissions = self._get_all_internal_permissions(
            current_assignments)
        allowed_applications = []
        for application in application_permissions:
            permissions = application_permissions[application]

            # Check if the manage internal permission is present
            if MANAGE_ROLES_PERMISSION in permissions:
                allowed_applications.append(application)

        return allowed_applications

    def _get_allowed_applications(self, current_assignments):
        application_permissions = self._get_all_internal_permissions(
            current_assignments)
        allowed_applications = []
        for application in application_permissions:
            permissions = application_permissions[application]

            # Check if the manage internal permission is present
            if MANAGE_APPLICATION_PERMISSION in permissions:
                allowed_applications.append(application)

        return allowed_applications

    def _get_allowed_roles(self, current_assignments):
        application_permissions = self._get_all_internal_permissions(
            current_assignments)
        allowed_roles = {}
        for application in application_permissions:
            permissions = application_permissions[application]
            roles_to_add = []
            # Now check if the internal permissions are present
            if ASSIGN_ALL_PUBLIC_ROLES_PERMISSION in permissions:
                # add all public roles in the application
                roles_to_add += set([r['id'] for r
                    in self.driver.list_roles(application_id=application)])

            elif ASSIGN_OWNED_PUBLIC_ROLES_PERMISSION in permissions:
                # add only the public roles the user has in the application
                roles_to_add += [a['role_id'] for a
                    in current_assignments
                    if a['application_id'] == application]

            # Add the internal permissions if necesary
            if ASSIGN_INTERNAL_ROLES_PERMISSION in permissions:
                roles_to_add += [r['id'] for r
                    in self.driver.list_roles(is_internal=True)]

            if roles_to_add:
                allowed_roles[application] = roles_to_add

        return allowed_roles

@dependency.requires('assignment_api', 'identity_api', 'oauth2_api')
@six.add_metaclass(abc.ABCMeta)
class RolesDriver(object):
    """Interface description for Roles and Permissions driver."""

    # ROLES
    @abc.abstractmethod
    def list_roles(self, **kwargs):
        """List all created roles

        :returns: roles list as dict

        """
        raise exception.NotImplemented()

    @abc.abstractmethod
    def create_role(self, role):
        """Create a new role

        :param role: role data
        :type role: dict
        :returns: role as dict

        """
        raise exception.NotImplemented()

    @abc.abstractmethod
    def get_role(self, role_id):
        """Get role details

        :param role_id: role id
        :type role_id: string
        :returns: role

        """
        raise exception.NotImplemented()

    @abc.abstractmethod
    def update_role(self, role_id, role):
        """Update role details

        :param role_id: id of role to update
        :type role_id: string
        :param role: new role data
        :type role: dict
        :returns: role

        """
        raise exception.NotImplemented()

    @abc.abstractmethod
    def delete_role(self, role_id):
        """Delete role.

        :param role_id: id of role to delete
        :type role_id: string
        :returns: None.

        """
        raise exception.NotImplemented()

    # ROLE-USER
    @abc.abstractmethod
    def list_role_user_assignments(self, user_id=None, organization_id=None,
                              application_id=None):
        """List all role to userassignments. Filtering by user, organization and/or
        application

        :param user_id: user to filter by. Optional parameter
        :type user_id: string
        :param organization_id: organization-scope in which we want to list the
            roles of the user. If we want user-scoped roles it should be the id of
            the user default organization (the project created with same name as user
            when user registration). Optional parameter
        :type organization_id: string
        :param application_id: application to filter by. Optional parameter
        :type application_id: string
        :returns: list of assignments
        """
        raise exception.NotImplemented()

    @abc.abstractmethod
    def add_role_to_user(self, role_id, user_id, organization_id, application_id):
        """Grant role to a user.

        :param role_id: id of role to add user to
        :type role_id: string
        :param user_id: user to add to role
        :type user_id: string
        :param organization_id: organization-scope in which we are giving the
            role to the user. If is a user-scoped role it should be the id of
            the user default organization (the project created with same name as user
            when user registration)
        :type organization_id: string
        :param application_id: application in which assign the role
        :type application_id: string
        :returns: None.

        """
        raise exception.NotImplemented()

    @abc.abstractmethod
    def remove_role_from_user(self, role_id, user_id,
                              organization_id, application_id):
        """Revoke an user's role.

        :param role_id: id of role to remove user from
        :type role_id: string
        :param user_id: user to remove from role
        :type user_id: string
        :param organization_id: organization-scope in which the role was given to the user.
            If is a user-scoped role it should be the id of the user default organization
            (the project created with same name as user when user registration)
        :type organization_id: string
        :param application_id: application in which the role was assigned
        :type application_id: string
        :returns: None.

        """
        raise exception.NotImplemented()

    # ROLE-ORGANIZATION
    @abc.abstractmethod
    def list_role_organization_assignments(self, organization_id=None,
                                           application_id=None):
        """List all role to organization assignments. Filtering by organization and/or
        application.

        :param organization_id: organization to filter by. Optional parameter
        :type organization_id: string
        :param application_id: application to filter by. Optional parameter
        :type application_id: string
        :returns: list of assignments
        """
        raise exception.NotImplemented()

    @abc.abstractmethod
    def add_role_to_organization(self, role_id, organization_id, application_id):
        """Grant role to a organization.

        :param role_id: id of role to add organization to
        :type role_id: string
        :param organization_id: organization to add to role
        :type organization_id: string
        :param application_id: application in which assign the role
        :type application_id: string
        :returns: None.

        """
        raise exception.NotImplemented()

    @abc.abstractmethod
    def remove_role_from_organization(self, role_id,
                                      organization_id, application_id):
        """Revoke an organization's role.

        :param role_id: id of role to remove organization from
        :type role_id: string
        :param organization_id: organization to remove from role
        :type organization_id: string
        :param application_id: application in which the role was assigned
        :type application_id: string
        :returns: None.

        """
        raise exception.NotImplemented()

    # PERMISSIONS
    @abc.abstractmethod
    def list_permissions(self, **kwargs):
        """List all created permissions

        :returns: permissions list as dict

        """
        raise exception.NotImplemented()

    @abc.abstractmethod
    def create_permission(self, permission):
        """Create a new permission

        :param permission: permission data
        :type permission: dict
        :returns: permission as dict

        """
        raise exception.NotImplemented()

    @abc.abstractmethod
    def get_permission(self, permission_id):
        """Get permission details

        :param permission_id: permission id
        :type permission_id: string
        :returns: permission

        """
        raise exception.NotImplemented()

    @abc.abstractmethod
    def update_permission(self, permission_id, permission):
        """Update permission details

        :param permission_id: id of permission to update
        :type permission_id: string
        :param permission: new permission data
        :type permission: dict
        :returns: permission

        """
        raise exception.NotImplemented()

    @abc.abstractmethod
    def delete_permission(self, permission_id):
        """Delete permission.

        :param permission_id: id of permission to delete
        :type permission_id: string
        :returns: None.

        """
        raise exception.NotImplemented()

    @abc.abstractmethod
    def list_permissions_for_role(self, role_id):
        """List permissions for role.

        :param role_id: id of role to remove permission from
        :type role_id: string
        :param permission_id: permission to remove from role
        :type permission_id: string
        :returns: None.

        """
        raise exception.NotImplemented()

    @abc.abstractmethod
    def add_permission_to_role(self, role_id, permission_id):
        """Delete role.

        :param role_id: id of role to add permission to
        :type role_id: string
        :param permission_id: permission to add to role
        :type permission_id: string
        :returns: None.

        """
        raise exception.NotImplemented()

    @abc.abstractmethod
    def remove_permission_from_role(self, role_id, permission_id):
        """Remove Permission from role.

        :param role_id: id of role to remove permission from
        :type role_id: string
        :param permission_id: permission to remove from role
        :type permission_id: string
        :returns: None.

        """
        raise exception.NotImplemented()

<commit_msg>delete user and organization role assignments on delete<commit_after># Copyright (C) 2014 Universidad Politecnica de Madrid
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
#      http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.

import abc
import six

from keystone import exception
from keystone import notifications
from keystone.common import dependency
from keystone.common import extension
from keystone.common import manager
from keystone.openstack.common import log

LOG = log.getLogger(__name__)

EXTENSION_DATA = {
    'name': 'UPM-FIWARE Roles API',
    'namespace': 'https://github.com/ging/keystone/'
                 'OS-ROLES/v1.0',
    'alias': 'OS-ROLES',
    'updated': '2014-11-3T12:00:0-00:00',
    'description': 'UPM\'s Roles provider for applications in the FIWARE GE \
                    Identity Manager implementation',
    'links': [
        {
            'rel': 'describedby',
            # TODO(garcianavalon): needs a description
            'type': 'text/html',
            'href': 'https://github.com/ging/keystone/wiki',
        }
    ]}
extension.register_admin_extension(EXTENSION_DATA['alias'], EXTENSION_DATA)
extension.register_public_extension(EXTENSION_DATA['alias'], EXTENSION_DATA)

ASSIGN_ALL_PUBLIC_ROLES_PERMISSION = 'Get and assign all public application roles'
ASSIGN_OWNED_PUBLIC_ROLES_PERMISSION = 'Get and assign only public owned roles'
ASSIGN_INTERNAL_ROLES_PERMISSION = 'Get and assign all internal application roles'
MANAGE_APPLICATION_PERMISSION = 'Manage the application'
MANAGE_ROLES_PERMISSION = 'Manage roles'

@dependency.requires('assignment_api')
@dependency.provider('roles_api')
class RolesManager(manager.Manager):
    """Roles and Permissions Manager.

    See :mod:`keystone.common.manager.Manager` for more details on
    how this dynamically calls the backend.

    """

    def __init__(self):
        super(RolesManager, self).__init__(
            'keystone.contrib.roles.backends.sql.Roles')

        self.event_callbacks = {
            notifications.ACTIONS.deleted: {
                'user': [self.delete_user_assignments],
            },
            notifications.ACTIONS.deleted: {
                'project': [self.delete_organization_assignments],
            },
        }

    def delete_user_assignments(self, service, resource_type, operation,
                                payload):
        user_id = payload['resource_info']
        assignments = self.driver.list_role_user_assignments(
            user_id=user_id)
        for assignment in assignments:
            self.driver.remove_role_from_organization(
                assignment.role_id,
                assignment.organization_id,
                assignment.application_id)

    def delete_organization_assignments(self, service, resource_type,
                                        operation, payload):
        org_id = payload['resource_info']
        assignments = self.driver.list_role_organization_assignments(
            organization_id=org_id)
        for assignment in assignments:
            self.driver.remove_role_from_organization(
                assignment.role_id,
                assignment.user_id,
                org_id,
                assignment.application_id)

    def get_authorized_organizations(self, user,
                                    application_id,
                                    remove_default_organization=False):
        # roles associated with this user in the application
        assignments = self.driver.list_role_user_assignments(
            user_id=user['id'], application_id=application_id)

        # organizations the user is in
        organizations = self.assignment_api.list_projects_for_user(user['id'])

        # filter to only organizations with roles
        organizations = [org for org in organizations
            if org['id'] in [a['organization_id'] for a in assignments]]

        for organization in organizations:
            role_ids = [a['role_id'] for a in assignments
                        if a['organization_id'] == organization['id']]
            # Load roles' names
            organization['roles'] = [dict(id=r['id'], name=r['name']) for r
                    in [self.driver.get_role(id) for id in role_ids]]

        if remove_default_organization:
            # always remove the default org
            organizations = [org for org in organizations
                if not org['id'] == user['default_project_id']]

        return organizations

    def list_applications_user_allowed_to_manage_roles(self, user_id,
                                                       organization_id):
        """List all the applications in which the user has at least
        one role with the permission 'Manage the application' permission.
        """
        assignments = self.driver.list_role_user_assignments(
            user_id, organization_id)
        return self._get_allowed_applications_manage_roles(assignments)

    def list_applications_organization_allowed_to_manage_roles(self,
                                                               organization_id):
        """List all the applications in which the organization has at least
        one role with the permission 'Manage the application' permission.
        """
        assignments = self.driver.list_role_organization_assignments(
            organization_id)
        return self._get_allowed_applications_manage_roles(assignments)

    def list_applications_user_allowed_to_manage(self, user_id,
                                                 organization_id):
        """List all the applications in which the user has at least
        one role with the permission 'Manage the application' permission.
        """
        assignments = self.driver.list_role_user_assignments(
            user_id, organization_id)
        return self._get_allowed_applications(assignments)

    def list_applications_organization_allowed_to_manage(self,
                                                         organization_id):
        """List all the applications in which the organization has at least
        one role with the permission 'Manage the application' permission.
        """
        assignments = self.driver.list_role_organization_assignments(
            organization_id)
        return self._get_allowed_applications(assignments)

    def list_roles_user_allowed_to_assign(self, user_id, organization_id):
        """List the roles that a given user can assign. To be able to assign roles
        a user needs a certain permission. It can be the 'get and assign all
        application's roles' or the 'get and assign owned roles'

        :param user_id: user with roles
        :type user_id: string
        :param organization_id: organization-scope
        :type organization_id: string
        :returns: dictionary with application ids as keys and list
            of role ids as values
        """
        assignments = self.driver.list_role_user_assignments(
            user_id, organization_id)
        return self._get_allowed_roles(assignments)

    def list_roles_organization_allowed_to_assign(self, organization_id):
        """List the roles that a given organization can assign. To be able to assign roles
        a organization needs a certain permission. It can be the 'get and assign all
        application's roles' or the 'get and assign owned roles'

        :param organization_id: organization with roles
        :type organization_id: string
        :returns: dictionary with application ids as keys and list
            of role ids as values
        """
        assignments = self.driver.list_role_organization_assignments(
            organization_id)
        return self._get_allowed_roles(assignments)

    def _get_all_internal_permissions(self, current_assignments):
        applications = set([a['application_id'] for a in current_assignments])
        permissions = {}
        for application_id in applications:
            owned_roles = [a['role_id'] for a in current_assignments
                           if a['application_id'] == application_id]
            permissions[application_id] = []
            for role_id in owned_roles:
                permissions[application_id] += \
                    [p['name'] for p in
                     self.driver.list_permissions_for_role(role_id)
                     if p['is_internal'] == True]
        return permissions

    def _get_allowed_applications_manage_roles(self, current_assignments):
        application_permissions = self._get_all_internal_permissions(
            current_assignments)
        allowed_applications = []
        for application in application_permissions:
            permissions = application_permissions[application]

            # Check if the manage internal permission is present
            if MANAGE_ROLES_PERMISSION in permissions:
                allowed_applications.append(application)

        return allowed_applications

    def _get_allowed_applications(self, current_assignments):
        application_permissions = self._get_all_internal_permissions(
            current_assignments)
        allowed_applications = []
        for application in application_permissions:
            permissions = application_permissions[application]

            # Check if the manage internal permission is present
            if MANAGE_APPLICATION_PERMISSION in permissions:
                allowed_applications.append(application)

        return allowed_applications

    def _get_allowed_roles(self, current_assignments):
        application_permissions = self._get_all_internal_permissions(
            current_assignments)
        allowed_roles = {}
        for application in application_permissions:
            permissions = application_permissions[application]
            roles_to_add = []
            # Now check if the internal permissions are present
            if ASSIGN_ALL_PUBLIC_ROLES_PERMISSION in permissions:
                # add all public roles in the application
                roles_to_add += set([r['id'] for r
                    in self.driver.list_roles(application_id=application)])

            elif ASSIGN_OWNED_PUBLIC_ROLES_PERMISSION in permissions:
                # add only the public roles the user has in the application
                roles_to_add += [a['role_id'] for a
                    in current_assignments
                    if a['application_id'] == application]

            # Add the internal permissions if necesary
            if ASSIGN_INTERNAL_ROLES_PERMISSION in permissions:
                roles_to_add += [r['id'] for r
                    in self.driver.list_roles(is_internal=True)]

            if roles_to_add:
                allowed_roles[application] = roles_to_add

        return allowed_roles

@dependency.requires('assignment_api', 'identity_api', 'oauth2_api')
@six.add_metaclass(abc.ABCMeta)
class RolesDriver(object):
    """Interface description for Roles and Permissions driver."""

    # ROLES
    @abc.abstractmethod
    def list_roles(self, **kwargs):
        """List all created roles

        :returns: roles list as dict

        """
        raise exception.NotImplemented()

    @abc.abstractmethod
    def create_role(self, role):
        """Create a new role

        :param role: role data
        :type role: dict
        :returns: role as dict

        """
        raise exception.NotImplemented()

    @abc.abstractmethod
    def get_role(self, role_id):
        """Get role details

        :param role_id: role id
        :type role_id: string
        :returns: role

        """
        raise exception.NotImplemented()

    @abc.abstractmethod
    def update_role(self, role_id, role):
        """Update role details

        :param role_id: id of role to update
        :type role_id: string
        :param role: new role data
        :type role: dict
        :returns: role

        """
        raise exception.NotImplemented()

    @abc.abstractmethod
    def delete_role(self, role_id):
        """Delete role.

        :param role_id: id of role to delete
        :type role_id: string
        :returns: None.

        """
        raise exception.NotImplemented()

    # ROLE-USER
    @abc.abstractmethod
    def list_role_user_assignments(self, user_id=None, organization_id=None,
                              application_id=None):
        """List all role to userassignments. Filtering by user, organization and/or
        application

        :param user_id: user to filter by. Optional parameter
        :type user_id: string
        :param organization_id: organization-scope in which we want to list the
            roles of the user. If we want user-scoped roles it should be the id of
            the user default organization (the project created with same name as user
            when user registration). Optional parameter
        :type organization_id: string
        :param application_id: application to filter by. Optional parameter
        :type application_id: string
        :returns: list of assignments
        """
        raise exception.NotImplemented()

    @abc.abstractmethod
    def add_role_to_user(self, role_id, user_id, organization_id, application_id):
        """Grant role to a user.

        :param role_id: id of role to add user to
        :type role_id: string
        :param user_id: user to add to role
        :type user_id: string
        :param organization_id: organization-scope in which we are giving the
            role to the user. If is a user-scoped role it should be the id of
            the user default organization (the project created with same name as user
            when user registration)
        :type organization_id: string
        :param application_id: application in which assign the role
        :type application_id: string
        :returns: None.

        """
        raise exception.NotImplemented()

    @abc.abstractmethod
    def remove_role_from_user(self, role_id, user_id,
                              organization_id, application_id):
        """Revoke an user's role.

        :param role_id: id of role to remove user from
        :type role_id: string
        :param user_id: user to remove from role
        :type user_id: string
        :param organization_id: organization-scope in which the role was given to the user.
            If is a user-scoped role it should be the id of the user default organization
            (the project created with same name as user when user registration)
        :type organization_id: string
        :param application_id: application in which the role was assigned
        :type application_id: string
        :returns: None.

        """
        raise exception.NotImplemented()

    # ROLE-ORGANIZATION
    @abc.abstractmethod
    def list_role_organization_assignments(self, organization_id=None,
                                           application_id=None):
        """List all role to organization assignments. Filtering by organization and/or
        application.

        :param organization_id: organization to filter by. Optional parameter
        :type organization_id: string
        :param application_id: application to filter by. Optional parameter
        :type application_id: string
        :returns: list of assignments
        """
        raise exception.NotImplemented()

    @abc.abstractmethod
    def add_role_to_organization(self, role_id, organization_id, application_id):
        """Grant role to a organization.

        :param role_id: id of role to add organization to
        :type role_id: string
        :param organization_id: organization to add to role
        :type organization_id: string
        :param application_id: application in which assign the role
        :type application_id: string
        :returns: None.

        """
        raise exception.NotImplemented()

    @abc.abstractmethod
    def remove_role_from_organization(self, role_id,
                                      organization_id, application_id):
        """Revoke an organization's role.

        :param role_id: id of role to remove organization from
        :type role_id: string
        :param organization_id: organization to remove from role
        :type organization_id: string
        :param application_id: application in which the role was assigned
        :type application_id: string
        :returns: None.

        """
        raise exception.NotImplemented()

    # PERMISSIONS
    @abc.abstractmethod
    def list_permissions(self, **kwargs):
        """List all created permissions

        :returns: permissions list as dict

        """
        raise exception.NotImplemented()

    @abc.abstractmethod
    def create_permission(self, permission):
        """Create a new permission

        :param permission: permission data
        :type permission: dict
        :returns: permission as dict

        """
        raise exception.NotImplemented()

    @abc.abstractmethod
    def get_permission(self, permission_id):
        """Get permission details

        :param permission_id: permission id
        :type permission_id: string
        :returns: permission

        """
        raise exception.NotImplemented()

    @abc.abstractmethod
    def update_permission(self, permission_id, permission):
        """Update permission details

        :param permission_id: id of permission to update
        :type permission_id: string
        :param permission: new permission data
        :type permission: dict
        :returns: permission

        """
        raise exception.NotImplemented()

    @abc.abstractmethod
    def delete_permission(self, permission_id):
        """Delete permission.

        :param permission_id: id of permission to delete
        :type permission_id: string
        :returns: None.

        """
        raise exception.NotImplemented()

    @abc.abstractmethod
    def list_permissions_for_role(self, role_id):
        """List permissions for role.

        :param role_id: id of role to remove permission from
        :type role_id: string
        :param permission_id: permission to remove from role
        :type permission_id: string
        :returns: None.

        """
        raise exception.NotImplemented()

    @abc.abstractmethod
    def add_permission_to_role(self, role_id, permission_id):
        """Delete role.

        :param role_id: id of role to add permission to
        :type role_id: string
        :param permission_id: permission to add to role
        :type permission_id: string
        :returns: None.

        """
        raise exception.NotImplemented()

    @abc.abstractmethod
    def remove_permission_from_role(self, role_id, permission_id):
        """Remove Permission from role.

        :param role_id: id of role to remove permission from
        :type role_id: string
        :param permission_id: permission to remove from role
        :type permission_id: string
        :returns: None.

        """
        raise exception.NotImplemented()

<|endoftext|>


<commit_before>import math
import random
import congruences as congr

def miller_rabin_pass(a, n):
        d = n - 1
        s = 0
        while d % 2 == 0:
                d >>= 1
                s += 1
        a_to_power = pow(a, d, n)
        if a_to_power == 1:
                return True
        for i in xrange(s-1):
                if a_to_power == n - 1:
                        return True
                a_to_power = (a_to_power * a_to_power) % n
        return a_to_power == n - 1

def isPrime(n):
        if n < 2:
                return False
        # Uses Miller-Rabin primality test
        for repeat in xrange(20):
                a = 0
                while a == 0:
                        a = random.randrange(n)
                if not miller_rabin_pass(a, n):
                        return False
        return True

def twinPrimes(num):
        primeList = []
        if num >= 5:
                primeList.append((3, 5))
        if num >= 7:
                primeList.append((5, 7))
        for x in xrange(0, num + 1, 30):
                if isPrime(x - 1) and isPrime(x + 1):
                        primeList.append((x - 1, x + 1))
                if isPrime(x + 11) and isPrime(x + 13) and num >= x + 13:
                        primeList.append((x + 11, x + 13))
                if isPrime(x + 17) and isPrime(x + 19) and num >= x + 19:
                        primeList.append((x + 17, x + 19))
        return primeList

def sieve(n):
        # Uses Sieve of Eratosthenes to generate list of primes below n
        sqrtn = int(n ** 0.5)
        sieve = [True] * (n + 1)
        sieve[0] = False
        sieve[1] = False

        for i in range(2, sqrtn + 1):
                if sieve[i]:
                        m = n // i - i
                        sieve[i*i: n + 1 :i] = [False] * (m + 1)

        sieve = [i for i in range(n+1) if sieve[i]]
        return sieve

def atkin(num):
        # Uses Sieve of Atkin to generate list of primes below n
        primes = [0] * (num + 1)
        if num < 2:
                return []
        if num == 2:
                return 2

        # n = 3x^2 + y^2
        threexSquared = 3
        for dxSquared in xrange(0, 12 * int(math.sqrt(( num - 1 ) / 3)), 24):
                threexSquared += dxSquared
                y_limit = int(12 * math.sqrt(num - threexSquared) - 36)
                n = threexSquared + 16
                for dn in xrange(-12, y_limit + 1, 72):
                        n += dn
                        primes[n] = not primes[n]
                n = threexSquared + 4
                for dn in xrange(12, y_limit + 1, 72):
                        n += dn
                        primes[n] = not primes[n]
        # n = 4x^2 + y^2
        fourxSquared = 0
        for d4xSquared in xrange(4, 8 * int(math.sqrt((num - 1 ) / 4)) + 4, 8):
                fourxSquared += d4xSquared
                n = fourxSquared + 1
                if fourxSquared % 3:
                        for dn in xrange(0, 4 * int(math.sqrt(num - fourxSquared)) - 3, 8):
                                n += dn
                                primes[n] = not primes[n]
                else:
                        y_limit = 12 * int(math.sqrt(num - fourxSquared)) - 36
                        n = fourxSquared + 25
                        for dn in xrange(-24, y_limit + 1, 72):
                                n += dn
                                primes[n] = not primes[n]
                        n = fourxSquared + 1
                        for dn in xrange(24, y_limit + 1, 72):
                                n += dn
                                primes[n] = not primes[n]
        # n = 3x^2 - y^2 section
        xSquared = 1
        for x in xrange(3, int(math.sqrt(num / 2)) + 1, 2):
                xSquared += 4 * x - 4
                n = 3 * xSquared
                if n > num:
                        min_y = ((int(math.sqrt(n - num)) >> 2) << 2)
                        ySquared = min_y * min_y
                        n -= ySquared
                        s = 4 * min_y + 4
                else:
                        s = 4
                for dn in xrange(s, 4 * x, 8):
                        n -= dn
                        if n <= num and n % 12 == 11:
                                primes[n] = not primes[n]
        xSquared = 0
        for x in xrange(2, int(math.sqrt(num / 2)) + 1, 2):
                xSquared += 4 * x - 4
                n = 3 * xSquared
                if n > num:
                        min_y = ((int(math.sqrt(n - num)) >> 2) << 2) - 1
                        yy = min_y*min_y
                        n -= yy
                        s = 4 * min_y + 4
                else:
                        n -= 1
                        s = 0
                for dn in xrange(s, 4 * x, 8):
                        n -= dn
                        if n <= num and n % 12 == 11:
                                primes[n] = not primes[n]
        # remove squares
        for n in xrange(5, int(math.sqrt(num)) + 1, 2):
                if primes[n]:
                        for k in range(n * n, num, n * n):
                                primes[k] = False
        return [2,3] + filter(primes.__getitem__, xrange(5, num, 2))

def pi(n):
        # Returns the number of primes less than or equal to n
        return len(atkin(n))

def getFactor(n):
        # Uses Brent's version of the Pollard rho factorization method to generate one of the factors of n
        if n % 2 == 0:
                return 2
        y, c, m = random.randint(1, n - 1), random.randint(1, n - 1), random.randint(1, n - 1)
        g, r, q = 1, 1, 1
        while g == 1:
                x = y
                for i in range(r):
                        y = ((y*y) % n + c) % n
                k = 0
                while (k < r and g == 1):
                        ys = y
                        for i in range(min(m, r - k)):
                                y = ((y*y) % n + c) % n
                                q = q * (abs(x - y)) % n
                        g = congr.gcd(q, n)
                        k = k + m
                r *= 2
        if g == n:
                while True:
                        ys = ((ys*ys) % n + c) % n
                        g = congr.gcd(abs(x - ys), n)
                        if g > 1:
                                break

        return g

def primeFactors(n):
        if n == 1:
                return []
        if isPrime(n):
                return [n]
        factor = getFactor(n)
        return primeFactors(factor) + primeFactors(n / factor)

def primeFactorization(num):
        pFactor = []
        factors = primeFactors(num)
        for fac in set(factors):
                pFactor.append((fac, factors.count(fac)))
        return pFactor

def ulamSpiral(size):
        row = size/2
        col = size/2
        direction = 0
        changeCount = -1
        count = 0
        length = 1
        spiral = [[0 for x in range(size)] for x in range(size)]
        for a in xrange (0, size ** 2):
                spiral[row][col] = a + 1
                count += 1
                if (count == length):
                        count = 0
                        changeCount += 1
                        direction += 1
                        direction = ((direction - 1) % 4) + 1
                if (direction == 1):
                        col += 1
                elif (direction == 2):
                        row += 1
                elif (direction == 3):
                        col -= 1
                elif (direction == 4):
                        row -= 1
                if (changeCount == 2):
                        length += 1
                        changeCount = 0
        return spiral
<commit_msg>Fixed atkin<commit_after>import math
import random
import congruences as congr

def miller_rabin_pass(a, n):
        d = n - 1
        s = 0
        while d % 2 == 0:
                d >>= 1
                s += 1
        a_to_power = pow(a, d, n)
        if a_to_power == 1:
                return True
        for i in xrange(s-1):
                if a_to_power == n - 1:
                        return True
                a_to_power = (a_to_power * a_to_power) % n
        return a_to_power == n - 1

def isPrime(n):
        if n < 2:
                return False
        # Uses Miller-Rabin primality test
        for repeat in xrange(20):
                a = 0
                while a == 0:
                        a = random.randrange(n)
                if not miller_rabin_pass(a, n):
                        return False
        return True

def twinPrimes(num):
        primeList = []
        if num >= 5:
                primeList.append((3, 5))
        if num >= 7:
                primeList.append((5, 7))
        for x in xrange(0, num + 1, 30):
                if isPrime(x - 1) and isPrime(x + 1):
                        primeList.append((x - 1, x + 1))
                if isPrime(x + 11) and isPrime(x + 13) and num >= x + 13:
                        primeList.append((x + 11, x + 13))
                if isPrime(x + 17) and isPrime(x + 19) and num >= x + 19:
                        primeList.append((x + 17, x + 19))
        return primeList

def sieve(n):
        # Uses Sieve of Eratosthenes to generate list of primes below n
        sqrtn = int(n ** 0.5)
        sieve = [True] * (n + 1)
        sieve[0] = False
        sieve[1] = False

        for i in range(2, sqrtn + 1):
                if sieve[i]:
                        m = n // i - i
                        sieve[i*i: n + 1 :i] = [False] * (m + 1)

        sieve = [i for i in range(n+1) if sieve[i]]
        return sieve

def atkin(num):
        # Uses Sieve of Atkin to generate list of primes below n
        primes = [0] * (num + 1)
        if num < 2:
                return []
        if num == 2:
                return 2

        # n = 3x^2 + y^2
        threexSquared = 3
        for dxSquared in xrange(0, 12 * int(math.sqrt(( num - 1 ) / 3)), 24):
                threexSquared += dxSquared
                y_limit = int(12 * math.sqrt(num - threexSquared) - 36)
                n = threexSquared + 16
                for dn in xrange(-12, y_limit + 1, 72):
                        n += dn
                        primes[n] = not primes[n]
                n = threexSquared + 4
                for dn in xrange(12, y_limit + 1, 72):
                        n += dn
                        primes[n] = not primes[n]
        # n = 4x^2 + y^2
        fourxSquared = 0
        for d4xSquared in xrange(4, 8 * int(math.sqrt((num - 1 ) / 4)) + 4, 8):
                fourxSquared += d4xSquared
                n = fourxSquared + 1
                if fourxSquared % 3:
                        for dn in xrange(0, 4 * int(math.sqrt(num - fourxSquared)) - 3, 8):
                                n += dn
                                primes[n] = not primes[n]
                else:
                        y_limit = 12 * int(math.sqrt(num - fourxSquared)) - 36
                        n = fourxSquared + 25
                        for dn in xrange(-24, y_limit + 1, 72):
                                n += dn
                                primes[n] = not primes[n]
                        n = fourxSquared + 1
                        for dn in xrange(24, y_limit + 1, 72):
                                n += dn
                                primes[n] = not primes[n]
        # n = 3x^2 - y^2 section
        xSquared = 1
        for x in xrange(3, int(math.sqrt(num / 2)) + 1, 2):
                xSquared += 4 * x - 4
                n = 3 * xSquared
                if n > num:
                        min_y = ((int(math.sqrt(n - num)) >> 2) << 2)
                        ySquared = min_y * min_y
                        n -= ySquared
                        s = 4 * min_y + 4
                else:
                        s = 4
                for dn in xrange(s, 4 * x, 8):
                        n -= dn
                        if n <= num and n % 12 == 11:
                                primes[n] = not primes[n]
        xSquared = 0
        for x in xrange(2, int(math.sqrt(num / 2)) + 1, 2):
                xSquared += 4 * x - 4
                n = 3 * xSquared
                if n > num:
                        min_y = ((int(math.sqrt(n - num)) >> 2) << 2) - 1
                        yy = min_y*min_y
                        n -= yy
                        s = 4 * min_y + 4
                else:
                        n -= 1
                        s = 0
                for dn in xrange(s, 4 * x, 8):
                        n -= dn
                        if n <= num and n % 12 == 11:
                                primes[n] = not primes[n]
        # remove squares
        for n in xrange(5, int(math.sqrt(num)) + 1, 2):
                if primes[n]:
                        for k in range(n * n, num, n * n):
                                primes[k] = False
        if num == 2:
                return [2]
        return [2,3] + filter(primes.__getitem__, xrange(5, num, 2))

def pi(n):
        # Returns the number of primes less than or equal to n
        return len(atkin(n))

def getFactor(n):
        # Uses Brent's version of the Pollard rho factorization method to generate one of the factors of n
        if n % 2 == 0:
                return 2
        y, c, m = random.randint(1, n - 1), random.randint(1, n - 1), random.randint(1, n - 1)
        g, r, q = 1, 1, 1
        while g == 1:
                x = y
                for i in range(r):
                        y = ((y*y) % n + c) % n
                k = 0
                while (k < r and g == 1):
                        ys = y
                        for i in range(min(m, r - k)):
                                y = ((y*y) % n + c) % n
                                q = q * (abs(x - y)) % n
                        g = congr.gcd(q, n)
                        k = k + m
                r *= 2
        if g == n:
                while True:
                        ys = ((ys*ys) % n + c) % n
                        g = congr.gcd(abs(x - ys), n)
                        if g > 1:
                                break

        return g

def primeFactors(n):
        if n == 1:
                return []
        if isPrime(n):
                return [n]
        factor = getFactor(n)
        return primeFactors(factor) + primeFactors(n / factor)

def primeFactorization(num):
        pFactor = []
        factors = primeFactors(num)
        for fac in set(factors):
                pFactor.append((fac, factors.count(fac)))
        return pFactor

def ulamSpiral(size):
        row = size/2
        col = size/2
        direction = 0
        changeCount = -1
        count = 0
        length = 1
        spiral = [[0 for x in range(size)] for x in range(size)]
        for a in xrange (0, size ** 2):
                spiral[row][col] = a + 1
                count += 1
                if (count == length):
                        count = 0
                        changeCount += 1
                        direction += 1
                        direction = ((direction - 1) % 4) + 1
                if (direction == 1):
                        col += 1
                elif (direction == 2):
                        row += 1
                elif (direction == 3):
                        col -= 1
                elif (direction == 4):
                        row -= 1
                if (changeCount == 2):
                        length += 1
                        changeCount = 0
        return spiral
<|endoftext|>


<commit_before># Copyright 2013-2017 The Meson development team

# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at

#     http://www.apache.org/licenses/LICENSE-2.0

# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# This file contains the detection logic for external dependencies useful for
# development purposes, such as testing, debugging, etc..

import os
import re
import shutil

from .. import mlog
from .. import mesonlib
from ..mesonlib import version_compare, Popen_safe, stringlistify, extract_as_list
from .base import (
    DependencyException, ExternalDependency, PkgConfigDependency,
    strip_system_libdirs, ConfigToolDependency,
)

class GTestDependency(ExternalDependency):
    def __init__(self, environment, kwargs):
        super().__init__('gtest', environment, 'cpp', kwargs)
        self.main = kwargs.get('main', False)
        self.src_dirs = ['/usr/src/gtest/src', '/usr/src/googletest/googletest/src']
        self.detect()

    def detect(self):
        self.version = '1.something_maybe'
        gtest_detect = self.compiler.find_library("gtest", self.env, [])
        gtest_main_detect = self.compiler.find_library("gtest_main", self.env, [])
        if gtest_detect and (not self.main or gtest_main_detect):
            self.is_found = True
            self.compile_args = []
            self.link_args = gtest_detect
            if self.main:
                self.link_args += gtest_main_detect
            self.sources = []
            mlog.log('Dependency GTest found:', mlog.green('YES'), '(prebuilt)')
        elif self.detect_srcdir():
            self.is_found = True
            self.compile_args = ['-I' + self.src_include_dir]
            self.link_args = []
            if self.main:
                self.sources = [self.all_src, self.main_src]
            else:
                self.sources = [self.all_src]
            mlog.log('Dependency GTest found:', mlog.green('YES'), '(building self)')
        else:
            mlog.log('Dependency GTest found:', mlog.red('NO'))
            self.is_found = False

    def detect_srcdir(self):
        for s in self.src_dirs:
            if os.path.exists(s):
                self.src_dir = s
                self.all_src = mesonlib.File.from_absolute_file(
                    os.path.join(self.src_dir, 'gtest-all.cc'))
                self.main_src = mesonlib.File.from_absolute_file(
                    os.path.join(self.src_dir, 'gtest_main.cc'))
                self.src_include_dir = os.path.normpath(os.path.join(self.src_dir, '..'))
                return True
        return False

    def need_threads(self):
        return True

class GMockDependency(ExternalDependency):
    def __init__(self, environment, kwargs):
        super().__init__('gmock', environment, 'cpp', kwargs)
        self.version = '1.something_maybe'
        # GMock may be a library or just source.
        # Work with both.
        gmock_detect = self.compiler.find_library("gmock", self.env, [])
        if gmock_detect:
            self.is_found = True
            self.compile_args = []
            self.link_args = gmock_detect
            self.sources = []
            mlog.log('Dependency GMock found:', mlog.green('YES'), '(prebuilt)')
            return

        for d in ['/usr/src/googletest/googlemock/src', '/usr/src/gmock/src', '/usr/src/gmock']:
            if os.path.exists(d):
                self.is_found = True
                # Yes, we need both because there are multiple
                # versions of gmock that do different things.
                d2 = os.path.normpath(os.path.join(d, '..'))
                self.compile_args = ['-I' + d, '-I' + d2]
                self.link_args = []
                all_src = mesonlib.File.from_absolute_file(os.path.join(d, 'gmock-all.cc'))
                main_src = mesonlib.File.from_absolute_file(os.path.join(d, 'gmock_main.cc'))
                if kwargs.get('main', False):
                    self.sources = [all_src, main_src]
                else:
                    self.sources = [all_src]
                mlog.log('Dependency GMock found:', mlog.green('YES'), '(building self)')
                return
        mlog.log('Dependency GMock found:', mlog.red('NO'))
        self.is_found = False

class LLVMDependency(ConfigToolDependency):
    """
    LLVM uses a special tool, llvm-config, which has arguments for getting
    c args, cxx args, and ldargs as well as version.
    """

    # Ordered list of llvm-config binaries to try. Start with base, then try
    # newest back to oldest (3.5 is arbitrary), and finally the devel version.
    # Please note that llvm-config-6.0 is a development snapshot and it should
    # not be moved to the beginning of the list. The only difference between
    # llvm-config-6.0 and llvm-config-devel is that the former is used by
    # Debian and the latter is used by FreeBSD.
    tools = [
        'llvm-config', # base
        'llvm-config-5.0', 'llvm-config50', # latest stable release
        'llvm-config-4.0', 'llvm-config40', # old stable releases
        'llvm-config-3.9', 'llvm-config39',
        'llvm-config-3.8', 'llvm-config38',
        'llvm-config-3.7', 'llvm-config37',
        'llvm-config-3.6', 'llvm-config36',
        'llvm-config-3.5', 'llvm-config35',
        'llvm-config-6.0', 'llvm-config-devel', # development snapshot
    ]
    tool_name = 'llvm-config'
    __cpp_blacklist = {'-DNDEBUG'}

    def __init__(self, environment, kwargs):
        # It's necessary for LLVM <= 3.8 to use the C++ linker. For 3.9 and 4.0
        # the C linker works fine if only using the C API.
        super().__init__('LLVM', environment, 'cpp', kwargs)
        self.provided_modules = []
        self.required_modules = set()
        if not self.is_found:
            return
        self.static = kwargs.get('static', False)

        # Currently meson doesn't really attempt to handle pre-release versions,
        # so strip the 'svn' off the end, since it will probably cuase problems
        # for users who want the patch version.
        #
        # If LLVM is built from svn then "svn" will be appended to the version
        # string, if it's built from a git mirror then "git-<very short sha>"
        # will be appended instead.
        self.version = self.version.rstrip('svn')
        self.version = self.version.split('git')[0]

        self.provided_modules = self.get_config_value(['--components'], 'modules')
        modules = stringlistify(extract_as_list(kwargs, 'modules'))
        self.check_components(modules)
        opt_modules = stringlistify(extract_as_list(kwargs, 'optional_modules'))
        self.check_components(opt_modules, required=False)

        cargs = set(self.get_config_value(['--cppflags'], 'compile_args'))
        self.compile_args = list(cargs.difference(self.__cpp_blacklist))

        if version_compare(self.version, '>= 3.9'):
            self._set_new_link_args()
        else:
            self._set_old_link_args()
        self.link_args = strip_system_libdirs(environment, self.link_args)

    def _set_new_link_args(self):
        """How to set linker args for LLVM versions >= 3.9"""
        link_args = ['--link-static', '--system-libs'] if self.static else ['--link-shared']
        self.link_args = self.get_config_value(
            ['--libs', '--ldflags'] + link_args + list(self.required_modules),
            'link_args')

    def _set_old_link_args(self):
        """Setting linker args for older versions of llvm.

        Old versions of LLVM bring an extra level of insanity with them.
        llvm-config will provide the correct arguments for static linking, but
        not for shared-linnking, we have to figure those out ourselves, because
        of course we do.
        """
        if self.static:
            self.link_args = self.get_config_value(
                ['--libs', '--ldflags', '--system-libs'] + list(self.required_modules),
                'link_args')
        else:
            # llvm-config will provide arguments for static linking, so we get
            # to figure out for ourselves what to link with. We'll do that by
            # checking in the directory provided by --libdir for a library
            # called libLLVM-<ver>.(so|dylib|dll)
            libdir = self.get_config_value(['--libdir'], 'link_args')[0]

            expected_name = 'libLLVM-{}'.format(self.version)
            re_name = re.compile(r'{}.(so|dll|dylib)'.format(expected_name))

            for file_ in os.listdir(libdir):
                if re_name.match(file_):
                    self.link_args = ['-L{}'.format(libdir),
                                      '-l{}'.format(os.path.splitext(file_.lstrip('lib'))[0])]
                    break
            else:
                raise DependencyException(
                    'Could not find a dynamically linkable library for LLVM.')

    def check_components(self, modules, required=True):
        """Check for llvm components (modules in meson terms).

        The required option is whether the module is required, not whether LLVM
        is required.
        """
        for mod in sorted(set(modules)):
            if mod not in self.provided_modules:
                mlog.log('LLVM module', mlog.bold(mod), 'found:', mlog.red('NO'),
                         '(optional)' if not required else '')
                if required:
                    self.is_found = False
                    if self.required:
                        raise DependencyException(
                            'Could not find required LLVM Component: {}'.format(mod))
            else:
                self.required_modules.add(mod)
                mlog.log('LLVM module', mlog.bold(mod), 'found:', mlog.green('YES'))

    def need_threads(self):
        return True

class ValgrindDependency(PkgConfigDependency):
    '''
    Consumers of Valgrind usually only need the compile args and do not want to
    link to its (static) libraries.
    '''
    def __init__(self, env, kwargs):
        super().__init__('valgrind', env, kwargs)

    def get_link_args(self):
        return []
<commit_msg>dependencies/dev: remove unused imports<commit_after># Copyright 2013-2017 The Meson development team

# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at

#     http://www.apache.org/licenses/LICENSE-2.0

# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# This file contains the detection logic for external dependencies useful for
# development purposes, such as testing, debugging, etc..

import os
import re

from .. import mlog
from .. import mesonlib
from ..mesonlib import version_compare, stringlistify, extract_as_list
from .base import (
    DependencyException, ExternalDependency, PkgConfigDependency,
    strip_system_libdirs, ConfigToolDependency,
)

class GTestDependency(ExternalDependency):
    def __init__(self, environment, kwargs):
        super().__init__('gtest', environment, 'cpp', kwargs)
        self.main = kwargs.get('main', False)
        self.src_dirs = ['/usr/src/gtest/src', '/usr/src/googletest/googletest/src']
        self.detect()

    def detect(self):
        self.version = '1.something_maybe'
        gtest_detect = self.compiler.find_library("gtest", self.env, [])
        gtest_main_detect = self.compiler.find_library("gtest_main", self.env, [])
        if gtest_detect and (not self.main or gtest_main_detect):
            self.is_found = True
            self.compile_args = []
            self.link_args = gtest_detect
            if self.main:
                self.link_args += gtest_main_detect
            self.sources = []
            mlog.log('Dependency GTest found:', mlog.green('YES'), '(prebuilt)')
        elif self.detect_srcdir():
            self.is_found = True
            self.compile_args = ['-I' + self.src_include_dir]
            self.link_args = []
            if self.main:
                self.sources = [self.all_src, self.main_src]
            else:
                self.sources = [self.all_src]
            mlog.log('Dependency GTest found:', mlog.green('YES'), '(building self)')
        else:
            mlog.log('Dependency GTest found:', mlog.red('NO'))
            self.is_found = False

    def detect_srcdir(self):
        for s in self.src_dirs:
            if os.path.exists(s):
                self.src_dir = s
                self.all_src = mesonlib.File.from_absolute_file(
                    os.path.join(self.src_dir, 'gtest-all.cc'))
                self.main_src = mesonlib.File.from_absolute_file(
                    os.path.join(self.src_dir, 'gtest_main.cc'))
                self.src_include_dir = os.path.normpath(os.path.join(self.src_dir, '..'))
                return True
        return False

    def need_threads(self):
        return True

class GMockDependency(ExternalDependency):
    def __init__(self, environment, kwargs):
        super().__init__('gmock', environment, 'cpp', kwargs)
        self.version = '1.something_maybe'
        # GMock may be a library or just source.
        # Work with both.
        gmock_detect = self.compiler.find_library("gmock", self.env, [])
        if gmock_detect:
            self.is_found = True
            self.compile_args = []
            self.link_args = gmock_detect
            self.sources = []
            mlog.log('Dependency GMock found:', mlog.green('YES'), '(prebuilt)')
            return

        for d in ['/usr/src/googletest/googlemock/src', '/usr/src/gmock/src', '/usr/src/gmock']:
            if os.path.exists(d):
                self.is_found = True
                # Yes, we need both because there are multiple
                # versions of gmock that do different things.
                d2 = os.path.normpath(os.path.join(d, '..'))
                self.compile_args = ['-I' + d, '-I' + d2]
                self.link_args = []
                all_src = mesonlib.File.from_absolute_file(os.path.join(d, 'gmock-all.cc'))
                main_src = mesonlib.File.from_absolute_file(os.path.join(d, 'gmock_main.cc'))
                if kwargs.get('main', False):
                    self.sources = [all_src, main_src]
                else:
                    self.sources = [all_src]
                mlog.log('Dependency GMock found:', mlog.green('YES'), '(building self)')
                return
        mlog.log('Dependency GMock found:', mlog.red('NO'))
        self.is_found = False

class LLVMDependency(ConfigToolDependency):
    """
    LLVM uses a special tool, llvm-config, which has arguments for getting
    c args, cxx args, and ldargs as well as version.
    """

    # Ordered list of llvm-config binaries to try. Start with base, then try
    # newest back to oldest (3.5 is arbitrary), and finally the devel version.
    # Please note that llvm-config-6.0 is a development snapshot and it should
    # not be moved to the beginning of the list. The only difference between
    # llvm-config-6.0 and llvm-config-devel is that the former is used by
    # Debian and the latter is used by FreeBSD.
    tools = [
        'llvm-config', # base
        'llvm-config-5.0', 'llvm-config50', # latest stable release
        'llvm-config-4.0', 'llvm-config40', # old stable releases
        'llvm-config-3.9', 'llvm-config39',
        'llvm-config-3.8', 'llvm-config38',
        'llvm-config-3.7', 'llvm-config37',
        'llvm-config-3.6', 'llvm-config36',
        'llvm-config-3.5', 'llvm-config35',
        'llvm-config-6.0', 'llvm-config-devel', # development snapshot
    ]
    tool_name = 'llvm-config'
    __cpp_blacklist = {'-DNDEBUG'}

    def __init__(self, environment, kwargs):
        # It's necessary for LLVM <= 3.8 to use the C++ linker. For 3.9 and 4.0
        # the C linker works fine if only using the C API.
        super().__init__('LLVM', environment, 'cpp', kwargs)
        self.provided_modules = []
        self.required_modules = set()
        if not self.is_found:
            return
        self.static = kwargs.get('static', False)

        # Currently meson doesn't really attempt to handle pre-release versions,
        # so strip the 'svn' off the end, since it will probably cuase problems
        # for users who want the patch version.
        #
        # If LLVM is built from svn then "svn" will be appended to the version
        # string, if it's built from a git mirror then "git-<very short sha>"
        # will be appended instead.
        self.version = self.version.rstrip('svn')
        self.version = self.version.split('git')[0]

        self.provided_modules = self.get_config_value(['--components'], 'modules')
        modules = stringlistify(extract_as_list(kwargs, 'modules'))
        self.check_components(modules)
        opt_modules = stringlistify(extract_as_list(kwargs, 'optional_modules'))
        self.check_components(opt_modules, required=False)

        cargs = set(self.get_config_value(['--cppflags'], 'compile_args'))
        self.compile_args = list(cargs.difference(self.__cpp_blacklist))

        if version_compare(self.version, '>= 3.9'):
            self._set_new_link_args()
        else:
            self._set_old_link_args()
        self.link_args = strip_system_libdirs(environment, self.link_args)

    def _set_new_link_args(self):
        """How to set linker args for LLVM versions >= 3.9"""
        link_args = ['--link-static', '--system-libs'] if self.static else ['--link-shared']
        self.link_args = self.get_config_value(
            ['--libs', '--ldflags'] + link_args + list(self.required_modules),
            'link_args')

    def _set_old_link_args(self):
        """Setting linker args for older versions of llvm.

        Old versions of LLVM bring an extra level of insanity with them.
        llvm-config will provide the correct arguments for static linking, but
        not for shared-linnking, we have to figure those out ourselves, because
        of course we do.
        """
        if self.static:
            self.link_args = self.get_config_value(
                ['--libs', '--ldflags', '--system-libs'] + list(self.required_modules),
                'link_args')
        else:
            # llvm-config will provide arguments for static linking, so we get
            # to figure out for ourselves what to link with. We'll do that by
            # checking in the directory provided by --libdir for a library
            # called libLLVM-<ver>.(so|dylib|dll)
            libdir = self.get_config_value(['--libdir'], 'link_args')[0]

            expected_name = 'libLLVM-{}'.format(self.version)
            re_name = re.compile(r'{}.(so|dll|dylib)'.format(expected_name))

            for file_ in os.listdir(libdir):
                if re_name.match(file_):
                    self.link_args = ['-L{}'.format(libdir),
                                      '-l{}'.format(os.path.splitext(file_.lstrip('lib'))[0])]
                    break
            else:
                raise DependencyException(
                    'Could not find a dynamically linkable library for LLVM.')

    def check_components(self, modules, required=True):
        """Check for llvm components (modules in meson terms).

        The required option is whether the module is required, not whether LLVM
        is required.
        """
        for mod in sorted(set(modules)):
            if mod not in self.provided_modules:
                mlog.log('LLVM module', mlog.bold(mod), 'found:', mlog.red('NO'),
                         '(optional)' if not required else '')
                if required:
                    self.is_found = False
                    if self.required:
                        raise DependencyException(
                            'Could not find required LLVM Component: {}'.format(mod))
            else:
                self.required_modules.add(mod)
                mlog.log('LLVM module', mlog.bold(mod), 'found:', mlog.green('YES'))

    def need_threads(self):
        return True

class ValgrindDependency(PkgConfigDependency):
    '''
    Consumers of Valgrind usually only need the compile args and do not want to
    link to its (static) libraries.
    '''
    def __init__(self, env, kwargs):
        super().__init__('valgrind', env, kwargs)

    def get_link_args(self):
        return []
<|endoftext|>


<commit_before># encoding: utf8
from __future__ import print_function

from optparse import OptionParser
import os
import sys

import pokedex.db
import pokedex.db.load
import pokedex.db.tables
import pokedex.lookup
from pokedex import defaults

def main(*argv):
    if len(argv) <= 1:
        command_help()

    command = argv[1]
    args = argv[2:]

    # XXX there must be a better way to get Unicode argv
    # XXX this doesn't work on Windows durp
    enc = sys.stdin.encoding or 'utf8'
    args = [_.decode(enc) if isinstance(_, bytes) else _ for _ in args]

    # Find the command as a function in this file
    func = globals().get("command_%s" % command, None)
    if func:
        func(*args)
    else:
        command_help()

def setuptools_entry():
    main(*sys.argv)

def get_parser(verbose=True):
    """Returns an OptionParser prepopulated with the global options.

    `verbose` is whether or not the options should be verbose by default.
    """
    parser = OptionParser()
    parser.add_option('-e', '--engine', dest='engine_uri', default=None)
    parser.add_option('-i', '--index', dest='index_dir', default=None)
    parser.add_option('-q', '--quiet', dest='verbose', default=verbose, action='store_false')
    parser.add_option('-v', '--verbose', dest='verbose', default=verbose, action='store_true')
    return parser

def get_session(options):
    """Given a parsed options object, connects to the database and returns a
    session.
    """

    engine_uri = options.engine_uri
    got_from = 'command line'

    if engine_uri is None:
        engine_uri, got_from = defaults.get_default_db_uri_with_origin()

    session = pokedex.db.connect(engine_uri)

    if options.verbose:
        print("Connected to database %(engine)s (from %(got_from)s)"
            % dict(engine=session.bind.url, got_from=got_from))

    return session

def get_lookup(options, session=None, recreate=False):
    """Given a parsed options object, opens the whoosh index and returns a
    PokedexLookup object.
    """

    if recreate and not session:
        raise ValueError("get_lookup() needs an explicit session to regen the index")

    index_dir = options.index_dir
    got_from = 'command line'

    if index_dir is None:
        index_dir, got_from = defaults.get_default_index_dir_with_origin()

    if options.verbose:
        print("Opened lookup index %(index_dir)s (from %(got_from)s)"
            % dict(index_dir=index_dir, got_from=got_from))

    lookup = pokedex.lookup.PokedexLookup(index_dir, session=session)

    if recreate:
        lookup.rebuild_index()

    return lookup

def get_csv_directory(options):
    """Prints and returns the csv directory we're about to use."""

    if not options.verbose:
        return

    csvdir = options.directory
    got_from = 'command line'

    if csvdir is None:
        csvdir, got_from = defaults.get_default_csv_dir_with_origin()

    print("Using CSV directory %(csvdir)s (from %(got_from)s)"
        % dict(csvdir=csvdir, got_from=got_from))

    return csvdir

### Plumbing commands

def command_dump(*args):
    parser = get_parser(verbose=True)
    parser.add_option('-d', '--directory', dest='directory', default=None)
    parser.add_option('-l', '--langs', dest='langs', default=None,
        help="Comma-separated list of languages to dump all strings for. "
            "Default is English ('en')")
    options, tables = parser.parse_args(list(args))

    session = get_session(options)
    get_csv_directory(options)

    if options.langs is not None:
        langs = [l.strip() for l in options.langs.split(',')]
    else:
        langs = None

    pokedex.db.load.dump(session, directory=options.directory,
                                  tables=tables,
                                  verbose=options.verbose,
                                  langs=langs)

def command_load(*args):
    parser = get_parser(verbose=True)
    parser.add_option('-d', '--directory', dest='directory', default=None)
    parser.add_option('-D', '--drop-tables', dest='drop_tables', default=False, action='store_true')
    parser.add_option('-r', '--recursive', dest='recursive', default=False, action='store_true')
    parser.add_option('-S', '--safe', dest='safe', default=False, action='store_true',
        help="Do not use backend-specific optimalizations.")
    parser.add_option('-l', '--langs', dest='langs', default=None,
        help="Comma-separated list of extra languages to load, or 'none' for none. "
            "Default is to load 'em all. Example: 'fr,de'")
    options, tables = parser.parse_args(list(args))

    if not options.engine_uri:
        print("WARNING: You're reloading the default database, but not the lookup index.  They")
        print("         might get out of sync, and pokedex commands may not work correctly!")
        print("To fix this, run `pokedex reindex` when this command finishes.  Or, just use")
        print("`pokedex setup` to do both at once.")
        print()

    if options.langs == 'none':
        langs = []
    elif options.langs is None:
        langs = None
    else:
        langs = [l.strip() for l in options.langs.split(',')]

    session = get_session(options)
    get_csv_directory(options)

    pokedex.db.load.load(session, directory=options.directory,
                                  drop_tables=options.drop_tables,
                                  tables=tables,
                                  verbose=options.verbose,
                                  safe=options.safe,
                                  recursive=options.recursive,
                                  langs=langs)

def command_reindex(*args):
    parser = get_parser(verbose=True)
    options, _ = parser.parse_args(list(args))

    session = get_session(options)
    lookup = get_lookup(options, session=session, recreate=True)

    print("Recreated lookup index.")

def command_setup(*args):
    parser = get_parser(verbose=False)
    options, _ = parser.parse_args(list(args))

    options.directory = None

    session = get_session(options)
    get_csv_directory(options)
    pokedex.db.load.load(session, directory=None, drop_tables=True,
                                  verbose=options.verbose,
                                  safe=False)

    lookup = get_lookup(options, session=session, recreate=True)

    print("Recreated lookup index.")

def command_status(*args):
    parser = get_parser(verbose=True)
    options, _ = parser.parse_args(list(args))
    options.verbose = True
    options.directory = None

    # Database, and a lame check for whether it's been inited at least once
    session = get_session(options)
    print("  - OK!  Connected successfully.")

    if pokedex.db.tables.Pokemon.__table__.exists(session.bind):
        print("  - OK!  Database seems to contain some data.")
    else:
        print("  - WARNING: Database appears to be empty.")

    # CSV; simple checks that the dir exists
    csvdir = get_csv_directory(options)
    if not os.path.exists(csvdir):
        print("  - ERROR: No such directory!")
    elif not os.path.isdir(csvdir):
        print("  - ERROR: Not a directory!")
    else:
        print("  - OK!  Directory exists.")

        if os.access(csvdir, os.R_OK):
            print("  - OK!  Can read from directory.")
        else:
            print("  - ERROR: Can't read from directory!")

        if os.access(csvdir, os.W_OK):
            print("  - OK!  Can write to directory.")
        else:
            print("  - WARNING: Can't write to directory!  "
                "`dump` will not work.  You may need to sudo.")

    # Index; the PokedexLookup constructor covers most tests and will
    # cheerfully bomb if they fail
    lookup = get_lookup(options, recreate=False)
    print("  - OK!  Opened successfully.")

### User-facing commands

def command_lookup(*args):
    parser = get_parser(verbose=False)
    options, words = parser.parse_args(list(args))

    name = u' '.join(words)

    session = get_session(options)
    lookup = get_lookup(options, session=session, recreate=False)

    results = lookup.lookup(name)
    if not results:
        print("No matches.")
    elif results[0].exact:
        print("Matched:")
    else:
        print("Fuzzy-matched:")

    for result in results:
        if hasattr(result.object, 'full_name'):
            name = result.object.full_name
        else:
            name = result.object.name

        print("%s: %s" % (result.object.__tablename__, name), end='')
        if result.language:
            print("(%s in %s)" % (result.name, result.language))
        else:
            print()

def command_help():
    print(u"""pokedex -- a command-line Pokédex interface
usage: pokedex {command} [options...]
Run `pokedex setup` first, or nothing will work!
See https://github.com/veekun/pokedex/wiki/CLI for more documentation.

Commands:
    help                Displays this message.
    lookup [thing]      Look up something in the Pokédex.

System commands:
    load                Load Pokédex data into a database from CSV files.
    dump                Dump Pokédex data from a database into CSV files.
    reindex             Rebuilds the lookup index from the database.
    setup               Combines load and reindex.
    status              No effect, but prints which engine, index, and csv
                        directory would be used for other commands.

Global options:
    -e|--engine=URI     By default, all commands try to use a SQLite database
                        in the pokedex install directory.  Use this option (or
                        a POKEDEX_DB_ENGINE environment variable) to specify an
                        alternate database.
    -i|--index=DIR      By default, all commands try to put the lookup index in
                        the pokedex install directory.  Use this option (or a
                        POKEDEX_INDEX_DIR environment variable) to specify an
                        alternate loction.
    -q|--quiet          Don't print system output.  This is the default for
                        non-system commands and setup.
    -v|--verbose        Print system output.  This is the default for system
                        commands, except setup.

System options:
    -d|--directory=DIR  By default, load and dump will use the CSV files in the
                        pokedex install directory.  Use this option to specify
                        a different directory.

Load options:
    -D|--drop-tables    Drop all tables before loading data.
    -S|--safe           Disable engine-specific optimizations.
    -r|--recursive      Load (and drop) all dependent tables.
    -l|--langs          Load translations for the given languages.
                        By default, all available translations are loaded.
                        Separate multiple languages by a comma (-l en,de,fr)

Dump options:
    -l|--langs          Dump unofficial texts for given languages.
                        By default, English (en) is dumped.
                        Separate multiple languages by a comma (-l en,de,fr)
                        Use 'none' to not dump any unofficial texts.

    Additionally, load and dump accept a list of table names (possibly with
    wildcards) and/or csv fileames as an argument list.
""".encode(sys.getdefaultencoding(), 'replace'))

    sys.exit(0)

if __name__ == '__main__':
    main(*sys.argv)
<commit_msg>Little better CLI handling of Unicode in Py2<commit_after># encoding: utf8
from __future__ import print_function

import locale
from optparse import OptionParser
import os
import sys

import pokedex.db
import pokedex.db.load
import pokedex.db.tables
import pokedex.lookup
from pokedex import defaults

def main(*argv):
    if len(argv) <= 1:
        command_help()

    command = argv[1]
    args = argv[2:]

    # XXX there must be a better way to get Unicode argv
    # XXX this doesn't work on Windows durp
    enc = sys.stdin.encoding or 'utf8'
    args = [_.decode(enc) if isinstance(_, bytes) else _ for _ in args]

    # Find the command as a function in this file
    func = globals().get("command_%s" % command, None)
    if func:
        func(*args)
    else:
        command_help()

def setuptools_entry():
    main(*sys.argv)

def get_parser(verbose=True):
    """Returns an OptionParser prepopulated with the global options.

    `verbose` is whether or not the options should be verbose by default.
    """
    parser = OptionParser()
    parser.add_option('-e', '--engine', dest='engine_uri', default=None)
    parser.add_option('-i', '--index', dest='index_dir', default=None)
    parser.add_option('-q', '--quiet', dest='verbose', default=verbose, action='store_false')
    parser.add_option('-v', '--verbose', dest='verbose', default=verbose, action='store_true')
    return parser

def get_session(options):
    """Given a parsed options object, connects to the database and returns a
    session.
    """

    engine_uri = options.engine_uri
    got_from = 'command line'

    if engine_uri is None:
        engine_uri, got_from = defaults.get_default_db_uri_with_origin()

    session = pokedex.db.connect(engine_uri)

    if options.verbose:
        print("Connected to database %(engine)s (from %(got_from)s)"
            % dict(engine=session.bind.url, got_from=got_from))

    return session

def get_lookup(options, session=None, recreate=False):
    """Given a parsed options object, opens the whoosh index and returns a
    PokedexLookup object.
    """

    if recreate and not session:
        raise ValueError("get_lookup() needs an explicit session to regen the index")

    index_dir = options.index_dir
    got_from = 'command line'

    if index_dir is None:
        index_dir, got_from = defaults.get_default_index_dir_with_origin()

    if options.verbose:
        print("Opened lookup index %(index_dir)s (from %(got_from)s)"
            % dict(index_dir=index_dir, got_from=got_from))

    lookup = pokedex.lookup.PokedexLookup(index_dir, session=session)

    if recreate:
        lookup.rebuild_index()

    return lookup

def get_csv_directory(options):
    """Prints and returns the csv directory we're about to use."""

    if not options.verbose:
        return

    csvdir = options.directory
    got_from = 'command line'

    if csvdir is None:
        csvdir, got_from = defaults.get_default_csv_dir_with_origin()

    print("Using CSV directory %(csvdir)s (from %(got_from)s)"
        % dict(csvdir=csvdir, got_from=got_from))

    return csvdir

### Plumbing commands

def command_dump(*args):
    parser = get_parser(verbose=True)
    parser.add_option('-d', '--directory', dest='directory', default=None)
    parser.add_option('-l', '--langs', dest='langs', default=None,
        help="Comma-separated list of languages to dump all strings for. "
            "Default is English ('en')")
    options, tables = parser.parse_args(list(args))

    session = get_session(options)
    get_csv_directory(options)

    if options.langs is not None:
        langs = [l.strip() for l in options.langs.split(',')]
    else:
        langs = None

    pokedex.db.load.dump(session, directory=options.directory,
                                  tables=tables,
                                  verbose=options.verbose,
                                  langs=langs)

def command_load(*args):
    parser = get_parser(verbose=True)
    parser.add_option('-d', '--directory', dest='directory', default=None)
    parser.add_option('-D', '--drop-tables', dest='drop_tables', default=False, action='store_true')
    parser.add_option('-r', '--recursive', dest='recursive', default=False, action='store_true')
    parser.add_option('-S', '--safe', dest='safe', default=False, action='store_true',
        help="Do not use backend-specific optimalizations.")
    parser.add_option('-l', '--langs', dest='langs', default=None,
        help="Comma-separated list of extra languages to load, or 'none' for none. "
            "Default is to load 'em all. Example: 'fr,de'")
    options, tables = parser.parse_args(list(args))

    if not options.engine_uri:
        print("WARNING: You're reloading the default database, but not the lookup index.  They")
        print("         might get out of sync, and pokedex commands may not work correctly!")
        print("To fix this, run `pokedex reindex` when this command finishes.  Or, just use")
        print("`pokedex setup` to do both at once.")
        print()

    if options.langs == 'none':
        langs = []
    elif options.langs is None:
        langs = None
    else:
        langs = [l.strip() for l in options.langs.split(',')]

    session = get_session(options)
    get_csv_directory(options)

    pokedex.db.load.load(session, directory=options.directory,
                                  drop_tables=options.drop_tables,
                                  tables=tables,
                                  verbose=options.verbose,
                                  safe=options.safe,
                                  recursive=options.recursive,
                                  langs=langs)

def command_reindex(*args):
    parser = get_parser(verbose=True)
    options, _ = parser.parse_args(list(args))

    session = get_session(options)
    lookup = get_lookup(options, session=session, recreate=True)

    print("Recreated lookup index.")

def command_setup(*args):
    parser = get_parser(verbose=False)
    options, _ = parser.parse_args(list(args))

    options.directory = None

    session = get_session(options)
    get_csv_directory(options)
    pokedex.db.load.load(session, directory=None, drop_tables=True,
                                  verbose=options.verbose,
                                  safe=False)

    lookup = get_lookup(options, session=session, recreate=True)

    print("Recreated lookup index.")

def command_status(*args):
    parser = get_parser(verbose=True)
    options, _ = parser.parse_args(list(args))
    options.verbose = True
    options.directory = None

    # Database, and a lame check for whether it's been inited at least once
    session = get_session(options)
    print("  - OK!  Connected successfully.")

    if pokedex.db.tables.Pokemon.__table__.exists(session.bind):
        print("  - OK!  Database seems to contain some data.")
    else:
        print("  - WARNING: Database appears to be empty.")

    # CSV; simple checks that the dir exists
    csvdir = get_csv_directory(options)
    if not os.path.exists(csvdir):
        print("  - ERROR: No such directory!")
    elif not os.path.isdir(csvdir):
        print("  - ERROR: Not a directory!")
    else:
        print("  - OK!  Directory exists.")

        if os.access(csvdir, os.R_OK):
            print("  - OK!  Can read from directory.")
        else:
            print("  - ERROR: Can't read from directory!")

        if os.access(csvdir, os.W_OK):
            print("  - OK!  Can write to directory.")
        else:
            print("  - WARNING: Can't write to directory!  "
                "`dump` will not work.  You may need to sudo.")

    # Index; the PokedexLookup constructor covers most tests and will
    # cheerfully bomb if they fail
    lookup = get_lookup(options, recreate=False)
    print("  - OK!  Opened successfully.")

### User-facing commands

def command_lookup(*args):
    parser = get_parser(verbose=False)
    options, words = parser.parse_args(list(args))

    name = u' '.join(words)

    session = get_session(options)
    lookup = get_lookup(options, session=session, recreate=False)

    results = lookup.lookup(name)
    if not results:
        print("No matches.")
    elif results[0].exact:
        print("Matched:")
    else:
        print("Fuzzy-matched:")

    for result in results:
        if hasattr(result.object, 'full_name'):
            name = result.object.full_name
        else:
            name = result.object.name

        print("%s: %s" % (result.object.__tablename__, name), end='')
        if result.language:
            print("(%s in %s)" % (result.name, result.language))
        else:
            print()

def command_help():
    print(u"""pokedex -- a command-line Pokédex interface
usage: pokedex {command} [options...]
Run `pokedex setup` first, or nothing will work!
See https://github.com/veekun/pokedex/wiki/CLI for more documentation.

Commands:
    help                Displays this message.
    lookup [thing]      Look up something in the Pokédex.

System commands:
    load                Load Pokédex data into a database from CSV files.
    dump                Dump Pokédex data from a database into CSV files.
    reindex             Rebuilds the lookup index from the database.
    setup               Combines load and reindex.
    status              No effect, but prints which engine, index, and csv
                        directory would be used for other commands.

Global options:
    -e|--engine=URI     By default, all commands try to use a SQLite database
                        in the pokedex install directory.  Use this option (or
                        a POKEDEX_DB_ENGINE environment variable) to specify an
                        alternate database.
    -i|--index=DIR      By default, all commands try to put the lookup index in
                        the pokedex install directory.  Use this option (or a
                        POKEDEX_INDEX_DIR environment variable) to specify an
                        alternate loction.
    -q|--quiet          Don't print system output.  This is the default for
                        non-system commands and setup.
    -v|--verbose        Print system output.  This is the default for system
                        commands, except setup.

System options:
    -d|--directory=DIR  By default, load and dump will use the CSV files in the
                        pokedex install directory.  Use this option to specify
                        a different directory.

Load options:
    -D|--drop-tables    Drop all tables before loading data.
    -S|--safe           Disable engine-specific optimizations.
    -r|--recursive      Load (and drop) all dependent tables.
    -l|--langs          Load translations for the given languages.
                        By default, all available translations are loaded.
                        Separate multiple languages by a comma (-l en,de,fr)

Dump options:
    -l|--langs          Dump unofficial texts for given languages.
                        By default, English (en) is dumped.
                        Separate multiple languages by a comma (-l en,de,fr)
                        Use 'none' to not dump any unofficial texts.

    Additionally, load and dump accept a list of table names (possibly with
    wildcards) and/or csv fileames as an argument list.
""".encode(locale.getdefaultlocale()[1], 'replace'))

    sys.exit(0)

if __name__ == '__main__':
    main(*sys.argv)
<|endoftext|>