<commit_before>'''
This module contains all the stuff to make your way from python code to
a dynamic library, see __init__.py for exported interfaces.
'''

from pythran.backend import Cxx, Python
from pythran.config import cfg, make_extension
from pythran.cxxgen import PythonModule, Define, Include, Line, Statement
from pythran.cxxgen import FunctionBody, FunctionDeclaration, Value, Block
from pythran.cxxgen import ReturnStatement
from pythran.middlend import refine
from pythran.passmanager import PassManager
from pythran.tables import pythran_ward
from pythran.types import tog
from pythran.types.types import extract_constructed_types
from pythran.types.type_dependencies import pytype_to_deps
from pythran.types.conversion import pytype_to_ctype
from pythran.spec import load_specfile, Spec
from pythran.spec import spec_to_string
from pythran.syntax import check_specs
from pythran.version import __version__
import pythran.frontend as frontend

from datetime import datetime
from distutils.errors import CompileError
from numpy.distutils.core import setup
from numpy.distutils.extension import Extension
import numpy.distutils.ccompiler

from tempfile import mkstemp, mkdtemp
import gast as ast
import logging
import os.path
import shutil
import sys
import glob
import hashlib
from functools import reduce

logger = logging.getLogger('pythran')

# hook taken from numpy.distutils.compiler
# with useless steps  and warning removed
def CCompiler_customize(self, _, need_cxx=0):
    logger.info('customize %s', self.__class__.__name__)
    numpy.distutils.ccompiler.customize_compiler(self)
    if need_cxx:
        # In general, distutils uses -Wstrict-prototypes, but this option is
        # not valid for C++ code, only for C.  Remove it if it's there to
        # avoid a spurious warning on every compilation.
        try:
            self.compiler_so.remove('-Wstrict-prototypes')
        except (AttributeError, ValueError):
            pass

numpy.distutils.ccompiler.replace_method(numpy.distutils.ccompiler.CCompiler,
                                         'customize', CCompiler_customize)

def _extract_all_constructed_types(v):
    return sorted(set(reduce(lambda x, y: x + y,
                             (extract_constructed_types(t) for t in v), [])),
                  key=len)

def _extract_specs_dependencies(specs):
    """ Extract types dependencies from specs for each exported signature. """
    deps = set()
    # for each function
    for signatures in specs.functions.values():
        # for each signature
        for signature in signatures:
            # for each argument
            for t in signature:
                deps.update(pytype_to_deps(t))
    # and each capsule
    for signature in specs.capsules.values():
        # for each argument
        for t in signature:
            deps.update(pytype_to_deps(t))
    # Keep "include" first
    return sorted(deps, key=lambda x: "include" not in x)

def _parse_optimization(optimization):
    '''Turns an optimization of the form
        my_optim
        my_package.my_optim
        into the associated symbol'''
    splitted = optimization.split('.')
    if len(splitted) == 1:
        splitted = ['pythran', 'optimizations'] + splitted
    return reduce(getattr, splitted[1:], __import__(splitted[0]))

def _get_temp(content, suffix=".cpp"):
    '''Get a temporary file for given content, default extension is .cpp
       It is user's responsability to delete when done.'''
    fd, fdpath = mkstemp(suffix)
    with os.fdopen(fd, "w") as cpp:
        cpp.write(content)
    return fd, fdpath

class HasArgument(ast.NodeVisitor):
    '''Checks if a given function has arguments'''
    def __init__(self, fname):
        self.fname = fname

    def visit_Module(self, node):
        for n in node.body:
            if isinstance(n, ast.FunctionDef) and n.name == self.fname:
                return [arg.id for arg in n.args.args]
        return []

def front_middle_end(module_name, code, specs=None, optimizations=None):
    """Front-end and middle-end compilation steps"""
    pm = PassManager(module_name)

    # front end
    ir, renamings, docstrings = frontend.parse(pm, code)

    # middle-end
    optimizations = (optimizations or
                     cfg.get('pythran', 'optimizations').split())
    optimizations = [_parse_optimization(opt) for opt in optimizations]
    refine(pm, ir, optimizations)

    return ir, renamings, docstrings, pm, optimizations

# PUBLIC INTERFACE STARTS HERE

def generate_py(module_name, code, specs=None, optimizations=None):
    '''python + pythran spec -> py code

    Prints and returns the optimized python code.

    '''

    ir, renamings, docstrings, pm, optimizations = front_middle_end(
        module_name, code, specs, optimizations)

    content = pm.dump(Python, ir)
    print(content)
    return content

def generate_cxx(module_name, code, specs=None, optimizations=None):
    '''python + pythran spec -> c++ code
    returns a PythonModule object and an error checker

    the error checker can be used to print more detailed info on the origin of
    a compile error (e.g. due to bad typing)

    '''

    ir, renamings, docstrings, pm, optimizations = front_middle_end(
        module_name, code, specs, optimizations)

    # back-end
    content = pm.dump(Cxx, ir)

    # instantiate the meta program
    if specs is None:

        class Generable(object):
            def __init__(self, content):
                self.content = content

            def __str__(self):
                return str(self.content)

            generate = __str__

        mod = Generable(content)

        def error_checker():
            tog.typecheck(ir)

    else:

        # uniform typing
        if isinstance(specs, dict):
            specs = Spec(specs, {})

        def error_checker():
            types = tog.typecheck(ir)
            check_specs(ir, specs, renamings, types)

        specs.to_docstrings(docstrings)

        if isinstance(code, bytes):
            code_bytes = code
        else:
            code_bytes = code.encode('ascii', 'ignore')
        metainfo = {'hash': hashlib.sha256(code_bytes).hexdigest(),
                    'version': __version__,
                    'date': datetime.now()}

        mod = PythonModule(module_name, docstrings, metainfo)
        mod.add_to_preamble(Define("BOOST_SIMD_NO_STRICT_ALIASING", "1"))
        mod.add_to_includes(
            Include("pythonic/core.hpp"),
            Include("pythonic/python/core.hpp"),
            # FIXME: only include these when needed
            Include("pythonic/types/bool.hpp"),
            Include("pythonic/types/int.hpp"),
            Line("#ifdef _OPENMP\n#include <omp.h>\n#endif")
        )
        mod.add_to_includes(*[Include(inc) for inc in
                              _extract_specs_dependencies(specs)])
        mod.add_to_includes(*content.body)
        mod.add_to_includes(
            Include("pythonic/python/exception_handler.hpp"),
        )

        def warded(module_name, internal_name):
            return pythran_ward + '{0}::{1}'.format(module_name, internal_name)

        for function_name, signatures in specs.functions.items():
            internal_func_name = renamings.get(function_name,
                                               function_name)
            # global variables are functions with no signatures :-)
            if not signatures:
                mod.add_global_var(function_name,
                                   "{}()()".format(warded(module_name,
                                                          internal_func_name)))

            for sigid, signature in enumerate(signatures):
                numbered_function_name = "{0}{1}".format(internal_func_name,
                                                         sigid)
                arguments_types = [pytype_to_ctype(t) for t in signature]
                arguments_names = HasArgument(internal_func_name).visit(ir)
                arguments = [n for n, _ in
                             zip(arguments_names, arguments_types)]
                name_fmt = pythran_ward + "{0}::{1}::type{2}"
                args_list = ", ".join(arguments_types)
                specialized_fname = name_fmt.format(module_name,
                                                    internal_func_name,
                                                    "<{0}>".format(args_list)
                                                    if arguments_names else "")
                result_type = "typename %s::result_type" % specialized_fname
                mod.add_pyfunction(
                    FunctionBody(
                        FunctionDeclaration(
                            Value(
                                result_type,
                                numbered_function_name),
                            [Value(t + '&&', a)
                             for t, a in zip(arguments_types, arguments)]),
                        Block([Statement("""
                            PyThreadState *_save = PyEval_SaveThread();
                            try {{
                                auto res = {0}()({1});
                                PyEval_RestoreThread(_save);
                                return res;
                            }}
                            catch(...) {{
                                PyEval_RestoreThread(_save);
                                throw;
                            }}
                            """.format(warded(module_name,
                                              internal_func_name),
                                       ', '.join(arguments)))])
                    ),
                    function_name,
                    arguments_types,
                    signature
                )

        for function_name, signature in specs.capsules.items():
            internal_func_name = renamings.get(function_name,
                                               function_name)

            arguments_types = [pytype_to_ctype(t) for t in signature]
            arguments_names = HasArgument(internal_func_name).visit(ir)
            arguments = [n for n, _ in
                         zip(arguments_names, arguments_types)]
            name_fmt = pythran_ward + "{0}::{1}::type{2}"
            args_list = ", ".join(arguments_types)
            specialized_fname = name_fmt.format(module_name,
                                                internal_func_name,
                                                "<{0}>".format(args_list)
                                                if arguments_names else "")
            result_type = "typename %s::result_type" % specialized_fname
            docstring = spec_to_string(function_name, signature)
            mod.add_capsule(
                FunctionBody(
                    FunctionDeclaration(
                        Value(result_type, function_name),
                        [Value(t, a)
                         for t, a in zip(arguments_types, arguments)]),
                    Block([ReturnStatement("{0}()({1})".format(
                        warded(module_name, internal_func_name),
                        ', '.join(arguments)))])
                ),
                function_name,
                docstring
            )

    return mod, error_checker

def compile_cxxfile(module_name, cxxfile, output_binary=None, **kwargs):
    '''c++ file -> native module
    Return the filename of the produced shared library
    Raises CompileError on failure

    '''

    builddir = mkdtemp()
    buildtmp = mkdtemp()

    extension_args = make_extension(**kwargs)

    extension = Extension(module_name,
                          [cxxfile],
                          language="c++",
                          **extension_args)

    try:
        setup(name=module_name,
              ext_modules=[extension],
              # fake CLI call
              script_name='setup.py',
              script_args=['--verbose'
                           if logger.isEnabledFor(logging.INFO)
                           else '--quiet',
                           'build_ext',
                           '--build-lib', builddir,
                           '--build-temp', buildtmp,
                          ]
             )
    except SystemExit as e:
        raise CompileError(str(e))

    [target] = glob.glob(os.path.join(builddir, module_name + "*"))
    if not output_binary:
        output_binary = os.path.join(os.getcwd(),
                                     module_name + os.path.splitext(target)[1])
    shutil.move(target, output_binary)
    shutil.rmtree(builddir)
    shutil.rmtree(buildtmp)

    logger.info("Generated module: " + module_name)
    logger.info("Output: " + output_binary)

    return output_binary

def compile_cxxcode(module_name, cxxcode, output_binary=None, keep_temp=False,
                    **kwargs):
    '''c++ code (string) -> temporary file -> native module.
    Returns the generated .so.

    '''

    # Get a temporary C++ file to compile
    _, fdpath = _get_temp(cxxcode)
    output_binary = compile_cxxfile(module_name, fdpath,
                                    output_binary, **kwargs)
    if not keep_temp:
        # remove tempfile
        os.remove(fdpath)
    else:
        logger.warn("Keeping temporary generated file:" + fdpath)

    return output_binary

def compile_pythrancode(module_name, pythrancode, specs=None,
                        opts=None, cpponly=False, pyonly=False,
                        output_file=None, **kwargs):
    '''Pythran code (string) -> c++ code -> native module
    Returns the generated .so (or .cpp if `cpponly` is set to true).

    '''

    # Autodetect the Pythran spec if not given as parameter
    from pythran.spec import spec_parser
    if specs is None:
        specs = spec_parser(pythrancode)

    if pyonly:
        # Only generate the optimized python code
        return generate_py(module_name, pythrancode, specs, opts)

    # Generate C++, get a PythonModule object
    module, error_checker = generate_cxx(module_name, pythrancode, specs, opts)

    if 'ENABLE_PYTHON_MODULE' in kwargs.get('undef_macros', []):
        module.preamble.insert(0, Line('#undef ENABLE_PYTHON_MODULE'))

    if cpponly:
        # User wants only the C++ code
        _, tmp_file = _get_temp(str(module))
        if not output_file:
            output_file = module_name + ".cpp"
        shutil.move(tmp_file, output_file)
        logger.info("Generated C++ source file: " + output_file)
    else:
        # Compile to binary
        try:
            output_file = compile_cxxcode(module_name,
                                          str(module),
                                          output_binary=output_file,
                                          **kwargs)
        except CompileError as ce:
            logger.warn("Compilation error, trying hard to find its origin...")
            error_checker()
            logger.warn("Nop, I'm going to flood you with C++ errors!")
            raise

    return output_file

def compile_pythranfile(file_path, output_file=None, module_name=None,
                        cpponly=False, pyonly=False, **kwargs):
    """
    Pythran file -> c++ file -> native module.

    Returns the generated .so (or .cpp if `cpponly` is set to true).

    Usage without an existing spec file

    >>> with open('pythran_test.py', 'w') as fd:
    ...    fd.write('def foo(i): return i ** 2')
    >>> cpp_path = compile_pythranfile('pythran_test.py', cpponly=True)

    Usage with an existing spec file:
    >>> with open('pythran_test.pythran', 'w') as fd:
    ...    fd.write('export foo(int)')
    >>> so_path = compile_pythranfile('pythran_test.py')
    """
    if not output_file:
        # derive module name from input file name
        _, basename = os.path.split(file_path)
        module_name = module_name or os.path.splitext(basename)[0]

    else:
        # derive module name from destination output_file name
        _, basename = os.path.split(output_file)
        module_name = module_name or os.path.splitext(basename)[0]

    # Add compiled module path to search for imported modules
    sys.path.append(os.path.dirname(file_path))

    # Look for an extra spec file
    spec_file = os.path.splitext(file_path)[0] + '.pythran'
    if os.path.isfile(spec_file):
        specs = load_specfile(open(spec_file).read())
        kwargs.setdefault('specs', specs)

    output_file = compile_pythrancode(module_name, open(file_path).read(),
                                      output_file=output_file,
                                      cpponly=cpponly, pyonly=pyonly,
                                      **kwargs)

    return output_file

def test_compile():
    '''Simple passthrough compile test.
    May raises CompileError Exception.

    '''
    code = '''
        #define BOOST_PYTHON_MAX_ARITY 4
        #include <pythonic/core.hpp>
    '''

    output_file = compile_cxxcode('test', code)
    output_file and os.remove(output_file)
<commit_msg>Cleanup toolchain processing<commit_after>'''
This module contains all the stuff to make your way from python code to
a dynamic library, see __init__.py for exported interfaces.
'''

from pythran.backend import Cxx, Python
from pythran.config import cfg, make_extension
from pythran.cxxgen import PythonModule, Define, Include, Line, Statement
from pythran.cxxgen import FunctionBody, FunctionDeclaration, Value, Block
from pythran.cxxgen import ReturnStatement
from pythran.middlend import refine
from pythran.passmanager import PassManager
from pythran.tables import pythran_ward
from pythran.types import tog
from pythran.types.types import extract_constructed_types
from pythran.types.type_dependencies import pytype_to_deps
from pythran.types.conversion import pytype_to_ctype
from pythran.spec import load_specfile, Spec
from pythran.spec import spec_to_string
from pythran.syntax import check_specs
from pythran.version import __version__
import pythran.frontend as frontend

from datetime import datetime
from distutils.errors import CompileError
from numpy.distutils.core import setup
from numpy.distutils.extension import Extension
import numpy.distutils.ccompiler

from tempfile import mkdtemp, NamedTemporaryFile
import gast as ast
import logging
import os.path
import shutil
import sys
import glob
import hashlib
from functools import reduce

logger = logging.getLogger('pythran')

# hook taken from numpy.distutils.compiler
# with useless steps  and warning removed
def CCompiler_customize(self, _, need_cxx=0):
    logger.info('customize %s', self.__class__.__name__)
    numpy.distutils.ccompiler.customize_compiler(self)
    if need_cxx:
        # In general, distutils uses -Wstrict-prototypes, but this option is
        # not valid for C++ code, only for C.  Remove it if it's there to
        # avoid a spurious warning on every compilation.
        try:
            self.compiler_so.remove('-Wstrict-prototypes')
        except (AttributeError, ValueError):
            pass

numpy.distutils.ccompiler.replace_method(numpy.distutils.ccompiler.CCompiler,
                                         'customize', CCompiler_customize)

def _extract_all_constructed_types(v):
    return sorted(set(reduce(lambda x, y: x + y,
                             (extract_constructed_types(t) for t in v), [])),
                  key=len)

def _extract_specs_dependencies(specs):
    """ Extract types dependencies from specs for each exported signature. """
    deps = set()
    # for each function
    for signatures in specs.functions.values():
        # for each signature
        for signature in signatures:
            # for each argument
            for t in signature:
                deps.update(pytype_to_deps(t))
    # and each capsule
    for signature in specs.capsules.values():
        # for each argument
        for t in signature:
            deps.update(pytype_to_deps(t))
    # Keep "include" first
    return sorted(deps, key=lambda x: "include" not in x)

def _parse_optimization(optimization):
    '''Turns an optimization of the form
        my_optim
        my_package.my_optim
        into the associated symbol'''
    splitted = optimization.split('.')
    if len(splitted) == 1:
        splitted = ['pythran', 'optimizations'] + splitted
    return reduce(getattr, splitted[1:], __import__(splitted[0]))

def _write_temp(content, suffix):
    '''write `content` to a temporary XXX`suffix` file and return the filename.
       It is user's responsibility to delete when done.'''
    with NamedTemporaryFile(mode='w', suffix=suffix, delete=False) as out:
        out.write(content)
        return out.name

class HasArgument(ast.NodeVisitor):
    '''Checks if a given function has arguments'''
    def __init__(self, fname):
        self.fname = fname

    def visit_Module(self, node):
        for n in node.body:
            if isinstance(n, ast.FunctionDef) and n.name == self.fname:
                return [arg.id for arg in n.args.args]
        return []

def front_middle_end(module_name, code, optimizations=None):
    """Front-end and middle-end compilation steps"""
    pm = PassManager(module_name)

    # front end
    ir, renamings, docstrings = frontend.parse(pm, code)

    # middle-end
    if optimizations is None:
        optimizations = cfg.get('pythran', 'optimizations').split()
    optimizations = [_parse_optimization(opt) for opt in optimizations]
    refine(pm, ir, optimizations)

    return pm, ir, renamings, docstrings

# PUBLIC INTERFACE STARTS HERE

def generate_py(module_name, code, optimizations=None):
    '''python + pythran spec -> py code

    Prints and returns the optimized python code.

    '''

    pm, ir, _, _ = front_middle_end(module_name, code, optimizations)

    return pm.dump(Python, ir)

def generate_cxx(module_name, code, specs=None, optimizations=None):
    '''python + pythran spec -> c++ code
    returns a PythonModule object and an error checker

    the error checker can be used to print more detailed info on the origin of
    a compile error (e.g. due to bad typing)

    '''

    pm, ir, renamings, docstrings = front_middle_end(module_name, code,
                                                     optimizations)

    # back-end
    content = pm.dump(Cxx, ir)

    # instantiate the meta program
    if specs is None:

        class Generable(object):
            def __init__(self, content):
                self.content = content

            def __str__(self):
                return str(self.content)

            generate = __str__

        mod = Generable(content)

        def error_checker():
            tog.typecheck(ir)

    else:

        # uniform typing
        if isinstance(specs, dict):
            specs = Spec(specs, {})

        def error_checker():
            types = tog.typecheck(ir)
            check_specs(ir, specs, renamings, types)

        specs.to_docstrings(docstrings)

        if isinstance(code, bytes):
            code_bytes = code
        else:
            code_bytes = code.encode('ascii', 'ignore')
        metainfo = {'hash': hashlib.sha256(code_bytes).hexdigest(),
                    'version': __version__,
                    'date': datetime.now()}

        mod = PythonModule(module_name, docstrings, metainfo)
        mod.add_to_preamble(Define("BOOST_SIMD_NO_STRICT_ALIASING", "1"))
        mod.add_to_includes(
            Include("pythonic/core.hpp"),
            Include("pythonic/python/core.hpp"),
            # FIXME: only include these when needed
            Include("pythonic/types/bool.hpp"),
            Include("pythonic/types/int.hpp"),
            Line("#ifdef _OPENMP\n#include <omp.h>\n#endif")
        )
        mod.add_to_includes(*[Include(inc) for inc in
                              _extract_specs_dependencies(specs)])
        mod.add_to_includes(*content.body)
        mod.add_to_includes(
            Include("pythonic/python/exception_handler.hpp"),
        )

        def warded(module_name, internal_name):
            return pythran_ward + '{0}::{1}'.format(module_name, internal_name)

        for function_name, signatures in specs.functions.items():
            internal_func_name = renamings.get(function_name,
                                               function_name)
            # global variables are functions with no signatures :-)
            if not signatures:
                mod.add_global_var(function_name,
                                   "{}()()".format(warded(module_name,
                                                          internal_func_name)))

            for sigid, signature in enumerate(signatures):
                numbered_function_name = "{0}{1}".format(internal_func_name,
                                                         sigid)
                arguments_types = [pytype_to_ctype(t) for t in signature]
                arguments_names = HasArgument(internal_func_name).visit(ir)
                arguments = [n for n, _ in
                             zip(arguments_names, arguments_types)]
                name_fmt = pythran_ward + "{0}::{1}::type{2}"
                args_list = ", ".join(arguments_types)
                specialized_fname = name_fmt.format(module_name,
                                                    internal_func_name,
                                                    "<{0}>".format(args_list)
                                                    if arguments_names else "")
                result_type = "typename %s::result_type" % specialized_fname
                mod.add_pyfunction(
                    FunctionBody(
                        FunctionDeclaration(
                            Value(
                                result_type,
                                numbered_function_name),
                            [Value(t + '&&', a)
                             for t, a in zip(arguments_types, arguments)]),
                        Block([Statement("""
                            PyThreadState *_save = PyEval_SaveThread();
                            try {{
                                auto res = {0}()({1});
                                PyEval_RestoreThread(_save);
                                return res;
                            }}
                            catch(...) {{
                                PyEval_RestoreThread(_save);
                                throw;
                            }}
                            """.format(warded(module_name,
                                              internal_func_name),
                                       ', '.join(arguments)))])
                    ),
                    function_name,
                    arguments_types,
                    signature
                )

        for function_name, signature in specs.capsules.items():
            internal_func_name = renamings.get(function_name,
                                               function_name)

            arguments_types = [pytype_to_ctype(t) for t in signature]
            arguments_names = HasArgument(internal_func_name).visit(ir)
            arguments = [n for n, _ in
                         zip(arguments_names, arguments_types)]
            name_fmt = pythran_ward + "{0}::{1}::type{2}"
            args_list = ", ".join(arguments_types)
            specialized_fname = name_fmt.format(module_name,
                                                internal_func_name,
                                                "<{0}>".format(args_list)
                                                if arguments_names else "")
            result_type = "typename %s::result_type" % specialized_fname
            docstring = spec_to_string(function_name, signature)
            mod.add_capsule(
                FunctionBody(
                    FunctionDeclaration(
                        Value(result_type, function_name),
                        [Value(t, a)
                         for t, a in zip(arguments_types, arguments)]),
                    Block([ReturnStatement("{0}()({1})".format(
                        warded(module_name, internal_func_name),
                        ', '.join(arguments)))])
                ),
                function_name,
                docstring
            )

    return mod, error_checker

def compile_cxxfile(module_name, cxxfile, output_binary=None, **kwargs):
    '''c++ file -> native module
    Return the filename of the produced shared library
    Raises CompileError on failure

    '''

    builddir = mkdtemp()
    buildtmp = mkdtemp()

    extension_args = make_extension(**kwargs)

    extension = Extension(module_name,
                          [cxxfile],
                          language="c++",
                          **extension_args)

    try:
        setup(name=module_name,
              ext_modules=[extension],
              # fake CLI call
              script_name='setup.py',
              script_args=['--verbose'
                           if logger.isEnabledFor(logging.INFO)
                           else '--quiet',
                           'build_ext',
                           '--build-lib', builddir,
                           '--build-temp', buildtmp]
              )
    except SystemExit as e:
        raise CompileError(str(e))

    target, = glob.glob(os.path.join(builddir, module_name + "*"))
    if not output_binary:
        output_binary = os.path.join(os.getcwd(),
                                     module_name + os.path.splitext(target)[1])
    shutil.move(target, output_binary)
    shutil.rmtree(builddir)
    shutil.rmtree(buildtmp)

    logger.info("Generated module: " + module_name)
    logger.info("Output: " + output_binary)

    return output_binary

def compile_cxxcode(module_name, cxxcode, output_binary=None, keep_temp=False,
                    **kwargs):
    '''c++ code (string) -> temporary file -> native module.
    Returns the generated .so.

    '''

    # Get a temporary C++ file to compile
    fdpath = _write_temp(cxxcode, '.cpp')
    output_binary = compile_cxxfile(module_name, fdpath,
                                    output_binary, **kwargs)
    if not keep_temp:
        # remove tempfile
        os.remove(fdpath)
    else:
        logger.warn("Keeping temporary generated file:" + fdpath)

    return output_binary

def compile_pythrancode(module_name, pythrancode, specs=None,
                        opts=None, cpponly=False, pyonly=False,
                        output_file=None, **kwargs):
    '''Pythran code (string) -> c++ code -> native module

    if `cpponly` is set to true, return the generated C++ filename
    if `pyonly` is set to true, prints the generated Python filename,
       unless `output_file` is set
    otherwise, return the generated native library filename
    '''

    if pyonly:
        # Only generate the optimized python code
        content = generate_py(module_name, pythrancode, opts)
        if output_file is None:
            print(content)
            return None
        else:
            return _write_temp(content, '.py')

    # Autodetect the Pythran spec if not given as parameter
    from pythran.spec import spec_parser
    if specs is None:
        specs = spec_parser(pythrancode)

    # Generate C++, get a PythonModule object
    module, error_checker = generate_cxx(module_name, pythrancode, specs, opts)

    if 'ENABLE_PYTHON_MODULE' in kwargs.get('undef_macros', []):
        module.preamble.insert(0, Line('#undef ENABLE_PYTHON_MODULE'))

    if cpponly:
        # User wants only the C++ code
        tmp_file = _write_temp(str(module), '.cpp')
        if not output_file:
            output_file = module_name + ".cpp"
        shutil.move(tmp_file, output_file)
        logger.info("Generated C++ source file: " + output_file)
    else:
        # Compile to binary
        try:
            output_file = compile_cxxcode(module_name,
                                          str(module),
                                          output_binary=output_file,
                                          **kwargs)
        except CompileError as ce:
            logger.warn("Compilation error, trying hard to find its origin...")
            error_checker()
            logger.warn("Nop, I'm going to flood you with C++ errors!")
            raise

    return output_file

def compile_pythranfile(file_path, output_file=None, module_name=None,
                        cpponly=False, pyonly=False, **kwargs):
    """
    Pythran file -> c++ file -> native module.

    Returns the generated .so (or .cpp if `cpponly` is set to true).

    Usage without an existing spec file

    >>> with open('pythran_test.py', 'w') as fd:
    ...    fd.write('def foo(i): return i ** 2')
    >>> cpp_path = compile_pythranfile('pythran_test.py', cpponly=True)

    Usage with an existing spec file:
    >>> with open('pythran_test.pythran', 'w') as fd:
    ...    fd.write('export foo(int)')
    >>> so_path = compile_pythranfile('pythran_test.py')
    """
    if not output_file:
        # derive module name from input file name
        _, basename = os.path.split(file_path)
        module_name = module_name or os.path.splitext(basename)[0]

    else:
        # derive module name from destination output_file name
        _, basename = os.path.split(output_file)
        module_name = module_name or os.path.splitext(basename)[0]

    # Add compiled module path to search for imported modules
    sys.path.append(os.path.dirname(file_path))

    # Look for an extra spec file
    spec_file = os.path.splitext(file_path)[0] + '.pythran'
    if os.path.isfile(spec_file):
        specs = load_specfile(open(spec_file).read())
        kwargs.setdefault('specs', specs)

    output_file = compile_pythrancode(module_name, open(file_path).read(),
                                      output_file=output_file,
                                      cpponly=cpponly, pyonly=pyonly,
                                      **kwargs)

    return output_file

def test_compile():
    '''Simple passthrough compile test.
    May raises CompileError Exception.

    '''
    code = '''
        #define BOOST_PYTHON_MAX_ARITY 4
        #include <pythonic/core.hpp>
    '''

    output_file = compile_cxxcode('test', code)
    output_file and os.remove(output_file)
<|endoftext|>


<commit_before># -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals

class DefaultSettings(object):
    """
    Default settings for Shopit.
    """
    def _setting(self, name, default=None):
        from django.conf import settings
        return getattr(settings, name, default)

    @property
    def SHOPIT_ERROR_MESSAGES(self):
        """
        A dictionary with error messages used in Shopit.
        """
        from django.utils.translation import ugettext_lazy as _

        default = {
            'duplicate_slug': _("This slug is already used. Try another one."),
            'group_has_group': _("Only variant products have a group."),
            'variant_no_group': _("Variants must have a group selected."),
            'variant_has_category': _("Variant products can't specify categorization. It's inherited from their group."),  # noqa
            'varinat_group_variant': _("Can't set group to variant."),
            'not_group_has_variants': _("This product has variants, you need to delete them before changing it's kind."),  # noqa
            'not_group_has_available_attributes': _("Only group products can have Availible attributes specified."),
            'group_no_available_attributes': _("Group product should have Availible attributes specified."),
            'variant_has_tax': _("Variant products can't specify tax, their group tax percentage will be used instead."),  # noqa
            'variant_no_attributes': _("Variant must have their unique set of attributes specified."),
            'variant_already_exists': _("A Variant with this attributes for selected group already exists."),
            'not_variant_has_attributes': _("Only Variant products can have attributes."),
            'attribute_no_choices': _("Choices must be specified."),
            'attribute_duplicate_choices': _("Attribute can't have duplicate choices."),
            'incorrect_attribute_choice': _("Selected choice doesn't match the seleced attribute."),
            'no_attachment_or_url': _("Missing the attachment or url."),
            'wrong_extension': _("File extension not allowed for this attachment kind."),
            'discount_not_negative': _('A discount should be subtracting the price, amount or percent needs to be negative.'),  # noqa
            'variant_has_relations': _('Only Single and Group products can have relations.'),
            'relation_base_is_product': _("You can't set relation to self."),
            'modifier_no_condition_path': _("You have to select a condition."),
            'cart_discount_code_exists': _("Code is already applied to your cart."),
            'cart_discount_code_invalid': _("Code is invalid or expired."),
            'cart_discount_code_wrong_customer': _("Code is invalid or expired."),
        }
        return default.update(self._setting('SHOPIT_ERROR_MESSAGES', {}))

    @property
    def SHOPIT_ADDRESS_COUNTRIES(self):
        """
        Country choices used in checkout address forms. If empty all countries
        are used from ``shop.models.address.ISO_3166_CODES``.
        """
        return self._setting('SHOPIT_ADDRESS_COUNTRIES', ())

    @property
    def SHOPIT_PRIMARY_ADDRESS(self):
        """
        A primary address to be used in a checkout proccess. Can be 'shipping'
        or 'billing'. Depending on wich address is selected, the other one will
        get the option to use the primary one instead of having to fill it out.
        """
        from django.core.exceptions import ImproperlyConfigured

        value = self._setting('SHOPIT_PRIMARY_ADDRESS', 'shipping')
        if value not in ['shipping', 'billing']:
            raise ImproperlyConfigured("Setting `SHOPIT_PRIMARY_ADDRESS` must be either 'shipping' or 'billing'.")
        return value

    @property
    def SHOPIT_PHONE_NUMBER_REQUIRED(self):
        """
        A flag to control if customer's phone number is required.
        """
        return self._setting('SHOPIT_PHONE_NUMBER_REQUIRED', False)

    @property
    def SHOPIT_PRODUCT_SERIALIZER_FIELDS(self):
        """
        A list of base serializer fields for a common product.
        """
        default = ['id', 'name', 'slug', 'caption', 'code', 'kind', 'url', 'add_to_cart_url', 'price', 'is_available']
        return self._setting('SHOPIT_PRODUCT_SERIALIZER_FIELDS', default)

    @property
    def SHOPIT_PRODUCT_DETAIL_SERIALIZER_FIELDS(self):
        """
        A list of serializer fields for a product detail.
        """
        default = list(set(self.PRODUCT_SERIALIZER_FIELDS + ['variants', 'attributes']))
        return self._setting('SHOPIT_PRODUCT_DETAIL_SERIALIZER_FIELDS', default)

    @property
    def SHOPIT_ATTRIBUTE_TEMPLATES(self):
        """
        Template choices used when rendering an attribute.
        """
        return self._setting('SHOPIT_ATTRIBUTE_TEMPLATES', ())

    @property
    def SHOPIT_RELATION_KINDS(self):
        """
        Relation kind choices on a ``ProductRelation`` model.
        """
        from django.utils.translation import ugettext_lazy as _

        default = (
            ('up-sell', _('Up-sell')),
            ('cross-sell', _('Cross-sell')),
        )
        return self._setting('SHOPIT_RELATION_KINDS', default)

    @property
    def SHOPIT_REVIEW_RATINGS(self):
        """
        Rating choices for product reviews.
        """
        return self._setting('SHOPIT_REVIEW_RATINGS', ())

    @property
    def SHOPIT_REVIEW_ACTIVE_DEFAULT(self):
        """
        Is review active by default when created.
        """
        return self._setting('SHOPIT_REVIEW_ACTIVE_DEFAULT', True)

    @property
    def SHOPIT_ASYNC_PRODUCT_LIST(self):
        """
        A boolean that enables you to optimize ``ProductListView`` and
        ``CategoryDetailView`` when products are fetched asynchronously (ajax).
        """
        return self._setting('SHOPIT_ASYNC_PRODUCT_LIST', False)

    @property
    def SHOPIT_ADD_PRODUCT_LIST_TO_CONTEXT(self):
        """
        A boolean to control if ``product_list`` is added to context when
        accessing a ``ProductListView`` or ``CategoryDetailView``.
        """
        return self._setting('SHOPIT_ADD_PRODUCT_LIST_TO_CONTEXT', not self.SHOPIT_ASYNC_PRODUCT_LIST)

    @property
    def SHOPIT_DEFAULT_PRODUCT_ORDER(self):
        """
        A default product list ordering.
        Must be on of 'name|-name|price|-price'.
        """
        from django.core.exceptions import ImproperlyConfigured

        value = self._setting('SHOPIT_DEFAULT_PRODUCT_ORDER', None)
        if value not in [None, 'name', '-name', 'price', '-price']:
            raise ImproperlyConfigured("Setting `SHOPIT_DEFAULT_PRODUCT_ORDER` must be 'name|-name|price|-price'.")
        return value

    @property
    def SHOPIT_FLAG_TEMPLATES(self):
        """
        Template choices used when rendering a Flag.
        """
        return self._setting('SHOPIT_FLAG_TEMPLATES', ())

    @property
    def SHOPIT_MODIFIER_CONDITIONS(self):
        """
        A list of ``ModifierCondition`` classes that will be used as choices
        for conditions in a Modifier.
        """
        default = [
            'shopit.modifier_conditions.PriceGreaterThanCondition',
            'shopit.modifier_conditions.PriceLessThanCondition',
            'shopit.modifier_conditions.QuantityGreaterThanCondition',
            'shopit.modifier_conditions.QuantityLessThanCondition',
        ]
        return self._setting('SHOPIT_MODIFIER_CONDITIONS', default)

    @property
    def SHOPIT_TEXT_EDITOR(self):
        """
        A text editor widget used to render a rich textarea in Shopit.
        """
        return self._setting('SHOPIT_TEXT_EDITOR', 'djangocms_text_ckeditor.widgets.TextEditorWidget')

    @property
    def SHOPIT_SINGLE_APPHOOK(self):
        """
        Load urls under a single ``ShopitApphook``, or leave the ability to
        add apps separately.
        """
        return self._setting('SHOPIT_SINGLE_APPHOOK', False)

    @property
    def SHOPIT_FILTER_ATTRIBUTES_INCLUDES_VARIANTS(self):
        """
        Designates if products of kind ``VARIANT`` should be included in
        attribute filtered results.
        """
        return self._setting('SHOPIT_FILTER_ATTRIBUTES_INCLUDES_VARIANTS', False)

    def __getattr__(self, key):
        if not key.startswith('SHOPIT_'):
            key = 'SHOPIT_{0}'.format(key)
        return getattr(self, key)

app_settings = DefaultSettings()
<commit_msg>fix messages<commit_after># -*- coding: utf-8 -*-
from __future__ import absolute_import, unicode_literals

class DefaultSettings(object):
    """
    Default settings for Shopit.
    """
    def _setting(self, name, default=None):
        from django.conf import settings
        return getattr(settings, name, default)

    @property
    def SHOPIT_ERROR_MESSAGES(self):
        """
        A dictionary with error messages used in Shopit.
        """
        from django.utils.translation import ugettext_lazy as _

        default = {
            'duplicate_slug': _("This slug is already used. Try another one."),
            'group_has_group': _("Only variant products have a group."),
            'variant_no_group': _("Variants must have a group selected."),
            'variant_has_category': _("Variant products can't specify categorization. It's inherited from their group."),  # noqa
            'varinat_group_variant': _("Can't set group to variant."),
            'not_group_has_variants': _("This product has variants, you need to delete them before changing it's kind."),  # noqa
            'not_group_has_available_attributes': _("Only group products can have Availible attributes specified."),
            'group_no_available_attributes': _("Group product should have Availible attributes specified."),
            'variant_has_tax': _("Variant products can't specify tax, their group tax percentage will be used instead."),  # noqa
            'variant_no_attributes': _("Variant must have their unique set of attributes specified."),
            'variant_already_exists': _("A Variant with this attributes for selected group already exists."),
            'not_variant_has_attributes': _("Only Variant products can have attributes."),
            'attribute_no_choices': _("Choices must be specified."),
            'attribute_duplicate_choices': _("Attribute can't have duplicate choices."),
            'incorrect_attribute_choice': _("Selected choice doesn't match the seleced attribute."),
            'no_attachment_or_url': _("Missing the attachment or url."),
            'wrong_extension': _("File extension not allowed for this attachment kind."),
            'discount_not_negative': _('A discount should be subtracting the price, amount or percent needs to be negative.'),  # noqa
            'variant_has_relations': _('Only Single and Group products can have relations.'),
            'relation_base_is_product': _("You can't set relation to self."),
            'modifier_no_condition_path': _("You have to select a condition."),
            'cart_discount_code_exists': _("Code is already applied to your cart."),
            'cart_discount_code_invalid': _("Code is invalid or expired."),
            'cart_discount_code_wrong_customer': _("Code is invalid or expired."),
        }
        default.update(self._setting('SHOPIT_ERROR_MESSAGES', {}))
        return default

    @property
    def SHOPIT_ADDRESS_COUNTRIES(self):
        """
        Country choices used in checkout address forms. If empty all countries
        are used from ``shop.models.address.ISO_3166_CODES``.
        """
        return self._setting('SHOPIT_ADDRESS_COUNTRIES', ())

    @property
    def SHOPIT_PRIMARY_ADDRESS(self):
        """
        A primary address to be used in a checkout proccess. Can be 'shipping'
        or 'billing'. Depending on wich address is selected, the other one will
        get the option to use the primary one instead of having to fill it out.
        """
        from django.core.exceptions import ImproperlyConfigured

        value = self._setting('SHOPIT_PRIMARY_ADDRESS', 'shipping')
        if value not in ['shipping', 'billing']:
            raise ImproperlyConfigured("Setting `SHOPIT_PRIMARY_ADDRESS` must be either 'shipping' or 'billing'.")
        return value

    @property
    def SHOPIT_PHONE_NUMBER_REQUIRED(self):
        """
        A flag to control if customer's phone number is required.
        """
        return self._setting('SHOPIT_PHONE_NUMBER_REQUIRED', False)

    @property
    def SHOPIT_PRODUCT_SERIALIZER_FIELDS(self):
        """
        A list of base serializer fields for a common product.
        """
        default = ['id', 'name', 'slug', 'caption', 'code', 'kind', 'url', 'add_to_cart_url', 'price', 'is_available']
        return self._setting('SHOPIT_PRODUCT_SERIALIZER_FIELDS', default)

    @property
    def SHOPIT_PRODUCT_DETAIL_SERIALIZER_FIELDS(self):
        """
        A list of serializer fields for a product detail.
        """
        default = list(set(self.PRODUCT_SERIALIZER_FIELDS + ['variants', 'attributes']))
        return self._setting('SHOPIT_PRODUCT_DETAIL_SERIALIZER_FIELDS', default)

    @property
    def SHOPIT_ATTRIBUTE_TEMPLATES(self):
        """
        Template choices used when rendering an attribute.
        """
        return self._setting('SHOPIT_ATTRIBUTE_TEMPLATES', ())

    @property
    def SHOPIT_RELATION_KINDS(self):
        """
        Relation kind choices on a ``ProductRelation`` model.
        """
        from django.utils.translation import ugettext_lazy as _

        default = (
            ('up-sell', _('Up-sell')),
            ('cross-sell', _('Cross-sell')),
        )
        return self._setting('SHOPIT_RELATION_KINDS', default)

    @property
    def SHOPIT_REVIEW_RATINGS(self):
        """
        Rating choices for product reviews.
        """
        return self._setting('SHOPIT_REVIEW_RATINGS', ())

    @property
    def SHOPIT_REVIEW_ACTIVE_DEFAULT(self):
        """
        Is review active by default when created.
        """
        return self._setting('SHOPIT_REVIEW_ACTIVE_DEFAULT', True)

    @property
    def SHOPIT_ASYNC_PRODUCT_LIST(self):
        """
        A boolean that enables you to optimize ``ProductListView`` and
        ``CategoryDetailView`` when products are fetched asynchronously (ajax).
        """
        return self._setting('SHOPIT_ASYNC_PRODUCT_LIST', False)

    @property
    def SHOPIT_ADD_PRODUCT_LIST_TO_CONTEXT(self):
        """
        A boolean to control if ``product_list`` is added to context when
        accessing a ``ProductListView`` or ``CategoryDetailView``.
        """
        return self._setting('SHOPIT_ADD_PRODUCT_LIST_TO_CONTEXT', not self.SHOPIT_ASYNC_PRODUCT_LIST)

    @property
    def SHOPIT_DEFAULT_PRODUCT_ORDER(self):
        """
        A default product list ordering.
        Must be on of 'name|-name|price|-price'.
        """
        from django.core.exceptions import ImproperlyConfigured

        value = self._setting('SHOPIT_DEFAULT_PRODUCT_ORDER', None)
        if value not in [None, 'name', '-name', 'price', '-price']:
            raise ImproperlyConfigured("Setting `SHOPIT_DEFAULT_PRODUCT_ORDER` must be 'name|-name|price|-price'.")
        return value

    @property
    def SHOPIT_FLAG_TEMPLATES(self):
        """
        Template choices used when rendering a Flag.
        """
        return self._setting('SHOPIT_FLAG_TEMPLATES', ())

    @property
    def SHOPIT_MODIFIER_CONDITIONS(self):
        """
        A list of ``ModifierCondition`` classes that will be used as choices
        for conditions in a Modifier.
        """
        default = [
            'shopit.modifier_conditions.PriceGreaterThanCondition',
            'shopit.modifier_conditions.PriceLessThanCondition',
            'shopit.modifier_conditions.QuantityGreaterThanCondition',
            'shopit.modifier_conditions.QuantityLessThanCondition',
        ]
        return self._setting('SHOPIT_MODIFIER_CONDITIONS', default)

    @property
    def SHOPIT_TEXT_EDITOR(self):
        """
        A text editor widget used to render a rich textarea in Shopit.
        """
        return self._setting('SHOPIT_TEXT_EDITOR', 'djangocms_text_ckeditor.widgets.TextEditorWidget')

    @property
    def SHOPIT_SINGLE_APPHOOK(self):
        """
        Load urls under a single ``ShopitApphook``, or leave the ability to
        add apps separately.
        """
        return self._setting('SHOPIT_SINGLE_APPHOOK', False)

    @property
    def SHOPIT_FILTER_ATTRIBUTES_INCLUDES_VARIANTS(self):
        """
        Designates if products of kind ``VARIANT`` should be included in
        attribute filtered results.
        """
        return self._setting('SHOPIT_FILTER_ATTRIBUTES_INCLUDES_VARIANTS', False)

    def __getattr__(self, key):
        if not key.startswith('SHOPIT_'):
            key = 'SHOPIT_{0}'.format(key)
        return getattr(self, key)

app_settings = DefaultSettings()
<|endoftext|>


<commit_before># -*- coding:utf-8 -*-

from dateutil.relativedelta import relativedelta
from datetime import datetime
from calendar import timegm

def is_whole_hour(utc_time):
    """
        input:
            utc_time -> (utc) timestamp or datetime.
    """
    if isinstance(utc_time, datetime):
        if (utc_time.microsecond == 0) and \
           (utc_time.second == 0) and \
           (utc_time.minute == 0):
            return True
        else:
            return False
    else:
        return float(utc_time) % 3600 == 0

def is_whole_day(utc_time):
    """
        input:
            utc_time -> (utc) timestamp or datetime.
    """
    if not isinstance(utc_time, datetime):
        utc_time = datetime.utcfromtimestamp(float(utc_time))

    if not is_whole_hour(utc_time):
        return False

    return utc_time.hour == 0

def is_whole_week(utc_time):
    """
        input:
            utc_time -> (utc) timestamp or datetime.
    """
    if not isinstance(utc_time, datetime):
        utc_time = datetime.utcfromtimestamp(float(utc_time))

    if not is_whole_day(utc_time):
        return False

    return utc_time.weekday() == 0    # monday

def is_whole_month(utc_time):
    """
        input:
            utc_time -> (utc) timestamp or datetime.
    """
    if not isinstance(utc_time, datetime):
        utc_time = datetime.utcfromtimestamp(float(utc_time))

    if not is_whole_day(utc_time):
        return False

    return utc_time.day == 1

def is_whole_year(utc_time):
    """
        input:
            utc_time -> (utc) timestamp or datetime.
    """
    if not isinstance(utc_time, datetime):
        utc_time = datetime.utcfromtimestamp(float(utc_time))

    if not is_whole_month(utc_time):
        return False

    return utc_time.month == 1

def datetime_2_utctimestamp(dt):
    """
        datetime to utc timestamp
    """
    return timegm(dt.utctimetuple())

def timestamp_natural_sub(timestamp, **fields):
    """
        timetamp subtraction (natural calendar)
    """
    delta = {}
    if fields.has_key('years'):
        delta['years'] = fields['years']
    if fields.has_key('months'):
        delta['months'] = fields['months']
    if fields.has_key('days'):
        delta['days'] = fields['days']
    if fields.has_key('hours'):
        delta['hours'] = fields['hours']
    if fields.has_key('minutes'):
        delta['minutes'] = fields['minutes']
    if fields.has_key('seconds'):
        delta['seconds'] = fields['seconds']

    dt = datetime.utcfromtimestamp(timestamp)
    return datetime_2_utctimestamp(dt - relativedelta(**delta))

def timestamp_natural_add(timestamp, **fields):
    """
        timestamp addition (natural calendar)
    """
    delta = {}
    if fields.has_key('years'):
        delta['years'] = fields['years']
    if fields.has_key('months'):
        delta['months'] = fields['months']
    if fields.has_key('days'):
        delta['days'] = fields['days']
    if fields.has_key('hours'):
        delta['hours'] = fields['hours']
    if fields.has_key('minutes'):
        delta['minutes'] = fields['minutes']
    if fields.has_key('seconds'):
        delta['seconds'] = fields['seconds']

    dt = datetime.utcfromtimestamp(timestamp)
    return datetime_2_utctimestamp(dt + relativedelta(**delta))

<commit_msg>natural add,sub add microseconds<commit_after># -*- coding:utf-8 -*-

from dateutil.relativedelta import relativedelta
from datetime import datetime
from calendar import timegm

def is_whole_hour(utc_time):
    """
        input:
            utc_time -> (utc) timestamp or datetime.
    """
    if isinstance(utc_time, datetime):
        if (utc_time.microsecond == 0) and \
           (utc_time.second == 0) and \
           (utc_time.minute == 0):
            return True
        else:
            return False
    else:
        return float(utc_time) % 3600 == 0

def is_whole_day(utc_time):
    """
        input:
            utc_time -> (utc) timestamp or datetime.
    """
    if not isinstance(utc_time, datetime):
        utc_time = datetime.utcfromtimestamp(float(utc_time))

    if not is_whole_hour(utc_time):
        return False

    return utc_time.hour == 0

def is_whole_week(utc_time):
    """
        input:
            utc_time -> (utc) timestamp or datetime.
    """
    if not isinstance(utc_time, datetime):
        utc_time = datetime.utcfromtimestamp(float(utc_time))

    if not is_whole_day(utc_time):
        return False

    return utc_time.weekday() == 0    # monday

def is_whole_month(utc_time):
    """
        input:
            utc_time -> (utc) timestamp or datetime.
    """
    if not isinstance(utc_time, datetime):
        utc_time = datetime.utcfromtimestamp(float(utc_time))

    if not is_whole_day(utc_time):
        return False

    return utc_time.day == 1

def is_whole_year(utc_time):
    """
        input:
            utc_time -> (utc) timestamp or datetime.
    """
    if not isinstance(utc_time, datetime):
        utc_time = datetime.utcfromtimestamp(float(utc_time))

    if not is_whole_month(utc_time):
        return False

    return utc_time.month == 1

def datetime_2_utctimestamp(dt):
    """
        datetime to utc timestamp
    """
    return timegm(dt.utctimetuple())

def timestamp_natural_sub(timestamp, **fields):
    """
        timetamp subtraction (natural calendar)
    """
    delta = {}
    if fields.has_key('years'):
        delta['years'] = fields['years']
    if fields.has_key('months'):
        delta['months'] = fields['months']
    if fields.has_key('days'):
        delta['days'] = fields['days']
    if fields.has_key('hours'):
        delta['hours'] = fields['hours']
    if fields.has_key('minutes'):
        delta['minutes'] = fields['minutes']
    if fields.has_key('seconds'):
        delta['seconds'] = fields['seconds']
    if fields.has_key('microseconds'):
        delta['microseconds'] = fields['microseconds']

    dt = datetime.utcfromtimestamp(timestamp)
    return datetime_2_utctimestamp(dt - relativedelta(**delta))

def timestamp_natural_add(timestamp, **fields):
    """
        timestamp addition (natural calendar)
    """
    delta = {}
    if fields.has_key('years'):
        delta['years'] = fields['years']
    if fields.has_key('months'):
        delta['months'] = fields['months']
    if fields.has_key('days'):
        delta['days'] = fields['days']
    if fields.has_key('hours'):
        delta['hours'] = fields['hours']
    if fields.has_key('minutes'):
        delta['minutes'] = fields['minutes']
    if fields.has_key('seconds'):
        delta['seconds'] = fields['seconds']
    if fields.has_key('microseconds'):
        delta['microseconds'] = fields['microseconds']

    dt = datetime.utcfromtimestamp(timestamp)
    return datetime_2_utctimestamp(dt + relativedelta(**delta))

<|endoftext|>


<commit_before>"""
This is the top-level namespace for the pyvision 3 library
"""
from .constants import *
from .pv_exceptions import *
from .geometry import Point, Rect, CenteredRect, in_bounds, integer_bounds, integer_coords_array
from .image import Image
from .affine import AffineTransformer, AffineRotation, AffineTranslate
from .imagebuffer import ImageBuffer
from .video import VideoInterface, Video, VideoFromFileList, VideoFromDir, VideoFromImageStack
from .montage import ImageMontage, VideoMontage

from pyvision.video_proc.backgroundsubtract import \
    FrameDifferenceModel, MedianModel, ApproximateMedianModel, AbstractBGModel, StaticModel, \
    BG_SUBTRACT_STATIC, BG_SUBTRACT_FRAME_DIFF, BG_SUBTRACT_MEDIAN, BG_SUBTRACT_APPROX_MEDIAN
from pyvision.video_proc.motiondetection import \
    MotionDetector, MD_BOUNDING_RECTS, MD_STANDARDIZED_RECTS

from pyvision.dataset_tools.crops import crop_regions, crop_negative_regions, random_rect_gen
from pyvision.dataset_tools.tile_selection import TileSelector, \
    tiles_from_dir, tiles_from_files, tiles_from_vid

<commit_msg>Added a hard-coded version constant. TODO: is to make sure setup.py refers to the same version info as this constant.<commit_after>"""
This is the top-level namespace for the pyvision 3 library
"""
__version__ = "0.1.12"
from .constants import *
from .pv_exceptions import *
from .geometry import Point, Rect, CenteredRect, in_bounds, integer_bounds, integer_coords_array
from .image import Image
from .affine import AffineTransformer, AffineRotation, AffineTranslate
from .imagebuffer import ImageBuffer
from .video import VideoInterface, Video, VideoFromFileList, VideoFromDir, VideoFromImageStack
from .montage import ImageMontage, VideoMontage

from pyvision.video_proc.backgroundsubtract import \
    FrameDifferenceModel, MedianModel, ApproximateMedianModel, AbstractBGModel, StaticModel, \
    BG_SUBTRACT_STATIC, BG_SUBTRACT_FRAME_DIFF, BG_SUBTRACT_MEDIAN, BG_SUBTRACT_APPROX_MEDIAN
from pyvision.video_proc.motiondetection import \
    MotionDetector, MD_BOUNDING_RECTS, MD_STANDARDIZED_RECTS

from pyvision.dataset_tools.crops import crop_regions, crop_negative_regions, random_rect_gen
from pyvision.dataset_tools.tile_selection import TileSelector, \
    tiles_from_dir, tiles_from_files, tiles_from_vid

<|endoftext|>


<commit_before>"""Tests for ``pulsar.client.amqp_exchange``."""
import time
import threading

from pulsar.client import amqp_exchange

from .test_utils import (
    skip_unless_module,
    timed,
)

TEST_CONNECTION = "memory://test_amqp"

@skip_unless_module("kombu")
@timed(15)
def test_amqp():
    """Test the client PulsarExchange abstraction with an in-memory connection."""
    manager1_exchange = amqp_exchange.PulsarExchange(TEST_CONNECTION, "manager_test")
    manager3_exchange = amqp_exchange.PulsarExchange(TEST_CONNECTION, "manager3_test")
    manager2_exchange = amqp_exchange.PulsarExchange(TEST_CONNECTION, "manager2_test")
    thread1 = TestThread("manager_test", manager1_exchange)
    thread2 = TestThread("manager2_test", manager2_exchange)
    thread3 = TestThread("manager3_test", manager3_exchange)
    thread1.start()
    thread2.start()
    thread3.start()
    manager1_exchange.publish("manager_test", u"cow1")
    manager2_exchange.publish("manager2_test", u"cow2")
    manager3_exchange.publish("manager3_test", u"cow3")
    time.sleep(0.1)
    thread1.wait_for_message(u"cow1")
    thread2.wait_for_message(u"cow2")
    thread3.wait_for_message(u"cow3")

class TestThread(threading.Thread):

    def __init__(self, queue_name, exchange):
        super(TestThread, self).__init__(target=self.run)
        self.queue_name = queue_name
        self.daemon = True
        self.exchange = exchange
        self.message = None

    def __nonzero__(self):
        return self.message is None

    __bool__ = __nonzero__  # Both needed Py2 v 3

    def run(self):
        def callback(body, message):
            self.message = body
            message.ack()

        self.exchange.consume(self.queue_name, callback=callback, check=self)

    def wait_for_message(self, expected_message):
        while self:
            time.sleep(.05)
        if self.message != expected_message:
            msg = "Expected [%s], got [%s]." % (expected_message, self.message)
            raise AssertionError(msg)

        self.join(2)

__all__ = ["test_amqp"]
<commit_msg>Sleep after starting thread<commit_after>"""Tests for ``pulsar.client.amqp_exchange``."""
import time
import threading

from pulsar.client import amqp_exchange

from .test_utils import (
    skip_unless_module,
    timed,
)

TEST_CONNECTION = "memory://test_amqp"

@skip_unless_module("kombu")
@timed(15)
def test_amqp():
    """Test the client PulsarExchange abstraction with an in-memory connection."""
    manager1_exchange = amqp_exchange.PulsarExchange(TEST_CONNECTION, "manager_test")
    manager3_exchange = amqp_exchange.PulsarExchange(TEST_CONNECTION, "manager3_test")
    manager2_exchange = amqp_exchange.PulsarExchange(TEST_CONNECTION, "manager2_test")
    thread1 = TestThread("manager_test", manager1_exchange)
    thread2 = TestThread("manager2_test", manager2_exchange)
    thread3 = TestThread("manager3_test", manager3_exchange)
    thread1.start()
    thread2.start()
    thread3.start()
    time.sleep(0.5)
    manager1_exchange.publish("manager_test", u"cow1")
    manager2_exchange.publish("manager2_test", u"cow2")
    manager3_exchange.publish("manager3_test", u"cow3")
    time.sleep(0.1)
    thread1.wait_for_message(u"cow1")
    thread2.wait_for_message(u"cow2")
    thread3.wait_for_message(u"cow3")

class TestThread(threading.Thread):

    def __init__(self, queue_name, exchange):
        super(TestThread, self).__init__(target=self.run)
        self.queue_name = queue_name
        self.daemon = True
        self.exchange = exchange
        self.message = None

    def __nonzero__(self):
        return self.message is None

    __bool__ = __nonzero__  # Both needed Py2 v 3

    def run(self):
        def callback(body, message):
            self.message = body
            message.ack()

        self.exchange.consume(self.queue_name, callback=callback, check=self)

    def wait_for_message(self, expected_message):
        while self:
            time.sleep(.05)
        if self.message != expected_message:
            msg = "Expected [%s], got [%s]." % (expected_message, self.message)
            raise AssertionError(msg)

        self.join(2)

__all__ = ["test_amqp"]
<|endoftext|>


<commit_before>import datetime

from flask import jsonify
from flask import abort
from flask import url_for
from flask.ext.sqlalchemy import SQLAlchemy
from flask.ext.whooshalchemy import whoosh_index

db = SQLAlchemy()

class BasicMixin(object):

    id_ = db.Column(db.Integer, primary_key=True)
    create_time = db.Column(db.DateTime, default=datetime.datetime.utcnow)          # First time the column is created.

    @classmethod
    def get_or_404(cls, id_=None, **kwargs):
        if id_ is not None:
            obj = cls.query.get(id_)
        else:
            objs = cls.query.filter_by(**kwargs)
            obj = objs and objs[0] or None
        if obj is None:
            abort(404)
        return obj

    def to_json(self, *columns):
        dct = self.to_dict(*columns)
        for key in dct:
            if isinstance(dct[key], datetime.datetime):
                dct[key] = dct[key].strftime('%Y-%m-%d %H:%M:%S')
        return jsonify(dct)

    def to_dict(self, *columns):
        dct = {}
        for col in columns:
            dct[col] = getattr(self, col)
        return dct

    def __unicode__(self):
        return "<Model %s>%d: %s" % (self.__class__.__name__, self.id_,
                                     getattr(self, 'name', None) or
                                     getattr(self, 'display_name', None) or
                                     getattr(self, 'title', ''))

class SessionMixin(object):
    def save(self):
        db.session.add(self)
        db.session.commit()
        return self

    def delete(self):
        db.session.delete(self)
        db.session.commit()
        return self

class User(db.Model, BasicMixin, SessionMixin):
    __tablename__ = 'users'

    username = db.Column(db.String(50), unique=True)
    name = db.Column(db.String(100))
    email = db.Column(db.String(50))
    avatar = db.Column(db.String(100))
    github_id = db.Column(db.String(20), unique=True)
    github_username = db.Column(db.String(50))
    role = db.Column(db.Integer)

    sighs = db.relationship('Sigh')
    tags = db.relationship('Tag')
    comments = db.relationship('Comment')

    @property
    def profile_url(self):
        return url_for('frontend.render_profile', user_id=self.id_)

    def split_avatar(self, width):
        return '{}&s={}'.format(self.avatar, width)

tag_identifier = db.Table('tag_identifier',
                          db.Column('tag_id', db.Integer, db.ForeignKey('tags.id_')),
                          db.Column('sigh_id', db.Integer, db.ForeignKey('sighs.id_')))

class Sigh(db.Model, BasicMixin, SessionMixin):
    __tablename__ = 'sighs'
    __searchable__ = ['content']

    creator_id = db.Column(db.Integer, db.ForeignKey('users.id_'))
    creator = db.relationship('User')
    content = db.Column(db.Text, nullable=False)
    type_ = db.Column(db.Enum('sigh', 'wtf', 'fml', name='content_types'), nullable=False)
    is_anonymous = db.Column(db.Boolean, default=False)
    comments = db.relationship('Comment', order_by="desc(Comment.create_time)")
    tags = db.relationship('Tag', secondary=tag_identifier)

class Tag(db.Model, BasicMixin, SessionMixin):
    __tablename__ = 'tags'

    display_name = db.Column(db.String(50), unique=True, nullable=False)
    searchable_name = db.Column(db.String(500), unique=True, nullable=False)
    creator_id = db.Column(db.Integer, db.ForeignKey('users.id_'))      # This could be NULL when it's created bu system
    sighs = db.relationship('Sigh', order_by="desc(Sigh.id_)", secondary=tag_identifier)

class Comment(db.Model, BasicMixin, SessionMixin):
    __tablename__ = 'comments'

    content = db.Column(db.Text, nullable=False)
    sigh_id = db.Column(db.Integer, db.ForeignKey('sighs.id_'))
    sigh = db.relationship('Sigh')
    creator_id = db.Column(db.Integer, db.ForeignKey('users.id_'))
    creator = db.relationship('User')

def index(app):
    for model in [Sigh]:
        whoosh_index(app, model)
<commit_msg>Add sighs_count property to Tag.<commit_after>import datetime

from flask import jsonify
from flask import abort
from flask import url_for
from flask.ext.sqlalchemy import SQLAlchemy
from flask.ext.whooshalchemy import whoosh_index

db = SQLAlchemy()

class BasicMixin(object):

    id_ = db.Column(db.Integer, primary_key=True)
    create_time = db.Column(db.DateTime, default=datetime.datetime.utcnow)          # First time the column is created.

    @classmethod
    def get_or_404(cls, id_=None, **kwargs):
        if id_ is not None:
            obj = cls.query.get(id_)
        else:
            objs = cls.query.filter_by(**kwargs)
            obj = objs and objs[0] or None
        if obj is None:
            abort(404)
        return obj

    def to_json(self, *columns):
        dct = self.to_dict(*columns)
        for key in dct:
            if isinstance(dct[key], datetime.datetime):
                dct[key] = dct[key].strftime('%Y-%m-%d %H:%M:%S')
        return jsonify(dct)

    def to_dict(self, *columns):
        dct = {}
        for col in columns:
            dct[col] = getattr(self, col)
        return dct

    def __unicode__(self):
        return "<Model %s>%d: %s" % (self.__class__.__name__, self.id_,
                                     getattr(self, 'name', None) or
                                     getattr(self, 'display_name', None) or
                                     getattr(self, 'title', ''))

class SessionMixin(object):
    def save(self):
        db.session.add(self)
        db.session.commit()
        return self

    def delete(self):
        db.session.delete(self)
        db.session.commit()
        return self

class User(db.Model, BasicMixin, SessionMixin):
    __tablename__ = 'users'

    username = db.Column(db.String(50), unique=True)
    name = db.Column(db.String(100))
    email = db.Column(db.String(50))
    avatar = db.Column(db.String(100))
    github_id = db.Column(db.String(20), unique=True)
    github_username = db.Column(db.String(50))
    role = db.Column(db.Integer)

    sighs = db.relationship('Sigh')
    tags = db.relationship('Tag')
    comments = db.relationship('Comment')

    @property
    def profile_url(self):
        return url_for('frontend.render_profile', user_id=self.id_)

    def split_avatar(self, width):
        return '{}&s={}'.format(self.avatar, width)

tag_identifier = db.Table('tag_identifier',
                          db.Column('tag_id', db.Integer, db.ForeignKey('tags.id_')),
                          db.Column('sigh_id', db.Integer, db.ForeignKey('sighs.id_')))

class Sigh(db.Model, BasicMixin, SessionMixin):
    __tablename__ = 'sighs'
    __searchable__ = ['content']

    creator_id = db.Column(db.Integer, db.ForeignKey('users.id_'))
    creator = db.relationship('User')
    content = db.Column(db.Text, nullable=False)
    type_ = db.Column(db.Enum('sigh', 'wtf', 'fml', name='content_types'), nullable=False)
    is_anonymous = db.Column(db.Boolean, default=False)
    comments = db.relationship('Comment', order_by="desc(Comment.create_time)")
    tags = db.relationship('Tag', secondary=tag_identifier)

class Tag(db.Model, BasicMixin, SessionMixin):
    __tablename__ = 'tags'

    display_name = db.Column(db.String(50), unique=True, nullable=False)
    searchable_name = db.Column(db.String(500), unique=True, nullable=False)
    creator_id = db.Column(db.Integer, db.ForeignKey('users.id_'))      # This could be NULL when it's created bu system
    sighs = db.relationship('Sigh', order_by="desc(Sigh.id_)", secondary=tag_identifier)

    @property
    def sighs_count(self):
        query = db.session.query(tag_identifier).filter_by(tag_id=self.id_)
        return query.count()

class Comment(db.Model, BasicMixin, SessionMixin):
    __tablename__ = 'comments'

    content = db.Column(db.Text, nullable=False)
    sigh_id = db.Column(db.Integer, db.ForeignKey('sighs.id_'))
    sigh = db.relationship('Sigh')
    creator_id = db.Column(db.Integer, db.ForeignKey('users.id_'))
    creator = db.relationship('User')

def index(app):
    for model in [Sigh]:
        whoosh_index(app, model)
<|endoftext|>


<commit_before>"""
Definition of descriptors.
"""

# Standard library modules.

# Third party modules.

# Local modules.
from pyxray.cbook import Immutable, Cachable, Validable, Reprable

# Globals and constants variables.

class _Descriptor(Immutable, Validable, Cachable, Reprable):
    pass

class Element(metaclass=_Descriptor,
              attrs=('atomic_number',)):

    @classmethod
    def validate(cls, atomic_number):
        if atomic_number < 1 or atomic_number > 118:
            raise ValueError('Atomic number ({0}) must be [1, 118]'
                             .format(atomic_number))

    def _repr_inner(self):
        return 'z={0}'.format(self.z)

    @property
    def z(self):
        return self.atomic_number

class AtomicShell(metaclass=_Descriptor,
                  attrs=('principal_quantum_number',)):

    @classmethod
    def validate(cls, principal_quantum_number):
        if principal_quantum_number < 1:
            raise ValueError('Principal quantum number ({0}) must be [1, inf['
                             .format(principal_quantum_number))

    def _repr_inner(self):
        return 'n={0}'.format(self.n)

    @property
    def n(self):
        return self.principal_quantum_number

class AtomicSubshell(metaclass=_Descriptor,
                     attrs=('atomic_shell',
                            'azimuthal_quantum_number',
                            'total_angular_momentum_nominator')):

    @classmethod
    def validate(cls,
                 atomic_shell,
                 azimuthal_quantum_number,
                 total_angular_momentum_nominator):
        if not isinstance(atomic_shell, AtomicShell):
            atomic_shell = AtomicShell(atomic_shell)

        lmin = 0
        lmax = atomic_shell.principal_quantum_number - 1
        jmin_n = 2 * abs(azimuthal_quantum_number - 0.5)
        jmax_n = 2 * abs(azimuthal_quantum_number + 0.5)

        if azimuthal_quantum_number < lmin or \
                azimuthal_quantum_number > lmax:
            raise ValueError('Azimuthal quantum number ({0}) must be between [{1}, {2}]'
                             .format(azimuthal_quantum_number, lmin, lmax))
        if total_angular_momentum_nominator < jmin_n or \
                total_angular_momentum_nominator > jmax_n:
            raise ValueError('Total angular momentum ({0}) must be between [{1}, {2}]'
                             .format(total_angular_momentum_nominator, jmin_n, jmax_n))

        return (atomic_shell,
                azimuthal_quantum_number,
                total_angular_momentum_nominator)

    def _repr_inner(self):
        return 'n={0}, l={1}, j={2:.1f}'.format(self.n, self.l, self.j)

    @property
    def principal_quantum_number(self):
        return self.atomic_shell.principal_quantum_number

    @property
    def n(self):
        return self.principal_quantum_number

    @property
    def l(self):
        return self.azimuthal_quantum_number

    @property
    def j_n(self):
        return self.total_angular_momentum_nominator

    @property
    def total_angular_momentum(self):
        return self.total_angular_momentum_nominator / 2.0

    @property
    def j(self):
        return self.total_angular_momentum

class Transition(metaclass=_Descriptor,
                 attrs=('source_subshell',
                        'destination_subshell',
                        'secondary_destination_subshell')):

    @classmethod
    def validate(cls,
                 source_subshell,
                 destination_subshell,
                 secondary_destination_subshell=None):
        #TODO: Validate transition
#        """
#        Inspired from NIST EPQ library by Nicholas Ritchie.
#        """
#        def electric_dipole_permitted(n0, l0, j0_n, n1, l1, j1_n):
#            delta_j_n = abs(j1_n - j0_n)
#            if delta_j_n > 2:
#                return False
#            assert delta_j_n == 0 or delta_j_n == 2
#            return abs(l1 - l0) == 1
#
#        def electric_quadrupole_permitted(n0, l0, j0_n, n1, l1, j1_n):
#            delta_j_n = abs(j1_n - j0_n)
#            if delta_j_n > 4:
#                return False
#            assert delta_j_n == 0 or delta_j_n == 2 or delta_j_n == 4
#
#            delta_l = abs(l1 - l0)
#            return delta_l == 0 or delta_l == 2
#
#        if n0 == n1:
#            return False
#
#        return electric_dipole_permitted(n0, l0, j0_n, n1, l1, j1_n) or \
#                electric_quadrupole_permitted(n0, l0, j0_n, n1, l1, j1_n)
        return (source_subshell,
                destination_subshell,
                secondary_destination_subshell)

    def _repr_inner(self):
        r = '[n={src.n}, l={src.l}, j={src.j:.1f}]'
        r += ' -> [n={dest.n}, l={dest.l}, j={dest.j:.1f}]'
        if self.secondary_destination_subshell is not None:
            r += ' -> [n={dest2.n}, l={dest2.l}, j={dest2.j:.1f}]'
        return r.format(src=self.source_subshell,
                        dest=self.destination_subshell,
                        dest2=self.secondary_destination_subshell)

    def is_radiative(self):
        return self.secondary_destination_subshell is None

    def is_nonradiative(self):
        return not self.is_radiative()

    def is_coster_kronig(self):
        return self.source_subshell.n == self.destination_subshell.n

class TransitionSet(metaclass=_Descriptor,
                    attrs=('transitions',)):

    @classmethod
    def validate(cls, transitions):
        transitions = frozenset(transitions)
        return (transitions,)

    def _repr_inner(self):
        return '{0:d} transitions'.format(len(self.transitions))

class Language(metaclass=_Descriptor,
               attrs=('code',)):

    @classmethod
    def validate(cls, code):
        lencode = len(code)
        if lencode < 2 or lencode > 3:
            raise ValueError('Code must be between 2 and 3 characters')
        code = code.lower()
        return (code,)

class Notation(metaclass=_Descriptor,
               attrs=('name',)):

    @classmethod
    def validate(cls, name):
        if not name:
            raise ValueError('Name cannot be empty')
        name = name.lower()
        return (name,)

class Reference(metaclass=_Descriptor,
                attrs=('bibtexkey', 'author', 'year', 'title', 'type',
                       'booktitle', 'editor', 'pages', 'edition',
                       'journal', 'school', 'address', 'url', 'note',
                       'number', 'series', 'volume', 'publisher',
                       'organization', 'chapter', 'howpublished', 'doi')):

    @classmethod
    def validate(cls, bibtexkey, author=None, year=None, title=None,
                type=None, booktitle=None, editor=None, pages=None, #@ReservedAssignment
                edition=None, journal=None, school=None, address=None,
                url=None, note=None, number=None, series=None, volume=None,
                publisher=None, organization=None, chapter=None,
                howpublished=None, doi=None):
        if not bibtexkey:
            raise ValueError('A BibTeX key must be defined')

        return (bibtexkey, author, year, title, type, booktitle, editor,
                pages, edition, journal, school, address, url, note,
                number, series, volume, publisher, organization,
                chapter, howpublished, doi)

    def _repr_inner(self):
        return '{0}'.format(self.bibtexkey)
<commit_msg>Validate transition and transitionset<commit_after>"""
Definition of descriptors.
"""

# Standard library modules.

# Third party modules.

# Local modules.
from pyxray.cbook import Immutable, Cachable, Validable, Reprable

# Globals and constants variables.

class _Descriptor(Immutable, Validable, Cachable, Reprable):
    pass

class Element(metaclass=_Descriptor,
              attrs=('atomic_number',)):

    @classmethod
    def validate(cls, atomic_number):
        if atomic_number < 1 or atomic_number > 118:
            raise ValueError('Atomic number ({0}) must be [1, 118]'
                             .format(atomic_number))

    def _repr_inner(self):
        return 'z={0}'.format(self.z)

    @property
    def z(self):
        return self.atomic_number

class AtomicShell(metaclass=_Descriptor,
                  attrs=('principal_quantum_number',)):

    @classmethod
    def validate(cls, principal_quantum_number):
        if principal_quantum_number < 1:
            raise ValueError('Principal quantum number ({0}) must be [1, inf['
                             .format(principal_quantum_number))

    def _repr_inner(self):
        return 'n={0}'.format(self.n)

    @property
    def n(self):
        return self.principal_quantum_number

class AtomicSubshell(metaclass=_Descriptor,
                     attrs=('atomic_shell',
                            'azimuthal_quantum_number',
                            'total_angular_momentum_nominator')):

    @classmethod
    def validate(cls,
                 atomic_shell,
                 azimuthal_quantum_number,
                 total_angular_momentum_nominator):
        if not isinstance(atomic_shell, AtomicShell):
            atomic_shell = AtomicShell(atomic_shell)

        lmin = 0
        lmax = atomic_shell.principal_quantum_number - 1
        jmin_n = 2 * abs(azimuthal_quantum_number - 0.5)
        jmax_n = 2 * abs(azimuthal_quantum_number + 0.5)

        if azimuthal_quantum_number < lmin or \
                azimuthal_quantum_number > lmax:
            raise ValueError('Azimuthal quantum number ({0}) must be between [{1}, {2}]'
                             .format(azimuthal_quantum_number, lmin, lmax))
        if total_angular_momentum_nominator < jmin_n or \
                total_angular_momentum_nominator > jmax_n:
            raise ValueError('Total angular momentum ({0}) must be between [{1}, {2}]'
                             .format(total_angular_momentum_nominator, jmin_n, jmax_n))

        return (atomic_shell,
                azimuthal_quantum_number,
                total_angular_momentum_nominator)

    def _repr_inner(self):
        return 'n={0}, l={1}, j={2:.1f}'.format(self.n, self.l, self.j)

    @property
    def principal_quantum_number(self):
        return self.atomic_shell.principal_quantum_number

    @property
    def n(self):
        return self.principal_quantum_number

    @property
    def l(self):
        return self.azimuthal_quantum_number

    @property
    def j_n(self):
        return self.total_angular_momentum_nominator

    @property
    def total_angular_momentum(self):
        return self.total_angular_momentum_nominator / 2.0

    @property
    def j(self):
        return self.total_angular_momentum

class Transition(metaclass=_Descriptor,
                 attrs=('source_subshell',
                        'destination_subshell',
                        'secondary_destination_subshell')):

    @classmethod
    def validate(cls,
                 source_subshell,
                 destination_subshell,
                 secondary_destination_subshell=None):
        if not isinstance(source_subshell, AtomicSubshell):
            source_subshell = AtomicSubshell(source_subshell)
        if not isinstance(destination_subshell, AtomicSubshell):
            destination_subshell = AtomicSubshell(destination_subshell)
        if secondary_destination_subshell and \
                not isinstance(secondary_destination_subshell, AtomicSubshell):
            secondary_destination_subshell = AtomicSubshell(secondary_destination_subshell)

        return (source_subshell,
                destination_subshell,
                secondary_destination_subshell)

    def _repr_inner(self):
        r = '[n={src.n}, l={src.l}, j={src.j:.1f}]'
        r += ' -> [n={dest.n}, l={dest.l}, j={dest.j:.1f}]'
        if self.secondary_destination_subshell is not None:
            r += ' -> [n={dest2.n}, l={dest2.l}, j={dest2.j:.1f}]'
        return r.format(src=self.source_subshell,
                        dest=self.destination_subshell,
                        dest2=self.secondary_destination_subshell)

    def is_radiative(self):
        """
        Inspired from NIST EPQ library by Nicholas Ritchie.
        """
        def electric_dipole_permitted(n0, l0, j0_n, n1, l1, j1_n):
            delta_j_n = abs(j1_n - j0_n)
            if delta_j_n > 2:
                return False
            assert delta_j_n == 0 or delta_j_n == 2
            return abs(l1 - l0) == 1

        def electric_quadrupole_permitted(n0, l0, j0_n, n1, l1, j1_n):
            delta_j_n = abs(j1_n - j0_n)
            if delta_j_n > 4:
                return False
            assert delta_j_n == 0 or delta_j_n == 2 or delta_j_n == 4

            delta_l = abs(l1 - l0)
            return delta_l == 0 or delta_l == 2

        if self.secondary_destination_subshell is None:
            return False

        n0 = self.source_subshell.n
        l0 = self.source_subshell.l
        j0_n = self.source_subshell.j_n
        n1 = self.destination_subshell.n
        l1 = self.destination_subshell.l
        j1_n = self.destination_subshell.j_n

        if n0 == n1:
            return False

        if not(electric_dipole_permitted(n0, l0, j0_n, n1, l1, j1_n) or \
               electric_quadrupole_permitted(n0, l0, j0_n, n1, l1, j1_n)):
            return False

        return True

    def is_nonradiative(self):
        return not self.is_radiative()

    def is_coster_kronig(self):
        return self.source_subshell.n == self.destination_subshell.n

class TransitionSet(metaclass=_Descriptor,
                    attrs=('transitions',)):

    @classmethod
    def validate(cls, transitions):
        transitions2 = set()
        for transition in transitions:
            if not isinstance(transition, Transition):
                transition = Transition(*transition)
            transitions2.add(transition)
        return (frozenset(transitions2),)

    def _repr_inner(self):
        return '{0:d} transitions'.format(len(self.transitions))

class Language(metaclass=_Descriptor,
               attrs=('code',)):

    @classmethod
    def validate(cls, code):
        lencode = len(code)
        if lencode < 2 or lencode > 3:
            raise ValueError('Code must be between 2 and 3 characters')
        code = code.lower()
        return (code,)

class Notation(metaclass=_Descriptor,
               attrs=('name',)):

    @classmethod
    def validate(cls, name):
        if not name:
            raise ValueError('Name cannot be empty')
        name = name.lower()
        return (name,)

class Reference(metaclass=_Descriptor,
                attrs=('bibtexkey', 'author', 'year', 'title', 'type',
                       'booktitle', 'editor', 'pages', 'edition',
                       'journal', 'school', 'address', 'url', 'note',
                       'number', 'series', 'volume', 'publisher',
                       'organization', 'chapter', 'howpublished', 'doi')):

    @classmethod
    def validate(cls, bibtexkey, author=None, year=None, title=None,
                type=None, booktitle=None, editor=None, pages=None, #@ReservedAssignment
                edition=None, journal=None, school=None, address=None,
                url=None, note=None, number=None, series=None, volume=None,
                publisher=None, organization=None, chapter=None,
                howpublished=None, doi=None):
        if not bibtexkey:
            raise ValueError('A BibTeX key must be defined')

        return (bibtexkey, author, year, title, type, booktitle, editor,
                pages, edition, journal, school, address, url, note,
                number, series, volume, publisher, organization,
                chapter, howpublished, doi)

    def _repr_inner(self):
        return '{0}'.format(self.bibtexkey)
<|endoftext|>


<commit_before>from django.db import models
import re
import datetime

def resave_searchable_models():
    for model in models.get_models():
        if SimpleSearchableModel in model.__bases__:
            for o in model.objects.all():
                o.save()

class TimestampModelMixin(models.Model):
    created_at = models.DateTimeField(blank=True, null=True, editable=False)
    modified_at = models.DateTimeField(blank=True, null=True, editable=False)

    def save(self, *args, **kwargs):
        self.modified_at = datetime.datetime.now()
        if not self.id:
            self.created_at = self.modified_at
        super(TimestampModelMixin,self).save(*args, **kwargs)

    class Meta:
        abstract = True

class SimpleSearchableModel(models.Model):

    qi_simple_searchable_search_field = models.TextField(db_index=True, editable=False, blank=True, null=True, unique=False)

    def save(self, *args, **kwargs):
        if hasattr(self, "search_fields"):
            search_str = ""
            for f in self.search_fields:
                search_str += "%s " % getattr(self,f)
            self.qi_simple_searchable_search_field = search_str
        else:
            self.qi_simple_searchable_search_field = self.__unicode__()

        super(SimpleSearchableModel,self).save(*args, **kwargs)

    @classmethod
    def search(cls, query, delimiter=" ", ignorable_chars=None):
        # Accept a list of ignorable characters to strip from the query (dashes in phone numbers, etc)
        if ignorable_chars:
            ignorable_re = re.compile("[%s]+"%("".join(ignorable_chars)))
            query = ignorable_re.sub('',query)

        # Split the querystring by a given delimiter.
        if delimiter and delimiter != "":
            queries = query.split(delimiter)
        else:
            queries = [query]

        results = cls.objects.all()
        for q in queries:
            if q != "":
                results = results.filter(qi_simple_searchable_search_field__icontains=q)

        return results

    class Meta:
        abstract = True<commit_msg>pulling db_index=True<commit_after>from django.db import models
import re
import datetime

def resave_searchable_models():
    for model in models.get_models():
        if SimpleSearchableModel in model.__bases__:
            for o in model.objects.all():
                o.save()

class TimestampModelMixin(models.Model):
    created_at = models.DateTimeField(blank=True, null=True, editable=False)
    modified_at = models.DateTimeField(blank=True, null=True, editable=False)

    def save(self, *args, **kwargs):
        self.modified_at = datetime.datetime.now()
        if not self.id:
            self.created_at = self.modified_at
        super(TimestampModelMixin,self).save(*args, **kwargs)

    class Meta:
        abstract = True

class SimpleSearchableModel(models.Model):

    qi_simple_searchable_search_field = models.TextField(editable=False, blank=True, null=True, unique=False) # db_index=True

    def save(self, *args, **kwargs):
        if hasattr(self, "search_fields"):
            search_str = ""
            for f in self.search_fields:
                search_str += "%s " % getattr(self,f)
            self.qi_simple_searchable_search_field = search_str
        else:
            self.qi_simple_searchable_search_field = self.__unicode__()

        super(SimpleSearchableModel,self).save(*args, **kwargs)

    @classmethod
    def search(cls, query, delimiter=" ", ignorable_chars=None):
        # Accept a list of ignorable characters to strip from the query (dashes in phone numbers, etc)
        if ignorable_chars:
            ignorable_re = re.compile("[%s]+"%("".join(ignorable_chars)))
            query = ignorable_re.sub('',query)

        # Split the querystring by a given delimiter.
        if delimiter and delimiter != "":
            queries = query.split(delimiter)
        else:
            queries = [query]

        results = cls.objects.all()
        for q in queries:
            if q != "":
                results = results.filter(qi_simple_searchable_search_field__icontains=q)

        return results

    class Meta:
        abstract = True<|endoftext|>