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

from __future__ import absolute_import
from __future__ import division

import hashlib
import logging
import os
import pkg_resources
import re
import subprocess

import six
import yaml

import guild

from guild import cli
from guild import config
from guild import init
from guild import namespace
from guild import util

log = logging.getLogger("guild")

class Config(object):

    def __init__(self, args):
        self.env_dir = os.path.abspath(args.dir)
        self.env_name = self._init_env_name(args.name, self.env_dir)
        self.guild = args.guild
        self.guild_pkg_reqs = self._init_guild_pkg_reqs(args)
        self.venv_python = self._init_venv_python(args, self.guild_pkg_reqs)
        self.user_reqs = self._init_user_reqs(args)
        self.paths = args.path
        self.tensorflow = args.tensorflow
        self.local_resource_cache = args.local_resource_cache
        self.prompt_params = self._init_prompt_params()
        self.no_progress = args.no_progress

    @staticmethod
    def _init_env_name(name, abs_env_dir):
        if name:
            return name
        basename = os.path.basename(abs_env_dir)
        if basename != "env":
            return basename
        return os.path.basename(os.path.dirname(abs_env_dir))

    @staticmethod
    def _init_venv_python(args, pkg_reqs):
        if args.python:
            return _python_interpreter_for_arg(args.python)
        return _python_interpreter_for_reqs(pkg_reqs)

    @staticmethod
    def _init_guild_pkg_reqs(args):
        if args.no_reqs:
            return ()
        reqs = list(_iter_all_guild_pkg_reqs(config.cwd(), args.path))
        _apply_tensorflow_arg(args.tensorflow, reqs)
        return tuple(sorted(reqs))

    @staticmethod
    def _init_user_reqs(args):
        # -r options may be used with --no-reqs, in which case -r
        # takes precedence (--no-reqs may still be used to suppress
        # installation of Guild package reqs)
        if args.requirement:
            return args.requirement
        elif not args.no_reqs:
            default_reqs = os.path.join(config.cwd(), "requirements.txt")
            if os.path.exists(default_reqs):
                return (default_reqs,)
        return ()

    def _init_prompt_params(self):
        params = []
        params.append(("Location", _shorten_path(self.env_dir)))
        params.append(("Name", self.env_name))
        if self.venv_python:
            params.append(("Python interpreter", self.venv_python))
        else:
            params.append(("Python interpreter", "default"))
        if self.guild:
            params.append(("Guild version", self.guild))
        else:
            params.append(("Guild version", _implicit_guild_version()))
        if self.guild_pkg_reqs:
            params.append(("Guild package requirements", self.guild_pkg_reqs))
        if self.user_reqs:
            params.append(("Python requirements", self.user_reqs))
        if self.paths:
            params.append(("Additional paths", self.paths))
        if self.local_resource_cache:
            params.append(("Resource cache", "local"))
        else:
            params.append(("Resource cache", "shared"))
        return params

    def _maybe_guild_pkg_reqs(self):
        if self.no_guild_pkg_reqs:
            return []

    def as_kw(self):
        return self.__dict__

def _iter_all_guild_pkg_reqs(dir, search_path, seen=None):
    seen = seen or set()
    src = os.path.abspath(os.path.join(dir, "guild.yml"))
    if not os.path.exists(src):
        return
    if src in seen:
        return
    seen.add(src)
    for req in _guild_pkg_reqs(src):
        dir_on_path = _find_req_on_path(req, search_path)
        if dir_on_path:
            for req in _iter_all_guild_pkg_reqs(dir_on_path, search_path, seen):
                yield req
        else:
            yield req

def _guild_pkg_reqs(src):
    pkg = _guildfile_pkg(src)
    if not pkg:
        return []
    return _pkg_requires(pkg, src)

def _guildfile_pkg(src):
    data = _guildfile_data(src)
    if not isinstance(data, list):
        data = [data]
    for item in data:
        if isinstance(item, dict) and "package" in item:
            return item
    return None

def _guildfile_data(src):
    # Use low level parsing to bypass path-related errors.
    try:
        f = open(src, "r")
        return yaml.load(f)
    except Exception as e:
        log.warning(
            "cannot read Guild package requirements for %s (%s) - ignoring",
            src, e)
        return []

def _pkg_requires(pkg_data, src):
    requires = pkg_data.get("requires") or []
    if isinstance(requires, six.string_types):
        requires = [requires]
    if not isinstance(requires, list):
        log.warning(
            "invalid package requires list in %s (%r) - ignoring",
            src, pkg_data)
        return []
    return requires

def _find_req_on_path(req, path):
    req_subpath = req.replace(".", os.path.sep)
    for root in path:
        full_path = os.path.join(root, req_subpath)
        if os.path.exists(full_path):
            return full_path
    return None

def _validate_guild_reqs(reqs, guildfile_path):
    # Make sure we can convert each to pip reqs
    for req in reqs:
        try:
            namespace.pip_info(req)
        except namespace.NamespaceError:
            cli.error(
                "invalid required Guild package '%s' in %s\n"
                "Either correct the package name or use --no-reqs "
                "to skip installing required Guild packages"
                % (req, guildfile_path))
    return reqs

def _apply_tensorflow_arg(tensorflow_arg, reqs):
    """Applies tensorflow arg to package reqs.

    If tensorflow arg is specified, occurences of 'tensorflow-any'.

    If tensorflow arg is not specified, occurences of 'tensorflow-any'
    are replaced with the default tensorflow package for the system.
    """
    if tensorflow_arg:
        tensorflow_pkg = tensorflow_arg
    elif util.gpu_available():
        tensorflow_pkg = "tensorflow-gpu"
    else:
        tensorflow_pkg = "tensorflow"
    for i, spec in enumerate(reqs):
        for req in _parse_requirements(spec):
            if req.name == "tensorflow-any":
                req.name = tensorflow_pkg
                reqs[i] = str(req)

def _parse_requirements(spec):
    parsed = pkg_resources.parse_requirements(spec)
    try:
        return list(parsed)
    except pkg_resources.RequirementParseError as e:
        log.warning("invalid requirement %r: %s", spec, e)
        return []

def _shorten_path(path):
    return path.replace(os.path.expanduser("~"), "~")

def _implicit_guild_version():
    reqs_file = _guild_reqs_file()
    if reqs_file:
        return "from source (%s)" % os.path.dirname(reqs_file)
    else:
        return guild.__version__

def main(args):
    _error_if_active_env()
    config = Config(args)
    if args.yes or _confirm(config):
        _init(config)

def _error_if_active_env():
    active_env = os.getenv("VIRTUAL_ENV")
    if active_env:
        cli.error(
            "cannot run init from an activate environment (%s)\n"
            "Deactivate the environment by running 'deactivate' "
            "and try again."
            % active_env)

def _confirm(config):
    cli.out("You are about to initialize a Guild environment:")
    for name, val in config.prompt_params:
        if isinstance(val, tuple):
            cli.out("  {}:".format(name))
            for x in val:
                cli.out("    {}".format(x))
        else:
            cli.out("  {}: {}".format(name, val))
    return cli.confirm("Continue?", default=True)

def _init(config):
    _init_guild_env(config)
    _init_venv(config)
    _install_guild(config)
    _install_guild_pkg_reqs(config)
    _install_user_reqs(config)
    _install_paths(config)
    _initialized_msg(config)

def _init_guild_env(config):
    cli.out("Initializing Guild environment in {}".format(config.env_dir))
    init.init_env(config.env_dir, config.local_resource_cache)

def _init_venv(config):
    cmd_args = _venv_cmd_args(config)
    if not cmd_args:
        cli.out("Skipping virtual env")
        return
    cli.out("Creating virtual environment")
    log.debug("venv args: %s", cmd_args)
    try:
        subprocess.check_call(cmd_args)
    except subprocess.CalledProcessError as e:
        cli.error(str(e), exit_status=e.returncode)

def _venv_cmd_args(config):
    args = ["virtualenv", config.env_dir]
    args.extend(["--prompt", "({}) ".format(config.env_name)])
    if config.venv_python:
        args.extend(["--python", config.venv_python])
    return args

def _install_guild(config):
    """Install Guild into env.

    If the running Guild program is from `guild/scripts` (i.e. running
    in dev mode), only the Guild requirements, as defined in
    `guild/requirements.txt` are installed, under the assumption that
    `guild/scripts` is in `PATH` or an alias is being used and
    therefore Guild will be available from the env. In this case only
    the requirements need to be installed.

    If the running Guild program is not from `guild/scripts`, Guild
    will be installed using the env `pip` program.
    """
    if config.guild:
        _install_guild_dist(config)
    else:
        guild_reqs = _guild_reqs_file()
        if guild_reqs:
            _install_guild_reqs(guild_reqs, config)
        else:
            _install_default_guild_dist(config)

def _install_guild_dist(config):
    assert config.guild
    # If config.guild can be parsed as a version, use as
    # 'guildai==VERSION'
    if config.guild[0].isdigit():
        cli.out("Installing Guild %s" % config.guild)
        req = "guildai==%s" % config.guild
    else:
        cli.out("Installing %s" % config.guild)
        req = config.guild
    _install_reqs([req], config)

def _guild_reqs_file():
    guild_location = pkg_resources.resource_filename("guild", "")
    guild_parent = os.path.dirname(guild_location)
    path = os.path.join(guild_parent, "requirements.txt")
    try:
        f = open(path, "r")
    except (IOError, OSError):
        pass
    else:
        with f:
            if "guildai" in f.readline():
                return path
    return None

def _install_guild_reqs(req_files, config):
    cli.out("Installing Guild requirements")
    _install_reqs([req_files], config)

def _install_default_guild_dist(config):
    req = "guildai==%s" % guild.__version__
    cli.out("Installing Guild %s" % req)
    _install_reqs([req], config)

def _pip_bin(env_dir):
    pip_bin = os.path.join(env_dir, "bin", "pip")
    assert os.path.exists(pip_bin), pip_bin
    return pip_bin

def _pip_extra_opts(config):
    if config.no_progress:
        return ["--progress", "off"]
    else:
        return []

def _install_reqs(reqs, config):
    cmd_args = [_pip_bin(config.env_dir), "install"] + _pip_extra_opts(config)
    for req in reqs:
        if os.path.exists(req):
            cmd_args.extend(["-r", req])
        else:
            cmd_args.append(req)
    log.debug("pip cmd: %s", cmd_args)
    try:
        subprocess.check_call(cmd_args, env={"PATH": ""})
    except subprocess.CalledProcessError as e:
        cli.error(str(e), exit_status=e.returncode)

def _install_guild_pkg_reqs(config):
    if config.guild_pkg_reqs:
        cli.out("Installing Python requirements")
        reqs = _pip_package_reqs(config.guild_pkg_reqs)
        _install_reqs(reqs, config)

def _pip_package_reqs(guild_reqs):
    reqs = []
    for guild_req in guild_reqs:
        pip_info = namespace.pip_info(guild_req)
        reqs.append(pip_info.project_name)
    return reqs

def _install_user_reqs(config):
    if config.user_reqs:
        cli.out("Installing Python requirements")
        _install_reqs(config.user_reqs, config)

def _install_paths(config):
    if not config.paths:
        return
    site_packages = _env_site_packages(config.env_dir)
    for path in config.paths:
        _write_path(path, site_packages)

def _env_site_packages(env_dir):
    python_bin = os.path.join(env_dir, "bin", "python")
    out = subprocess.check_output([python_bin, "-m", "site"])
    site_packages_pattern = r"(%s.+?site-packages)" % env_dir
    m = re.search(site_packages_pattern, out)
    assert m, out
    return m.group(1)

def _write_path(path, target_dir):
    path = os.path.abspath(path)
    with open(_pth_filename(target_dir, path), "w") as f:
        f.write(path)

def _pth_filename(target_dir, path):
    digest = hashlib.md5(path).hexdigest()[:8]
    pth_name = "%s-%s.pth" % (os.path.basename(path), digest)
    return os.path.join(target_dir, pth_name)

def _python_interpreter_for_arg(arg):
    if arg.startswith("python"):
        return arg
    # Assume arg is a version
    return "python{}".format(arg)

def _python_interpreter_for_reqs(reqs):
    """Returns best guess Python interpreter for reqs.

    If reqs contains a `python` package req, we use it to guess the
    interpreter.
    """
    python_reqs = list(_iter_python_reqs(reqs))
    python_vers = [ver for _path, ver in sorted(util.python_interpreters())]
    for req in python_reqs:
        matching_vers = list(req.specifier.filter(python_vers))
        if matching_vers:
            return "python%s" % matching_vers[0]
    return None

def _iter_python_reqs(reqs):
    for spec in reqs:
        for req in _parse_requirements(spec):
            if req.name == "python":
                yield req

def _initialized_msg(config):
    cli.out(
        "Guild environment initialized in {}."
        "\n".format(_shorten_path(config.env_dir)))
    cli.out("To activate it " "run:\n")
    env_parent = os.path.dirname(config.env_dir)
    if env_parent != os.getcwd():
        cli.out("  source guild-env {}".format(config.env_dir))
    else:
        env_name = os.path.basename(config.env_dir)
        if env_name != "env":
            cli.out("  source guild-env {}".format(env_name))
        else:
            cli.out("  source guild-env")
    cli.out()

def _source_cmd(env_dir):
    name = os.path.basename(env_dir)
    if name == "env":
        return "source guild-env"
    else:
        return "source guild-env {}".format(name)
<commit_msg>Don't try to install python req as pip package<commit_after># Copyright 2017-2019 TensorHub, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from __future__ import absolute_import
from __future__ import division

import hashlib
import logging
import os
import pkg_resources
import re
import subprocess

import six
import yaml

import guild

from guild import cli
from guild import config
from guild import init
from guild import util

log = logging.getLogger("guild")

class Config(object):

    def __init__(self, args):
        self.env_dir = os.path.abspath(args.dir)
        self.env_name = self._init_env_name(args.name, self.env_dir)
        self.guild = args.guild
        self.guild_pkg_reqs = self._init_guild_pkg_reqs(args)
        self.venv_python = self._init_venv_python(args, self.guild_pkg_reqs)
        self.user_reqs = self._init_user_reqs(args)
        self.paths = args.path
        self.tensorflow = args.tensorflow
        self.local_resource_cache = args.local_resource_cache
        self.prompt_params = self._init_prompt_params()
        self.no_progress = args.no_progress

    @staticmethod
    def _init_env_name(name, abs_env_dir):
        if name:
            return name
        basename = os.path.basename(abs_env_dir)
        if basename != "env":
            return basename
        return os.path.basename(os.path.dirname(abs_env_dir))

    @staticmethod
    def _init_venv_python(args, pkg_reqs):
        if args.python:
            return _python_interpreter_for_arg(args.python)
        return _python_interpreter_for_reqs(pkg_reqs)

    @staticmethod
    def _init_guild_pkg_reqs(args):
        if args.no_reqs:
            return ()
        reqs = list(_iter_all_guild_pkg_reqs(config.cwd(), args.path))
        _apply_tensorflow_arg(args.tensorflow, reqs)
        return tuple(sorted(reqs))

    @staticmethod
    def _init_user_reqs(args):
        # -r options may be used with --no-reqs, in which case -r
        # takes precedence (--no-reqs may still be used to suppress
        # installation of Guild package reqs)
        if args.requirement:
            return args.requirement
        elif not args.no_reqs:
            default_reqs = os.path.join(config.cwd(), "requirements.txt")
            if os.path.exists(default_reqs):
                return (default_reqs,)
        return ()

    def _init_prompt_params(self):
        params = []
        params.append(("Location", _shorten_path(self.env_dir)))
        params.append(("Name", self.env_name))
        if self.venv_python:
            params.append(("Python interpreter", self.venv_python))
        else:
            params.append(("Python interpreter", "default"))
        if self.guild:
            params.append(("Guild version", self.guild))
        else:
            params.append(("Guild version", _implicit_guild_version()))
        if self.guild_pkg_reqs:
            params.append(("Guild package requirements", self.guild_pkg_reqs))
        if self.user_reqs:
            params.append(("Python requirements", self.user_reqs))
        if self.paths:
            params.append(("Additional paths", self.paths))
        if self.local_resource_cache:
            params.append(("Resource cache", "local"))
        else:
            params.append(("Resource cache", "shared"))
        return params

    def _maybe_guild_pkg_reqs(self):
        if self.no_guild_pkg_reqs:
            return []

    def as_kw(self):
        return self.__dict__

def _iter_all_guild_pkg_reqs(dir, search_path, seen=None):
    seen = seen or set()
    src = os.path.abspath(os.path.join(dir, "guild.yml"))
    if not os.path.exists(src):
        return
    if src in seen:
        return
    seen.add(src)
    for req in _guild_pkg_reqs(src):
        dir_on_path = _find_req_on_path(req, search_path)
        if dir_on_path:
            for req in _iter_all_guild_pkg_reqs(dir_on_path, search_path, seen):
                yield req
        else:
            yield req

def _guild_pkg_reqs(src):
    pkg = _guildfile_pkg(src)
    if not pkg:
        return []
    return _pkg_requires(pkg, src)

def _guildfile_pkg(src):
    data = _guildfile_data(src)
    if not isinstance(data, list):
        data = [data]
    for item in data:
        if isinstance(item, dict) and "package" in item:
            return item
    return None

def _guildfile_data(src):
    # Use low level parsing to bypass path-related errors.
    try:
        f = open(src, "r")
        return yaml.load(f)
    except Exception as e:
        log.warning(
            "cannot read Guild package requirements for %s (%s) - ignoring",
            src, e)
        return []

def _pkg_requires(pkg_data, src):
    requires = pkg_data.get("requires") or []
    if isinstance(requires, six.string_types):
        requires = [requires]
    if not isinstance(requires, list):
        log.warning(
            "invalid package requires list in %s (%r) - ignoring",
            src, pkg_data)
        return []
    return requires

def _find_req_on_path(req, path):
    req_subpath = req.replace(".", os.path.sep)
    for root in path:
        full_path = os.path.join(root, req_subpath)
        if os.path.exists(full_path):
            return full_path
    return None

def _apply_tensorflow_arg(tensorflow_arg, reqs):
    """Applies tensorflow arg to package reqs.

    If tensorflow arg is specified, occurences of 'tensorflow-any'.

    If tensorflow arg is not specified, occurences of 'tensorflow-any'
    are replaced with the default tensorflow package for the system.
    """
    if tensorflow_arg:
        tensorflow_pkg = tensorflow_arg
    elif util.gpu_available():
        tensorflow_pkg = "tensorflow-gpu"
    else:
        tensorflow_pkg = "tensorflow"
    for i, spec in enumerate(reqs):
        try:
            req = pkg_resources.Requirement.parse(spec)
        except pkg_resources.RequirementParseError:
            pass
        else:
            if req.name == "tensorflow-any":
                req.name = tensorflow_pkg
                reqs[i] = str(req)

def _shorten_path(path):
    return path.replace(os.path.expanduser("~"), "~")

def _implicit_guild_version():
    reqs_file = _guild_reqs_file()
    if reqs_file:
        return "from source (%s)" % os.path.dirname(reqs_file)
    else:
        return guild.__version__

def main(args):
    _error_if_active_env()
    config = Config(args)
    if args.yes or _confirm(config):
        _init(config)

def _error_if_active_env():
    active_env = os.getenv("VIRTUAL_ENV")
    if active_env:
        cli.error(
            "cannot run init from an activate environment (%s)\n"
            "Deactivate the environment by running 'deactivate' "
            "and try again."
            % active_env)

def _confirm(config):
    cli.out("You are about to initialize a Guild environment:")
    for name, val in config.prompt_params:
        if isinstance(val, tuple):
            cli.out("  {}:".format(name))
            for x in val:
                cli.out("    {}".format(x))
        else:
            cli.out("  {}: {}".format(name, val))
    return cli.confirm("Continue?", default=True)

def _init(config):
    _init_guild_env(config)
    _init_venv(config)
    _install_guild(config)
    _install_guild_pkg_reqs(config)
    _install_user_reqs(config)
    _install_paths(config)
    _initialized_msg(config)

def _init_guild_env(config):
    cli.out("Initializing Guild environment in {}".format(config.env_dir))
    init.init_env(config.env_dir, config.local_resource_cache)

def _init_venv(config):
    cmd_args = _venv_cmd_args(config)
    if not cmd_args:
        cli.out("Skipping virtual env")
        return
    cli.out("Creating virtual environment")
    log.debug("venv args: %s", cmd_args)
    try:
        subprocess.check_call(cmd_args)
    except subprocess.CalledProcessError as e:
        cli.error(str(e), exit_status=e.returncode)

def _venv_cmd_args(config):
    args = ["virtualenv", config.env_dir]
    args.extend(["--prompt", "({}) ".format(config.env_name)])
    if config.venv_python:
        args.extend(["--python", config.venv_python])
    return args

def _install_guild(config):
    """Install Guild into env.

    If the running Guild program is from `guild/scripts` (i.e. running
    in dev mode), only the Guild requirements, as defined in
    `guild/requirements.txt` are installed, under the assumption that
    `guild/scripts` is in `PATH` or an alias is being used and
    therefore Guild will be available from the env. In this case only
    the requirements need to be installed.

    If the running Guild program is not from `guild/scripts`, Guild
    will be installed using the env `pip` program.
    """
    if config.guild:
        _install_guild_dist(config)
    else:
        guild_reqs = _guild_reqs_file()
        if guild_reqs:
            _install_guild_reqs(guild_reqs, config)
        else:
            _install_default_guild_dist(config)

def _install_guild_dist(config):
    assert config.guild
    # If config.guild can be parsed as a version, use as
    # 'guildai==VERSION'
    if config.guild[0].isdigit():
        cli.out("Installing Guild %s" % config.guild)
        req = "guildai==%s" % config.guild
    else:
        cli.out("Installing %s" % config.guild)
        req = config.guild
    _install_reqs([req], config)

def _guild_reqs_file():
    guild_location = pkg_resources.resource_filename("guild", "")
    guild_parent = os.path.dirname(guild_location)
    path = os.path.join(guild_parent, "requirements.txt")
    try:
        f = open(path, "r")
    except (IOError, OSError):
        pass
    else:
        with f:
            if "guildai" in f.readline():
                return path
    return None

def _install_guild_reqs(req_files, config):
    cli.out("Installing Guild requirements")
    _install_reqs([req_files], config)

def _install_default_guild_dist(config):
    req = "guildai==%s" % guild.__version__
    cli.out("Installing Guild %s" % req)
    _install_reqs([req], config)

def _pip_bin(env_dir):
    pip_bin = os.path.join(env_dir, "bin", "pip")
    assert os.path.exists(pip_bin), pip_bin
    return pip_bin

def _pip_extra_opts(config):
    if config.no_progress:
        return ["--progress", "off"]
    else:
        return []

def _install_reqs(reqs, config):
    cmd_args = [_pip_bin(config.env_dir), "install"] + _pip_extra_opts(config)
    for req in reqs:
        if os.path.exists(req):
            cmd_args.extend(["-r", req])
        else:
            cmd_args.append(req)
    log.debug("pip cmd: %s", cmd_args)
    try:
        subprocess.check_call(cmd_args, env={"PATH": ""})
    except subprocess.CalledProcessError as e:
        cli.error(str(e), exit_status=e.returncode)

def _install_guild_pkg_reqs(config):
    reqs = _guild_to_pip_package_reqs(config.guild_pkg_reqs)
    if reqs:
        cli.out("Installing Guild package requirements")
        _install_reqs(reqs, config)

def _guild_to_pip_package_reqs(guild_reqs):
    return [req for req in guild_reqs if not _is_python_req(req)]

def _is_python_req(spec):
    try:
        req = pkg_resources.Requirement.parse(spec)
    except pkg_resources.RequirementParseError:
        return False
    else:
        return req.name == "python"

def _install_user_reqs(config):
    if config.user_reqs:
        cli.out("Installing Python requirements")
        _install_reqs(config.user_reqs, config)

def _install_paths(config):
    if not config.paths:
        return
    site_packages = _env_site_packages(config.env_dir)
    for path in config.paths:
        _write_path(path, site_packages)

def _env_site_packages(env_dir):
    python_bin = os.path.join(env_dir, "bin", "python")
    out = subprocess.check_output([python_bin, "-m", "site"])
    site_packages_pattern = r"(%s.+?site-packages)" % env_dir
    m = re.search(site_packages_pattern, out)
    assert m, out
    return m.group(1)

def _write_path(path, target_dir):
    path = os.path.abspath(path)
    with open(_pth_filename(target_dir, path), "w") as f:
        f.write(path)

def _pth_filename(target_dir, path):
    digest = hashlib.md5(path).hexdigest()[:8]
    pth_name = "%s-%s.pth" % (os.path.basename(path), digest)
    return os.path.join(target_dir, pth_name)

def _python_interpreter_for_arg(arg):
    if arg.startswith("python"):
        return arg
    # Assume arg is a version
    return "python{}".format(arg)

def _python_interpreter_for_reqs(reqs):
    """Returns best guess Python interpreter for reqs.

    If reqs contains a `python` package req, we use it to guess the
    interpreter.
    """
    python_reqs = list(_iter_python_reqs(reqs))
    python_vers = [ver for _path, ver in sorted(util.python_interpreters())]
    for req in python_reqs:
        matching_vers = list(req.specifier.filter(python_vers))
        if matching_vers:
            return "python%s" % matching_vers[0]
    return None

def _iter_python_reqs(reqs):
    for spec in reqs:
        try:
            req = pkg_resources.Requirement.parse(spec)
        except pkg_resources.RequirementParseError:
            pass
        else:
            if req.name == "python":
                yield req

def _initialized_msg(config):
    cli.out(
        "Guild environment initialized in {}."
        "\n".format(_shorten_path(config.env_dir)))
    cli.out("To activate it " "run:\n")
    env_parent = os.path.dirname(config.env_dir)
    if env_parent != os.getcwd():
        cli.out("  source guild-env {}".format(config.env_dir))
    else:
        env_name = os.path.basename(config.env_dir)
        if env_name != "env":
            cli.out("  source guild-env {}".format(env_name))
        else:
            cli.out("  source guild-env")
    cli.out()

def _source_cmd(env_dir):
    name = os.path.basename(env_dir)
    if name == "env":
        return "source guild-env"
    else:
        return "source guild-env {}".format(name)
<|endoftext|>


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

from __future__ import absolute_import
from __future__ import division

import logging
import os
import re
import socket

import click

import guild
import guild.run

from guild import cli
from guild import config
from guild import util
from guild import var
from guild import view

from guild.commands import runs_impl

log = logging.getLogger("guild")

VIEW_FILES_REFRESH_INTERVAL = 3

class ViewDataImpl(view.ViewData):

    def __init__(self, args):
        self._args = args

    def runs(self):
        return runs_impl.runs_for_args(self._args)

    def runs_data(self):
        return list(self._runs_data_iter(self.runs()))

    @staticmethod
    def one_run(run_id_prefix):
        try:
            id, path = next(var.find_runs(run_id_prefix))
        except StopIteration:
            return None
        else:
            run = guild.run.Run(id, path)
            return runs_impl.init_opref_attr(run)

    def one_run_data(self, run_id_prefix):
        run = self.one_run(run_id_prefix)
        if not run:
            return None
        return self._run_data(run)

    def _runs_data_iter(self, runs):
        for run in runs:
            try:
                yield self._run_data(run)
            except Exception as e:
                if log.getEffectiveLevel() <= logging.DEBUG:
                    log.exception("error processing run data for %s", run.id)
                else:
                    log.error("error processing run data for %s: %r", run.id, e)

    def _run_data(self, run):
        formatted = runs_impl.format_run(run)
        return {
            "id": run.id,
            "shortId": run.short_id,
            "path": run.path,
            "operation": formatted["operation"],
            "opModel": run.opref.model_name,
            "opName": run.opref.op_name,
            "started": formatted["started"],
            "stopped": formatted["stopped"],
            "time": self._run_duration(run),
            "label": formatted["label"],
            "status": run.status,
            "exitStatus": formatted["exit_status"] or None,
            "command": formatted["command"],
            "otherAttrs": self._other_attrs(run),
            "flags": run.get("flags", {}),
            "env": run.get("env", {}),
            "deps": self._format_deps(run.get("deps", {})),
            "files": self._format_files(run.iter_files(), run.path),
        }

    @staticmethod
    def _run_duration(run):
        started = run.get("started")
        if run.status == "running":
            return util.format_duration(started)
        stopped = run.get("stopped")
        if stopped:
            return util.format_duration(started, stopped)
        return ""

    @staticmethod
    def _other_attrs(run):
        return {
            name: run.get(name)
            for name in runs_impl.other_attr_names(run)
        }

    def _format_deps(self, deps):
        runs_dir = var.runs_dir()
        runs = {}
        for paths in deps.values():
            for path in paths:
                if not path.startswith(runs_dir):
                    continue
                subdir = path[len(runs_dir)+1:]
                parts = subdir.split(os.path.sep)
                run_id = parts[0]
                try:
                    run, run_paths = runs[run_id]
                except KeyError:
                    run = self._run_for_id(run_id)
                    run_paths = []
                    runs[run_id] = run, run_paths
                run_paths.append(os.path.join(*parts[1:]))
        formatted = [
            self._format_dep(run, paths)
            for run, paths in runs.values()
        ]
        return sorted(formatted, key=lambda x: x["operation"])

    @staticmethod
    def _run_for_id(run_id):
        return runs_impl.init_opref_attr(var.get_run(run_id))

    @staticmethod
    def _format_dep(run, paths):
        return {
            "run": run.short_id,
            "operation": runs_impl.format_op_desc(run.opref, nowarn=True),
            "paths": paths
        }

    def _format_files(self, files, root):
        filtered = [
            path for path in files
            if os.path.islink(path) or os.path.isfile(path)
        ]
        formatted = [self._format_file(path, root) for path in filtered]
        return sorted(formatted, key=lambda x: x["path"])

    def _format_file(self, path, root):
        typeDesc, icon, iconTooltip, viewer = self._file_type_info(path)
        opDesc, opRun = self._op_source_info(path)
        relpath = path[len(root)+1:]
        mtime = util.safe_mtime(path)
        if mtime:
            mtime = int(mtime * 1000)
        return {
            "path": relpath,
            "size": util.safe_filesize(path),
            "mtime": mtime,
            "type": typeDesc,
            "icon": icon,
            "iconTooltip": iconTooltip,
            "viewer": viewer,
            "operation": opDesc,
            "run": opRun,
        }

    def _file_type_info(self, path):
        typeDesc, icon, iconTooltip, viewer = self._base_file_type_info(path)
        if os.path.islink(path):
            target = os.path.realpath(path)
            link_type = "directory" if os.path.isdir(target) else "file"
            if target.startswith(var.runs_dir()):
                typeDesc = "Link to operation output"
            elif target.startswith(var.cache_dir()):
                typeDesc = "Link to resource {}".format(link_type)
            else:
                typeDesc = "Link"
            icon = "folder-move" if link_type == "directory" else "file-send"
            iconTooltip = "Link"
        return typeDesc, icon, iconTooltip, viewer

    @staticmethod
    def _base_file_type_info(path):
        path_lower = path.lower()
        if re.search(r"\.tfevents\.", path_lower):
            return "Event log", "file-chart", "File", None
        elif re.search(r"saved_model\.pb$", path_lower):
            return "SavedModel protocol buffer", "file", "File", None
        elif re.search(r"graph\.pb$", path_lower):
            return "GraphDef protocol buffer", "file", "File", None
        elif re.search(r"saved_model\.pbtxt$", path_lower):
            return (
                "SavedModel protocol buffer", "file-document", "Text file",
                "text")
        elif re.search(r"graph\.pbtxt$", path_lower):
            return (
                "GraphDef protocol buffer", "file-document", "Text file",
                "text")
        elif re.search(r"\.index$", path_lower):
            return "Checkpoint index", "file", "File", None
        elif re.search(r"\.meta$", path_lower):
            return "Checkpoint meta graph", "file", "File", None
        elif re.search(r"[/\\]checkpoint$", path_lower):
            return "Latest checkpoint marker", "file", "File", None
        elif re.search(r"data-\d+-of-\d+$", path_lower):
            return "Checkpoint values", "file", "File", None
        elif re.search(r"\.tfrecord$", path_lower):
            return "Dataset file", "file", "File", None
        elif re.search(r"\.(jpg|jpeg|gif|png|tiff)$", path_lower):
            return "Image", "file-image", "Image", "image"
        elif re.search(r"\.mid", path_lower):
            return "Audio", "file-music", "Audio", "midi"
        elif re.search(r"\.(wav|mp3)", path_lower):
            return "Audio", "file-music", "Audio", None
        elif re.search(r"\.(csv|tsv)", path_lower):
            return "Table", "file-delimited", "Delimited", "table"
        else:
            if util.is_text_file(path):
                return "Text file", "file-document", "Text file", "text"
            else:
                return "File", "file", "File", None

    def _op_source_info(self, path):
        if not os.path.islink(path):
            return None, None
        path = os.path.realpath(path)
        runs_dir = var.runs_dir()
        if not path.startswith(runs_dir):
            return None, None
        subdir = path[len(runs_dir)+1:]
        parts = subdir.split(os.path.sep, 1)
        try:
            run = self._run_for_id(parts[0])
        except LookupError:
            return "%s (deleted)" % parts[0][:8], None
        else:
            operation = runs_impl.format_op_desc(run.opref, nowarn=True)
            return operation, run.short_id

    def config(self):
        cwd = util.format_dir(config.cwd())
        return {
            "cwd": cwd,
            "titleLabel": self._title_label(cwd),
            "version": guild.version(),
        }

    def _title_label(self, cwd):
        if len(self._args.runs) == 1:
            return self._single_run_title_label(self._args.runs[0])
        elif self._args.all:
            return self._all_title_label(self._args)
        else:
            return self._cwd_title_label(cwd)

    @staticmethod
    def _single_run_title_label(run_id):
        return "[{}]".format(run_id)

    @staticmethod
    def _all_title_label(args):
        if args.ops:
            return "all {}".format(" ".join(args.ops))
        else:
            return "all"

    @staticmethod
    def _cwd_title_label(cwd):
        parts = cwd.split(os.path.sep)
        if len(parts) < 2:
            return cwd
        else:
            return os.path.join(*parts[-2:])

def main(args):
    if args.files:
        _view_files(args)
    else:
        _start_view(args)

def _view_files(args):
    with util.TempDir("guild-view-") as logdir:
        log.debug("Using logdir %s", logdir)
        list_runs_cb = lambda: runs_impl.runs_for_args(args)
        monitor = util.RunsMonitor(
            list_runs_cb, logdir, VIEW_FILES_REFRESH_INTERVAL)
        monitor.start()
        click.launch(logdir)
        print("Monitoring runs at %s (Press CTRL+C to quit)" % logdir)
        try:
            util.wait_forever()
        except KeyboardInterrupt:
            pass
        log.debug("Stopping")
        monitor.stop()
        log.debug("Removing logdir %s", logdir) # Handled by ctx mgr
    if util.PLATFORM != "Windows":
        cli.out()

def _start_view(args):
    data = ViewDataImpl(args)
    host = _host(args)
    port = args.port or util.free_port()
    if args.test:
        _start_tester(host, port)
        args.no_open = True
    try:
        view.serve_forever(
            data,
            host,
            port,
            args.no_open,
            args.dev,
            args.logging)
    except socket.gaierror as e:
        cli.error(str(e))

def _host(args):
    if args.host:
        return args.host
    if args.dev:
        return "localhost"
    return "0.0.0.0"

def _start_tester(host, port):
    from . import view_tester
    view_tester.start_tester(host, port, os._exit)
<commit_msg>Handle missing run deps<commit_after># Copyright 2017-2018 TensorHub, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

from __future__ import absolute_import
from __future__ import division

import logging
import os
import re
import socket

import click

import guild
import guild.run

from guild import cli
from guild import config
from guild import util
from guild import var
from guild import view

from guild.commands import runs_impl

log = logging.getLogger("guild")

VIEW_FILES_REFRESH_INTERVAL = 3

class ViewDataImpl(view.ViewData):

    def __init__(self, args):
        self._args = args

    def runs(self):
        return runs_impl.runs_for_args(self._args)

    def runs_data(self):
        return list(self._runs_data_iter(self.runs()))

    @staticmethod
    def one_run(run_id_prefix):
        try:
            id, path = next(var.find_runs(run_id_prefix))
        except StopIteration:
            return None
        else:
            run = guild.run.Run(id, path)
            return runs_impl.init_opref_attr(run)

    def one_run_data(self, run_id_prefix):
        run = self.one_run(run_id_prefix)
        if not run:
            return None
        return self._run_data(run)

    def _runs_data_iter(self, runs):
        for run in runs:
            try:
                yield self._run_data(run)
            except Exception as e:
                if log.getEffectiveLevel() <= logging.DEBUG:
                    log.exception("error processing run data for %s", run.id)
                else:
                    log.error("error processing run data for %s: %r", run.id, e)

    def _run_data(self, run):
        formatted = runs_impl.format_run(run)
        return {
            "id": run.id,
            "shortId": run.short_id,
            "path": run.path,
            "operation": formatted["operation"],
            "opModel": run.opref.model_name,
            "opName": run.opref.op_name,
            "started": formatted["started"],
            "stopped": formatted["stopped"],
            "time": self._run_duration(run),
            "label": formatted["label"],
            "status": run.status,
            "exitStatus": formatted["exit_status"] or None,
            "command": formatted["command"],
            "otherAttrs": self._other_attrs(run),
            "flags": run.get("flags", {}),
            "env": run.get("env", {}),
            "deps": self._format_deps(run.get("deps", {})),
            "files": self._format_files(run.iter_files(), run.path),
        }

    @staticmethod
    def _run_duration(run):
        started = run.get("started")
        if run.status == "running":
            return util.format_duration(started)
        stopped = run.get("stopped")
        if stopped:
            return util.format_duration(started, stopped)
        return ""

    @staticmethod
    def _other_attrs(run):
        return {
            name: run.get(name)
            for name in runs_impl.other_attr_names(run)
        }

    def _format_deps(self, deps):
        runs_dir = var.runs_dir()
        runs = {}
        for paths in deps.values():
            for path in paths:
                if not path.startswith(runs_dir):
                    continue
                subdir = path[len(runs_dir)+1:]
                parts = subdir.split(os.path.sep)
                run_id = parts[0]
                try:
                    run, run_paths = runs[run_id]
                except KeyError:
                    run_paths = []
                    try:
                        run = self._run_for_id(run_id)
                    except LookupError:
                        # Dep run is missing - silently drop it from
                        # the list of deps.
                        pass
                    else:
                        runs[run_id] = run, run_paths
                run_paths.append(os.path.join(*parts[1:]))
        formatted = [
            self._format_dep(run, paths)
            for run, paths in runs.values()
        ]
        return sorted(formatted, key=lambda x: x["operation"])

    @staticmethod
    def _run_for_id(run_id):
        run = var.get_run(run_id)
        return runs_impl.init_opref_attr(run)

    @staticmethod
    def _format_dep(run, paths):
        return {
            "run": run.short_id,
            "operation": runs_impl.format_op_desc(run.opref, nowarn=True),
            "paths": paths
        }

    def _format_files(self, files, root):
        filtered = [
            path for path in files
            if os.path.islink(path) or os.path.isfile(path)
        ]
        formatted = [self._format_file(path, root) for path in filtered]
        return sorted(formatted, key=lambda x: x["path"])

    def _format_file(self, path, root):
        typeDesc, icon, iconTooltip, viewer = self._file_type_info(path)
        opDesc, opRun = self._op_source_info(path)
        relpath = path[len(root)+1:]
        mtime = util.safe_mtime(path)
        if mtime:
            mtime = int(mtime * 1000)
        return {
            "path": relpath,
            "size": util.safe_filesize(path),
            "mtime": mtime,
            "type": typeDesc,
            "icon": icon,
            "iconTooltip": iconTooltip,
            "viewer": viewer,
            "operation": opDesc,
            "run": opRun,
        }

    def _file_type_info(self, path):
        typeDesc, icon, iconTooltip, viewer = self._base_file_type_info(path)
        if os.path.islink(path):
            target = os.path.realpath(path)
            link_type = "directory" if os.path.isdir(target) else "file"
            if target.startswith(var.runs_dir()):
                typeDesc = "Link to operation output"
            elif target.startswith(var.cache_dir()):
                typeDesc = "Link to resource {}".format(link_type)
            else:
                typeDesc = "Link"
            icon = "folder-move" if link_type == "directory" else "file-send"
            iconTooltip = "Link"
        return typeDesc, icon, iconTooltip, viewer

    @staticmethod
    def _base_file_type_info(path):
        path_lower = path.lower()
        if re.search(r"\.tfevents\.", path_lower):
            return "Event log", "file-chart", "File", None
        elif re.search(r"saved_model\.pb$", path_lower):
            return "SavedModel protocol buffer", "file", "File", None
        elif re.search(r"graph\.pb$", path_lower):
            return "GraphDef protocol buffer", "file", "File", None
        elif re.search(r"saved_model\.pbtxt$", path_lower):
            return (
                "SavedModel protocol buffer", "file-document", "Text file",
                "text")
        elif re.search(r"graph\.pbtxt$", path_lower):
            return (
                "GraphDef protocol buffer", "file-document", "Text file",
                "text")
        elif re.search(r"\.index$", path_lower):
            return "Checkpoint index", "file", "File", None
        elif re.search(r"\.meta$", path_lower):
            return "Checkpoint meta graph", "file", "File", None
        elif re.search(r"[/\\]checkpoint$", path_lower):
            return "Latest checkpoint marker", "file", "File", None
        elif re.search(r"data-\d+-of-\d+$", path_lower):
            return "Checkpoint values", "file", "File", None
        elif re.search(r"\.tfrecord$", path_lower):
            return "Dataset file", "file", "File", None
        elif re.search(r"\.(jpg|jpeg|gif|png|tiff)$", path_lower):
            return "Image", "file-image", "Image", "image"
        elif re.search(r"\.mid", path_lower):
            return "Audio", "file-music", "Audio", "midi"
        elif re.search(r"\.(wav|mp3)", path_lower):
            return "Audio", "file-music", "Audio", None
        elif re.search(r"\.(csv|tsv)", path_lower):
            return "Table", "file-delimited", "Delimited", "table"
        else:
            if util.is_text_file(path):
                return "Text file", "file-document", "Text file", "text"
            else:
                return "File", "file", "File", None

    def _op_source_info(self, path):
        if not os.path.islink(path):
            return None, None
        path = os.path.realpath(path)
        runs_dir = var.runs_dir()
        if not path.startswith(runs_dir):
            return None, None
        subdir = path[len(runs_dir)+1:]
        parts = subdir.split(os.path.sep, 1)
        try:
            run = self._run_for_id(parts[0])
        except LookupError:
            return "%s (deleted)" % parts[0][:8], None
        else:
            operation = runs_impl.format_op_desc(run.opref, nowarn=True)
            return operation, run.short_id

    def config(self):
        cwd = util.format_dir(config.cwd())
        return {
            "cwd": cwd,
            "titleLabel": self._title_label(cwd),
            "version": guild.version(),
        }

    def _title_label(self, cwd):
        if len(self._args.runs) == 1:
            return self._single_run_title_label(self._args.runs[0])
        elif self._args.all:
            return self._all_title_label(self._args)
        else:
            return self._cwd_title_label(cwd)

    @staticmethod
    def _single_run_title_label(run_id):
        return "[{}]".format(run_id)

    @staticmethod
    def _all_title_label(args):
        if args.ops:
            return "all {}".format(" ".join(args.ops))
        else:
            return "all"

    @staticmethod
    def _cwd_title_label(cwd):
        parts = cwd.split(os.path.sep)
        if len(parts) < 2:
            return cwd
        else:
            return os.path.join(*parts[-2:])

def main(args):
    if args.files:
        _view_files(args)
    else:
        _start_view(args)

def _view_files(args):
    with util.TempDir("guild-view-") as logdir:
        log.debug("Using logdir %s", logdir)
        list_runs_cb = lambda: runs_impl.runs_for_args(args)
        monitor = util.RunsMonitor(
            list_runs_cb, logdir, VIEW_FILES_REFRESH_INTERVAL)
        monitor.start()
        click.launch(logdir)
        print("Monitoring runs at %s (Press CTRL+C to quit)" % logdir)
        try:
            util.wait_forever()
        except KeyboardInterrupt:
            pass
        log.debug("Stopping")
        monitor.stop()
        log.debug("Removing logdir %s", logdir) # Handled by ctx mgr
    if util.PLATFORM != "Windows":
        cli.out()

def _start_view(args):
    data = ViewDataImpl(args)
    host = _host(args)
    port = args.port or util.free_port()
    if args.test:
        _start_tester(host, port)
        args.no_open = True
    try:
        view.serve_forever(
            data,
            host,
            port,
            args.no_open,
            args.dev,
            args.logging)
    except socket.gaierror as e:
        cli.error(str(e))

def _host(args):
    if args.host:
        return args.host
    if args.dev:
        return "localhost"
    return "0.0.0.0"

def _start_tester(host, port):
    from . import view_tester
    view_tester.start_tester(host, port, os._exit)
<|endoftext|>


<commit_before># Author:  Lisandro Dalcin
# Contact: dalcinl@gmail.com
# Id: $Id$

# --------------------------------------------------------------------

__author__    = 'Lisandro Dalcin'
__credits__   = "PETSc Team <petsc-maint@mcs.anl.gov>"
__version__   = '1.0.0'

__docformat__ = 'reStructuredText'

# --------------------------------------------------------------------

def init(args=None, arch=None):
    """
    Initializes PETSc.

    :Parameters:
      - `args`: command-line arguments, usually the 'sys.argv' list.
      - `arch`: specific configuration to use.

    .. note:: This function should be called only once, typically at
      the very beginning of the bootstrap script of an application.
    """
    import petsc4py.lib
    PETSc = petsc4py.lib.Import(arch)
    if args is None:
        args = []
    elif isinstance(args, str):
        args = str(args).split()
    else:
        args = [str(a) for a in args]
        args = [a for a in args if a]
    if args and args[0].startswith('-'):
        import sys
        argv = getattr(sys, 'argv', None)
        program_name = None
        if argv and type(argv[0]) is str:
            program_name = argv[0]
        if not program_name:
            program_name = getattr(sys, 'executable', 'python')
        args.insert(0, program_name)
    PETSc._initialize(args)

# --------------------------------------------------------------------

def get_include():
    """
    Return the directory in the package that contains header files.

    Extension modules that need to compile against petsc4py should use
    this function to locate the appropriate include directory. Using
    Python distutils (or perhaps NumPy distutils)::

      import petsc4py
      Extension('extension_name', ...
                include_dirs=[..., petsc4py.get_include()])
    """
    from os.path import dirname, join
    return join(dirname(__file__), 'include')

# --------------------------------------------------------------------

if __name__ == '__main__':
    import petsc4py.help
    petsc4py.help.help()

# --------------------------------------------------------------------
<commit_msg>Extend the package docstring<commit_after># Author:  Lisandro Dalcin
# Contact: dalcinl@gmail.com
# Id: $Id$

# --------------------------------------------------------------------

"""
PETSc for Python
================

This package is an interface to PETSc libraries.

PETSc_ (the Portable, Extensible Toolkit for Scientific Computation)
is a suite of data structures and routines for the scalable (parallel)
solution of scientific applications modeled by partial differential
equations. It employs the MPI_ standard for all message-passing
communication.

.. _PETSc: http://www-unix.mcs.anl.gov/petsc
.. _MPI:   http://www.mpi-forum.org

"""

__author__    = 'Lisandro Dalcin'
__version__   = '1.0.0'
__credits__   = "PETSc Team <petsc-maint@mcs.anl.gov>"

# --------------------------------------------------------------------

def init(args=None, arch=None):
    """
    Initializes PETSc.

    :Parameters:
      - `args`: command-line arguments, usually the 'sys.argv' list.
      - `arch`: specific configuration to use.

    .. note:: This function should be called only once, typically at
      the very beginning of the bootstrap script of an application.
    """
    import petsc4py.lib
    PETSc = petsc4py.lib.Import(arch)
    if args is None:
        args = []
    elif isinstance(args, str):
        args = str(args).split()
    else:
        args = [str(a) for a in args]
        args = [a for a in args if a]
    if args and args[0].startswith('-'):
        import sys
        argv = getattr(sys, 'argv', None)
        program_name = None
        if argv and type(argv[0]) is str:
            program_name = argv[0]
        if not program_name:
            program_name = getattr(sys, 'executable', 'python')
        args.insert(0, program_name)
    PETSc._initialize(args)

# --------------------------------------------------------------------

def get_include():
    """
    Return the directory in the package that contains header files.

    Extension modules that need to compile against petsc4py should use
    this function to locate the appropriate include directory. Using
    Python distutils (or perhaps NumPy distutils)::

      import petsc4py
      Extension('extension_name', ...
                include_dirs=[..., petsc4py.get_include()])
    """
    from os.path import dirname, join
    return join(dirname(__file__), 'include')

# --------------------------------------------------------------------

if __name__ == '__main__':
    import petsc4py.help
    petsc4py.help.help()

# --------------------------------------------------------------------
<|endoftext|>


<commit_before># -*- coding: iso-8859-1 -*-
# -----------------------------------------------------------------------------
# display - Interface to the display code
# -----------------------------------------------------------------------------
# $Id$
#
# -----------------------------------------------------------------------------
# kaa-display - X11/SDL Display module
# Copyright (C) 2005 Dirk Meyer, Jason Tackaberry
#
# First Edition: Dirk Meyer <dmeyer@tzi.de>
# Maintainer:    Dirk Meyer <dmeyer@tzi.de>
#
# Please see the file doc/CREDITS for a complete list of authors.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MER-
# CHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
# Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# -----------------------------------------------------------------------------

# python imports
import weakref
import time
import threading
import select
import os

# the display module
import _Display

# kaa notifier for the socket callback
import kaa.notifier
from kaa.notifier import Signal

# default X11 display
_default_x11_display = None

_keysym_names = {
    338: "up",
    340: "down",
    337: "left",
    339: "right",

    446: "F1",
    447: "F2",
    448: "F3",
    449: "F4",
    450: "F5",
    451: "F6",
    452: "F7",
    453: "F8",
    454: "F9",
    455: "F10",
    456: "F11",
    457: "F21",

    355: "ins",
    511: "del",
    336: "home",
    343: "end",
    283: "esc",
    269: "enter",
    264: "backspace",
    32: "space",

    489: "left-alt",
    490: "right-alt",
    483: "left-ctrl",
    484: "right-ctrl",
    481: "left-shift",
    482: "right-shift",
    359: "menu",
    275: "pause",

    # keypad
    427: "kp_plus",
    429: "kp_minus"
}

class X11Display(object):

    XEVENT_MOTION_NOTIFY = 6
    XEVENT_EXPOSE = 12
    XEVENT_BUTTON_PRESS = 4
    XEVENT_KEY_PRESS = 2
    XEVENT_EXPOSE = 12
    XEVENT_UNMAP_NOTIFY = 18
    XEVENT_MAP_NOTIFY = 19
    XEVENT_CONFIGURE_NOTIFY = 22

    #XEVENT_WINDOW_EVENTS = (6, 12, 4, 2, 22, 18, 19)

    def __init__(self, dispname = ""):
        self._display = _Display.X11Display(dispname)
        self._windows = {}

        dispatcher = kaa.notifier.WeakSocketDispatcher(self.handle_events)
        dispatcher.register(self.socket)
        # Also connect to the idle signal. It is a bad hack, but when
        # drawing is done, the socket is read and we will miss keypress
        # events when doing drawings.
        kaa.notifier.signals['idle'].connect_weak(self.handle_events)

    def handle_events(self):
        window_events = {}
        for event, data in self._display.handle_events():
            wid = 0
            if event in X11Display.XEVENT_WINDOW_EVENTS:
                wid = data["window"]
            if wid:
                if wid not in window_events:
                    window_events[wid] = []
                window_events[wid].append((event, data))

        for wid, events in window_events.items():
            assert(wid in self._windows)
            window = self._windows[wid]()
            if not window:
                # Window no longer exists.
                del self._windows[wid]
            else:
                window.handle_events(events)

        # call the socket again
        return True

    def __getattr__(self, attr):
        if attr in ("socket,"):
            return getattr(self._display, attr)

        return getattr(super(X11Display, self), attr)

    def sync(self):
        return self._display.sync()

    def lock(self):
        return self._display.lock()

    def unlock(self):
        return self._display.unlock()

    def get_size(self, screen = -1):
        return self._display.get_size(screen)

    def get_string(self):
        return self._display.get_string()

X11Display.XEVENT_WINDOW_EVENTS_LIST = filter(lambda x: x.find("XEVENT_") != -1, dir(X11Display))
X11Display.XEVENT_WINDOW_EVENTS = map(lambda x: getattr(X11Display, x), X11Display.XEVENT_WINDOW_EVENTS_LIST)

def _get_display(display):
    if not display:
        global _default_x11_display
        if not _default_x11_display:
            _default_x11_display = X11Display()
        display = _default_x11_display

    assert(type(display) == X11Display)
    return display

class X11Window(object):
    def __init__(self, display = None, window = None, **kwargs):
        display = _get_display(display)
        if window:
            self._window = window
        else:
            assert("size" in kwargs)
            if "title" not in kwargs:
                kwargs["title"] = "Kaa Window"
            self._window = _Display.X11Window(display._display,
                                              kwargs["size"], kwargs["title"])

        self._display = display
        display._windows[self._window.ptr] = weakref.ref(self)
        self._cursor_hide_timeout = -1
        self._cursor_hide_timer = kaa.notifier.WeakOneShotTimer(self._cursor_hide_cb)
        self._cursor_visible = True
        self._fs_size_save = None

        self.signals = {
            "key_press_event": Signal(),
            "expose_event": Signal(),
            "map_event": Signal(),
            "unmap_event": Signal(),
        }

    def get_display(self):
        return self._display

    def show(self):
        self._window.show()
        self._display.handle_events()

    def hide(self):
        self._window.hide()
        self._display.handle_events()

    def set_visible(self, visible = True):
        if visible:
            self.show()
        else:
            self.hide()

    def get_visible(self):
        return self._window.get_visible()

    def render_imlib2_image(self, i, dst_pos = (0, 0), src_pos = (0, 0),
                            size = (-1, -1), dither = True, blend = False):
        return _Display.render_imlib2_image(self._window, i._image, dst_pos, \
                                            src_pos, size, dither, blend)

    def handle_events(self, events):
        expose_regions = []
        for event, data in events:
            if event == X11Display.XEVENT_MOTION_NOTIFY:
                # Mouse moved, so show cursor.
                if self._cursor_hide_timeout != 0 and not self._cursor_visible:
                    self.set_cursor_visible(True)

                self._cursor_hide_timer.start(self._cursor_hide_timeout)

            elif event == X11Display.XEVENT_KEY_PRESS:
                key = data["key"]
                if key in _keysym_names:
                    key = _keysym_names[key]
                elif key < 255:
                    key = chr(key)
                self.signals["key_press_event"].emit(key)

            elif event == X11Display.XEVENT_EXPOSE:
                # Queue expose regions so we only need to emit one signal.
                expose_regions.append((data["pos"], data["size"]))

            elif event == X11Display.XEVENT_MAP_NOTIFY:
                self.signals["map_event"].emit()

        if len(expose_regions) > 0:
            self.signals["expose_event"].emit(expose_regions)

    def move(self, pos, force = False):
        return self.set_geometry(pos, (-1, -1))

    def resize(self, size, force = False):
        return self.set_geometry((-1, -1), size, force)

    def set_geometry(self, pos, size, force = False):
        if self.get_fullscreen() and not force:
            self._fs_size_save = size
            return False

        self._window.set_geometry(pos, size)
        self._display.handle_events()
        return True

    def get_geometry(self):
        return self._window.get_geometry()

    def get_size(self):
        return self.get_geometry()[1]

    def get_pos(self):
        return self.get_geometry()[0]

    def set_cursor_visible(self, visible):
        self._window.set_cursor_visible(visible)
        self._cursor_visible = visible
        self._display.handle_events()

    def _cursor_hide_cb(self):
        self.set_cursor_visible(False)

    def set_cursor_hide_timeout(self, timeout):
        self._cursor_hide_timeout = timeout
        self._cursor_hide_timer.start(self._cursor_hide_timeout)

    def set_fullscreen(self, fs = True):
        if not fs:
            if self._fs_size_save:
                self.resize(self._fs_size_save, force = True)
                self._window.set_fullscreen(False)
                self._fs_size_save = None
                return True

            return False

        if self._fs_size_save:
            return False

        self._fs_size_save = self.get_size()
        display_size = self.get_display().get_size()
        self._window.set_fullscreen(True)
        self.resize(display_size, force = True)

    def get_fullscreen(self):
        return self._fs_size_save != None

    def get_id(self):
        return hex(self._window.ptr)

class EvasX11Window(X11Window):
    def __init__(self, gl = False, display = None, size = (640, 480),
                 title = "Evas", **kwargs):
        import kaa.evas

        if not gl:
            f = _Display.new_evas_software_x11
        else:
            f = _Display.new_evas_gl_x11

        assert(type(size) == tuple)
        display = _get_display(display)
        self._evas = kaa.evas.Evas()
        window = f(self._evas._evas, display._display, size = size,
                   title = title)
        self._evas.output_size_set(size)
        self._evas.viewport_set((0, 0), size)
        # Ensures the display remains alive until after Evas gets deallocated
        # during garbage collection.
        self._evas._dependencies.append(display._display)
        super(EvasX11Window, self).__init__(display, window)

    def handle_events(self, events):
        needs_render = False
        for event, args in events:
            if event == X11Display.XEVENT_EXPOSE:
                self._evas.damage_rectangle_add((args[0], args[1]))
                needs_render = True
            elif event == X11Display.XEVENT_CONFIGURE_NOTIFY:
                if args[1] != self._evas.output_size_get():
                    # This doesn't act right for gl.
                    self._evas.output_size_set(args[1])
                    self._evas.viewport_set((0, 0), args[1])
                    needs_render = True

        super(EvasX11Window, self).handle_events(events)

        if needs_render:
            self._evas.render()
            self._display.handle_events()

    def get_evas(self):
        return self._evas
<commit_msg>Fix EvasWindow to reflect event API change (dicts instead of tuples for each event)<commit_after># -*- coding: iso-8859-1 -*-
# -----------------------------------------------------------------------------
# display - Interface to the display code
# -----------------------------------------------------------------------------
# $Id$
#
# -----------------------------------------------------------------------------
# kaa-display - X11/SDL Display module
# Copyright (C) 2005 Dirk Meyer, Jason Tackaberry
#
# First Edition: Dirk Meyer <dmeyer@tzi.de>
# Maintainer:    Dirk Meyer <dmeyer@tzi.de>
#
# Please see the file doc/CREDITS for a complete list of authors.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of MER-
# CHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
# Public License for more details.
#
# You should have received a copy of the GNU General Public License along
# with this program; if not, write to the Free Software Foundation, Inc.,
# 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
#
# -----------------------------------------------------------------------------

# python imports
import weakref
import time
import threading
import select
import os

# the display module
import _Display

# kaa notifier for the socket callback
import kaa.notifier
from kaa.notifier import Signal

# default X11 display
_default_x11_display = None

_keysym_names = {
    338: "up",
    340: "down",
    337: "left",
    339: "right",

    446: "F1",
    447: "F2",
    448: "F3",
    449: "F4",
    450: "F5",
    451: "F6",
    452: "F7",
    453: "F8",
    454: "F9",
    455: "F10",
    456: "F11",
    457: "F21",

    355: "ins",
    511: "del",
    336: "home",
    343: "end",
    283: "esc",
    269: "enter",
    264: "backspace",
    32: "space",

    489: "left-alt",
    490: "right-alt",
    483: "left-ctrl",
    484: "right-ctrl",
    481: "left-shift",
    482: "right-shift",
    359: "menu",
    275: "pause",

    # keypad
    427: "kp_plus",
    429: "kp_minus"
}

class X11Display(object):

    XEVENT_MOTION_NOTIFY = 6
    XEVENT_EXPOSE = 12
    XEVENT_BUTTON_PRESS = 4
    XEVENT_KEY_PRESS = 2
    XEVENT_EXPOSE = 12
    XEVENT_UNMAP_NOTIFY = 18
    XEVENT_MAP_NOTIFY = 19
    XEVENT_CONFIGURE_NOTIFY = 22

    #XEVENT_WINDOW_EVENTS = (6, 12, 4, 2, 22, 18, 19)

    def __init__(self, dispname = ""):
        self._display = _Display.X11Display(dispname)
        self._windows = {}

        dispatcher = kaa.notifier.WeakSocketDispatcher(self.handle_events)
        dispatcher.register(self.socket)
        # Also connect to the idle signal. It is a bad hack, but when
        # drawing is done, the socket is read and we will miss keypress
        # events when doing drawings.
        kaa.notifier.signals['idle'].connect_weak(self.handle_events)

    def handle_events(self):
        window_events = {}
        for event, data in self._display.handle_events():
            wid = 0
            if event in X11Display.XEVENT_WINDOW_EVENTS:
                wid = data["window"]
            if wid:
                if wid not in window_events:
                    window_events[wid] = []
                window_events[wid].append((event, data))

        for wid, events in window_events.items():
            assert(wid in self._windows)
            window = self._windows[wid]()
            if not window:
                # Window no longer exists.
                del self._windows[wid]
            else:
                window.handle_events(events)

        # call the socket again
        return True

    def __getattr__(self, attr):
        if attr in ("socket,"):
            return getattr(self._display, attr)

        return getattr(super(X11Display, self), attr)

    def sync(self):
        return self._display.sync()

    def lock(self):
        return self._display.lock()

    def unlock(self):
        return self._display.unlock()

    def get_size(self, screen = -1):
        return self._display.get_size(screen)

    def get_string(self):
        return self._display.get_string()

X11Display.XEVENT_WINDOW_EVENTS_LIST = filter(lambda x: x.find("XEVENT_") != -1, dir(X11Display))
X11Display.XEVENT_WINDOW_EVENTS = map(lambda x: getattr(X11Display, x), X11Display.XEVENT_WINDOW_EVENTS_LIST)

def _get_display(display):
    if not display:
        global _default_x11_display
        if not _default_x11_display:
            _default_x11_display = X11Display()
        display = _default_x11_display

    assert(type(display) == X11Display)
    return display

class X11Window(object):
    def __init__(self, display = None, window = None, **kwargs):
        display = _get_display(display)
        if window:
            self._window = window
        else:
            assert("size" in kwargs)
            if "title" not in kwargs:
                kwargs["title"] = "Kaa Window"
            self._window = _Display.X11Window(display._display,
                                              kwargs["size"], kwargs["title"])

        self._display = display
        display._windows[self._window.ptr] = weakref.ref(self)
        self._cursor_hide_timeout = -1
        self._cursor_hide_timer = kaa.notifier.WeakOneShotTimer(self._cursor_hide_cb)
        self._cursor_visible = True
        self._fs_size_save = None

        self.signals = {
            "key_press_event": Signal(),
            "expose_event": Signal(),
            "map_event": Signal(),
            "unmap_event": Signal(),
        }

    def get_display(self):
        return self._display

    def show(self):
        self._window.show()
        self._display.handle_events()

    def hide(self):
        self._window.hide()
        self._display.handle_events()

    def set_visible(self, visible = True):
        if visible:
            self.show()
        else:
            self.hide()

    def get_visible(self):
        return self._window.get_visible()

    def render_imlib2_image(self, i, dst_pos = (0, 0), src_pos = (0, 0),
                            size = (-1, -1), dither = True, blend = False):
        return _Display.render_imlib2_image(self._window, i._image, dst_pos, \
                                            src_pos, size, dither, blend)

    def handle_events(self, events):
        expose_regions = []
        for event, data in events:
            if event == X11Display.XEVENT_MOTION_NOTIFY:
                # Mouse moved, so show cursor.
                if self._cursor_hide_timeout != 0 and not self._cursor_visible:
                    self.set_cursor_visible(True)

                self._cursor_hide_timer.start(self._cursor_hide_timeout)

            elif event == X11Display.XEVENT_KEY_PRESS:
                key = data["key"]
                if key in _keysym_names:
                    key = _keysym_names[key]
                elif key < 255:
                    key = chr(key)
                self.signals["key_press_event"].emit(key)

            elif event == X11Display.XEVENT_EXPOSE:
                # Queue expose regions so we only need to emit one signal.
                expose_regions.append((data["pos"], data["size"]))

            elif event == X11Display.XEVENT_MAP_NOTIFY:
                self.signals["map_event"].emit()

        if len(expose_regions) > 0:
            self.signals["expose_event"].emit(expose_regions)

    def move(self, pos, force = False):
        return self.set_geometry(pos, (-1, -1))

    def resize(self, size, force = False):
        return self.set_geometry((-1, -1), size, force)

    def set_geometry(self, pos, size, force = False):
        if self.get_fullscreen() and not force:
            self._fs_size_save = size
            return False

        self._window.set_geometry(pos, size)
        self._display.handle_events()
        return True

    def get_geometry(self):
        return self._window.get_geometry()

    def get_size(self):
        return self.get_geometry()[1]

    def get_pos(self):
        return self.get_geometry()[0]

    def set_cursor_visible(self, visible):
        self._window.set_cursor_visible(visible)
        self._cursor_visible = visible
        self._display.handle_events()

    def _cursor_hide_cb(self):
        self.set_cursor_visible(False)

    def set_cursor_hide_timeout(self, timeout):
        self._cursor_hide_timeout = timeout
        self._cursor_hide_timer.start(self._cursor_hide_timeout)

    def set_fullscreen(self, fs = True):
        if not fs:
            if self._fs_size_save:
                self.resize(self._fs_size_save, force = True)
                self._window.set_fullscreen(False)
                self._fs_size_save = None
                return True

            return False

        if self._fs_size_save:
            return False

        self._fs_size_save = self.get_size()
        display_size = self.get_display().get_size()
        self._window.set_fullscreen(True)
        self.resize(display_size, force = True)

    def get_fullscreen(self):
        return self._fs_size_save != None

    def get_id(self):
        return hex(self._window.ptr)

class EvasX11Window(X11Window):
    def __init__(self, gl = False, display = None, size = (640, 480),
                 title = "Evas", **kwargs):
        import kaa.evas

        if not gl:
            f = _Display.new_evas_software_x11
        else:
            f = _Display.new_evas_gl_x11

        assert(type(size) == tuple)
        display = _get_display(display)
        self._evas = kaa.evas.Evas()
        window = f(self._evas._evas, display._display, size = size,
                   title = title)
        self._evas.output_size_set(size)
        self._evas.viewport_set((0, 0), size)
        # Ensures the display remains alive until after Evas gets deallocated
        # during garbage collection.
        self._evas._dependencies.append(display._display)
        super(EvasX11Window, self).__init__(display, window)

    def handle_events(self, events):
        needs_render = False
        for event, data in events:
            if event == X11Display.XEVENT_EXPOSE:
                self._evas.damage_rectangle_add((data["pos"], data["size"]))
                needs_render = True
            elif event == X11Display.XEVENT_CONFIGURE_NOTIFY:
                if data["size"] != self._evas.output_size_get():
                    # This doesn't act right for gl.
                    self._evas.output_size_set(data["size"])
                    self._evas.viewport_set((0, 0), data["size"])
                    needs_render = True

        super(EvasX11Window, self).handle_events(events)

        if needs_render:
            self._evas.render()
            self._display.handle_events()

    def get_evas(self):
        return self._evas
<|endoftext|>


<commit_before># -*- coding: iso-8859-1 -*-
# -----------------------------------------------------------------------------
# __init__.py - Interface to kaa.epg
# -----------------------------------------------------------------------------
# $Id$
# -----------------------------------------------------------------------------
# kaa.epg - EPG Database
# Copyright (C) 2004-2006 Jason Tackaberry, Dirk Meyer, Rob Shortt
#
# First Edition: Jason Tackaberry <tack@sault.org>
#
# Please see the file AUTHORS for a complete list of authors.
#
# This library is free software; you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License version
# 2.1 as published by the Free Software Foundation.
#
# This library is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301 USA
#
# -----------------------------------------------------------------------------

__all__ = [ 'connect', 'Channel', 'Program', 'Client', 'Server', 'QExpr',
            'get_channels', 'get_channel', 'search', 'sources' ]

# python imports
import logging

# kaa imports
import kaa.notifier
from kaa.db import QExpr

# kaa.epg imports
from config import config
from channel import Channel
from program import Program
from client import Client, DISCONNECTED, CONNECTING, CONNECTED
from server import Server
from sources import *

# get logging object
log = logging.getLogger('epg')

# connected client object
guide = Client()

def connect(address, auth_secret=''):
    """
    Connect to the epg server with the given address.
    """
    if not guide.status == DISCONNECTED:
        log.warning('connecting to a new epg database')
        guide.connect(address, auth_secret)
    guide.connect(address, auth_secret)
    return guide

def get_channels(sort=False):
    """
    Return a list of all channels.
    """
    if not guide.status == DISCONNECTED:
        return guide.get_channels(sort)
    return []

def get_channel(name):
    """
    Return the channel with the given name.
    """
    if not guide.status == DISCONNECTED:
        return guide.get_channel(name)
    return []

def search(channel=None, time=None, block=False, **kwargs):
    """
    Search the db. This will call the search function on server side using
    kaa.ipc. This function will return an InProgress object. If block is
    True the function to block using kaa.notifier.step() until the result
    arrived from the server.
    """
    if block:
        wait = guide.search(channel, time, **kwargs)
        while not wait.is_finished:
            kaa.notifier.step()
        return wait()
    return guide.search(channel, time, **kwargs)

def is_connected():
    return guide and guide.status == CONNECTED
<commit_msg>import VERSION into module root __init__<commit_after># -*- coding: iso-8859-1 -*-
# -----------------------------------------------------------------------------
# __init__.py - Interface to kaa.epg
# -----------------------------------------------------------------------------
# $Id$
# -----------------------------------------------------------------------------
# kaa.epg - EPG Database
# Copyright (C) 2004-2006 Jason Tackaberry, Dirk Meyer, Rob Shortt
#
# First Edition: Jason Tackaberry <tack@sault.org>
#
# Please see the file AUTHORS for a complete list of authors.
#
# This library is free software; you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License version
# 2.1 as published by the Free Software Foundation.
#
# This library is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301 USA
#
# -----------------------------------------------------------------------------

__all__ = [ 'connect', 'Channel', 'Program', 'Client', 'Server', 'QExpr',
            'get_channels', 'get_channel', 'search', 'sources' ]

# python imports
import logging

# kaa imports
import kaa.notifier
from kaa.db import QExpr

# kaa.epg imports
from version import VERSION
from config import config
from channel import Channel
from program import Program
from client import Client, DISCONNECTED, CONNECTING, CONNECTED
from server import Server
from sources import *

# get logging object
log = logging.getLogger('epg')

# connected client object
guide = Client()

def connect(address, auth_secret=''):
    """
    Connect to the epg server with the given address.
    """
    if not guide.status == DISCONNECTED:
        log.warning('connecting to a new epg database')
        guide.connect(address, auth_secret)
    guide.connect(address, auth_secret)
    return guide

def get_channels(sort=False):
    """
    Return a list of all channels.
    """
    if not guide.status == DISCONNECTED:
        return guide.get_channels(sort)
    return []

def get_channel(name):
    """
    Return the channel with the given name.
    """
    if not guide.status == DISCONNECTED:
        return guide.get_channel(name)
    return []

def search(channel=None, time=None, block=False, **kwargs):
    """
    Search the db. This will call the search function on server side using
    kaa.ipc. This function will return an InProgress object. If block is
    True the function to block using kaa.notifier.step() until the result
    arrived from the server.
    """
    if block:
        wait = guide.search(channel, time, **kwargs)
        while not wait.is_finished:
            kaa.notifier.step()
        return wait()
    return guide.search(channel, time, **kwargs)

def is_connected():
    return guide and guide.status == CONNECTED
<|endoftext|>