<commit_before>from . import api
from flask_restful import Resource
from .decorators import requires_auth
from .. import graph, db
from ..models import Person, Link
from flask import _app_ctx_stack, request
from sqlalchemy import or_

class pingAPI(Resource):
    decorators = [requires_auth]

    def get(self):
        return 'Here is a ping'

    def put(self):
        return 'Here is a ping'

    def delete(self):
        return 'Here is a ping'

# relation_name:{value:'Father', type:'multiselect-input'},
# birth_date:{value:'2017-02-15', type:'pikaday-input'}

class relationshipsAPI(Resource):
    decorators = [requires_auth]

    def format_response(self, nodes, user_id):
        relations = []
        for node in nodes:
            if node != user_id:
                relation = Person.query.get(node)
                relations.append({
                    'id': {'value': relation.id, 'type': 'hidden-input',
                           'input_name': 'data_id', 'label': 'ID'},
                    'first_name': {'value': relation.baptism_name,
                                   'type': 'alpha-input',
                                   'input_name': 'mod_first-name',
                                   'label': 'First Name'},
                    'ethnic_name': {'value': relation.ethnic_name,
                                    'type': 'alpha-input',
                                    'input_name': 'mod_ethnic-name',
                                    'label': 'Ethnic Name'},
                    'last_name': {'value': relation.surname,
                                  'type': 'alpha-input',
                                  'input_name': 'mod_last-name',
                                  'label': 'Last Name'},
                    'email': {'value': relation.email,
                              'type': 'email-input',
                              'input_name': 'mod_email',
                              'label': 'Email'}
                })
        return relations

    def get(self):
        user_email = _app_ctx_stack.top.current_user['email']
        user = Person.query.filter_by(email=user_email).first()
        node_list = graph.get_subgraph(user).nodes()
        response = self.format_response(node_list, user.id)
        return response

    def put(self):
        print(request.get_json('data'))
        return {'message': 'Relationship added/updated'}

    def delete(self):
        user_email = _app_ctx_stack.top.current_user['email']
        delete_person_id = int(request.data)
        user = Person.query.filter_by(email=user_email).first()
        Person.query.filter_by(id=delete_person_id).delete()
        Link.query.filter(
            or_(Link.ascendant_id == delete_person_id,
                Link.descendant_id == delete_person_id)).delete()
        db.session.commit()
        graph.delete_node(delete_person_id)
        node_list = graph.get_subgraph(user).nodes()
        response = self.format_response(node_list, user.id)
        return response

api.add_resource(relationshipsAPI, '/relationships', endpoint='relationships')
api.add_resource(pingAPI, '/ping', endpoint='ping')
<commit_msg>consistent client and server form data structure<commit_after>from . import api
from flask_restful import Resource
from .decorators import requires_auth
from .. import graph, db
from ..models import Person, Link
from flask import _app_ctx_stack, request
from sqlalchemy import or_

class pingAPI(Resource):
    decorators = [requires_auth]

    def get(self):
        return 'Here is a ping'

    def put(self):
        return 'Here is a ping'

    def delete(self):
        return 'Here is a ping'

# relation_name:{value:'Father', type:'multiselect-input'},
# birth_date:{value:'2017-02-15', type:'pikaday-input'}

class relationshipsAPI(Resource):
    decorators = [requires_auth]

    def format_response(self, nodes, user_id):
        relatives = []
        for node in nodes:
            if node != user_id:
                relation = Person.query.get(node)
                relatives.append([
                    {
                        'type': 'hidden-input', 'value': relation.id or '',
                        'label': 'ID', 'field_name': 'id'
                    },
                    {
                        'type': 'alpha-input',
                        'value': relation.baptism_name or '',
                        'label': 'First Name',
                        'validators': ['required', 'alpha'],
                        'field_name': 'first_name'
                    },
                    {
                        'type': 'alpha-input',
                        'value': relation.ethnic_name or '',
                        'label': 'Ethnic Name',
                        'validators': ['required', 'alpha'],
                        'field_name': 'ethnic_name'},
                    {
                        'type': 'alpha-input', 'value': relation.surname or '',
                        'label': 'Last Name',
                        'validators': ['required', 'alpha'],
                        'field_name': 'last_name'
                    },
                    {
                        'type': 'email-input', 'value': relation.email or '',
                        'label': 'Email', 'validators': ['required', 'email'],
                        'field_name': 'email'
                    }
                ])
        return relatives

    def get(self):
        user_email = _app_ctx_stack.top.current_user['email']
        user = Person.query.filter_by(email=user_email).first()
        node_list = graph.get_subgraph(user).nodes()
        response = self.format_response(node_list, user.id)
        return response

    def put(self):
        print(request.get_json('data'))
        return {'message': 'Relationship added/updated'}

    def delete(self):
        user_email = _app_ctx_stack.top.current_user['email']
        delete_person_id = int(request.data)
        user = Person.query.filter_by(email=user_email).first()
        Person.query.filter_by(id=delete_person_id).delete()
        Link.query.filter(
            or_(Link.ascendant_id == delete_person_id,
                Link.descendant_id == delete_person_id)).delete()
        db.session.commit()
        graph.delete_node(delete_person_id)
        node_list = graph.get_subgraph(user).nodes()
        response = self.format_response(node_list, user.id)
        return response

api.add_resource(relationshipsAPI, '/relationships', endpoint='relationships')
api.add_resource(pingAPI, '/ping', endpoint='ping')
<|endoftext|>


<commit_before>import doctest
import io
import os
import re
import types
import sys
import time
import traceback
import unittest

from functools import partial

# Python 2.7: _WritelnDecorator moved.
try:
    from unittest import _WritelnDecorator
except ImportError:
    from unittest.runner import _WritelnDecorator

import nose

# Python 2.7: nose uses unittest's builtin SkipTest class
try:
    SkipTest = unittest.case.SkipTest
except AttributeError:
    SkipTest = nose.SkipTest

import six

from nose.plugins import Plugin

################################################################################
## Functions for constructing specifications based on nose testing objects.
################################################################################

def dispatch_on_type(dispatch_table, instance):
    for type, func in dispatch_table:
        if type is True or isinstance(instance, type):
            return func(instance)

def remove_leading(needle, haystack):
    """Remove leading needle string (if exists).

    >>> remove_leading('Test', 'TestThisAndThat')
    'ThisAndThat'
    >>> remove_leading('Test', 'ArbitraryName')
    'ArbitraryName'
    """
    if haystack[:len(needle)] == needle:
        return haystack[len(needle):]
    return haystack

def remove_trailing(needle, haystack):
    """Remove trailing needle string (if exists).

    >>> remove_trailing('Test', 'ThisAndThatTest')
    'ThisAndThat'
    >>> remove_trailing('Test', 'ArbitraryName')
    'ArbitraryName'
    """
    if haystack[-len(needle):] == needle:
        return haystack[:-len(needle)]
    return haystack

def remove_leading_and_trailing(needle, haystack):
    return remove_leading(needle, remove_trailing(needle, haystack))

def camel2word(string):
    """Covert name from CamelCase to "Normal case".

    >>> camel2word('CamelCase')
    'Camel case'
    >>> camel2word('CaseWithSpec')
    'Case with spec'
    """
    def wordize(match):
        return ' ' + match.group(1).lower()

    return string[0] + re.sub(r'([A-Z])', wordize, string[1:])

def complete_english(string):
    """
    >>> complete_english('dont do this')
    "don't do this"
    >>> complete_english('doesnt is matched as well')
    "doesn't is matched as well"
    """
    for x, y in [("dont", "don't"),
                ("doesnt", "doesn't"),
                ("wont", "won't"),
                ("wasnt", "wasn't")]:
        string = string.replace(x, y)
    return string

def underscore2word(string):
    return string.replace('_', ' ')

def argumentsof(test):
    if test.arg:
        if len(test.arg) == 1:
            return " for %s" % test.arg[0]
        else:
            return " for %s" % (test.arg,)
    return ""

def underscored2spec(name):
    return complete_english(underscore2word(remove_trailing('_test', remove_leading('test_', name))))

def camelcase2spec(name):
    return camel2word(
        remove_trailing('_',
            remove_leading_and_trailing('Test', name)))

def camelcaseDescription(object):
    description = object.__doc__ or camelcase2spec(object.__name__)
    return description.strip()

def underscoredDescription(object):
    return object.__doc__ or underscored2spec(object.__name__).capitalize()

def doctestContextDescription(doctest):
    return doctest._dt_test.name

def noseMethodDescription(test):
    return test.method.__doc__ or underscored2spec(test.method.__name__)

def unittestMethodDescription(test):
    if test._testMethodDoc is None:
        return underscored2spec(test._testMethodName)
    else:
        description = test._testMethodDoc.split("\n")
        return "".join([text.strip() for text in description])

def noseFunctionDescription(test):
    # Special case for test generators.
    if test.descriptor is not None:
        if hasattr(test.test, 'description'):
            return test.test.description
        return "holds for %s" % ', '.join(map(six.text_type, test.arg))
    return test.test.__doc__ or underscored2spec(test.test.__name__)

# Different than other similar functions, this one returns a generator
# of specifications.
def doctestExamplesDescription(test):
    for ex in test._dt_test.examples:
        source = ex.source.replace("\n", " ")
        want = None
        if '#' in source:
            source, want = source.rsplit('#', 1)
        elif ex.exc_msg:
            want = "throws \"%s\"" % ex.exc_msg.rstrip()
        elif ex.want:
            want = "returns %s" % ex.want.replace("\n", " ")

        if want:
            yield "%s %s" % (source.strip(), want.strip())

def testDescription(test):
    supported_test_types = [
        (nose.case.MethodTestCase, noseMethodDescription),
        (nose.case.FunctionTestCase, noseFunctionDescription),
        (doctest.DocTestCase, doctestExamplesDescription),
        (unittest.TestCase, unittestMethodDescription),
    ]
    return dispatch_on_type(supported_test_types, test.test)

def contextDescription(context):
    supported_context_types = [
        (types.ModuleType, underscoredDescription),
        (types.FunctionType, underscoredDescription),
        (doctest.DocTestCase, doctestContextDescription),
        (type, camelcaseDescription),
    ]

    if not six.PY3:
        supported_context_types += [
            # Handle both old and new style classes.
            (types.ClassType, camelcaseDescription),
        ]

    return dispatch_on_type(supported_context_types, context)

def testContext(test):
    # Test generators set their own contexts.
    if isinstance(test.test, nose.case.FunctionTestCase) \
           and test.test.descriptor is not None:
        return test.test.descriptor
    # So do doctests.
    elif isinstance(test.test, doctest.DocTestCase):
        return test.test
    else:
        return test.context

################################################################################
## Output stream that can be easily enabled and disabled.
################################################################################

class OutputStream(_WritelnDecorator):
    def __init__(self, on_stream, off_stream):
        self.capture_stream = io.BytesIO()
        self.on_stream = on_stream
        self.off_stream = off_stream
        self.stream = on_stream

    def on(self):
        self.stream = self.on_stream

    def off(self):
        self.stream = self.off_stream

    def capture(self):
        self.capture_stream.truncate()
        self.stream = self.capture_stream

    def get_captured(self):
        self.capture_stream.seek(0)
        return self.capture_stream.read()

def depth(context):
    level = 0
    while hasattr(context, '_parent'):
        level += 1
        context = context._parent
    return level

class SpecOutputStream(OutputStream):
    def print_text(self, text):
        self.on()
        self.write(text)
        self.off()

    def print_line(self, line=''):
        self.print_text(line + "\n")

    @property
    def _indent(self):
        return "    " * self._depth

    def print_context(self, context):
        # Ensure parents get printed too (e.g. an outer class with nothing but
        # inner classes will otherwise never get printed.)
        if (
            hasattr(context, '_parent')
            and not getattr(context._parent, '_printed', False)
        ):
            self.print_context(context._parent)
        # Adjust indentation depth
        self._depth = depth(context)
        self.print_line("\n%s%s" % (self._indent, contextDescription(context)))
        context._printed = True

    def print_spec(self, color_func, test, status=None):
        spec = testDescription(test).strip()
        if not isinstance(spec, types.GeneratorType):
            spec = [spec]
        for s in spec:
            name = "- %s" % s
            paren = (" (%s)" % status) if status else ""
            indent = getattr(self, '_indent', "")
            self.print_line(indent + color_func(name + paren))

################################################################################
## Color helpers.
################################################################################

color_end = "\x1b[1;0m"
colors = dict(
    green="32",
    red="31",
    yellow="33",
    purple="35",
    cyan="36",
    blue="34"
)

def colorize(color, text, bold=False):
    bold = 1 if bold else 0
    return "\x1b[%s;%sm%s%s" % (bold, colors[color], text, color_end)

################################################################################
## Plugin itself.
################################################################################

class SpecPlugin(Plugin):
    """Generate specification from test class/method names.
    """
    score = 1100  # must be higher than Deprecated and Skip plugins scores

    def __init__(self, *args, **kwargs):
        super(SpecPlugin, self).__init__(*args, **kwargs)
        self._failures = []
        self._errors = []
        self.color = {}

    def options(self, parser, env=os.environ):
        Plugin.options(self, parser, env)
        parser.add_option('--no-spec-color', action='store_true',
                          dest='no_spec_color',
                          default=env.get('NOSE_NO_SPEC_COLOR'),
                          help="Don't show colors with --with-spec"
                          "[NOSE_NO_SPEC_COLOR]")
        parser.add_option('--spec-doctests', action='store_true',
                          dest='spec_doctests',
                          default=env.get('NOSE_SPEC_DOCTESTS'),
                          help="Include doctests in specifications "
                          "[NOSE_SPEC_DOCTESTS]")
        parser.add_option('--no-detailed-errors', action='store_false',
                          dest='detailedErrors',
                          help="Force detailed errors off")

    def configure(self, options, config):
        # Configure
        Plugin.configure(self, options, config)
        # Set options
        if options.enable_plugin_specplugin:
            options.verbosity = max(options.verbosity, 2)
        self.spec_doctests = options.spec_doctests
        # Color setup
        for label, color in list({
            'error': 'red',
            'ok': 'green',
            'deprecated': 'yellow',
            'skipped': 'yellow',
            'failure': 'red',
            'identifier': 'cyan',
            'file': 'blue',
        }.items()):
            # No color: just print() really
            func = lambda text, bold=False: text
            if not options.no_spec_color:
                # Color: colorizes!
                func = partial(colorize, color)
            # Store in dict (slightly quicker/nicer than getattr)
            self.color[label] = func
            # Add attribute for easier hardcoded access
            setattr(self, label, func)

    def begin(self):
        self.current_context = None
        self.start_time = time.time()

    def setOutputStream(self, stream):
        self.stream = SpecOutputStream(stream, open(os.devnull, 'w'))
        return self.stream

    def beforeTest(self, test):
        context = testContext(test)
        if context != self.current_context:
            self._print_context(context)
            self.current_context = context

        self.stream.off()

    def addSuccess(self, test):
        self._print_spec('ok', test)

    def addFailure(self, test, err):
        self._print_spec('failure', test, '')
        self._failures.append((test, err))

    def addError(self, test, err):
        def blurt(color, label):
            self._print_spec(color, test, label)

        klass = err[0]
        if issubclass(klass, nose.DeprecatedTest):
            blurt('deprecated', '')
        elif issubclass(klass, SkipTest):
            blurt('skipped', '')
        else:
            self._errors.append((test, err))
            blurt('error', '')

    def afterTest(self, test):
        self.stream.capture()

    def print_tracebacks(self, label, items):
        problem_color = {
            "ERROR": "error",
            "FAIL": "failure"
        }[label]
        for item in items:
            test, trace = item
            desc = test.shortDescription() or six.text_type(test)
            self.stream.writeln("=" * 70)
            self.stream.writeln("%s: %s" % (
                self.color[problem_color](label),
                self.identifier(desc, bold=True),
            ))
            self.stream.writeln("-" * 70)
            # format_exception() is...very odd re: how it breaks into lines.
            trace = "".join(traceback.format_exception(*trace)).split("\n")
            self.print_colorized_traceback(trace)

    def print_colorized_traceback(self, formatted_traceback, indent_level=0):
        indentation = "    " * indent_level
        for line in formatted_traceback:
            if line.startswith("  File"):
                m = re.match(r'  File "(.*)", line (\d*)(?:, in (.*))?$', line)
                if m:
                    filename, lineno, test = m.groups()
                    tb_lines = [
                        '  File "',
                        self.file(filename),
                        '", line ',
                        self.error(lineno),
                        ]
                    if test:
                        # this is missing for the first traceback in doctest
                        # failure report
                        tb_lines.extend([
                            ", in ",
                            self.identifier(test, bold=True)
                        ])
                    tb_lines.extend(["\n"])
                    self.stream.write(indentation)
                    self.stream.writelines(tb_lines)
                else:
                    six.print_(indentation + line, file=self.stream)
            elif line.startswith("    "):
                six.print_(self.identifier(indentation + line), file=self.stream)
            elif line.startswith("Traceback (most recent call last)"):
                six.print_(indentation + line, file=self.stream)
            else:
                six.print_(self.error(indentation + line), file=self.stream)

    def finalize(self, result):
        self.stream.on()
        six.print_("", file=self.stream)
        self.print_tracebacks("ERROR", self._errors)
        self.print_tracebacks("FAIL", self._failures)
        self.print_summary(result)

    def print_summary(self, result):
        # Setup
        num_tests = result.testsRun
        success = result.wasSuccessful()
        # How many in how long
        six.print_("Ran %s test%s in %s" % (
            (self.ok if success else self.error)(num_tests),
            "s" if num_tests > 1 else "",
            self.format_seconds(time.time() - self.start_time)
        ), file=self.stream)
        # Did we fail, and if so, how badly?
        if success:
            skipped = len(result.skipped)
            skipped_str = "(" + self.skipped("%i skipped" % skipped) + ")"
            six.print_(self.ok("OK"), skipped_str if skipped else "", file=self.stream)
        else:
            types = (
                ('failures', 'failure'),
                ('errors', 'error'),
                ('skipped', 'skipped'),
            )
            pairs = []
            for label, color in types:
                num = len(getattr(result, label))
                text = six.text_type(num)
                if num:
                    text = self.color[color](text)
                pairs.append("%s=%s" % (label, text))
            six.print_("%s (%s)" % (
                self.failure("FAILED"),
                ", ".join(pairs)
            ), file=self.stream)
        six.print_("", file=self.stream)

    def format_seconds(self, n_seconds):
        """Format a time in seconds."""
        func = self.ok
        if n_seconds >= 60:
            n_minutes, n_seconds = divmod(n_seconds, 60)
            return "%s minutes %s seconds" % (
                        func("%d" % n_minutes),
                        func("%.3f" % n_seconds))
        else:
            return "%s seconds" % (
                        func("%.3f" % n_seconds))

    def _print_context(self, context):
        if isinstance(context, doctest.DocTestCase) and not self.spec_doctests:
            return
        self.stream.print_context(context)

    def _print_spec(self, color, test, status=None):
        if isinstance(test.test, doctest.DocTestCase) and not self.spec_doctests:
            return
        self.stream.print_spec(self.color[color], test, status)
<commit_msg>Useless import<commit_after>import doctest
import io
import os
import re
import types
import time
import traceback
import unittest

from functools import partial

# Python 2.7: _WritelnDecorator moved.
try:
    from unittest import _WritelnDecorator
except ImportError:
    from unittest.runner import _WritelnDecorator

import nose

# Python 2.7: nose uses unittest's builtin SkipTest class
try:
    SkipTest = unittest.case.SkipTest
except AttributeError:
    SkipTest = nose.SkipTest

import six

from nose.plugins import Plugin

################################################################################
## Functions for constructing specifications based on nose testing objects.
################################################################################

def dispatch_on_type(dispatch_table, instance):
    for type, func in dispatch_table:
        if type is True or isinstance(instance, type):
            return func(instance)

def remove_leading(needle, haystack):
    """Remove leading needle string (if exists).

    >>> remove_leading('Test', 'TestThisAndThat')
    'ThisAndThat'
    >>> remove_leading('Test', 'ArbitraryName')
    'ArbitraryName'
    """
    if haystack[:len(needle)] == needle:
        return haystack[len(needle):]
    return haystack

def remove_trailing(needle, haystack):
    """Remove trailing needle string (if exists).

    >>> remove_trailing('Test', 'ThisAndThatTest')
    'ThisAndThat'
    >>> remove_trailing('Test', 'ArbitraryName')
    'ArbitraryName'
    """
    if haystack[-len(needle):] == needle:
        return haystack[:-len(needle)]
    return haystack

def remove_leading_and_trailing(needle, haystack):
    return remove_leading(needle, remove_trailing(needle, haystack))

def camel2word(string):
    """Covert name from CamelCase to "Normal case".

    >>> camel2word('CamelCase')
    'Camel case'
    >>> camel2word('CaseWithSpec')
    'Case with spec'
    """
    def wordize(match):
        return ' ' + match.group(1).lower()

    return string[0] + re.sub(r'([A-Z])', wordize, string[1:])

def complete_english(string):
    """
    >>> complete_english('dont do this')
    "don't do this"
    >>> complete_english('doesnt is matched as well')
    "doesn't is matched as well"
    """
    for x, y in [("dont", "don't"),
                ("doesnt", "doesn't"),
                ("wont", "won't"),
                ("wasnt", "wasn't")]:
        string = string.replace(x, y)
    return string

def underscore2word(string):
    return string.replace('_', ' ')

def argumentsof(test):
    if test.arg:
        if len(test.arg) == 1:
            return " for %s" % test.arg[0]
        else:
            return " for %s" % (test.arg,)
    return ""

def underscored2spec(name):
    return complete_english(underscore2word(remove_trailing('_test', remove_leading('test_', name))))

def camelcase2spec(name):
    return camel2word(
        remove_trailing('_',
            remove_leading_and_trailing('Test', name)))

def camelcaseDescription(object):
    description = object.__doc__ or camelcase2spec(object.__name__)
    return description.strip()

def underscoredDescription(object):
    return object.__doc__ or underscored2spec(object.__name__).capitalize()

def doctestContextDescription(doctest):
    return doctest._dt_test.name

def noseMethodDescription(test):
    return test.method.__doc__ or underscored2spec(test.method.__name__)

def unittestMethodDescription(test):
    if test._testMethodDoc is None:
        return underscored2spec(test._testMethodName)
    else:
        description = test._testMethodDoc.split("\n")
        return "".join([text.strip() for text in description])

def noseFunctionDescription(test):
    # Special case for test generators.
    if test.descriptor is not None:
        if hasattr(test.test, 'description'):
            return test.test.description
        return "holds for %s" % ', '.join(map(six.text_type, test.arg))
    return test.test.__doc__ or underscored2spec(test.test.__name__)

# Different than other similar functions, this one returns a generator
# of specifications.
def doctestExamplesDescription(test):
    for ex in test._dt_test.examples:
        source = ex.source.replace("\n", " ")
        want = None
        if '#' in source:
            source, want = source.rsplit('#', 1)
        elif ex.exc_msg:
            want = "throws \"%s\"" % ex.exc_msg.rstrip()
        elif ex.want:
            want = "returns %s" % ex.want.replace("\n", " ")

        if want:
            yield "%s %s" % (source.strip(), want.strip())

def testDescription(test):
    supported_test_types = [
        (nose.case.MethodTestCase, noseMethodDescription),
        (nose.case.FunctionTestCase, noseFunctionDescription),
        (doctest.DocTestCase, doctestExamplesDescription),
        (unittest.TestCase, unittestMethodDescription),
    ]
    return dispatch_on_type(supported_test_types, test.test)

def contextDescription(context):
    supported_context_types = [
        (types.ModuleType, underscoredDescription),
        (types.FunctionType, underscoredDescription),
        (doctest.DocTestCase, doctestContextDescription),
        (type, camelcaseDescription),
    ]

    if not six.PY3:
        supported_context_types += [
            # Handle both old and new style classes.
            (types.ClassType, camelcaseDescription),
        ]

    return dispatch_on_type(supported_context_types, context)

def testContext(test):
    # Test generators set their own contexts.
    if isinstance(test.test, nose.case.FunctionTestCase) \
           and test.test.descriptor is not None:
        return test.test.descriptor
    # So do doctests.
    elif isinstance(test.test, doctest.DocTestCase):
        return test.test
    else:
        return test.context

################################################################################
## Output stream that can be easily enabled and disabled.
################################################################################

class OutputStream(_WritelnDecorator):
    def __init__(self, on_stream, off_stream):
        self.capture_stream = io.BytesIO()
        self.on_stream = on_stream
        self.off_stream = off_stream
        self.stream = on_stream

    def on(self):
        self.stream = self.on_stream

    def off(self):
        self.stream = self.off_stream

    def capture(self):
        self.capture_stream.truncate()
        self.stream = self.capture_stream

    def get_captured(self):
        self.capture_stream.seek(0)
        return self.capture_stream.read()

def depth(context):
    level = 0
    while hasattr(context, '_parent'):
        level += 1
        context = context._parent
    return level

class SpecOutputStream(OutputStream):
    def print_text(self, text):
        self.on()
        self.write(text)
        self.off()

    def print_line(self, line=''):
        self.print_text(line + "\n")

    @property
    def _indent(self):
        return "    " * self._depth

    def print_context(self, context):
        # Ensure parents get printed too (e.g. an outer class with nothing but
        # inner classes will otherwise never get printed.)
        if (
            hasattr(context, '_parent')
            and not getattr(context._parent, '_printed', False)
        ):
            self.print_context(context._parent)
        # Adjust indentation depth
        self._depth = depth(context)
        self.print_line("\n%s%s" % (self._indent, contextDescription(context)))
        context._printed = True

    def print_spec(self, color_func, test, status=None):
        spec = testDescription(test).strip()
        if not isinstance(spec, types.GeneratorType):
            spec = [spec]
        for s in spec:
            name = "- %s" % s
            paren = (" (%s)" % status) if status else ""
            indent = getattr(self, '_indent', "")
            self.print_line(indent + color_func(name + paren))

################################################################################
## Color helpers.
################################################################################

color_end = "\x1b[1;0m"
colors = dict(
    green="32",
    red="31",
    yellow="33",
    purple="35",
    cyan="36",
    blue="34"
)

def colorize(color, text, bold=False):
    bold = 1 if bold else 0
    return "\x1b[%s;%sm%s%s" % (bold, colors[color], text, color_end)

################################################################################
## Plugin itself.
################################################################################

class SpecPlugin(Plugin):
    """Generate specification from test class/method names.
    """
    score = 1100  # must be higher than Deprecated and Skip plugins scores

    def __init__(self, *args, **kwargs):
        super(SpecPlugin, self).__init__(*args, **kwargs)
        self._failures = []
        self._errors = []
        self.color = {}

    def options(self, parser, env=os.environ):
        Plugin.options(self, parser, env)
        parser.add_option('--no-spec-color', action='store_true',
                          dest='no_spec_color',
                          default=env.get('NOSE_NO_SPEC_COLOR'),
                          help="Don't show colors with --with-spec"
                          "[NOSE_NO_SPEC_COLOR]")
        parser.add_option('--spec-doctests', action='store_true',
                          dest='spec_doctests',
                          default=env.get('NOSE_SPEC_DOCTESTS'),
                          help="Include doctests in specifications "
                          "[NOSE_SPEC_DOCTESTS]")
        parser.add_option('--no-detailed-errors', action='store_false',
                          dest='detailedErrors',
                          help="Force detailed errors off")

    def configure(self, options, config):
        # Configure
        Plugin.configure(self, options, config)
        # Set options
        if options.enable_plugin_specplugin:
            options.verbosity = max(options.verbosity, 2)
        self.spec_doctests = options.spec_doctests
        # Color setup
        for label, color in list({
            'error': 'red',
            'ok': 'green',
            'deprecated': 'yellow',
            'skipped': 'yellow',
            'failure': 'red',
            'identifier': 'cyan',
            'file': 'blue',
        }.items()):
            # No color: just print() really
            func = lambda text, bold=False: text
            if not options.no_spec_color:
                # Color: colorizes!
                func = partial(colorize, color)
            # Store in dict (slightly quicker/nicer than getattr)
            self.color[label] = func
            # Add attribute for easier hardcoded access
            setattr(self, label, func)

    def begin(self):
        self.current_context = None
        self.start_time = time.time()

    def setOutputStream(self, stream):
        self.stream = SpecOutputStream(stream, open(os.devnull, 'w'))
        return self.stream

    def beforeTest(self, test):
        context = testContext(test)
        if context != self.current_context:
            self._print_context(context)
            self.current_context = context

        self.stream.off()

    def addSuccess(self, test):
        self._print_spec('ok', test)

    def addFailure(self, test, err):
        self._print_spec('failure', test, '')
        self._failures.append((test, err))

    def addError(self, test, err):
        def blurt(color, label):
            self._print_spec(color, test, label)

        klass = err[0]
        if issubclass(klass, nose.DeprecatedTest):
            blurt('deprecated', '')
        elif issubclass(klass, SkipTest):
            blurt('skipped', '')
        else:
            self._errors.append((test, err))
            blurt('error', '')

    def afterTest(self, test):
        self.stream.capture()

    def print_tracebacks(self, label, items):
        problem_color = {
            "ERROR": "error",
            "FAIL": "failure"
        }[label]
        for item in items:
            test, trace = item
            desc = test.shortDescription() or six.text_type(test)
            self.stream.writeln("=" * 70)
            self.stream.writeln("%s: %s" % (
                self.color[problem_color](label),
                self.identifier(desc, bold=True),
            ))
            self.stream.writeln("-" * 70)
            # format_exception() is...very odd re: how it breaks into lines.
            trace = "".join(traceback.format_exception(*trace)).split("\n")
            self.print_colorized_traceback(trace)

    def print_colorized_traceback(self, formatted_traceback, indent_level=0):
        indentation = "    " * indent_level
        for line in formatted_traceback:
            if line.startswith("  File"):
                m = re.match(r'  File "(.*)", line (\d*)(?:, in (.*))?$', line)
                if m:
                    filename, lineno, test = m.groups()
                    tb_lines = [
                        '  File "',
                        self.file(filename),
                        '", line ',
                        self.error(lineno),
                        ]
                    if test:
                        # this is missing for the first traceback in doctest
                        # failure report
                        tb_lines.extend([
                            ", in ",
                            self.identifier(test, bold=True)
                        ])
                    tb_lines.extend(["\n"])
                    self.stream.write(indentation)
                    self.stream.writelines(tb_lines)
                else:
                    six.print_(indentation + line, file=self.stream)
            elif line.startswith("    "):
                six.print_(self.identifier(indentation + line), file=self.stream)
            elif line.startswith("Traceback (most recent call last)"):
                six.print_(indentation + line, file=self.stream)
            else:
                six.print_(self.error(indentation + line), file=self.stream)

    def finalize(self, result):
        self.stream.on()
        six.print_("", file=self.stream)
        self.print_tracebacks("ERROR", self._errors)
        self.print_tracebacks("FAIL", self._failures)
        self.print_summary(result)

    def print_summary(self, result):
        # Setup
        num_tests = result.testsRun
        success = result.wasSuccessful()
        # How many in how long
        six.print_("Ran %s test%s in %s" % (
            (self.ok if success else self.error)(num_tests),
            "s" if num_tests > 1 else "",
            self.format_seconds(time.time() - self.start_time)
        ), file=self.stream)
        # Did we fail, and if so, how badly?
        if success:
            skipped = len(result.skipped)
            skipped_str = "(" + self.skipped("%i skipped" % skipped) + ")"
            six.print_(self.ok("OK"), skipped_str if skipped else "", file=self.stream)
        else:
            types = (
                ('failures', 'failure'),
                ('errors', 'error'),
                ('skipped', 'skipped'),
            )
            pairs = []
            for label, color in types:
                num = len(getattr(result, label))
                text = six.text_type(num)
                if num:
                    text = self.color[color](text)
                pairs.append("%s=%s" % (label, text))
            six.print_("%s (%s)" % (
                self.failure("FAILED"),
                ", ".join(pairs)
            ), file=self.stream)
        six.print_("", file=self.stream)

    def format_seconds(self, n_seconds):
        """Format a time in seconds."""
        func = self.ok
        if n_seconds >= 60:
            n_minutes, n_seconds = divmod(n_seconds, 60)
            return "%s minutes %s seconds" % (
                        func("%d" % n_minutes),
                        func("%.3f" % n_seconds))
        else:
            return "%s seconds" % (
                        func("%.3f" % n_seconds))

    def _print_context(self, context):
        if isinstance(context, doctest.DocTestCase) and not self.spec_doctests:
            return
        self.stream.print_context(context)

    def _print_spec(self, color, test, status=None):
        if isinstance(test.test, doctest.DocTestCase) and not self.spec_doctests:
            return
        self.stream.print_spec(self.color[color], test, status)
<|endoftext|>


<commit_before>
from unlock.util import TrialState, TrialTimeState, RunState, Trigger, SequenceState
from model import UnlockModel

import logging

class TimedStimuli(UnlockModel):
    """ Manages multiple timed, sequence-based stimuli. """
    def __init__(self, state):
        super(UnlockModel, self).__init__()
        self.state = state
        self.stimuli = set([])
        self.logger = logging.getLogger(__name__)

    def add_stimulus(self, stimulus):
        self.stimuli.add(stimulus)

    def start(self):
        self.state.start()
        for stimulus in self.stimuli:
            stimulus.start()

    def pause(self):
        for stimulus in self.stimuli:
            stimulus.stop()

    def stop(self):
        self.state.stop()
        for stimulus in self.stimuli:
            stimulus.stop()

    def process_command(self, command):
        if self.state.is_stopped():
           self.logger.debug("TimedSequenceStimuliManager.process_command: called when stopped")
           return

        if len(self.stimuli) < 1:
            self.logger.debug("TimedSequenceStimuliManager.process_command: no stimulus available ")
            return

        ret = Trigger.Null
        state, change_value = self.state.update_state(command.delta)
        self.logger.debug("state, change value ", state, change_value)
        if state == RunState.running:
            sequence_start_trigger = False
            for stimulus in self.stimuli:
                sequence_start_trigger = stimulus.process_command(command)
            if sequence_start_trigger:
                ret = Trigger.Start
        elif change_value == TrialState.rest_expiry:
            self.start()
        elif change_value == TrialState.trial_expiry:
            self.pause()

        return ret

    def get_state(self):
        raise NotImplementedError()

    @staticmethod
    def create(stimuli_duration):
        trial_state = TrialState.create(stimuli_duration, 0)
        return TimedStimuli(trial_state)

class TimedStimulus(UnlockModel):
    """
    Emits a sequence of values at fixed time interval.
    time_state: manages the time (when to emit the next value)
    seq_state: manages the values to emit (on, on, off, on, off, on, off, off, etc..)
    """
    def __init__(self, time_state, seq_state, repeat_count=1):
        super(TimedStimulus, self).__init__()
        self.time_state = time_state
        self.seq_state = seq_state
        self.state = False
        self.count = 0
        self.repeat_count = repeat_count
        self.logger = logging.getLogger(__name__)

    def get_state(self):
        return self.state

    def start(self):
        self.seq_state.start()
        self.state = self.seq_state.state()
        self.time_state.begin_trial()

    def stop(self):
        self.state = False

    def process_command(self, command):
        """
        Updates the stimulus to the next state in the presentation
        sequence if the trial time is exceeded.

        A value of Trigger.Start is returned at the start of the sequence.
        """
        trigger_value = Trigger.Null
        self.time_state.update_trial_time(command.delta)
        if self.time_state.is_trial_complete():
            self.state = self.seq_state.state()
            if self.seq_state.is_start():
                start_trigger = Trigger.Start
            elif self.seq_state.is_end():
                self.count += 1
                if self.count == self.repeat_count:
                    self.count = 0
                    trigger_value = Trigger.Stop

            self.time_state.begin_trial()
            self.seq_state.step()
            self.logger.debug("TimedStimulus trial complete; next state = ", self.state)
        return trigger_value

    @staticmethod
    def create(rate, sequence=(1,0), value_transformer_fn=lambda x: bool(x), repeat_count=1):
        flick_rate = 0.5/rate
        time_state = TrialTimeState(flick_rate, 0)
        seq_state = SequenceState(sequence, value_transformer_fn)
        return TimedStimulus(time_state, seq_state, repeat_count=repeat_count)

         <commit_msg>Added a repeat trigger to the TimedStimulus<commit_after>
from unlock.util import TrialState, TrialTimeState, RunState, Trigger, SequenceState
from model import UnlockModel

import logging

class TimedStimuli(UnlockModel):
    """ Manages multiple timed, sequence-based stimuli. """
    def __init__(self, state):
        super(UnlockModel, self).__init__()
        self.state = state
        self.stimuli = set([])
        self.logger = logging.getLogger(__name__)

    def add_stimulus(self, stimulus):
        self.stimuli.add(stimulus)

    def start(self):
        self.state.start()
        for stimulus in self.stimuli:
            stimulus.start()

    def pause(self):
        for stimulus in self.stimuli:
            stimulus.stop()

    def stop(self):
        self.state.stop()
        for stimulus in self.stimuli:
            stimulus.stop()

    def process_command(self, command):
        if self.state.is_stopped():
           self.logger.debug("TimedSequenceStimuliManager.process_command: called when stopped")
           return

        if len(self.stimuli) < 1:
            self.logger.debug("TimedSequenceStimuliManager.process_command: no stimulus available ")
            return

        ret = Trigger.Null
        state, change_value = self.state.update_state(command.delta)
        self.logger.debug("state, change value ", state, change_value)
        if state == RunState.running:
            sequence_start_trigger = False
            for stimulus in self.stimuli:
                sequence_start_trigger = stimulus.process_command(command)
            if sequence_start_trigger:
                ret = Trigger.Start
        elif change_value == TrialState.rest_expiry:
            self.start()
        elif change_value == TrialState.trial_expiry:
            self.pause()

        return ret

    def get_state(self):
        raise NotImplementedError()

    @staticmethod
    def create(stimuli_duration):
        trial_state = TrialState.create(stimuli_duration, 0)
        return TimedStimuli(trial_state)

class TimedStimulus(UnlockModel):
    """
    Emits a sequence of values at fixed time interval.
    time_state: manages the time (when to emit the next value)
    seq_state: manages the values to emit (on, on, off, on, off, on, off, off, etc..)
    """
    def __init__(self, time_state, seq_state, repeat_count=1):
        super(TimedStimulus, self).__init__()
        self.time_state = time_state
        self.seq_state = seq_state
        self.state = False
        self.count = 0
        self.repeat_count = repeat_count
        self.logger = logging.getLogger(__name__)

    def get_state(self):
        return self.state

    def start(self):
        self.seq_state.start()
        self.state = self.seq_state.state()
        self.time_state.begin_trial()

    def stop(self):
        self.state = False

    def process_command(self, command):
        """
        Updates the stimulus to the next state in the presentation
        sequence if the trial time is exceeded.

        A value of Trigger.Start is returned at the start of the sequence.
        """
        trigger_value = Trigger.Null
        self.time_state.update_trial_time(command.delta)
        if self.time_state.is_trial_complete():
            self.state = self.seq_state.state()
            if self.seq_state.is_start():
                start_trigger = Trigger.Start
            elif self.seq_state.is_end():
                self.count += 1
                if self.count == self.repeat_count:
                    self.count = 0
                    trigger_value = Trigger.Stop
                else:
                    trigger_value = Trigger.Repeat

            self.time_state.begin_trial()
            self.seq_state.step()
            self.logger.debug("TimedStimulus trial complete; next state = ", self.state)
        return trigger_value

    @staticmethod
    def create(rate, sequence=(1,0), value_transformer_fn=lambda x: bool(x), repeat_count=1):
        flick_rate = 0.5/rate
        time_state = TrialTimeState(flick_rate, 0)
        seq_state = SequenceState(sequence, value_transformer_fn)
        return TimedStimulus(time_state, seq_state, repeat_count=repeat_count)

         <|endoftext|>


<commit_before># -*- coding: utf-8 -*-
'''
Two Body Properties DataFrame
===============================
Two body properties are interatomic distances.
'''
from sklearn.neighbors import NearestNeighbors
from exa import _np as np
from exa import _pd as pd
from exa.config import Config
from exa.frames import DataFrame, ManyToMany
if Config.numba:
    from exa.jitted.iteration import repeat_i8, repeat_i8_array, pdist
    from exa.jitted.indexing import unordered_pairing_function
else:
    import numpy.repeat as repeat_i8
    import numpy.repeat as repeat_i8_array
    from exa.algorithms.iteration import pdist
    from exa.algorithms.indexing import unordered_pairing_function
from atomic import Isotope

bond_extra = 0.45
dmin = 0.3
dmax = 13.3

class Two(DataFrame):
    '''
    '''
    __pk__ = ['two']
    __fk__ = ['frame', 'atom0', 'atom1']
    __groupby__ = 'frame'

    def _compute_bond_count(self):
        '''
        '''
        bonded = self[self['bond'] == True]
        b1 = bonded.groupby('atom0').apply(lambda b: len(b))
        b2 = bonded.groupby('atom1').apply(lambda b: len(b))
        return b1.add(b2, fill_value=0)

class ProjectedTwo(DataFrame):
    '''
    '''
    __pk__ = ['prjd_two']
    __fk__ = ['frame', 'prjd_atom0', 'prjd_atom1']
    __groupby__ = 'frame'

    def _compute_bond_count(self):
        '''
        '''
        bonded = self[self['bond'] == True]
        b1 = bonded.groupby('prjd_atom0').apply(lambda b: len(b))
        b2 = bonded.groupby('prjd_atom1').apply(lambda b: len(b))
        return b1.add(b2, fill_value=0)

class AtomTwo(ManyToMany):
    '''
    '''
    __fks__ = ['atom', 'two']

class ProjectedAtomTwo(ManyToMany):
    '''
    '''
    __fks__ = ['prjd_atom', 'prjd_two']

def get_two_body(universe, k=None, dmax=dmax, dmin=dmin,
                 bond_extra=bond_extra, inplace=False):
    '''
    Compute two body information given a universe.

    For non-periodic systems the only required argument is the table of atom
    positions and symbols. For periodic systems, at a minimum, the atoms and
    frames dataframes must be provided.

    Bonds are computed semi-empirically and exist if:

    .. math::

        distance(A, B) < covalent\_radius(A) + covalent\_radius(B) + bond\_extra

    Args:
        atoms (:class:`~atomic.atom.Atom`): Table of nuclear positions and symbols
        frames (:class:`~pandas.DataFrame`): DataFrame of periodic cell dimensions (or False if )
        k (int): Number of distances (per atom) to compute
        dmax (float): Max distance of interest (larger distances are ignored)
        dmin (float): Min distance of interest (smaller distances are ignored)
        bond_extra (float): Extra distance to include when determining bonds (see above)

    Returns:
        df (:class:`~atomic.twobody.TwoBody`): Two body property table
    '''
    two = None
    atomtwo = None
    periodic = universe.is_periodic()
    if periodic:
        if len(universe.projected_atom) == 0:
            two, atomtwo = _periodic_from_unit_in_mem(universe, k, dmax, dmin, bond_extra)
        else:
            two, atomtwo = _periodic_from_projected_in_mem(universe, k, dmax, dmin, bond_extra)
    else:
        two = _free_in_mem(universe, dmax, dmin, bond_extra)
    if inplace and periodic:
        universe._prjd_two = two
        universe._prjd_atomtwo = atomtwo
    elif inplace:
        universe._two = two
        universe._atomtwo = atomtwo
    else:
        return (two, atomtwo)

def _free_in_mem(universe, dmax=12.3, dmin=0.3, bond_extra=bond_extra):
    '''
    Free boundary condition two body properties computed in memory.

    Args:
        universe (:class:`~atomic.universe.Universe`): The atomic universe
        dmax (float): Max distance of interest
        dmin (float): Minimum distance of interest
        bond_extra (float): Extra distance to add when determining bonds

    Returns:
        twobody (:class:`~atomic.twobody.TwoBody`)
    '''
    atom_groups = universe.atom.groupby('frame')
    n = atom_groups.ngroups
    atom0 = np.empty((n, ), dtype='O')
    atom1 = np.empty((n, ), dtype='O')
    distance = np.empty((n, ), dtype='O')
    frames = np.empty((n, ), dtype='O')
    for i, (frame, atom) in enumerate(atom_groups):
        df = DataFrame(atom)
        xyz = df._get_column_values('x', 'y', 'z')
        print(xyz.dtype)
        dists, i0, i1 = pdist(xyz)
        atom0[i] = df.iloc[i0].index.values
        atom1[i] = df.iloc[i1].index.values
        distance[i] = dists
        frames[i] = [frame] * len(dists)
    distance = np.concatenate(distance)
    atom0 = np.concatenate(atom0)
    atom1 = np.concatenate(atom1)
    frames = np.concatenate(frames)
    two = Two.from_dict({'distance': distance, 'atom0': atom0, 'atom1': atom1, 'frame': frames})
    symbol = universe.atom['symbol']
    two['symbol0'] = two['atom0'].map(symbol)
    two['symbol1'] = two['atom1'].map(symbol)
    two['symbols'] = two['symbol0'] + two['symbol1']
    two['mbl'] = two['symbols'].map(Isotope.symbols_to_radii_map)
    two['mbl'] += bond_extra
    two['bond'] = two['distance'] < two['mbl']
    return two

def _free_memmap():    # Same as _free_in_mem but using numpy.memmap
    raise NotImplementedError()

def _periodic_from_atom_in_mem(universe, k=None, dmax=dmax, dmin=dmin, bond_extra=bond_extra):
    '''
    Compute periodic two body properties given only the absolute positions and
    the cell dimensions.

    Args:
        universe (:class:`~atomic.universe.Universe`): The atomic universe
        k (int): Number of distances (per atom) to compute
        dmax (float): Max distance of interest
        dmin (float): Minimum distance of interest
        bond_extra (float): Extra distance to add when determining bonds

    Returns:
    '''
    raise NotImplementedError()

def _periodic_from_unit_in_mem(universe, k=None, dmax=dmax, dmin=dmin, bond_extra=bond_extra):
    '''
    Compute periodic two body properties given only the absolute positions and
    the cell dimensions.

    Args:
        universe (:class:`~atomic.universe.Universe`): The atomic universe
        k (int): Number of distances (per atom) to compute
        dmax (float): Max distance of interest
        dmin (float): Minimum distance of interest
        bond_extra (float): Extra distance to add when determining bonds

    Returns:
        periodic_twobody (:class:`~atomic.twobody.PeriodicTwoBody`): Periodic two body properties
    '''
    raise NotImplementedError()

def _periodic_from_projected_in_mem(universe, k=None, dmax=12.3, dmin=0.3, bond_extra=0.5):
    '''
    '''
    k = universe.frame['atom_count'].max() if k is None else k
    prjd_grps = universe.projected_atom.groupby('frame')
    unit_grps = universe.unit_atom.groupby('frame')
    n = prjd_grps.ngroups
    distances = np.empty((n, ), dtype='O')
    index1 = np.empty((n, ), dtype='O')
    index2 = np.empty((n, ), dtype='O')
    frames = np.empty((n, ), dtype='O')
    for i, (frame, prjd) in enumerate(prjd_grps):                    # Per frame, compute the distances,
        pxyz = DataFrame(prjd)._get_column_values('x', 'y', 'z')     # also saving the associated indices
        uxyz = DataFrame(unit_grps.get_group(frame))._get_column_values('x', 'y', 'z')
        dists, idxs = NearestNeighbors(n_neighbors=k, metric='euclidean').fit(pxyz).kneighbors(uxyz)
        distances[i] = dists.ravel()
        index1[i] = prjd.iloc[repeat_i8_array(idxs[:, 0], k)].index.values
        index2[i] = prjd.iloc[idxs.ravel()].index.values
        frames[i] = repeat_i8(frame, len(index1[i]))
    distances = np.concatenate(distances)
    index1 = np.concatenate(index1)
    index2 = np.concatenate(index2)
    frames = np.concatenate(frames)
    two = ProjectedTwo.from_dict({'distance': distances, 'frame': frames,
                                  'prjd_atom0': index1, 'prjd_atom1': index2})  # We will use prjd_atom0/2 to deduplicate data
    two = two[(two['distance'] > dmin) & (two['distance'] < dmax)]
    two['id'] = unordered_pairing_function(two['prjd_atom0'].values, two['prjd_atom1'].values)
    two = two.drop_duplicates('id').reset_index(drop=True)
    del two['id']
    symbols = universe.projected_atom['symbol']
    two['symbol1'] = two['prjd_atom0'].map(symbols)
    two['symbol2'] = two['prjd_atom1'].map(symbols)
    two['symbols'] = two['symbol1'] + two['symbol2']
    del two['symbol1']
    del two['symbol2']
    two['mbl'] = two['symbols'].map(Isotope.symbols_to_radii_map)
    two['mbl'] += bond_extra
    two['bond'] = two['distance'] < two['mbl']
    del two['mbl']
    two.index.names = ['prjd_two']
    prjd_two = two.index.tolist() * 2
    prjd_atom = two['prjd_atom0'].tolist() + two['prjd_atom1'].tolist()
    #atomtwo = pd.DataFrame.from_dict({'prjd_two': prjd_two, 'prjd_atom': prjd_atom})
    atomtwo = ProjectedAtomTwo.from_dict({'prjd_two': prjd_two, 'prjd_atom': prjd_atom})
    return two, atomtwo
    #return ProjectedTwo(two), ProjectedAtomTwo(atomtwo)
<commit_msg>Filter distances by dmin and dmax in FBC two body<commit_after># -*- coding: utf-8 -*-
'''
Two Body Properties DataFrame
===============================
Two body properties are interatomic distances.
'''
from sklearn.neighbors import NearestNeighbors
from exa import _np as np
from exa import _pd as pd
from exa.config import Config
from exa.frames import DataFrame, ManyToMany
if Config.numba:
    from exa.jitted.iteration import repeat_i8, repeat_i8_array, pdist
    from exa.jitted.indexing import unordered_pairing_function
else:
    import numpy.repeat as repeat_i8
    import numpy.repeat as repeat_i8_array
    from exa.algorithms.iteration import pdist
    from exa.algorithms.indexing import unordered_pairing_function
from atomic import Isotope

bond_extra = 0.45
dmin = 0.3
dmax = 13.3

class Two(DataFrame):
    '''
    '''
    __pk__ = ['two']
    __fk__ = ['frame', 'atom0', 'atom1']
    __groupby__ = 'frame'

    def _compute_bond_count(self):
        '''
        '''
        bonded = self[self['bond'] == True]
        b1 = bonded.groupby('atom0').apply(lambda b: len(b))
        b2 = bonded.groupby('atom1').apply(lambda b: len(b))
        return b1.add(b2, fill_value=0)

class ProjectedTwo(DataFrame):
    '''
    '''
    __pk__ = ['prjd_two']
    __fk__ = ['frame', 'prjd_atom0', 'prjd_atom1']
    __groupby__ = 'frame'

    def _compute_bond_count(self):
        '''
        '''
        bonded = self[self['bond'] == True]
        b1 = bonded.groupby('prjd_atom0').apply(lambda b: len(b))
        b2 = bonded.groupby('prjd_atom1').apply(lambda b: len(b))
        return b1.add(b2, fill_value=0)

class AtomTwo(ManyToMany):
    '''
    '''
    __fks__ = ['atom', 'two']

class ProjectedAtomTwo(ManyToMany):
    '''
    '''
    __fks__ = ['prjd_atom', 'prjd_two']

def get_two_body(universe, k=None, dmax=dmax, dmin=dmin,
                 bond_extra=bond_extra, inplace=False):
    '''
    Compute two body information given a universe.

    For non-periodic systems the only required argument is the table of atom
    positions and symbols. For periodic systems, at a minimum, the atoms and
    frames dataframes must be provided.

    Bonds are computed semi-empirically and exist if:

    .. math::

        distance(A, B) < covalent\_radius(A) + covalent\_radius(B) + bond\_extra

    Args:
        atoms (:class:`~atomic.atom.Atom`): Table of nuclear positions and symbols
        frames (:class:`~pandas.DataFrame`): DataFrame of periodic cell dimensions (or False if )
        k (int): Number of distances (per atom) to compute
        dmax (float): Max distance of interest (larger distances are ignored)
        dmin (float): Min distance of interest (smaller distances are ignored)
        bond_extra (float): Extra distance to include when determining bonds (see above)

    Returns:
        df (:class:`~atomic.twobody.TwoBody`): Two body property table
    '''
    two = None
    atomtwo = None
    periodic = universe.is_periodic()
    if periodic:
        if len(universe.projected_atom) == 0:
            two, atomtwo = _periodic_from_unit_in_mem(universe, k, dmax, dmin, bond_extra)
        else:
            two, atomtwo = _periodic_from_projected_in_mem(universe, k, dmax, dmin, bond_extra)
    else:
        two = _free_in_mem(universe, dmax, dmin, bond_extra)
    if inplace and periodic:
        universe._prjd_two = two
        universe._prjd_atomtwo = atomtwo
    elif inplace:
        universe._two = two
        universe._atomtwo = atomtwo
    else:
        return (two, atomtwo)

def _free_in_mem(universe, dmax=12.3, dmin=0.3, bond_extra=bond_extra):
    '''
    Free boundary condition two body properties computed in memory.

    Args:
        universe (:class:`~atomic.universe.Universe`): The atomic universe
        dmax (float): Max distance of interest
        dmin (float): Minimum distance of interest
        bond_extra (float): Extra distance to add when determining bonds

    Returns:
        twobody (:class:`~atomic.twobody.TwoBody`)
    '''
    atom_groups = universe.atom.groupby('frame')
    n = atom_groups.ngroups
    atom0 = np.empty((n, ), dtype='O')
    atom1 = np.empty((n, ), dtype='O')
    distance = np.empty((n, ), dtype='O')
    frames = np.empty((n, ), dtype='O')
    for i, (frame, atom) in enumerate(atom_groups):
        df = DataFrame(atom)
        xyz = df._get_column_values('x', 'y', 'z')
        print(xyz.dtype)
        dists, i0, i1 = pdist(xyz)
        atom0[i] = df.iloc[i0].index.values
        atom1[i] = df.iloc[i1].index.values
        distance[i] = dists
        frames[i] = [frame] * len(dists)
    distance = np.concatenate(distance)
    atom0 = np.concatenate(atom0)
    atom1 = np.concatenate(atom1)
    frames = np.concatenate(frames)
    two = Two.from_dict({'distance': distance, 'atom0': atom0, 'atom1': atom1, 'frame': frames})
    two = two[(two['distance'] > dmin) & (two['distance'] < dmax)].reset_index(drop=True)
    symbol = universe.atom['symbol']
    two['symbol0'] = two['atom0'].map(symbol)
    two['symbol1'] = two['atom1'].map(symbol)
    two['symbols'] = two['symbol0'] + two['symbol1']
    two['mbl'] = two['symbols'].map(Isotope.symbols_to_radii_map)
    two['mbl'] += bond_extra
    two['bond'] = two['distance'] < two['mbl']
    return two

def _free_memmap():    # Same as _free_in_mem but using numpy.memmap
    raise NotImplementedError()

def _periodic_from_atom_in_mem(universe, k=None, dmax=dmax, dmin=dmin, bond_extra=bond_extra):
    '''
    Compute periodic two body properties given only the absolute positions and
    the cell dimensions.

    Args:
        universe (:class:`~atomic.universe.Universe`): The atomic universe
        k (int): Number of distances (per atom) to compute
        dmax (float): Max distance of interest
        dmin (float): Minimum distance of interest
        bond_extra (float): Extra distance to add when determining bonds

    Returns:
    '''
    raise NotImplementedError()

def _periodic_from_unit_in_mem(universe, k=None, dmax=dmax, dmin=dmin, bond_extra=bond_extra):
    '''
    Compute periodic two body properties given only the absolute positions and
    the cell dimensions.

    Args:
        universe (:class:`~atomic.universe.Universe`): The atomic universe
        k (int): Number of distances (per atom) to compute
        dmax (float): Max distance of interest
        dmin (float): Minimum distance of interest
        bond_extra (float): Extra distance to add when determining bonds

    Returns:
        periodic_twobody (:class:`~atomic.twobody.PeriodicTwoBody`): Periodic two body properties
    '''
    raise NotImplementedError()

def _periodic_from_projected_in_mem(universe, k=None, dmax=12.3, dmin=0.3, bond_extra=0.5):
    '''
    '''
    k = universe.frame['atom_count'].max() if k is None else k
    prjd_grps = universe.projected_atom.groupby('frame')
    unit_grps = universe.unit_atom.groupby('frame')
    n = prjd_grps.ngroups
    distances = np.empty((n, ), dtype='O')
    index1 = np.empty((n, ), dtype='O')
    index2 = np.empty((n, ), dtype='O')
    frames = np.empty((n, ), dtype='O')
    for i, (frame, prjd) in enumerate(prjd_grps):                    # Per frame, compute the distances,
        pxyz = DataFrame(prjd)._get_column_values('x', 'y', 'z')     # also saving the associated indices
        uxyz = DataFrame(unit_grps.get_group(frame))._get_column_values('x', 'y', 'z')
        dists, idxs = NearestNeighbors(n_neighbors=k, metric='euclidean').fit(pxyz).kneighbors(uxyz)
        distances[i] = dists.ravel()
        index1[i] = prjd.iloc[repeat_i8_array(idxs[:, 0], k)].index.values
        index2[i] = prjd.iloc[idxs.ravel()].index.values
        frames[i] = repeat_i8(frame, len(index1[i]))
    distances = np.concatenate(distances)
    index1 = np.concatenate(index1)
    index2 = np.concatenate(index2)
    frames = np.concatenate(frames)
    two = ProjectedTwo.from_dict({'distance': distances, 'frame': frames,
                                  'prjd_atom0': index1, 'prjd_atom1': index2})  # We will use prjd_atom0/2 to deduplicate data
    two = two[(two['distance'] > dmin) & (two['distance'] < dmax)]
    two['id'] = unordered_pairing_function(two['prjd_atom0'].values, two['prjd_atom1'].values)
    two = two.drop_duplicates('id').reset_index(drop=True)
    del two['id']
    symbols = universe.projected_atom['symbol']
    two['symbol1'] = two['prjd_atom0'].map(symbols)
    two['symbol2'] = two['prjd_atom1'].map(symbols)
    two['symbols'] = two['symbol1'] + two['symbol2']
    del two['symbol1']
    del two['symbol2']
    two['mbl'] = two['symbols'].map(Isotope.symbols_to_radii_map)
    two['mbl'] += bond_extra
    two['bond'] = two['distance'] < two['mbl']
    del two['mbl']
    two.index.names = ['prjd_two']
    prjd_two = two.index.tolist() * 2
    prjd_atom = two['prjd_atom0'].tolist() + two['prjd_atom1'].tolist()
    #atomtwo = pd.DataFrame.from_dict({'prjd_two': prjd_two, 'prjd_atom': prjd_atom})
    atomtwo = ProjectedAtomTwo.from_dict({'prjd_two': prjd_two, 'prjd_atom': prjd_atom})
    return two, atomtwo
    #return ProjectedTwo(two), ProjectedAtomTwo(atomtwo)
<|endoftext|>


<commit_before>"""Leetcode 235. Lowest Common Ancestor of a Binary Search Tree
Easy

URL: https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-search-tree/

Given a binary search tree (BST), find the lowest common ancestor (LCA) of
two given nodes in the BST.

According to the definition of LCA on Wikipedia: "The lowest common ancestor is
defined between two nodes p and q as the lowest node in T that has both p and q
as descendants (where we allow a node to be a descendant of itself)."

Given binary search tree:  root = [6,2,8,0,4,7,9,null,null,3,5]
      6
    /   \
   2     8
  / \   / \
 0   4 7   9
    / \
   3   5

Example 1:
Input: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8
Output: 6
Explanation: The LCA of nodes 2 and 8 is 6.

Example 2:
Input: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 4
Output: 2
Explanation: The LCA of nodes 2 and 4 is 2,
since a node can be a descendant of itself according to the LCA definition.

Note:
- All of the nodes' values will be unique.
- p and q are different and both values will exist in the BST.
"""

# Definition for a binary tree node.
class TreeNode(object):
    def __init__(self, val):
        self.val = val
        self.left = None
        self.right = None

class Solution(object):
    def lowestCommonAncestor(self, root, p, q):
        """
        :type root: TreeNode
        :type p: TreeNode
        :type q: TreeNode
        :rtype: TreeNode
        """
        pass

def main():
    pass

if __name__ == '__main__':
    main()
<commit_msg>Complete recur sol<commit_after>"""Leetcode 235. Lowest Common Ancestor of a Binary Search Tree
Easy

URL: https://leetcode.com/problems/lowest-common-ancestor-of-a-binary-search-tree/

Given a binary search tree (BST), find the lowest common ancestor (LCA) of
two given nodes in the BST.

According to the definition of LCA on Wikipedia: "The lowest common ancestor is
defined between two nodes p and q as the lowest node in T that has both p and q
as descendants (where we allow a node to be a descendant of itself)."

Given binary search tree:  root = [6,2,8,0,4,7,9,null,null,3,5]
      6
    /   \
   2     8
  / \   / \
 0   4 7   9
    / \
   3   5

Example 1:
Input: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 8
Output: 6
Explanation: The LCA of nodes 2 and 8 is 6.

Example 2:
Input: root = [6,2,8,0,4,7,9,null,null,3,5], p = 2, q = 4
Output: 2
Explanation: The LCA of nodes 2 and 4 is 2,
since a node can be a descendant of itself according to the LCA definition.

Note:
- All of the nodes' values will be unique.
- p and q are different and both values will exist in the BST.
"""

# Definition for a binary tree node.
class TreeNode(object):
    def __init__(self, val):
        self.val = val
        self.left = None
        self.right = None

class SolutionRecur(object):
    def lowestCommonAncestor(self, root, p, q):
        """
        :type root: TreeNode
        :type p: TreeNode
        :type q: TreeNode
        :rtype: TreeNode
        """
        # Check both p & q are in the node's left or right subtree.
        # If not, the node splits p and q, so returh the node.
        if p.val < root.val and q.val < root.val:
            return self.lowestCommonAncestor(root.left, p, q)
        elif p.val > root.val and q.val > root.val:
            return self.lowestCommonAncestor(root.right, p, q)
        else:
            return root

def main():
    #      6
    #    /   \
    #   2     8
    #  / \   / \
    # 0   4 7   9
    #    / \
    #   3   5
    root = TreeNode(6)
    root.left = TreeNode(2)
    root.right = TreeNode(8)
    root.left.left = TreeNode(0)
    root.left.right = TreeNode(4)
    root.right.left = TreeNode(7)
    root.right.right = TreeNode(9)
    root.left.right.left = TreeNode(3)
    root.left.right.right = TreeNode(5)

    # Input: p = 2, q = 8
    # Output: 6
    p = root.left
    q = root.right
    print SolutionRecur().lowestCommonAncestor(root, p, q).val

    # Input: p = 2, q = 4
    # Output: 2
    p = root.left
    q = root.left.right
    print SolutionRecur().lowestCommonAncestor(root, p, q).val

if __name__ == '__main__':
    main()
<|endoftext|>


<commit_before>#!/usr/bin/python3
# General libraries
import asyncio
import configparser
import discord
import os
import logging
from discord.ext import commands

# Our specific stuff
from utils import *

#Parse a string to primitive type based on content using lambda expression
import string
parseStr = lambda x: x.isalpha() and x or x.isdigit() and \
                int(x) or x.isalnum() and x or \
                len(set(string.punctuation).intersection(x)) == 1 and \
                x.count('.') == 1 and float(x) or x

initial_extensions = [
    'cogs.basicpromotions',
    'cogs.alerts',
]

logging.basicConfig(level=logging.DEBUG)

config = configparser.ConfigParser()
for inifile in [os.path.expanduser('~')+'/.bayohwoolph.ini','bayohwoolph.local.ini','bayohwoolph.ini']:
    if os.path.isfile(inifile):
        config.read(inifile)
        break # First config file wins
MAIN = config['MAIN']

description = '''Dark Echo's barkeep'''

bot = commands.Bot(command_prefix=commands.when_mentioned_or(MAIN.get('commandchar')), description=description)

@bot.event
@asyncio.coroutine
def on_ready():
    print('Logged in as')
    print(bot.user.name)
    print(bot.user.id)
    print('------')

@bot.command()
@asyncio.coroutine
#Test method to populate an array from discord -Infinite
def getmembers():
    #Intialize array
    listOfMembers = []

    #Add members to array
    for member in bot.get_all_members():
        listOfMembers.append(Member(parseStr(member.id),member.name,member.nick,member.top_role,0))

    #Fetch the a member at [x] index added to the array.
    sentence = listOfMembers[8].displayMember()
    #Get length of array.
    length = len(listOfMembers)

    yield from bot.say(str(sentence))
    yield from bot.say("NumberofMembersinArray: "+str(length))

# Everything should go above this
if __name__ == '__main__':
    for extension in initial_extensions:
        try:
            bot.load_extension(extension)
        except Exception as e:
            print('Failed to load extension {}\n{}: {}'.format(extension, type(e).__name__, e))
    bot.run(MAIN.get('login_token'))
## Nothing goes after this comment! ##
<commit_msg>Turn logging down a bit, so key errors don't get hidden in the noise.<commit_after>#!/usr/bin/python3
# General libraries
import asyncio
import configparser
import discord
import os
import logging
from discord.ext import commands

# Our specific stuff
from utils import *

#Parse a string to primitive type based on content using lambda expression
import string
parseStr = lambda x: x.isalpha() and x or x.isdigit() and \
                int(x) or x.isalnum() and x or \
                len(set(string.punctuation).intersection(x)) == 1 and \
                x.count('.') == 1 and float(x) or x

initial_extensions = [
    'cogs.basicpromotions',
    'cogs.alerts',
]

# logging.basicConfig(level=logging.DEBUG)
logging.basicConfig(level=logging.INFO)

config = configparser.ConfigParser()
for inifile in [os.path.expanduser('~')+'/.bayohwoolph.ini','bayohwoolph.local.ini','bayohwoolph.ini']:
    if os.path.isfile(inifile):
        config.read(inifile)
        break # First config file wins
MAIN = config['MAIN']

description = '''Dark Echo's barkeep'''

bot = commands.Bot(command_prefix=commands.when_mentioned_or(MAIN.get('commandchar')), description=description)

@bot.event
@asyncio.coroutine
def on_ready():
    print('Logged in as')
    print(bot.user.name)
    print(bot.user.id)
    print('------')

@bot.command()
@asyncio.coroutine
#Test method to populate an array from discord -Infinite
def getmembers():
    #Intialize array
    listOfMembers = []

    #Add members to array
    for member in bot.get_all_members():
        listOfMembers.append(Member(parseStr(member.id),member.name,member.nick,member.top_role,0))

    #Fetch the a member at [x] index added to the array.
    sentence = listOfMembers[8].displayMember()
    #Get length of array.
    length = len(listOfMembers)

    yield from bot.say(str(sentence))
    yield from bot.say("NumberofMembersinArray: "+str(length))

# Everything should go above this
if __name__ == '__main__':
    for extension in initial_extensions:
        try:
            bot.load_extension(extension)
        except Exception as e:
            print('Failed to load extension {}\n{}: {}'.format(extension, type(e).__name__, e))
    bot.run(MAIN.get('login_token'))
## Nothing goes after this comment! ##
<|endoftext|>


<commit_before>import numpy as np

class odict(object):

    def __init__(self):
        self.keys = []
        self.values = []

    def __setitem__(self, key, value):
        if type(key) == int:
            if key > len(self.keys) - 1:
                raise Exception("Element %i does not exist" % key)
            else:
                self.values[key] = value
        elif type(key) in [str, np.string_]:
            if key in self.keys:
                index = self.keys.index(key)
                self.values[index] = value
            else:
                self.keys.append(key)
                self.values.append(value)
        else:
            raise Exception("Wrong type for key: %s" % type(key))

    def __getitem__(self, key):
        if type(key) == int:
            return self.values[key]
        elif type(key) in [str, np.string_]:
            index = self.keys.index(key)
            return self.values[index]
        else:
            raise Exception("Wrong type for key: %s" % type(key))

    def __repr__(self):
        string = "{"
        for i, key in enumerate(self.keys):
            if i > 0:
                string += ", "
            string += "\n%s : %s" % (key, self.values[i])
        string += "\n}"
        return string

    def __contains__(self, key):
        return key in self.keys

    def pop(self, key):
        index = self.keys.index(key)
        self.keys.pop(index)
        self.values.pop(index)

    def __len__(self):
        return len(self.keys)

    def rename(self, oldkey, newkey):
        index = self.keys.index(oldkey)
        self.keys[index] = newkey
        return

    def insert(self, position, key, value):
        self.keys.insert(position, key)
        self.values.insert(position, value)
        return

    def __iter__(self):
        return iter(self.keys)
<commit_msg>Allow unicode keys in odict<commit_after>import numpy as np

class odict(object):

    def __init__(self):
        self.keys = []
        self.values = []

    def __setitem__(self, key, value):
        if type(key) == int:
            if key > len(self.keys) - 1:
                raise Exception("Element %i does not exist" % key)
            else:
                self.values[key] = value
        elif type(key) in [str, np.string_, unicode]:
            if key in self.keys:
                index = self.keys.index(key)
                self.values[index] = value
            else:
                self.keys.append(key)
                self.values.append(value)
        else:
            raise Exception("Wrong type for key: %s" % type(key))

    def __getitem__(self, key):
        if type(key) == int:
            return self.values[key]
        elif type(key) in [str, np.string_]:
            index = self.keys.index(key)
            return self.values[index]
        else:
            raise Exception("Wrong type for key: %s" % type(key))

    def __repr__(self):
        string = "{"
        for i, key in enumerate(self.keys):
            if i > 0:
                string += ", "
            string += "\n%s : %s" % (key, self.values[i])
        string += "\n}"
        return string

    def __contains__(self, key):
        return key in self.keys

    def pop(self, key):
        index = self.keys.index(key)
        self.keys.pop(index)
        self.values.pop(index)

    def __len__(self):
        return len(self.keys)

    def rename(self, oldkey, newkey):
        index = self.keys.index(oldkey)
        self.keys[index] = newkey
        return

    def insert(self, position, key, value):
        self.keys.insert(position, key)
        self.values.insert(position, value)
        return

    def __iter__(self):
        return iter(self.keys)
<|endoftext|>


<commit_before># Copyright 2012-2022 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.

from __future__ import annotations

from .. import mlog
from ..mesonlib import (
    EnvironmentException, OptionKey,
    Popen_safe, join_args, search_version
)
from .linkers import (
    AppleDynamicLinker,
    GnuGoldDynamicLinker,
    GnuBFDDynamicLinker,
    MoldDynamicLinker,
    LLVMDynamicLinker,
    QualcommLLVMDynamicLinker,
    MSVCDynamicLinker,
    ClangClDynamicLinker,
    SolarisDynamicLinker,
    AIXDynamicLinker,
    OptlinkDynamicLinker,
)

import re
import shlex
import typing as T

if T.TYPE_CHECKING:
    from .linkers import DynamicLinker, GnuDynamicLinker
    from ..environment import Environment
    from ..compilers import Compiler
    from ..mesonlib import MachineChoice

defaults: T.Dict[str, T.List[str]] = {}
defaults['static_linker'] = ['ar', 'gar']
defaults['vs_static_linker'] = ['lib']
defaults['clang_cl_static_linker'] = ['llvm-lib']
defaults['cuda_static_linker'] = ['nvlink']
defaults['gcc_static_linker'] = ['gcc-ar']
defaults['clang_static_linker'] = ['llvm-ar']

def __failed_to_detect_linker(compiler: T.List[str], args: T.List[str], stdout: str, stderr: str) -> 'T.NoReturn':
    msg = 'Unable to detect linker for compiler `{}`\nstdout: {}\nstderr: {}'.format(
        join_args(compiler + args), stdout, stderr)
    raise EnvironmentException(msg)

def guess_win_linker(env: 'Environment', compiler: T.List[str], comp_class: T.Type['Compiler'],
                     comp_version: str, for_machine: MachineChoice, *,
                     use_linker_prefix: bool = True, invoked_directly: bool = True,
                     extra_args: T.Optional[T.List[str]] = None) -> 'DynamicLinker':
    env.coredata.add_lang_args(comp_class.language, comp_class, for_machine, env)

    # Explicitly pass logo here so that we can get the version of link.exe
    if not use_linker_prefix or comp_class.LINKER_PREFIX is None:
        check_args = ['/logo', '--version']
    elif isinstance(comp_class.LINKER_PREFIX, str):
        check_args = [comp_class.LINKER_PREFIX + '/logo', comp_class.LINKER_PREFIX + '--version']
    elif isinstance(comp_class.LINKER_PREFIX, list):
        check_args = comp_class.LINKER_PREFIX + ['/logo'] + comp_class.LINKER_PREFIX + ['--version']

    check_args += env.coredata.options[OptionKey('args', lang=comp_class.language, machine=for_machine)].value

    override = []  # type: T.List[str]
    value = env.lookup_binary_entry(for_machine, comp_class.language + '_ld')
    if value is not None:
        override = comp_class.use_linker_args(value[0], comp_version)
        check_args += override

    if extra_args is not None:
        check_args.extend(extra_args)

    p, o, _ = Popen_safe(compiler + check_args)
    if 'LLD' in o.split('\n')[0]:
        if '(compatible with GNU linkers)' in o:
            return LLVMDynamicLinker(
                compiler, for_machine, comp_class.LINKER_PREFIX,
                override, version=search_version(o))
        elif not invoked_directly:
            return ClangClDynamicLinker(
                for_machine, override, exelist=compiler, prefix=comp_class.LINKER_PREFIX,
                version=search_version(o), direct=False, machine=None)

    if value is not None and invoked_directly:
        compiler = value
        # We've already hanedled the non-direct case above

    p, o, e = Popen_safe(compiler + check_args)
    if 'LLD' in o.split('\n')[0]:
        return ClangClDynamicLinker(
            for_machine, [],
            prefix=comp_class.LINKER_PREFIX if use_linker_prefix else [],
            exelist=compiler, version=search_version(o), direct=invoked_directly)
    elif 'OPTLINK' in o:
        # Opltink's stdout *may* beging with a \r character.
        return OptlinkDynamicLinker(compiler, for_machine, version=search_version(o))
    elif o.startswith('Microsoft') or e.startswith('Microsoft'):
        out = o or e
        match = re.search(r'.*(X86|X64|ARM|ARM64).*', out)
        if match:
            target = str(match.group(1))
        else:
            target = 'x86'

        return MSVCDynamicLinker(
            for_machine, [], machine=target, exelist=compiler,
            prefix=comp_class.LINKER_PREFIX if use_linker_prefix else [],
            version=search_version(out), direct=invoked_directly)
    elif 'GNU coreutils' in o:
        import shutil
        fullpath = shutil.which(compiler[0])
        raise EnvironmentException(
            f"Found GNU link.exe instead of MSVC link.exe in {fullpath}.\n"
            "This link.exe is not a linker.\n"
            "You may need to reorder entries to your %PATH% variable to resolve this.")
    __failed_to_detect_linker(compiler, check_args, o, e)

def guess_nix_linker(env: 'Environment', compiler: T.List[str], comp_class: T.Type['Compiler'],
                     comp_version: str, for_machine: MachineChoice, *,
                     extra_args: T.Optional[T.List[str]] = None) -> 'DynamicLinker':
    """Helper for guessing what linker to use on Unix-Like OSes.

    :compiler: Invocation to use to get linker
    :comp_class: The Compiler Type (uninstantiated)
    :comp_version: The compiler version string
    :for_machine: which machine this linker targets
    :extra_args: Any additional arguments required (such as a source file)
    """
    env.coredata.add_lang_args(comp_class.language, comp_class, for_machine, env)
    extra_args = extra_args or []
    extra_args += env.coredata.options[OptionKey('args', lang=comp_class.language, machine=for_machine)].value

    if isinstance(comp_class.LINKER_PREFIX, str):
        check_args = [comp_class.LINKER_PREFIX + '--version'] + extra_args
    else:
        check_args = comp_class.LINKER_PREFIX + ['--version'] + extra_args

    override = []  # type: T.List[str]
    value = env.lookup_binary_entry(for_machine, comp_class.language + '_ld')
    if value is not None:
        override = comp_class.use_linker_args(value[0], comp_version)
        check_args += override

    mlog.debug('-----')
    mlog.debug(f'Detecting linker via: {join_args(compiler + check_args)}')
    p, o, e = Popen_safe(compiler + check_args)
    mlog.debug(f'linker returned {p}')
    mlog.debug(f'linker stdout:\n{o}')
    mlog.debug(f'linker stderr:\n{e}')

    v = search_version(o + e)
    linker: DynamicLinker
    if 'LLD' in o.split('\n')[0]:
        linker = LLVMDynamicLinker(
            compiler, for_machine, comp_class.LINKER_PREFIX, override, version=v)
    elif 'Snapdragon' in e and 'LLVM' in e:
        linker = QualcommLLVMDynamicLinker(
            compiler, for_machine, comp_class.LINKER_PREFIX, override, version=v)
    elif e.startswith('lld-link: '):
        # The LLD MinGW frontend didn't respond to --version before version 9.0.0,
        # and produced an error message about failing to link (when no object
        # files were specified), instead of printing the version number.
        # Let's try to extract the linker invocation command to grab the version.

        _, o, e = Popen_safe(compiler + check_args + ['-v'])

        try:
            linker_cmd = re.match(r'.*\n(.*?)\nlld-link: ', e, re.DOTALL).group(1)
            linker_cmd = shlex.split(linker_cmd)[0]
        except (AttributeError, IndexError, ValueError):
            pass
        else:
            _, o, e = Popen_safe([linker_cmd, '--version'])
            v = search_version(o)

        linker = LLVMDynamicLinker(compiler, for_machine, comp_class.LINKER_PREFIX, override, version=v)
    # first might be apple clang, second is for real gcc, the third is icc
    elif e.endswith('(use -v to see invocation)\n') or 'macosx_version' in e or 'ld: unknown option:' in e:
        if isinstance(comp_class.LINKER_PREFIX, str):
            cmd = compiler + [comp_class.LINKER_PREFIX + '-v'] + extra_args
        else:
            cmd = compiler + comp_class.LINKER_PREFIX + ['-v'] + extra_args
        mlog.debug('-----')
        mlog.debug(f'Detecting Apple linker via: {join_args(cmd)}')
        _, newo, newerr = Popen_safe(cmd)
        mlog.debug(f'linker stdout:\n{newo}')
        mlog.debug(f'linker stderr:\n{newerr}')

        for line in newerr.split('\n'):
            if 'PROJECT:ld' in line:
                v = line.split('-')[1]
                break
        else:
            __failed_to_detect_linker(compiler, check_args, o, e)
        linker = AppleDynamicLinker(compiler, for_machine, comp_class.LINKER_PREFIX, override, version=v)
    elif 'GNU' in o or 'GNU' in e:
        cls: T.Type[GnuDynamicLinker]
        # this is always the only thing on stdout, except for swift
        # which may or may not redirect the linker stdout to stderr
        if o.startswith('GNU gold') or e.startswith('GNU gold'):
            cls = GnuGoldDynamicLinker
        elif o.startswith('mold') or e.startswith('mold'):
            cls = MoldDynamicLinker
        else:
            cls = GnuBFDDynamicLinker
        linker = cls(compiler, for_machine, comp_class.LINKER_PREFIX, override, version=v)
    elif 'Solaris' in e or 'Solaris' in o:
        for line in (o+e).split('\n'):
            if 'ld: Software Generation Utilities' in line:
                v = line.split(':')[2].lstrip()
                break
        else:
            v = 'unknown version'
        linker = SolarisDynamicLinker(
            compiler, for_machine, comp_class.LINKER_PREFIX, override,
            version=v)
    elif 'ld: 0706-012 The -- flag is not recognized' in e:
        if isinstance(comp_class.LINKER_PREFIX, str):
            _, _, e = Popen_safe(compiler + [comp_class.LINKER_PREFIX + '-V'] + extra_args)
        else:
            _, _, e = Popen_safe(compiler + comp_class.LINKER_PREFIX + ['-V'] + extra_args)
        linker = AIXDynamicLinker(
            compiler, for_machine, comp_class.LINKER_PREFIX, override,
            version=search_version(e))
    else:
        __failed_to_detect_linker(compiler, check_args, o, e)
    return linker
<commit_msg>linkers: detection should invoke the linker with lang_link_args only<commit_after># Copyright 2012-2022 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.

from __future__ import annotations

from .. import mlog
from ..mesonlib import (
    EnvironmentException,
    Popen_safe, join_args, search_version
)
from .linkers import (
    AppleDynamicLinker,
    GnuGoldDynamicLinker,
    GnuBFDDynamicLinker,
    MoldDynamicLinker,
    LLVMDynamicLinker,
    QualcommLLVMDynamicLinker,
    MSVCDynamicLinker,
    ClangClDynamicLinker,
    SolarisDynamicLinker,
    AIXDynamicLinker,
    OptlinkDynamicLinker,
)

import re
import shlex
import typing as T

if T.TYPE_CHECKING:
    from .linkers import DynamicLinker, GnuDynamicLinker
    from ..environment import Environment
    from ..compilers import Compiler
    from ..mesonlib import MachineChoice

defaults: T.Dict[str, T.List[str]] = {}
defaults['static_linker'] = ['ar', 'gar']
defaults['vs_static_linker'] = ['lib']
defaults['clang_cl_static_linker'] = ['llvm-lib']
defaults['cuda_static_linker'] = ['nvlink']
defaults['gcc_static_linker'] = ['gcc-ar']
defaults['clang_static_linker'] = ['llvm-ar']

def __failed_to_detect_linker(compiler: T.List[str], args: T.List[str], stdout: str, stderr: str) -> 'T.NoReturn':
    msg = 'Unable to detect linker for compiler `{}`\nstdout: {}\nstderr: {}'.format(
        join_args(compiler + args), stdout, stderr)
    raise EnvironmentException(msg)

def guess_win_linker(env: 'Environment', compiler: T.List[str], comp_class: T.Type['Compiler'],
                     comp_version: str, for_machine: MachineChoice, *,
                     use_linker_prefix: bool = True, invoked_directly: bool = True,
                     extra_args: T.Optional[T.List[str]] = None) -> 'DynamicLinker':
    env.coredata.add_lang_args(comp_class.language, comp_class, for_machine, env)

    # Explicitly pass logo here so that we can get the version of link.exe
    if not use_linker_prefix or comp_class.LINKER_PREFIX is None:
        check_args = ['/logo', '--version']
    elif isinstance(comp_class.LINKER_PREFIX, str):
        check_args = [comp_class.LINKER_PREFIX + '/logo', comp_class.LINKER_PREFIX + '--version']
    elif isinstance(comp_class.LINKER_PREFIX, list):
        check_args = comp_class.LINKER_PREFIX + ['/logo'] + comp_class.LINKER_PREFIX + ['--version']

    check_args += env.coredata.get_external_link_args(for_machine, comp_class.language)

    override = []  # type: T.List[str]
    value = env.lookup_binary_entry(for_machine, comp_class.language + '_ld')
    if value is not None:
        override = comp_class.use_linker_args(value[0], comp_version)
        check_args += override

    if extra_args is not None:
        check_args.extend(extra_args)

    p, o, _ = Popen_safe(compiler + check_args)
    if 'LLD' in o.split('\n')[0]:
        if '(compatible with GNU linkers)' in o:
            return LLVMDynamicLinker(
                compiler, for_machine, comp_class.LINKER_PREFIX,
                override, version=search_version(o))
        elif not invoked_directly:
            return ClangClDynamicLinker(
                for_machine, override, exelist=compiler, prefix=comp_class.LINKER_PREFIX,
                version=search_version(o), direct=False, machine=None)

    if value is not None and invoked_directly:
        compiler = value
        # We've already hanedled the non-direct case above

    p, o, e = Popen_safe(compiler + check_args)
    if 'LLD' in o.split('\n')[0]:
        return ClangClDynamicLinker(
            for_machine, [],
            prefix=comp_class.LINKER_PREFIX if use_linker_prefix else [],
            exelist=compiler, version=search_version(o), direct=invoked_directly)
    elif 'OPTLINK' in o:
        # Opltink's stdout *may* beging with a \r character.
        return OptlinkDynamicLinker(compiler, for_machine, version=search_version(o))
    elif o.startswith('Microsoft') or e.startswith('Microsoft'):
        out = o or e
        match = re.search(r'.*(X86|X64|ARM|ARM64).*', out)
        if match:
            target = str(match.group(1))
        else:
            target = 'x86'

        return MSVCDynamicLinker(
            for_machine, [], machine=target, exelist=compiler,
            prefix=comp_class.LINKER_PREFIX if use_linker_prefix else [],
            version=search_version(out), direct=invoked_directly)
    elif 'GNU coreutils' in o:
        import shutil
        fullpath = shutil.which(compiler[0])
        raise EnvironmentException(
            f"Found GNU link.exe instead of MSVC link.exe in {fullpath}.\n"
            "This link.exe is not a linker.\n"
            "You may need to reorder entries to your %PATH% variable to resolve this.")
    __failed_to_detect_linker(compiler, check_args, o, e)

def guess_nix_linker(env: 'Environment', compiler: T.List[str], comp_class: T.Type['Compiler'],
                     comp_version: str, for_machine: MachineChoice, *,
                     extra_args: T.Optional[T.List[str]] = None) -> 'DynamicLinker':
    """Helper for guessing what linker to use on Unix-Like OSes.

    :compiler: Invocation to use to get linker
    :comp_class: The Compiler Type (uninstantiated)
    :comp_version: The compiler version string
    :for_machine: which machine this linker targets
    :extra_args: Any additional arguments required (such as a source file)
    """
    env.coredata.add_lang_args(comp_class.language, comp_class, for_machine, env)
    extra_args = extra_args or []
    extra_args += env.coredata.get_external_link_args(for_machine, comp_class.language)

    if isinstance(comp_class.LINKER_PREFIX, str):
        check_args = [comp_class.LINKER_PREFIX + '--version'] + extra_args
    else:
        check_args = comp_class.LINKER_PREFIX + ['--version'] + extra_args

    override = []  # type: T.List[str]
    value = env.lookup_binary_entry(for_machine, comp_class.language + '_ld')
    if value is not None:
        override = comp_class.use_linker_args(value[0], comp_version)
        check_args += override

    mlog.debug('-----')
    mlog.debug(f'Detecting linker via: {join_args(compiler + check_args)}')
    p, o, e = Popen_safe(compiler + check_args)
    mlog.debug(f'linker returned {p}')
    mlog.debug(f'linker stdout:\n{o}')
    mlog.debug(f'linker stderr:\n{e}')

    v = search_version(o + e)
    linker: DynamicLinker
    if 'LLD' in o.split('\n')[0]:
        linker = LLVMDynamicLinker(
            compiler, for_machine, comp_class.LINKER_PREFIX, override, version=v)
    elif 'Snapdragon' in e and 'LLVM' in e:
        linker = QualcommLLVMDynamicLinker(
            compiler, for_machine, comp_class.LINKER_PREFIX, override, version=v)
    elif e.startswith('lld-link: '):
        # The LLD MinGW frontend didn't respond to --version before version 9.0.0,
        # and produced an error message about failing to link (when no object
        # files were specified), instead of printing the version number.
        # Let's try to extract the linker invocation command to grab the version.

        _, o, e = Popen_safe(compiler + check_args + ['-v'])

        try:
            linker_cmd = re.match(r'.*\n(.*?)\nlld-link: ', e, re.DOTALL).group(1)
            linker_cmd = shlex.split(linker_cmd)[0]
        except (AttributeError, IndexError, ValueError):
            pass
        else:
            _, o, e = Popen_safe([linker_cmd, '--version'])
            v = search_version(o)

        linker = LLVMDynamicLinker(compiler, for_machine, comp_class.LINKER_PREFIX, override, version=v)
    # first might be apple clang, second is for real gcc, the third is icc
    elif e.endswith('(use -v to see invocation)\n') or 'macosx_version' in e or 'ld: unknown option:' in e:
        if isinstance(comp_class.LINKER_PREFIX, str):
            cmd = compiler + [comp_class.LINKER_PREFIX + '-v'] + extra_args
        else:
            cmd = compiler + comp_class.LINKER_PREFIX + ['-v'] + extra_args
        mlog.debug('-----')
        mlog.debug(f'Detecting Apple linker via: {join_args(cmd)}')
        _, newo, newerr = Popen_safe(cmd)
        mlog.debug(f'linker stdout:\n{newo}')
        mlog.debug(f'linker stderr:\n{newerr}')

        for line in newerr.split('\n'):
            if 'PROJECT:ld' in line:
                v = line.split('-')[1]
                break
        else:
            __failed_to_detect_linker(compiler, check_args, o, e)
        linker = AppleDynamicLinker(compiler, for_machine, comp_class.LINKER_PREFIX, override, version=v)
    elif 'GNU' in o or 'GNU' in e:
        cls: T.Type[GnuDynamicLinker]
        # this is always the only thing on stdout, except for swift
        # which may or may not redirect the linker stdout to stderr
        if o.startswith('GNU gold') or e.startswith('GNU gold'):
            cls = GnuGoldDynamicLinker
        elif o.startswith('mold') or e.startswith('mold'):
            cls = MoldDynamicLinker
        else:
            cls = GnuBFDDynamicLinker
        linker = cls(compiler, for_machine, comp_class.LINKER_PREFIX, override, version=v)
    elif 'Solaris' in e or 'Solaris' in o:
        for line in (o+e).split('\n'):
            if 'ld: Software Generation Utilities' in line:
                v = line.split(':')[2].lstrip()
                break
        else:
            v = 'unknown version'
        linker = SolarisDynamicLinker(
            compiler, for_machine, comp_class.LINKER_PREFIX, override,
            version=v)
    elif 'ld: 0706-012 The -- flag is not recognized' in e:
        if isinstance(comp_class.LINKER_PREFIX, str):
            _, _, e = Popen_safe(compiler + [comp_class.LINKER_PREFIX + '-V'] + extra_args)
        else:
            _, _, e = Popen_safe(compiler + comp_class.LINKER_PREFIX + ['-V'] + extra_args)
        linker = AIXDynamicLinker(
            compiler, for_machine, comp_class.LINKER_PREFIX, override,
            version=search_version(e))
    else:
        __failed_to_detect_linker(compiler, check_args, o, e)
    return linker
<|endoftext|>