<commit_before>from __future__ import print_function, division, absolute_import
from pprint import pprint
from contextlib import contextmanager
from collections import namedtuple, defaultdict
import warnings
import inspect

from numba import (bytecode, interpreter, typing, typeinfer, lowering,
                   objmode, irpasses, utils, config, type_annotations,
                   types, ir, assume, looplifting, macro)
from numba.targets import cpu

class Flags(utils.ConfigOptions):
    # These options are all false by default, but the defaults are
    # different with the @jit decorator (see targets.options.TargetOptions).

    OPTIONS = frozenset([
        # Enable loop-lifting
        'enable_looplift',
        # Enable pyobject mode (in general)
        'enable_pyobject',
        # Enable pyobject mode inside lifted loops
        'enable_pyobject_looplift',
        # Force pyobject mode inside the whole function
        'force_pyobject',
        'no_compile',
        'no_wraparound',
        'boundcheck',
        ])

DEFAULT_FLAGS = Flags()

CR_FIELDS = ["typing_context",
             "target_context",
             "entry_point",
             "typing_error",
             "type_annotation",
             "llvm_module",
             "llvm_func",
             "signature",
             "objectmode",
             "lifted",
             "fndesc"]

CompileResult = namedtuple("CompileResult", CR_FIELDS)
FunctionAttributes = namedtuple("FunctionAttributes",
    ['name', 'filename', 'lineno'])
DEFAULT_FUNCTION_ATTRIBUTES = FunctionAttributes('<anonymous>', '<unknown>', 0)

def get_function_attributes(func):
    '''
    Extract the function attributes from a Python function or object with
    *py_func* attribute, such as CPUOverloaded.

    Returns an instance of FunctionAttributes.
    '''
    if hasattr(func, 'py_func'):
        func = func.py_func  # This is a Overload object

    name, filename, lineno = DEFAULT_FUNCTION_ATTRIBUTES
    try:
        name = func.__name__
    except AttributeError:
        pass  # this "function" object isn't really a function

    try:
        possible_filename = inspect.getsourcefile(func)
        # Sometimes getsourcefile returns null
        if possible_filename is not None:
            filename = possible_filename
    except TypeError:
        pass  # built-in function, or other object unsupported by inspect

    try:
        lines, lineno = inspect.getsourcelines(func)
    except (IOError, TypeError):
        pass  # unable to read source code for function

    return FunctionAttributes(name, filename, lineno)

def compile_result(**kws):
    keys = set(kws.keys())
    fieldset = set(CR_FIELDS)
    badnames = keys - fieldset
    if badnames:
        raise NameError(*badnames)
    missing = fieldset - keys
    for k in missing:
        kws[k] = None
    return CompileResult(**kws)

def compile_isolated(func, args, return_type=None, flags=DEFAULT_FLAGS,
                     locals={}):
    """
    Compile the function is an isolated environment.
    Good for testing.
    """
    typingctx = typing.Context()
    targetctx = cpu.CPUContext(typingctx)
    return compile_extra(typingctx, targetctx, func, args, return_type, flags,
                         locals)

class _CompileStatus(object):
    """
    Used like a C record
    """
    __slots__ = 'fail_reason', 'use_python_mode', 'can_fallback'

    def __init__(self, can_fallback, use_python_mode):
        self.fail_reason = None
        self.use_python_mode = use_python_mode
        self.can_fallback = can_fallback

@contextmanager
def _fallback_context(status):
    try:
        yield
    except Exception as e:
        if not status.can_fallback:
            raise
        if utils.PYVERSION >= (3,):
            # Clear all references attached to the traceback
            e = e.with_traceback(None)
        status.fail_reason = e
        status.use_python_mode = True

def compile_extra(typingctx, targetctx, func, args, return_type, flags,
                  locals):
    """
    Args
    ----
    - return_type
        Use ``None`` to indicate
    """
    bc = bytecode.ByteCode(func=func)
    if config.DUMP_BYTECODE:
        print(bc.dump())
    func_attr = get_function_attributes(func)
    return compile_bytecode(typingctx, targetctx, bc, args,
                            return_type, flags, locals,
                            func_attr=func_attr)

def compile_bytecode(typingctx, targetctx, bc, args, return_type, flags,
                     locals, lifted=(), func_attr=DEFAULT_FUNCTION_ATTRIBUTES):
    status = _CompileStatus(can_fallback=flags.enable_pyobject,
                            use_python_mode=flags.force_pyobject)
    targetctx = targetctx.localized()
    targetctx.metadata['wraparound'] = not flags.no_wraparound

    return _compile_core(typingctx, targetctx, bc, args, return_type, flags,
                         locals, lifted, func_attr, status)

def compile_internal(typingctx, targetctx, func, args, return_type, locals={}):
    """Compiler entry point with compiling internal implementation within a
    nopython function.
    """

    status = _CompileStatus(can_fallback=False, use_python_mode=False)
    lifted = ()

    bc = bytecode.ByteCode(func=func)
    if config.DUMP_BYTECODE:
        print(bc.dump())
    func_attr = get_function_attributes(func)

    flags = DEFAULT_FLAGS.copy()
    flags.set("no_compile")

    return _compile_core(typingctx, targetctx, bc, args, return_type,
                         flags, locals, lifted, func_attr, status)

def _compile_core(typingctx, targetctx, bc, args, return_type, flags, locals,
                  lifted, func_attr, status):
    ### Front-end: Analyze bytecode, generate Numba IR, infer types
    interp = translate_stage(bc)
    nargs = len(interp.argspec.args)
    if len(args) > nargs:
        raise TypeError("Too many argument types")

    if not status.use_python_mode:
        with _fallback_context(status):
            legalize_given_types(args, return_type)
            # Type inference
            typemap, return_type, calltypes = type_inference_stage(typingctx,
                                                                   interp,
                                                                   args,
                                                                   return_type,
                                                                   locals)

        if status.fail_reason is not None:
            warnings.warn_explicit('Function "%s" failed type inference: %s'
                          % (func_attr.name, status.fail_reason),
                          config.NumbaWarning,
                          func_attr.filename, func_attr.lineno)

        if not status.use_python_mode:
            with _fallback_context(status):
                legalize_return_type(return_type, interp, targetctx)
            if status.fail_reason is not None:
                warnings.warn_explicit('Function "%s" has invalid return type: %s'
                                       % (func_attr.name, status.fail_reason),
                                       config.NumbaWarning,
                                       func_attr.filename, func_attr.lineno)

    if status.use_python_mode and flags.enable_looplift:
        assert not lifted

        # Try loop lifting
        loop_flags = flags.copy()
        outer_flags = flags.copy()
        # Do not recursively loop lift
        outer_flags.unset('enable_looplift')
        loop_flags.unset('enable_looplift')
        if not flags.enable_pyobject_looplift:
            loop_flags.unset('enable_pyobject')

        def dispatcher_factory(loopbc):
            from . import dispatcher
            return dispatcher.LiftedLoop(loopbc, typingctx, targetctx,
                                         locals, loop_flags)

        entry, loops = looplifting.lift_loop(bc, dispatcher_factory)
        if loops:
            # Some loops were extracted
            cres = compile_bytecode(typingctx, targetctx, entry, args,
                                    return_type, outer_flags, locals,
                                    lifted=tuple(loops),
                                    func_attr=func_attr)
            return cres

    if status.use_python_mode:
        # Fallback typing: everything is a python object
        typemap = defaultdict(lambda: types.pyobject)
        calltypes = defaultdict(lambda: types.pyobject)
        return_type = types.pyobject

    type_annotation = type_annotations.TypeAnnotation(interp=interp,
                                                      typemap=typemap,
                                                      calltypes=calltypes,
                                                      lifted=lifted)
    if config.ANNOTATE:
        print("ANNOTATION".center(80, '-'))
        print(type_annotation)
        print('=' * 80)

    ### Back-end: Generate LLVM IR from Numba IR, compile to machine code

    if status.use_python_mode:
        # Object mode compilation
        func, lmod, lfunc, fndesc = py_lowering_stage(targetctx, interp,
                                                      flags.no_compile)
        if len(args) != nargs:
            # append missing
            args = tuple(args) + (types.pyobject,) * (nargs - len(args))
    else:
        # Native mode compilation
        func, lmod, lfunc, fndesc = native_lowering_stage(targetctx,
                                                          interp,
                                                          typemap,
                                                          return_type,
                                                          calltypes,
                                                          flags.no_compile)

    signature = typing.signature(return_type, *args)

    assert lfunc.module is lmod
    cr = compile_result(typing_context=typingctx,
                        target_context=targetctx,
                        entry_point=func,
                        typing_error=status.fail_reason,
                        type_annotation=type_annotation,
                        llvm_func=lfunc,
                        llvm_module=lmod,
                        signature=signature,
                        objectmode=status.use_python_mode,
                        lifted=lifted,
                        fndesc=fndesc,)

    # Warn if compiled function in object mode and force_pyobject not set
    if status.use_python_mode and not flags.force_pyobject:
        warn_msg = ('Function "{name}" was compiled in object mode '
                         'without forceobj=True').format(name=func_attr.name)

        if len(lifted) > 0:
            warn_msg += ', but has lifted loops.'

        warnings.warn_explicit(warn_msg,
                               config.NumbaWarning, func_attr.filename,
                               func_attr.lineno)

    return cr

def _is_nopython_types(t):
    return t != types.pyobject and not isinstance(t, types.Dummy)

def legalize_given_types(args, return_type):
    # Filter argument types
    for i, a in enumerate(args):
        if not _is_nopython_types(a):
            raise TypeError("Arg %d of %s is not legal in nopython "
                            "mode" % (i, a))
    # Filter return type
    if (return_type and return_type != types.none and
            not _is_nopython_types(return_type)):
        raise TypeError('Return type of %s is not legal in nopython '
                        'mode' % (return_type,))

def legalize_return_type(return_type, interp, targetctx):
    """
    Only accept array return type iff it is passed into the function.
    Reject function object return types if in nopython mode.
    """
    assert assume.return_argument_array_only

    if isinstance(return_type, types.Array):
        # Walk IR to discover all return statements
        retstmts = []
        for bid, blk in interp.blocks.items():
            for inst in blk.body:
                if isinstance(inst, ir.Return):
                    retstmts.append(inst)

        assert retstmts, "No return statemants?"

        # FIXME: In the future, we can return an array that is either a dynamically
        #        allocated array or an array that is passed as argument.  This
        #        must be statically resolvable.

        # The return value must be the first modification of the value.
        arguments = frozenset("%s.1" % arg for arg in interp.argspec.args)

        if isinstance(return_type, types.Array):
            for ret in retstmts:
                if ret.value.name not in arguments:
                    raise TypeError("Only accept returning of array passed into the "
                                    "function as argument")

        # Legalized; tag return handling
        targetctx.metadata['return.array'] = 'arg'

    elif (isinstance(return_type, types.Function) or
            (isinstance(return_type, types.Dummy) and
             return_type.name == 'abs')):
        raise TypeError("Can't return function object in nopython mode")

def translate_stage(bytecode):
    interp = interpreter.Interpreter(bytecode=bytecode)

    if config.DUMP_CFG:
        interp.cfa.dump()

    interp.interpret()

    if config.DEBUG:
        interp.dump()

    macro.expand_macros(interp.blocks)

    if config.DUMP_IR:
        interp.dump()

    return interp

def type_inference_stage(typingctx, interp, args, return_type, locals={}):
    if len(args) != len(interp.argspec.args):
        raise TypeError("Mismatch number of argument types")

    infer = typeinfer.TypeInferer(typingctx, interp.blocks)

    # Seed argument types
    for arg, ty in zip(interp.argspec.args, args):
    	infer.seed_type(arg, ty)

    # Seed return type
    if return_type is not None:
        infer.seed_return(return_type)

    # Seed local types
    for k, v in locals.items():
        infer.seed_type(k, v)

    infer.build_constrain()
    infer.propagate()
    typemap, restype, calltypes = infer.unify()

    if config.DEBUG:
        pprint(typemap)
        pprint(restype)
        pprint(calltypes)

    return typemap, restype, calltypes

def native_lowering_stage(targetctx, interp, typemap, restype, calltypes,
                          nocompile):
    # Lowering
    fndesc = lowering.PythonFunctionDescriptor.from_specialized_function(
        interp, typemap, restype, calltypes, mangler=targetctx.mangler)

    lower = lowering.Lower(targetctx, fndesc, interp)
    lower.lower()

    # Linking depending libraries
    targetctx.link_dependencies(lower.module, targetctx.linking)

    if nocompile:
        return None, lower.module, lower.function, fndesc
    else:
        # Prepare for execution
        cfunc = targetctx.get_executable(lower.function, fndesc, lower.env)
        targetctx.insert_user_function(cfunc, fndesc)
        return cfunc, lower.module, lower.function, fndesc

def py_lowering_stage(targetctx, interp, nocompile):
    fndesc = lowering.PythonFunctionDescriptor.from_object_mode_function(interp)
    lower = objmode.PyLower(targetctx, fndesc, interp)
    lower.lower()

    if nocompile:
        return None, lower.module, lower.function, fndesc
    else:
        cfunc = targetctx.get_executable(lower.function, fndesc, lower.env)
        return cfunc, lower.module, lower.function, fndesc

def ir_optimize_for_py_stage(interp):
    """
    This passes breaks semantic for the type inferer but they reduces
    refct calls for object mode.
    """
    irpasses.RemoveRedundantAssign(interp).run()
    if config.DEBUG:
        print("ir optimize".center(80, '-'))
        interp.dump()
<commit_msg>Fix spelling error<commit_after>from __future__ import print_function, division, absolute_import
from pprint import pprint
from contextlib import contextmanager
from collections import namedtuple, defaultdict
import warnings
import inspect

from numba import (bytecode, interpreter, typing, typeinfer, lowering,
                   objmode, irpasses, utils, config, type_annotations,
                   types, ir, assume, looplifting, macro)
from numba.targets import cpu

class Flags(utils.ConfigOptions):
    # These options are all false by default, but the defaults are
    # different with the @jit decorator (see targets.options.TargetOptions).

    OPTIONS = frozenset([
        # Enable loop-lifting
        'enable_looplift',
        # Enable pyobject mode (in general)
        'enable_pyobject',
        # Enable pyobject mode inside lifted loops
        'enable_pyobject_looplift',
        # Force pyobject mode inside the whole function
        'force_pyobject',
        'no_compile',
        'no_wraparound',
        'boundcheck',
        ])

DEFAULT_FLAGS = Flags()

CR_FIELDS = ["typing_context",
             "target_context",
             "entry_point",
             "typing_error",
             "type_annotation",
             "llvm_module",
             "llvm_func",
             "signature",
             "objectmode",
             "lifted",
             "fndesc"]

CompileResult = namedtuple("CompileResult", CR_FIELDS)
FunctionAttributes = namedtuple("FunctionAttributes",
    ['name', 'filename', 'lineno'])
DEFAULT_FUNCTION_ATTRIBUTES = FunctionAttributes('<anonymous>', '<unknown>', 0)

def get_function_attributes(func):
    '''
    Extract the function attributes from a Python function or object with
    *py_func* attribute, such as CPUOverloaded.

    Returns an instance of FunctionAttributes.
    '''
    if hasattr(func, 'py_func'):
        func = func.py_func  # This is a Overload object

    name, filename, lineno = DEFAULT_FUNCTION_ATTRIBUTES
    try:
        name = func.__name__
    except AttributeError:
        pass  # this "function" object isn't really a function

    try:
        possible_filename = inspect.getsourcefile(func)
        # Sometimes getsourcefile returns null
        if possible_filename is not None:
            filename = possible_filename
    except TypeError:
        pass  # built-in function, or other object unsupported by inspect

    try:
        lines, lineno = inspect.getsourcelines(func)
    except (IOError, TypeError):
        pass  # unable to read source code for function

    return FunctionAttributes(name, filename, lineno)

def compile_result(**kws):
    keys = set(kws.keys())
    fieldset = set(CR_FIELDS)
    badnames = keys - fieldset
    if badnames:
        raise NameError(*badnames)
    missing = fieldset - keys
    for k in missing:
        kws[k] = None
    return CompileResult(**kws)

def compile_isolated(func, args, return_type=None, flags=DEFAULT_FLAGS,
                     locals={}):
    """
    Compile the function is an isolated environment.
    Good for testing.
    """
    typingctx = typing.Context()
    targetctx = cpu.CPUContext(typingctx)
    return compile_extra(typingctx, targetctx, func, args, return_type, flags,
                         locals)

class _CompileStatus(object):
    """
    Used like a C record
    """
    __slots__ = 'fail_reason', 'use_python_mode', 'can_fallback'

    def __init__(self, can_fallback, use_python_mode):
        self.fail_reason = None
        self.use_python_mode = use_python_mode
        self.can_fallback = can_fallback

@contextmanager
def _fallback_context(status):
    try:
        yield
    except Exception as e:
        if not status.can_fallback:
            raise
        if utils.PYVERSION >= (3,):
            # Clear all references attached to the traceback
            e = e.with_traceback(None)
        status.fail_reason = e
        status.use_python_mode = True

def compile_extra(typingctx, targetctx, func, args, return_type, flags,
                  locals):
    """
    Args
    ----
    - return_type
        Use ``None`` to indicate
    """
    bc = bytecode.ByteCode(func=func)
    if config.DUMP_BYTECODE:
        print(bc.dump())
    func_attr = get_function_attributes(func)
    return compile_bytecode(typingctx, targetctx, bc, args,
                            return_type, flags, locals,
                            func_attr=func_attr)

def compile_bytecode(typingctx, targetctx, bc, args, return_type, flags,
                     locals, lifted=(), func_attr=DEFAULT_FUNCTION_ATTRIBUTES):
    status = _CompileStatus(can_fallback=flags.enable_pyobject,
                            use_python_mode=flags.force_pyobject)
    targetctx = targetctx.localized()
    targetctx.metadata['wraparound'] = not flags.no_wraparound

    return _compile_core(typingctx, targetctx, bc, args, return_type, flags,
                         locals, lifted, func_attr, status)

def compile_internal(typingctx, targetctx, func, args, return_type, locals={}):
    """Compiler entry point with compiling internal implementation within a
    nopython function.
    """

    status = _CompileStatus(can_fallback=False, use_python_mode=False)
    lifted = ()

    bc = bytecode.ByteCode(func=func)
    if config.DUMP_BYTECODE:
        print(bc.dump())
    func_attr = get_function_attributes(func)

    flags = DEFAULT_FLAGS.copy()
    flags.set("no_compile")

    return _compile_core(typingctx, targetctx, bc, args, return_type,
                         flags, locals, lifted, func_attr, status)

def _compile_core(typingctx, targetctx, bc, args, return_type, flags, locals,
                  lifted, func_attr, status):
    ### Front-end: Analyze bytecode, generate Numba IR, infer types
    interp = translate_stage(bc)
    nargs = len(interp.argspec.args)
    if len(args) > nargs:
        raise TypeError("Too many argument types")

    if not status.use_python_mode:
        with _fallback_context(status):
            legalize_given_types(args, return_type)
            # Type inference
            typemap, return_type, calltypes = type_inference_stage(typingctx,
                                                                   interp,
                                                                   args,
                                                                   return_type,
                                                                   locals)

        if status.fail_reason is not None:
            warnings.warn_explicit('Function "%s" failed type inference: %s'
                          % (func_attr.name, status.fail_reason),
                          config.NumbaWarning,
                          func_attr.filename, func_attr.lineno)

        if not status.use_python_mode:
            with _fallback_context(status):
                legalize_return_type(return_type, interp, targetctx)
            if status.fail_reason is not None:
                warnings.warn_explicit('Function "%s" has invalid return type: %s'
                                       % (func_attr.name, status.fail_reason),
                                       config.NumbaWarning,
                                       func_attr.filename, func_attr.lineno)

    if status.use_python_mode and flags.enable_looplift:
        assert not lifted

        # Try loop lifting
        loop_flags = flags.copy()
        outer_flags = flags.copy()
        # Do not recursively loop lift
        outer_flags.unset('enable_looplift')
        loop_flags.unset('enable_looplift')
        if not flags.enable_pyobject_looplift:
            loop_flags.unset('enable_pyobject')

        def dispatcher_factory(loopbc):
            from . import dispatcher
            return dispatcher.LiftedLoop(loopbc, typingctx, targetctx,
                                         locals, loop_flags)

        entry, loops = looplifting.lift_loop(bc, dispatcher_factory)
        if loops:
            # Some loops were extracted
            cres = compile_bytecode(typingctx, targetctx, entry, args,
                                    return_type, outer_flags, locals,
                                    lifted=tuple(loops),
                                    func_attr=func_attr)
            return cres

    if status.use_python_mode:
        # Fallback typing: everything is a python object
        typemap = defaultdict(lambda: types.pyobject)
        calltypes = defaultdict(lambda: types.pyobject)
        return_type = types.pyobject

    type_annotation = type_annotations.TypeAnnotation(interp=interp,
                                                      typemap=typemap,
                                                      calltypes=calltypes,
                                                      lifted=lifted)
    if config.ANNOTATE:
        print("ANNOTATION".center(80, '-'))
        print(type_annotation)
        print('=' * 80)

    ### Back-end: Generate LLVM IR from Numba IR, compile to machine code

    if status.use_python_mode:
        # Object mode compilation
        func, lmod, lfunc, fndesc = py_lowering_stage(targetctx, interp,
                                                      flags.no_compile)
        if len(args) != nargs:
            # append missing
            args = tuple(args) + (types.pyobject,) * (nargs - len(args))
    else:
        # Native mode compilation
        func, lmod, lfunc, fndesc = native_lowering_stage(targetctx,
                                                          interp,
                                                          typemap,
                                                          return_type,
                                                          calltypes,
                                                          flags.no_compile)

    signature = typing.signature(return_type, *args)

    assert lfunc.module is lmod
    cr = compile_result(typing_context=typingctx,
                        target_context=targetctx,
                        entry_point=func,
                        typing_error=status.fail_reason,
                        type_annotation=type_annotation,
                        llvm_func=lfunc,
                        llvm_module=lmod,
                        signature=signature,
                        objectmode=status.use_python_mode,
                        lifted=lifted,
                        fndesc=fndesc,)

    # Warn if compiled function in object mode and force_pyobject not set
    if status.use_python_mode and not flags.force_pyobject:
        warn_msg = ('Function "{name}" was compiled in object mode '
                         'without forceobj=True').format(name=func_attr.name)

        if len(lifted) > 0:
            warn_msg += ', but has lifted loops.'

        warnings.warn_explicit(warn_msg,
                               config.NumbaWarning, func_attr.filename,
                               func_attr.lineno)

    return cr

def _is_nopython_types(t):
    return t != types.pyobject and not isinstance(t, types.Dummy)

def legalize_given_types(args, return_type):
    # Filter argument types
    for i, a in enumerate(args):
        if not _is_nopython_types(a):
            raise TypeError("Arg %d of %s is not legal in nopython "
                            "mode" % (i, a))
    # Filter return type
    if (return_type and return_type != types.none and
            not _is_nopython_types(return_type)):
        raise TypeError('Return type of %s is not legal in nopython '
                        'mode' % (return_type,))

def legalize_return_type(return_type, interp, targetctx):
    """
    Only accept array return type iff it is passed into the function.
    Reject function object return types if in nopython mode.
    """
    assert assume.return_argument_array_only

    if isinstance(return_type, types.Array):
        # Walk IR to discover all return statements
        retstmts = []
        for bid, blk in interp.blocks.items():
            for inst in blk.body:
                if isinstance(inst, ir.Return):
                    retstmts.append(inst)

        assert retstmts, "No return statements?"

        # FIXME: In the future, we can return an array that is either a dynamically
        #        allocated array or an array that is passed as argument.  This
        #        must be statically resolvable.

        # The return value must be the first modification of the value.
        arguments = frozenset("%s.1" % arg for arg in interp.argspec.args)

        if isinstance(return_type, types.Array):
            for ret in retstmts:
                if ret.value.name not in arguments:
                    raise TypeError("Only accept returning of array passed into the "
                                    "function as argument")

        # Legalized; tag return handling
        targetctx.metadata['return.array'] = 'arg'

    elif (isinstance(return_type, types.Function) or
            (isinstance(return_type, types.Dummy) and
             return_type.name == 'abs')):
        raise TypeError("Can't return function object in nopython mode")

def translate_stage(bytecode):
    interp = interpreter.Interpreter(bytecode=bytecode)

    if config.DUMP_CFG:
        interp.cfa.dump()

    interp.interpret()

    if config.DEBUG:
        interp.dump()

    macro.expand_macros(interp.blocks)

    if config.DUMP_IR:
        interp.dump()

    return interp

def type_inference_stage(typingctx, interp, args, return_type, locals={}):
    if len(args) != len(interp.argspec.args):
        raise TypeError("Mismatch number of argument types")

    infer = typeinfer.TypeInferer(typingctx, interp.blocks)

    # Seed argument types
    for arg, ty in zip(interp.argspec.args, args):
    	infer.seed_type(arg, ty)

    # Seed return type
    if return_type is not None:
        infer.seed_return(return_type)

    # Seed local types
    for k, v in locals.items():
        infer.seed_type(k, v)

    infer.build_constrain()
    infer.propagate()
    typemap, restype, calltypes = infer.unify()

    if config.DEBUG:
        pprint(typemap)
        pprint(restype)
        pprint(calltypes)

    return typemap, restype, calltypes

def native_lowering_stage(targetctx, interp, typemap, restype, calltypes,
                          nocompile):
    # Lowering
    fndesc = lowering.PythonFunctionDescriptor.from_specialized_function(
        interp, typemap, restype, calltypes, mangler=targetctx.mangler)

    lower = lowering.Lower(targetctx, fndesc, interp)
    lower.lower()

    # Linking depending libraries
    targetctx.link_dependencies(lower.module, targetctx.linking)

    if nocompile:
        return None, lower.module, lower.function, fndesc
    else:
        # Prepare for execution
        cfunc = targetctx.get_executable(lower.function, fndesc, lower.env)
        targetctx.insert_user_function(cfunc, fndesc)
        return cfunc, lower.module, lower.function, fndesc

def py_lowering_stage(targetctx, interp, nocompile):
    fndesc = lowering.PythonFunctionDescriptor.from_object_mode_function(interp)
    lower = objmode.PyLower(targetctx, fndesc, interp)
    lower.lower()

    if nocompile:
        return None, lower.module, lower.function, fndesc
    else:
        cfunc = targetctx.get_executable(lower.function, fndesc, lower.env)
        return cfunc, lower.module, lower.function, fndesc

def ir_optimize_for_py_stage(interp):
    """
    This passes breaks semantic for the type inferer but they reduces
    refct calls for object mode.
    """
    irpasses.RemoveRedundantAssign(interp).run()
    if config.DEBUG:
        print("ir optimize".center(80, '-'))
        interp.dump()
<|endoftext|>


<commit_before>from django.db import models

# Create your models here.
class Department(models.Model):
	companyId 	= models.ForeignKey(Company)
	name 		= models.CharField(max_length=30)
	registryDate	= models.DateTimeField(auto_now=True)

	def __unicode__(self):
		return self.name

class Project(modelsModel):
	companyId 	= models.ForeignKey(Company)
	name 		= models.CharField(max_length=30)
	registryDate	= models.DateTimeField(auto_now=True)

	def __unicode__(self):
		return self.name<commit_msg>Added ProjectDepartment to models. Fixed ids in models.<commit_after>from django.db import models

# Create your models here.
class Department(models.Model):
	company_id 	= models.ForeignKey(Company)
	name 		= models.CharField(max_length=30)
	registryDate	= models.DateTimeField(auto_now=True)

	def __unicode__(self):
		return self.name

class Project(modelsModel):
	company_id 	= models.ForeignKey(Company)
	name 		= models.CharField(max_length=30)
	registryDate	= models.DateTimeField(auto_now=True)

	def __unicode__(self):
		return self.name

class ProjectDepartment(modelsModel):
	department_id 	= models.ForeignKey(Department)
	project_id 	= models.ForeignKey(Project)
	<|endoftext|>


<commit_before>from character import character as character
import ply.yacc as yacc

class Parser:
    # error rule
    def __init__(self, lex):
        def p_obsidianFormat (p):
            '''
            obsidianFormat : blocks
            '''

        def p_blocks (p):
            '''
            blocks : blocks block
                | block
            '''

        def p_block (p):
            '''
            block : overview
                | defense_block
            '''

        def p_overview (p):
            '''
            overview : name statlist
            '''

        def p_statlist (p):
            '''statlist : statlist stat
                        | stat'''

        def p_stat (p):
            '''
            stat : stat EOL
                | stat SEMICOLON
                | challenge_rating
                | xp_value
                | core
                | bio
                | initiative
                | senses
            '''

        def p_name (p) :
            'name : words'
            self.character.name = p[1]

        def p_challenge_rating (p) :
            'challenge_rating : CR NUMBER'
            self.character.challenge = p[2]

        def p_xp_value (p) :
            'xp_value : XP NUMBER'
            self.character.xp_value = p[2]

        def p_core (p) :
            'core : race_definition class_definitions'
            self.character.classes = p[2]

        def p_race (p) :
            'race_definition : GENDER words'
            self.character.gender = p[1]
            self.character.race = p[2]

        def p_class_definitions (p) :
            '''
            class_definitions : class_definitions SOLIDUS class_definition
                | class_definition
            '''
            if len(p) > 2:
                class_list = p[1]
                class_list.append(p[3])
                p[0] = class_list
            else:
                p[0] = [p[1]];

        def p_class_definition (p) :
            'class_definition : CLASS NUMBER'
            p[0] = {'class' : p[1], 'level' : p[2]}

        def p_bio (p) :
            'bio : alignment size_definition type_definition'
            self.character.alignment = p[1]
            self.character.size = p[2]
            self.character.creature_type = p[3]

        def p_alignment (p) :
            'alignment : ALIGNMENT'
            moral = p[1][:1]
            ethical = p[1][1:]
            p[0] = {'moral' : 0, 'ethical' : 0}
            if moral == 'L':
                p[0]['moral'] = 1
            elif moral == 'C':
                p[0]['moral'] = -1

            if ethical == 'G':
                p[0]['ethical'] = 1
            elif ethical == 'E':
                p[0]['ethical'] = -1

        def p_size_definition (p) :
            '''
            size_definition : SIZE LPAREN SIZE_MOD RPAREN
                | SIZE
            '''
            if len(p) == 5:
                p[0] = p[1] + " (" + p[3] + ")"
            else:
                p[0] = p[1]

        def p_type_definition (p) :
            '''
            type_definition :  CREATURE_TYPE LPAREN wordlist RPAREN
                | CREATURE_TYPE
            '''
            if len(p) == 5:
                p[0] = {'primary' : p[1], 'subtypes' : p[3]}
            else :
                p[0] = {'primary' : p[1], 'subtypes' : []}

        def p_initiative (p):
            '''
            initiative : INIT modifier
            '''
            self.character.initiative = p[2]

        def p_senses (p):
            '''
            senses : SENSES WORD modifier
            '''
            self.character.senses = {p[2] : p[3]}

        def p_defense_block (p):
            '''
            defense_block : DEFENSE EOL defenses
		| DEFENSE defenses
            '''

	def p_defenses (p):
	    '''
	    defenses : defenses defense
		| defense
	    '''

	def p_defense (p):
	    '''
	    defense : defense EOL
		| ac_definition
		| hp_definition
		| speed
	    '''

	def p_ac_definition (p):
	    '''
	    ac_definition : AC NUMBER COMMA alternate_acs ac_sources
		| AC NUMBER
	    '''
	    self.character.ac = {"alternates" : p[4], "sources" : p[5]}

	def p_alternate_acs (p):
	    '''
	    alternate_acs : alternate_acs COMMA alternate_ac
		| alternate_ac
		|
	    '''
	    if len(p) == 4:
		p[0] = p[1] + [p[3]]
	    elif len(p) == 2:
		p[0] = [p[1]]
	    else:
		p[0] = []

	def p_alternate_ac (p):
	    '''
	    alternate_ac : AC_TYPE NUMBER
	    '''
	    p[0] = {"type" : p[1], "value" : p[2]};

	def p_ac_sources (p):
	    '''
	    ac_sources : LPAREN ac_source_list RPAREN
		|
	    '''
	    p[0] = p[2];

	def p_ac_source_list (p):
	    '''
	    ac_source_list : ac_source_list COMMA ac_source
		| ac_source
	    '''
	    if len(p) == 4:
		p[0] = p[1] + [p[3]]
	    else:
		p[0] = [p[1]]

	def p_ac_source (p):
	    '''
	    ac_source : modifier words
		| modifier DEX
	    '''
	    p[0] = { "name" : p[2], "value" : p[1] }

	def p_hp_definition (p):
	    '''
	    hp_definition : HP NUMBER hit_dice_definition
	    '''
	    self.character.hp = p[2]

	def p_hit_dice_definition (p):
	    '''
	    hit_dice_definition : LPAREN NUMBER HD SEMICOLON hit_dice_list modifier RPAREN
		| LPAREN hit_dice_list modifier RPAREN
		|
	    '''
	    if len(p) == 8:
		return p[5]
	    elif len(p) == 5:
		return p[2]
	    else:
		return []

	def p_hit_dice_list (p):
	    '''
	    hit_dice_list : hit_dice_list PLUS dice_definition
		| dice_definition
	    '''
	    if len(p) == 4:
		p[0] = p[1] + [p[3]]
	    elif len(p) == 2:
		p[0] = [p[1]]

	def p_speed (p):
	    '''
	    speed : SPEED NUMBER FEET
	    '''
	    self.character.speed = p[2]

        # utility rules

        def p_wordlist (p) :
            '''
            wordlist : wordlist COMMA words
                |  words
            '''
            if len(p) == 4:
                p[0] = p[1]
                p[0].append(p[3])
            else:
                p[0] = [p[1]]

        def p_words (p) :
            '''
            words : words WORD
                | WORD
            '''
            if len(p) == 3:
                p[0] = p[1] + " " + p[2]
            else :
                p[0] = p[1]

        def p_dice_definition (p):
            '''
            dice_definition : NUMBER D NUMBER
            '''
	    p[0] = str(p[1]) + "d" + str(p[3])

        def p_modifier (p):
            '''
            modifier : PLUS NUMBER
            '''
            p[0] = p[2]

        # Compute column.
        # token is a token instance
        def find_column(token):
            last_cr = self.input.rfind('\n',0,token.lexpos)
            if last_cr < 0:
                last_cr = 0
            column = (token.lexpos - last_cr) + 1
            return column

        # error rule
        def p_error (p):
            print "syntax error at " + str(p.type) + " token '" + str(p.value),
            print  "' at position " + str(find_column(p)) + ", line " + str(p.lineno)

            #print str(p) + " << current token"
            #for tok in lex.lexer:
            #    print str(tok)

            self.error = True

        self.error = False;
        self.lexer = lex.lexer

        tokens = lex.tokens

        self.parser = yacc.yacc()

        self.character = character();

    def parse(self, s):
        self.input = s;
        result = self.parser.parse(s, lexer=self.lexer)
        return result
<commit_msg> simplified hit dice rule and put all of the data into the character object<commit_after>from character import character as character
import ply.yacc as yacc

class Parser:
    # error rule
    def __init__(self, lex):
        def p_obsidianFormat (p):
            '''
            obsidianFormat : blocks
            '''

        def p_blocks (p):
            '''
            blocks : blocks block
                | block
            '''

        def p_block (p):
            '''
            block : overview
                | defense_block
            '''

        def p_overview (p):
            '''
            overview : name statlist
            '''

        def p_statlist (p):
            '''statlist : statlist stat
                        | stat'''

        def p_stat (p):
            '''
            stat : stat EOL
                | stat SEMICOLON
                | challenge_rating
                | xp_value
                | core
                | bio
                | initiative
                | senses
            '''

        def p_name (p) :
            'name : words'
            self.character.name = p[1]

        def p_challenge_rating (p) :
            'challenge_rating : CR NUMBER'
            self.character.challenge = p[2]

        def p_xp_value (p) :
            'xp_value : XP NUMBER'
            self.character.xp_value = p[2]

        def p_core (p) :
            'core : race_definition class_definitions'
            self.character.classes = p[2]

        def p_race (p) :
            'race_definition : GENDER words'
            self.character.gender = p[1]
            self.character.race = p[2]

        def p_class_definitions (p) :
            '''
            class_definitions : class_definitions SOLIDUS class_definition
                | class_definition
            '''
            if len(p) > 2:
                class_list = p[1]
                class_list.append(p[3])
                p[0] = class_list
            else:
                p[0] = [p[1]];

        def p_class_definition (p) :
            'class_definition : CLASS NUMBER'
            p[0] = {'class' : p[1], 'level' : p[2]}

        def p_bio (p) :
            'bio : alignment size_definition type_definition'
            self.character.alignment = p[1]
            self.character.size = p[2]
            self.character.creature_type = p[3]

        def p_alignment (p) :
            'alignment : ALIGNMENT'
            moral = p[1][:1]
            ethical = p[1][1:]
            p[0] = {'moral' : 0, 'ethical' : 0}
            if moral == 'L':
                p[0]['moral'] = 1
            elif moral == 'C':
                p[0]['moral'] = -1

            if ethical == 'G':
                p[0]['ethical'] = 1
            elif ethical == 'E':
                p[0]['ethical'] = -1

        def p_size_definition (p) :
            '''
            size_definition : SIZE LPAREN SIZE_MOD RPAREN
                | SIZE
            '''
            if len(p) == 5:
                p[0] = p[1] + " (" + p[3] + ")"
            else:
                p[0] = p[1]

        def p_type_definition (p) :
            '''
            type_definition :  CREATURE_TYPE LPAREN wordlist RPAREN
                | CREATURE_TYPE
            '''
            if len(p) == 5:
                p[0] = {'primary' : p[1], 'subtypes' : p[3]}
            else :
                p[0] = {'primary' : p[1], 'subtypes' : []}

        def p_initiative (p):
            '''
            initiative : INIT modifier
            '''
            self.character.initiative = p[2]

        def p_senses (p):
            '''
            senses : SENSES WORD modifier
            '''
            self.character.senses = {p[2] : p[3]}

        def p_defense_block (p):
            '''
            defense_block : DEFENSE EOL defenses
		| DEFENSE defenses
            '''

	def p_defenses (p):
	    '''
	    defenses : defenses defense
		| defense
	    '''

	def p_defense (p):
	    '''
	    defense : defense EOL
		| ac_definition
		| hp_definition
		| speed
	    '''

	def p_ac_definition (p):
	    '''
	    ac_definition : AC NUMBER COMMA alternate_acs ac_sources
		| AC NUMBER
	    '''
	    self.character.ac = {"alternates" : p[4], "sources" : p[5]}

	def p_alternate_acs (p):
	    '''
	    alternate_acs : alternate_acs COMMA alternate_ac
		| alternate_ac
		|
	    '''
	    if len(p) == 4:
		p[0] = p[1] + [p[3]]
	    elif len(p) == 2:
		p[0] = [p[1]]
	    else:
		p[0] = []

	def p_alternate_ac (p):
	    '''
	    alternate_ac : AC_TYPE NUMBER
	    '''
	    p[0] = {"type" : p[1], "value" : p[2]};

	def p_ac_sources (p):
	    '''
	    ac_sources : LPAREN ac_source_list RPAREN
		|
	    '''
	    p[0] = p[2];

	def p_ac_source_list (p):
	    '''
	    ac_source_list : ac_source_list COMMA ac_source
		| ac_source
	    '''
	    if len(p) == 4:
		p[0] = p[1] + [p[3]]
	    else:
		p[0] = [p[1]]

	def p_ac_source (p):
	    '''
	    ac_source : modifier words
		| modifier DEX
	    '''
	    p[0] = { "name" : p[2], "value" : p[1] }

	def p_hp_definition (p):
	    '''
	    hp_definition : HP NUMBER hit_dice_definition
	    '''
	    self.character.hp = p[2]

	def p_hit_dice_definition (p):
	    '''
	    hit_dice_definition : LPAREN NUMBER HD SEMICOLON hit_dice_list modifier RPAREN
	    '''
	    self.character.hit_dice = p[2]
	    self.character.hit_dice_list = p[5]
	    self.character.hp_modifier = p[6]

	def p_hit_dice_list (p):
	    '''
	    hit_dice_list : hit_dice_list PLUS dice_definition
		| dice_definition
	    '''
	    if len(p) == 4:
		p[0] = p[1] + [p[3]]
	    elif len(p) == 2:
		p[0] = [p[1]]

	def p_speed (p):
	    '''
	    speed : SPEED NUMBER FEET
	    '''
	    self.character.speed = p[2]

        # utility rules

        def p_wordlist (p) :
            '''
            wordlist : wordlist COMMA words
                |  words
            '''
            if len(p) == 4:
                p[0] = p[1]
                p[0].append(p[3])
            else:
                p[0] = [p[1]]

        def p_words (p) :
            '''
            words : words WORD
                | WORD
            '''
            if len(p) == 3:
                p[0] = p[1] + " " + p[2]
            else :
                p[0] = p[1]

        def p_dice_definition (p):
            '''
            dice_definition : NUMBER D NUMBER
            '''
	    p[0] = str(p[1]) + "d" + str(p[3])

        def p_modifier (p):
            '''
            modifier : PLUS NUMBER
            '''
            p[0] = p[2]

        # Compute column.
        # token is a token instance
        def find_column(token):
            last_cr = self.input.rfind('\n',0,token.lexpos)
            if last_cr < 0:
                last_cr = 0
            column = (token.lexpos - last_cr) + 1
            return column

        # error rule
        def p_error (p):
            print "syntax error at " + str(p.type) + " token '" + str(p.value),
            print  "' at position " + str(find_column(p)) + ", line " + str(p.lineno)

            #print str(p) + " << current token"
            #for tok in lex.lexer:
            #    print str(tok)

            self.error = True

        self.error = False;
        self.lexer = lex.lexer

        tokens = lex.tokens

        self.parser = yacc.yacc()

        self.character = character();

    def parse(self, s):
        self.input = s;
        result = self.parser.parse(s, lexer=self.lexer)
        return result
<|endoftext|>


<commit_before># A factory for Integrater implementations. At the moment this will
# support only XDS and the null integrater implementation.

from __future__ import absolute_import, division, print_function

import os

from xia2.DriverExceptions.NotAvailableError import NotAvailableError
from xia2.Handlers.Phil import PhilIndex
from xia2.Handlers.PipelineSelection import get_preferences
from xia2.Handlers.Streams import Debug
from xia2.Modules.Integrater.DialsIntegrater import DialsIntegrater
from xia2.Modules.Integrater.XDSIntegrater import XDSIntegrater

def IntegraterForXSweep(xsweep, json_file=None):
    """Create an Integrater implementation to work with the provided
    XSweep."""

    # FIXME this needs properly implementing...
    if xsweep is None:
        raise RuntimeError("XSweep instance needed")

    if not xsweep.__class__.__name__ == "XSweep":
        raise RuntimeError("XSweep instance needed")

    integrater = Integrater()

    if json_file is not None:
        assert os.path.isfile(json_file)
        Debug.write("Loading integrater from json: %s" % json_file)
        import time

        t0 = time.time()
        integrater = integrater.__class__.from_json(filename=json_file)
        t1 = time.time()
        Debug.write("Loaded integrater in %.2f seconds" % (t1 - t0))
    else:
        integrater.setup_from_imageset(xsweep.get_imageset())
    integrater.set_integrater_sweep_name(xsweep.get_name())

    # copy across resolution limits
    if xsweep.get_resolution_high() or xsweep.get_resolution_low():

        d_min = PhilIndex.params.xia2.settings.resolution.d_min
        d_max = PhilIndex.params.xia2.settings.resolution.d_max

        # override with sweep versions if set - xia2#146
        if xsweep.get_resolution_high():
            d_min = xsweep.get_resolution_high()
        if xsweep.get_resolution_low():
            d_max = xsweep.get_resolution_low()

        if d_min is not None and d_min != integrater.get_integrater_high_resolution():

            Debug.write("Assigning resolution limits from XINFO input:")
            Debug.write("d_min: %.3f" % d_min)
            integrater.set_integrater_high_resolution(d_min, user=True)

        if d_max is not None and d_max != integrater.get_integrater_low_resolution():

            Debug.write("Assigning resolution limits from XINFO input:")
            Debug.write("d_max: %.3f" % d_max)
            integrater.set_integrater_low_resolution(d_max, user=True)

    # check the epoch and perhaps pass this in for future reference
    # (in the scaling)
    if xsweep._epoch > 0:
        integrater.set_integrater_epoch(xsweep._epoch)

    # need to do the same for wavelength now as that could be wrong in
    # the image header...

    if xsweep.get_wavelength_value():
        Debug.write(
            "Integrater factory: Setting wavelength: %.6f"
            % xsweep.get_wavelength_value()
        )
        integrater.set_wavelength(xsweep.get_wavelength_value())

    # likewise the distance...
    if xsweep.get_distance():
        Debug.write(
            "Integrater factory: Setting distance: %.2f" % xsweep.get_distance()
        )
        integrater.set_distance(xsweep.get_distance())

    integrater.set_integrater_sweep(xsweep, reset=False)

    return integrater

def Integrater():
    """Return an  Integrater implementation."""

    # FIXME this should take an indexer as an argument...

    integrater = None
    preselection = get_preferences().get("integrater")

    if not integrater and (not preselection or preselection == "dials"):
        try:
            integrater = DialsIntegrater()
            Debug.write("Using Dials Integrater")
            if PhilIndex.params.xia2.settings.pipeline in ["dials", "dials-full"]:
                integrater.set_output_format("pickle")
        except NotAvailableError:
            if preselection == "dials":
                raise RuntimeError(
                    "preselected integrater dials not available: "
                    + "dials not installed?"
                )

    if not integrater and (not preselection or preselection == "xdsr"):
        try:
            integrater = XDSIntegrater()
            Debug.write("Using XDS Integrater in new resolution mode")
        except NotAvailableError:
            if preselection == "xdsr":
                raise RuntimeError(
                    "preselected integrater xdsr not available: " + "xds not installed?"
                )

    if not integrater:
        raise RuntimeError("no integrater implementations found")

    # check to see if resolution limits were passed in through the
    # command line...

    dmin = PhilIndex.params.xia2.settings.resolution.d_min
    dmax = PhilIndex.params.xia2.settings.resolution.d_max

    if dmin:
        Debug.write("Adding user-assigned resolution limits:")

        if dmax:

            Debug.write("dmin: %.3f dmax: %.2f" % (dmin, dmax))
            integrater.set_integrater_resolution(dmin, dmax, user=True)

        else:

            Debug.write("dmin: %.3f" % dmin)
            integrater.set_integrater_high_resolution(dmin, user=True)

    return integrater
<commit_msg>Bugfix, check scaler not pipeline, Fixes #363<commit_after># A factory for Integrater implementations. At the moment this will
# support only XDS and the null integrater implementation.

from __future__ import absolute_import, division, print_function

import os

from xia2.DriverExceptions.NotAvailableError import NotAvailableError
from xia2.Handlers.Phil import PhilIndex
from xia2.Handlers.PipelineSelection import get_preferences
from xia2.Handlers.Streams import Debug
from xia2.Modules.Integrater.DialsIntegrater import DialsIntegrater
from xia2.Modules.Integrater.XDSIntegrater import XDSIntegrater

def IntegraterForXSweep(xsweep, json_file=None):
    """Create an Integrater implementation to work with the provided
    XSweep."""

    # FIXME this needs properly implementing...
    if xsweep is None:
        raise RuntimeError("XSweep instance needed")

    if not xsweep.__class__.__name__ == "XSweep":
        raise RuntimeError("XSweep instance needed")

    integrater = Integrater()

    if json_file is not None:
        assert os.path.isfile(json_file)
        Debug.write("Loading integrater from json: %s" % json_file)
        import time

        t0 = time.time()
        integrater = integrater.__class__.from_json(filename=json_file)
        t1 = time.time()
        Debug.write("Loaded integrater in %.2f seconds" % (t1 - t0))
    else:
        integrater.setup_from_imageset(xsweep.get_imageset())
    integrater.set_integrater_sweep_name(xsweep.get_name())

    # copy across resolution limits
    if xsweep.get_resolution_high() or xsweep.get_resolution_low():

        d_min = PhilIndex.params.xia2.settings.resolution.d_min
        d_max = PhilIndex.params.xia2.settings.resolution.d_max

        # override with sweep versions if set - xia2#146
        if xsweep.get_resolution_high():
            d_min = xsweep.get_resolution_high()
        if xsweep.get_resolution_low():
            d_max = xsweep.get_resolution_low()

        if d_min is not None and d_min != integrater.get_integrater_high_resolution():

            Debug.write("Assigning resolution limits from XINFO input:")
            Debug.write("d_min: %.3f" % d_min)
            integrater.set_integrater_high_resolution(d_min, user=True)

        if d_max is not None and d_max != integrater.get_integrater_low_resolution():

            Debug.write("Assigning resolution limits from XINFO input:")
            Debug.write("d_max: %.3f" % d_max)
            integrater.set_integrater_low_resolution(d_max, user=True)

    # check the epoch and perhaps pass this in for future reference
    # (in the scaling)
    if xsweep._epoch > 0:
        integrater.set_integrater_epoch(xsweep._epoch)

    # need to do the same for wavelength now as that could be wrong in
    # the image header...

    if xsweep.get_wavelength_value():
        Debug.write(
            "Integrater factory: Setting wavelength: %.6f"
            % xsweep.get_wavelength_value()
        )
        integrater.set_wavelength(xsweep.get_wavelength_value())

    # likewise the distance...
    if xsweep.get_distance():
        Debug.write(
            "Integrater factory: Setting distance: %.2f" % xsweep.get_distance()
        )
        integrater.set_distance(xsweep.get_distance())

    integrater.set_integrater_sweep(xsweep, reset=False)

    return integrater

def Integrater():
    """Return an  Integrater implementation."""

    # FIXME this should take an indexer as an argument...

    integrater = None
    preselection = get_preferences().get("integrater")

    if not integrater and (not preselection or preselection == "dials"):
        try:
            integrater = DialsIntegrater()
            Debug.write("Using Dials Integrater")
            if PhilIndex.params.xia2.settings.scaler == "dials":
                integrater.set_output_format("pickle")
        except NotAvailableError:
            if preselection == "dials":
                raise RuntimeError(
                    "preselected integrater dials not available: "
                    + "dials not installed?"
                )

    if not integrater and (not preselection or preselection == "xdsr"):
        try:
            integrater = XDSIntegrater()
            Debug.write("Using XDS Integrater in new resolution mode")
        except NotAvailableError:
            if preselection == "xdsr":
                raise RuntimeError(
                    "preselected integrater xdsr not available: " + "xds not installed?"
                )

    if not integrater:
        raise RuntimeError("no integrater implementations found")

    # check to see if resolution limits were passed in through the
    # command line...

    dmin = PhilIndex.params.xia2.settings.resolution.d_min
    dmax = PhilIndex.params.xia2.settings.resolution.d_max

    if dmin:
        Debug.write("Adding user-assigned resolution limits:")

        if dmax:

            Debug.write("dmin: %.3f dmax: %.2f" % (dmin, dmax))
            integrater.set_integrater_resolution(dmin, dmax, user=True)

        else:

            Debug.write("dmin: %.3f" % dmin)
            integrater.set_integrater_high_resolution(dmin, user=True)

    return integrater
<|endoftext|>


<commit_before>import logging
import click
from itertools import chain
from collections import namedtuple
import asyncio

from tornado.platform.asyncio import AsyncIOMainLoop
from streamz import Stream, union, combine_latest, zip_latest
import attr

from .events import Trade
from .collectors import Collector
from .feeds import Feed
from .config import config

logger = logging.getLogger(__name__)

AsyncIOMainLoop().install()

ENVVAR_PREFIX = 'NUMISMATIC'

pass_state = click.make_pass_decorator(dict, ensure=True)

@click.group(chain=True)
@click.option('--cache-dir', '-d', default=None)
@click.option('--requester', '-r', default='base',
              type=click.Choice(['base', 'caching']))
@click.option('--log-level', '-l', default='info',
              type=click.Choice(['debug', 'info', 'warning', 'error',
                                 'critical']))
@pass_state
def coin(state, cache_dir, requester, log_level):
    '''Numismatic Command Line Interface

    Examples:

        coin list

        coin info

        coin info -a ETH

        coin prices

        coin prices -a XMR,ZEC -c EUR,ZAR

        coin prices -f luno

        coin history

        coin history -a ETH -c BTC -o ETH-BTH-history.json

        coin listen collect run

        coin listen -a BTC,ETH,XMR,ZEC collect -t Trade run -t 30

        coin listen -f bitfinex -f gdax collect run

        coin listen -f bitfinex -f gdax collect --raw run

        coin listen -f bitfinex -f gdax compare run
    '''
    logging.basicConfig(level=getattr(logging, log_level.upper()))
    state['cache_dir'] = cache_dir
    state['requester'] = requester
    state['output_stream'] = Stream()
    state['subscriptions'] = {}

@coin.command(name='list')
@click.option('--feed', '-f', default=config['DEFAULT']['Feed'],
              type=click.Choice(Feed._get_subclasses().keys()))
@click.option('--output', '-o', type=click.File('wt'), default='-')
@pass_state
def list_all(state, feed, output):
    "List all available assets"
    feed_client = Feed.factory(feed, cache_dir=state['cache_dir'],
                               requester=state['requester'])
    assets_list = feed_client.get_list()
    write(assets_list, output, sep=' ')

@coin.command()
@click.option('--feed', '-f', default=config['DEFAULT']['Feed'],
              type=click.Choice(Feed._get_subclasses().keys()))
@click.option('--assets', '-a', multiple=True,
              envvar=f'{ENVVAR_PREFIX}_ASSETS')
@click.option('--output', '-o', type=click.File('wt'), default='-')
@pass_state
def info(state, feed, assets, output):
    "Info about the requested assets"
    feed_client = Feed.factory(feed, cache_dir=state['cache_dir'],
                               requester=state['requester'])
    assets_info = feed_client.get_info(assets)
    write(assets_info, output)

@coin.command()
@click.option('--feed', '-f', default=config['DEFAULT']['Feed'],
              type=click.Choice(Feed._get_subclasses().keys()))
@click.option('--assets', '-a', multiple=True,
              envvar=f'{ENVVAR_PREFIX}_ASSETS')
@click.option('--currencies', '-c', multiple=True,
              envvar=f'{ENVVAR_PREFIX}_CURRENCIES')
@click.option('--output', '-o', type=click.File('wt'), default='-')
@pass_state
def prices(state, feed, assets, currencies, output):
    'Latest asset prices'
    feed_client = Feed.factory(feed, cache_dir=state['cache_dir'],
                               requester=state['requester'])
    prices = feed_client.get_prices(assets=assets, currencies=currencies)
    write(prices, output)

@coin.command()
@click.option('--feed', '-f', default=config['DEFAULT']['Feed'],
              type=click.Choice(Feed._get_subclasses().keys()))
@click.option('--assets', '-a', multiple=True,
              envvar=f'{ENVVAR_PREFIX}_ASSETS')
@click.option('--currencies', '-c', multiple=True,
              envvar=f'{ENVVAR_PREFIX}_CURRENCIES')
@click.option('--output', '-o', type=click.File('wt'), default='-')
@pass_state
def tickers(state, feed, assets, currencies, output):
    'Latest asset tickers'
    feed_client = Feed.factory(feed, cache_dir=state['cache_dir'],
                               requester=state['requester'])
    tickers = feed_client.get_tickers(assets=assets, currencies=currencies)
    write(tickers, output)

@coin.command()
@click.option('--feed', '-f', default=config['DEFAULT']['Feed'],
              type=click.Choice(Feed._get_subclasses().keys()))
@click.option('--freq', default='d', type=click.Choice(list('dhms')))
@click.option('--start-date', '-s', default=-30)
@click.option('--end-date', '-e', default=None)
@click.option('--assets', '-a', multiple=True,
              envvar=f'{ENVVAR_PREFIX}_ASSETS')
@click.option('--currencies', '-c', multiple=True,
              envvar=f'{ENVVAR_PREFIX}_CURRENCIES')
@click.option('--output', '-o', type=click.File('wt'), default='-')
@pass_state
def history(state, feed, assets, currencies, freq, start_date, end_date,
            output):
    'Historic asset prices and volumes'
    # FIXME: move the loop below into the Feed class
    feed_client = Feed.factory(feed, cache_dir=state['cache_dir'],
                               requester=state['requester'])
    data = feed_client.get_historical_data(assets, currencies, freq=freq,
                                           start_date=start_date,
                                           end_date=end_date)
    write(data, output)

# FIXME: Do we still want this?
def tabulate(data):
    if isinstance(data, dict):
        data_iter = data.values()
    elif isinstance(data, list):
        data_iter = data
    else:
        raise TypeError(f'data: {data}')
    headers = set(chain(*map(lambda v: v.keys(), data_iter)))
    DataTuple = namedtuple('DataTuple', ' '.join(headers))
    DataTuple.__new__.__defaults__ = (None,) * len(headers)
    return map(lambda d: DataTuple(**d), data_iter)

@coin.command()
@click.option('--feed', '-f', default='bitfinex',
              type=click.Choice(Feed._get_subclasses().keys()))
@click.option('--assets', '-a', multiple=True,
              envvar=f'{ENVVAR_PREFIX}_ASSETS')
@click.option('--currencies', '-c', multiple=True,
              envvar=f'{ENVVAR_PREFIX}_CURRENCIES')
@click.option('--raw-output', '-r', default=None, help="Path to write raw "
              "stream to")
@click.option('--raw-interval', '-i', default=1,
              type=float, help="The interval between writing the raw stream "
              "to disk")
# FIXME: The --channel and --api-key-* options are Feed specific and
#        should probably be handled differently.
@click.option('--channels', '-C', multiple=True)
@click.option('--api-key-id', default=None)
@click.option('--api-key-secret', default=None)
@pass_state
def listen(state, feed, assets, currencies, raw_output, raw_interval,
           channels, api_key_id, api_key_secret):
    'Listen to live events from a feed'
    feed_client = Feed.factory(feed)
    subscriptions = feed_client.subscribe(assets, currencies, channels)
    state['subscriptions'].update(subscriptions)

@coin.command()
@click.option('--market', '-m', default='all')
@click.option('--event', 'stream', flag_value='event', default=True)
@click.option('--raw', 'stream', flag_value='raw')
@click.option('--collector', '-c', default='file',
              type=click.Choice(Collector._get_subclasses().keys()))
@click.option('--output', '-o', default='-', type=click.Path())
@click.option('--filter', '-f', default='', type=str, multiple=True)
@click.option('--type', '-t', default=None, multiple=True,
              type=click.Choice(['None', 'Trade', 'Heartbeat', 'LimitOrder',
                                 'CancelOrder']))
@click.option('--text', 'format', flag_value='text', default=True)
@click.option('--json', 'format', flag_value='json')
@click.option('--interval', '-i', default=None, type=float)
@pass_state
def collect(state, market, stream, collector, filter, type, output, format, interval):
    'Collect events and write them to an output sink'
    subscriptions = state['subscriptions']
    if stream=='event':
        stream_name = 'event_stream'
    elif stream=='raw':
        stream_name = 'raw_stream'
    else:
        raise ValueError(stream)
    if market=='all':
        all_streams = [getattr(sub, stream_name) for sub in
                       subscriptions.values()]
        collect_stream = union(*all_streams)
    else:
        collect_stream = getattr(subscriptions[market], stream_name)
    collector_name = collector
    collector = Collector.factory(collector_name, event_stream=collect_stream,
                                  path=output, format=format, types=type,
                                  filters=filter, interval=interval)

@coin.command()
@click.option('--collector', '-c', default='file',
              type=click.Choice(Collector._get_subclasses().keys()))
@click.option('--output', '-o', default='-', type=click.Path())
@click.option('--interval', '-i', default=None, type=float)
@pass_state
def compare(state, collector, output, interval):
    'Compare prices events and write them to an output sink'
    subscriptions = state['subscriptions']
    streams = [sub.event_stream.filter(lambda ev: isinstance(ev, Trade))
               for sub in subscriptions.values()]
    compare_stream = combine_latest(*streams).map(
        lambda trades: {(t.exchange+'--'+t.symbol):t.price for t in trades})
    collector_name = collector
    collector = Collector.factory(collector_name, event_stream=compare_stream,
                                  path=output, interval=interval)

@coin.command()
@click.option('--timeout', '-t', default=0)
@pass_state
def run(state, timeout):
    """
    Run the asyncio event loop for a set amount of time. Set to 0 to run
    indefinitely.  Default is no-timeout."""
    if not timeout:
        # Allow to run indefinitely if timeout==0
        timeout = None
    loop = asyncio.get_event_loop()
    logger.debug('starting ...')
    tasks = {name:asyncio.Task(sub.listener) for name, sub in
             state['subscriptions'].items()}
    try:
        completed, pending = \
            loop.run_until_complete(asyncio.wait(tasks.values(),
                                    timeout=timeout))
    except KeyboardInterrupt:
        pass
    finally:
        logger.debug('cancelling ...')
        pending = {name:task for name, task in tasks.items()
                   if not task.done()}
        for task_name, task in pending.items():
            logger.debug(f'cancelling pending {task_name} ...')
            task.cancel()
        logger.debug('sleeping ...')
        loop.run_until_complete(asyncio.sleep(1))
        logger.debug('done')

def write(data, file, sep='\n'):
    for record in data:
        file.write(str(record)+sep)

if __name__ == '__main__':
    coin()
<commit_msg>Adds tickers example to cli help documentation<commit_after>import logging
import click
from itertools import chain
from collections import namedtuple
import asyncio

from tornado.platform.asyncio import AsyncIOMainLoop
from streamz import Stream, union, combine_latest, zip_latest
import attr

from .events import Trade
from .collectors import Collector
from .feeds import Feed
from .config import config

logger = logging.getLogger(__name__)

AsyncIOMainLoop().install()

ENVVAR_PREFIX = 'NUMISMATIC'

pass_state = click.make_pass_decorator(dict, ensure=True)

@click.group(chain=True)
@click.option('--cache-dir', '-d', default=None)
@click.option('--requester', '-r', default='base',
              type=click.Choice(['base', 'caching']))
@click.option('--log-level', '-l', default='info',
              type=click.Choice(['debug', 'info', 'warning', 'error',
                                 'critical']))
@pass_state
def coin(state, cache_dir, requester, log_level):
    '''Numismatic Command Line Interface

    Examples:

        coin list

        coin info

        coin info -a ETH

        coin prices

        coin prices -a XMR,ZEC -c EUR,ZAR

        coin prices -f luno

        coin tickers -f cryptocompare

        coin history

        coin history -a ETH -c BTC -o ETH-BTH-history.json

        coin listen collect run

        coin listen -a BTC,ETH,XMR,ZEC collect -t Trade run -t 30

        coin listen -f bitfinex -f gdax collect run

        coin listen -f bitfinex -f gdax collect --raw run

        coin listen -f bitfinex -f gdax compare run
    '''
    logging.basicConfig(level=getattr(logging, log_level.upper()))
    state['cache_dir'] = cache_dir
    state['requester'] = requester
    state['output_stream'] = Stream()
    state['subscriptions'] = {}

@coin.command(name='list')
@click.option('--feed', '-f', default=config['DEFAULT']['Feed'],
              type=click.Choice(Feed._get_subclasses().keys()))
@click.option('--output', '-o', type=click.File('wt'), default='-')
@pass_state
def list_all(state, feed, output):
    "List all available assets"
    feed_client = Feed.factory(feed, cache_dir=state['cache_dir'],
                               requester=state['requester'])
    assets_list = feed_client.get_list()
    write(assets_list, output, sep=' ')

@coin.command()
@click.option('--feed', '-f', default=config['DEFAULT']['Feed'],
              type=click.Choice(Feed._get_subclasses().keys()))
@click.option('--assets', '-a', multiple=True,
              envvar=f'{ENVVAR_PREFIX}_ASSETS')
@click.option('--output', '-o', type=click.File('wt'), default='-')
@pass_state
def info(state, feed, assets, output):
    "Info about the requested assets"
    feed_client = Feed.factory(feed, cache_dir=state['cache_dir'],
                               requester=state['requester'])
    assets_info = feed_client.get_info(assets)
    write(assets_info, output)

@coin.command()
@click.option('--feed', '-f', default=config['DEFAULT']['Feed'],
              type=click.Choice(Feed._get_subclasses().keys()))
@click.option('--assets', '-a', multiple=True,
              envvar=f'{ENVVAR_PREFIX}_ASSETS')
@click.option('--currencies', '-c', multiple=True,
              envvar=f'{ENVVAR_PREFIX}_CURRENCIES')
@click.option('--output', '-o', type=click.File('wt'), default='-')
@pass_state
def prices(state, feed, assets, currencies, output):
    'Latest asset prices'
    feed_client = Feed.factory(feed, cache_dir=state['cache_dir'],
                               requester=state['requester'])
    prices = feed_client.get_prices(assets=assets, currencies=currencies)
    write(prices, output)

@coin.command()
@click.option('--feed', '-f', default=config['DEFAULT']['Feed'],
              type=click.Choice(Feed._get_subclasses().keys()))
@click.option('--assets', '-a', multiple=True,
              envvar=f'{ENVVAR_PREFIX}_ASSETS')
@click.option('--currencies', '-c', multiple=True,
              envvar=f'{ENVVAR_PREFIX}_CURRENCIES')
@click.option('--output', '-o', type=click.File('wt'), default='-')
@pass_state
def tickers(state, feed, assets, currencies, output):
    'Latest asset tickers'
    feed_client = Feed.factory(feed, cache_dir=state['cache_dir'],
                               requester=state['requester'])
    tickers = feed_client.get_tickers(assets=assets, currencies=currencies)
    write(tickers, output)

@coin.command()
@click.option('--feed', '-f', default=config['DEFAULT']['Feed'],
              type=click.Choice(Feed._get_subclasses().keys()))
@click.option('--freq', default='d', type=click.Choice(list('dhms')))
@click.option('--start-date', '-s', default=-30)
@click.option('--end-date', '-e', default=None)
@click.option('--assets', '-a', multiple=True,
              envvar=f'{ENVVAR_PREFIX}_ASSETS')
@click.option('--currencies', '-c', multiple=True,
              envvar=f'{ENVVAR_PREFIX}_CURRENCIES')
@click.option('--output', '-o', type=click.File('wt'), default='-')
@pass_state
def history(state, feed, assets, currencies, freq, start_date, end_date,
            output):
    'Historic asset prices and volumes'
    # FIXME: move the loop below into the Feed class
    feed_client = Feed.factory(feed, cache_dir=state['cache_dir'],
                               requester=state['requester'])
    data = feed_client.get_historical_data(assets, currencies, freq=freq,
                                           start_date=start_date,
                                           end_date=end_date)
    write(data, output)

# FIXME: Do we still want this?
def tabulate(data):
    if isinstance(data, dict):
        data_iter = data.values()
    elif isinstance(data, list):
        data_iter = data
    else:
        raise TypeError(f'data: {data}')
    headers = set(chain(*map(lambda v: v.keys(), data_iter)))
    DataTuple = namedtuple('DataTuple', ' '.join(headers))
    DataTuple.__new__.__defaults__ = (None,) * len(headers)
    return map(lambda d: DataTuple(**d), data_iter)

@coin.command()
@click.option('--feed', '-f', default='bitfinex',
              type=click.Choice(Feed._get_subclasses().keys()))
@click.option('--assets', '-a', multiple=True,
              envvar=f'{ENVVAR_PREFIX}_ASSETS')
@click.option('--currencies', '-c', multiple=True,
              envvar=f'{ENVVAR_PREFIX}_CURRENCIES')
@click.option('--raw-output', '-r', default=None, help="Path to write raw "
              "stream to")
@click.option('--raw-interval', '-i', default=1,
              type=float, help="The interval between writing the raw stream "
              "to disk")
# FIXME: The --channel and --api-key-* options are Feed specific and
#        should probably be handled differently.
@click.option('--channels', '-C', multiple=True)
@click.option('--api-key-id', default=None)
@click.option('--api-key-secret', default=None)
@pass_state
def listen(state, feed, assets, currencies, raw_output, raw_interval,
           channels, api_key_id, api_key_secret):
    'Listen to live events from a feed'
    feed_client = Feed.factory(feed)
    subscriptions = feed_client.subscribe(assets, currencies, channels)
    state['subscriptions'].update(subscriptions)

@coin.command()
@click.option('--market', '-m', default='all')
@click.option('--event', 'stream', flag_value='event', default=True)
@click.option('--raw', 'stream', flag_value='raw')
@click.option('--collector', '-c', default='file',
              type=click.Choice(Collector._get_subclasses().keys()))
@click.option('--output', '-o', default='-', type=click.Path())
@click.option('--filter', '-f', default='', type=str, multiple=True)
@click.option('--type', '-t', default=None, multiple=True,
              type=click.Choice(['None', 'Trade', 'Heartbeat', 'LimitOrder',
                                 'CancelOrder']))
@click.option('--text', 'format', flag_value='text', default=True)
@click.option('--json', 'format', flag_value='json')
@click.option('--interval', '-i', default=None, type=float)
@pass_state
def collect(state, market, stream, collector, filter, type, output, format, interval):
    'Collect events and write them to an output sink'
    subscriptions = state['subscriptions']
    if stream=='event':
        stream_name = 'event_stream'
    elif stream=='raw':
        stream_name = 'raw_stream'
    else:
        raise ValueError(stream)
    if market=='all':
        all_streams = [getattr(sub, stream_name) for sub in
                       subscriptions.values()]
        collect_stream = union(*all_streams)
    else:
        collect_stream = getattr(subscriptions[market], stream_name)
    collector_name = collector
    collector = Collector.factory(collector_name, event_stream=collect_stream,
                                  path=output, format=format, types=type,
                                  filters=filter, interval=interval)

@coin.command()
@click.option('--collector', '-c', default='file',
              type=click.Choice(Collector._get_subclasses().keys()))
@click.option('--output', '-o', default='-', type=click.Path())
@click.option('--interval', '-i', default=None, type=float)
@pass_state
def compare(state, collector, output, interval):
    'Compare prices events and write them to an output sink'
    subscriptions = state['subscriptions']
    streams = [sub.event_stream.filter(lambda ev: isinstance(ev, Trade))
               for sub in subscriptions.values()]
    compare_stream = combine_latest(*streams).map(
        lambda trades: {(t.exchange+'--'+t.symbol):t.price for t in trades})
    collector_name = collector
    collector = Collector.factory(collector_name, event_stream=compare_stream,
                                  path=output, interval=interval)

@coin.command()
@click.option('--timeout', '-t', default=0)
@pass_state
def run(state, timeout):
    """
    Run the asyncio event loop for a set amount of time. Set to 0 to run
    indefinitely.  Default is no-timeout."""
    if not timeout:
        # Allow to run indefinitely if timeout==0
        timeout = None
    loop = asyncio.get_event_loop()
    logger.debug('starting ...')
    tasks = {name:asyncio.Task(sub.listener) for name, sub in
             state['subscriptions'].items()}
    try:
        completed, pending = \
            loop.run_until_complete(asyncio.wait(tasks.values(),
                                    timeout=timeout))
    except KeyboardInterrupt:
        pass
    finally:
        logger.debug('cancelling ...')
        pending = {name:task for name, task in tasks.items()
                   if not task.done()}
        for task_name, task in pending.items():
            logger.debug(f'cancelling pending {task_name} ...')
            task.cancel()
        logger.debug('sleeping ...')
        loop.run_until_complete(asyncio.sleep(1))
        logger.debug('done')

def write(data, file, sep='\n'):
    for record in data:
        file.write(str(record)+sep)

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


<commit_before>from __future__ import absolute_import
import venusian
from pyramid.decorator import reify
from pyramid.traversal import find_root
from types import MethodType
from .interfaces import CALCULATED_PROPERTIES

def includeme(config):
    config.registry[CALCULATED_PROPERTIES] = CalculatedProperties()
    config.add_directive('add_calculated_property', add_calculated_property)

class ItemNamespace(object):
    def __init__(self, context, request, defined=None, ns=None):
        self.context = context
        self.request = request
        self._defined = defined or {}
        if ns:
            self.__dict__.update(ns)
        self._results = {}

    @reify
    def _properties(self):
        return self.context.__json__(self.request)

    @reify
    def root(self):
        return find_root(self.context)

    @reify
    def registry(self):
        return self.request.registry

    def __getattr__(self, name):
        context = self.context
        request = self.request
        if name in self._defined:
            value = self._defined[name](self)
            setattr(self, name, value)
            return value
        if name in self._properties:
            value = self._properties[name]
            if name in context.type_info.schema_links:
                if isinstance(value, list):
                    value = [
                        request.resource_path(self.root.get_by_uuid(v))
                        for v in value
                    ]
                else:
                    value = request.resource_path(self.root.get_by_uuid(value))
            setattr(self, name, value)
            return value
        if name in context.rev:
            value = context.get_rev_links(name)
            value = [
                request.resource_path(self.root.get_by_uuid(v))
                for v in value
            ]
            setattr(self, name, value)
            return value
        raise AttributeError(name)

    def __call__(self, fn):
        try:
            return self._results[fn]
        except KeyError:
            pass

        if isinstance(fn, str):
            result = self._results[fn] = getattr(self, fn, None)
            return result

        start = 1 if isinstance(fn, MethodType) else 0
        # Not using inspect.getargspec as it is slow
        args = fn.__code__.co_varnames[start:fn.__code__.co_argcount]
        kw = {}
        for name in args:
            try:
                kw[name] = getattr(self, name)
            except AttributeError:
                pass

        result = self._results[fn] = fn(**kw)
        return result

class CalculatedProperties(object):
    def __init__(self):
        self.category_cls_props = {}

    def register_prop(self, fn, name, context, condition=None, schema=None,
                      attr=None, define=False, category='object'):
        prop = CalculatedProperty(fn, name, attr, condition, schema, define)
        cls_props = self.category_cls_props.setdefault(category, {})
        cls_props.setdefault(context, {})[name] = prop

    def props_for(self, context, category='object'):
        if isinstance(context, type):
            cls = context
        else:
            cls = type(context)
        props = {}
        cls_props = self.category_cls_props.get(category, {})
        for base in reversed(cls.mro()):
            props.update(cls_props.get(base, {}))
        return props

class CalculatedProperty(object):
    condition_args = None

    def __init__(self, fn, name, attr=None, condition=None, schema=None, define=False):
        self.fn = fn
        self.attr = attr
        self.name = name
        self.condition = condition
        self.define = define

        if schema is not None:
            if 'default' in schema:
                raise ValueError('schema may not specify default for calculated property')
            if 'linkFrom' not in schema.get('items', {}):
                schema = schema.copy()
                schema['calculatedProperty'] = True
        self.schema = schema

    def __call__(self, namespace):
        if self.condition is not None:
            if not namespace(self.condition):
                return None
        if self.attr:
            fn = getattr(namespace.context, self.attr)
        else:
            fn = self.fn
        return namespace(fn)

# Imperative configuration
def add_calculated_property(config, fn, name, context, condition=None, schema=None,
                            attr=None, define=False, category='object'):
    calculated_properties = config.registry[CALCULATED_PROPERTIES]
    config.action(
        ('calculated_property', context, category, name),
        calculated_properties.register_prop,
        (fn, name, context, condition, schema, attr, define, category),
    )

# Declarative configuration
def calculated_property(**settings):
    """ Register a calculated property
    """

    def decorate(wrapped):
        def callback(scanner, factory_name, factory):
            if settings.get('context') is None:
                settings['context'] = factory
            if settings.get('name') is None:
                settings['name'] = factory_name
            scanner.config.add_calculated_property(wrapped, **settings)

        info = venusian.attach(wrapped, callback, category='object')

        if info.scope == 'class':
            # if the decorator was attached to a method in a class, or
            # otherwise executed at class scope, we need to set an
            # 'attr' into the settings if one isn't already in there
            if settings.get('attr') is None:
                settings['attr'] = wrapped.__name__
            if settings.get('name') is None:
                settings['name'] = wrapped.__name__

        elif settings.get('context') is None:
            raise TypeError('must supply context type for function')

        return wrapped

    return decorate

def calculate_properties(context, request, ns=None, category='object'):
    calculated_properties = request.registry[CALCULATED_PROPERTIES]
    props = calculated_properties.props_for(context, category)
    defined = {name: prop for name, prop in props.items() if prop.define}
    if isinstance(context, type):
        context = None
    namespace = ItemNamespace(context, request, defined, ns)
    return {
        name: value
        for name, value in (
            (name, prop(namespace))
            for name, prop in props.items()
        ) if value is not None
    }
<commit_msg>Investigate action object<commit_after>from __future__ import absolute_import
import venusian
from pyramid.decorator import reify
from pyramid.traversal import find_root
from types import MethodType
from .interfaces import CALCULATED_PROPERTIES

import pprint

def includeme(config):
    config.registry[CALCULATED_PROPERTIES] = CalculatedProperties()
    config.add_directive('add_calculated_property', add_calculated_property)

class ItemNamespace(object):
    def __init__(self, context, request, defined=None, ns=None):
        self.context = context
        self.request = request
        self._defined = defined or {}
        if ns:
            self.__dict__.update(ns)
        self._results = {}

    @reify
    def _properties(self):
        return self.context.__json__(self.request)

    @reify
    def root(self):
        return find_root(self.context)

    @reify
    def registry(self):
        return self.request.registry

    def __getattr__(self, name):
        context = self.context
        request = self.request
        if name in self._defined:
            value = self._defined[name](self)
            setattr(self, name, value)
            return value
        if name in self._properties:
            value = self._properties[name]
            if name in context.type_info.schema_links:
                if isinstance(value, list):
                    value = [
                        request.resource_path(self.root.get_by_uuid(v))
                        for v in value
                    ]
                else:
                    value = request.resource_path(self.root.get_by_uuid(value))
            setattr(self, name, value)
            return value
        if name in context.rev:
            value = context.get_rev_links(name)
            value = [
                request.resource_path(self.root.get_by_uuid(v))
                for v in value
            ]
            setattr(self, name, value)
            return value
        raise AttributeError(name)

    def __call__(self, fn):
        try:
            return self._results[fn]
        except KeyError:
            pass

        if isinstance(fn, str):
            result = self._results[fn] = getattr(self, fn, None)
            return result

        start = 1 if isinstance(fn, MethodType) else 0
        # Not using inspect.getargspec as it is slow
        args = fn.__code__.co_varnames[start:fn.__code__.co_argcount]
        kw = {}
        for name in args:
            try:
                kw[name] = getattr(self, name)
            except AttributeError:
                pass

        result = self._results[fn] = fn(**kw)
        return result

class CalculatedProperties(object):
    def __init__(self):
        self.category_cls_props = {}

    def register_prop(self, fn, name, context, condition=None, schema=None,
                      attr=None, define=False, category='object'):
        prop = CalculatedProperty(fn, name, attr, condition, schema, define)
        cls_props = self.category_cls_props.setdefault(category, {})
        cls_props.setdefault(context, {})[name] = prop

    def props_for(self, context, category='object'):
        if isinstance(context, type):
            cls = context
        else:
            cls = type(context)
        props = {}
        cls_props = self.category_cls_props.get(category, {})
        for base in reversed(cls.mro()):
            props.update(cls_props.get(base, {}))
        return props

class CalculatedProperty(object):
    condition_args = None

    def __init__(self, fn, name, attr=None, condition=None, schema=None, define=False):
        self.fn = fn
        self.attr = attr
        self.name = name
        self.condition = condition
        self.define = define

        if schema is not None:
            if 'default' in schema:
                raise ValueError('schema may not specify default for calculated property')
            if 'linkFrom' not in schema.get('items', {}):
                schema = schema.copy()
                schema['calculatedProperty'] = True
        self.schema = schema

    def __call__(self, namespace):
        if self.condition is not None:
            if not namespace(self.condition):
                return None
        if self.attr:
            fn = getattr(namespace.context, self.attr)
        else:
            fn = self.fn
        return namespace(fn)

# Imperative configuration
def add_calculated_property(config, fn, name, context, condition=None, schema=None,
                            attr=None, define=False, category='object'):
    calculated_properties = config.registry[CALCULATED_PROPERTIES]
    config.action(
        ('calculated_property', context, category, name),
        calculated_properties.register_prop,
        (fn, name, context, condition, schema, attr, define, category),
    )

# Declarative configuration
def calculated_property(**settings):
    """ Register a calculated property
    """

    def decorate(wrapped):
        def callback(scanner, factory_name, factory):
            if settings.get('context') is None:
                settings['context'] = factory
            if settings.get('name') is None:
                settings['name'] = factory_name
            scanner.config.add_calculated_property(wrapped, **settings)

        info = venusian.attach(wrapped, callback, category='object')

        if info.scope == 'class':
            # if the decorator was attached to a method in a class, or
            # otherwise executed at class scope, we need to set an
            # 'attr' into the settings if one isn't already in there
            if settings.get('attr') is None:
                settings['attr'] = wrapped.__name__
            if settings.get('name') is None:
                settings['name'] = wrapped.__name__

        elif settings.get('context') is None:
            raise TypeError('must supply context type for function')

        return wrapped

    return decorate

def calculate_properties(context, request, ns=None, category='object'):
    pp = pprint.PrettyPrinter()
    pp.pprint(context)
    calculated_properties = request.registry[CALCULATED_PROPERTIES]
    props = calculated_properties.props_for(context, category)
    pp.pprint(props)
    defined = {name: prop for name, prop in props.items() if prop.define}
    if isinstance(context, type):
        context = None
    namespace = ItemNamespace(context, request, defined, ns)
    return {
        name: value
        for name, value in (
            (name, prop(namespace))
            for name, prop in props.items()
        ) if value is not None
    }
<|endoftext|>