<commit_before># -*- coding: utf-8 -*-
"""Git Repo object for libvcs.

.. todo::

    From https://github.com/saltstack/salt (Apache License):

    - [`GitRepo.remote`](libvcs.git.GitRepo.remote) (renamed to ``remote``)
    - [`GitRepo.remote`](libvcs.git.GitRepo.remote_set) (renamed to ``set_remote``)

    From pip (MIT Licnese):

    - [`GitRepo.remote`](libvcs.git.GitRepo.remote_set) (renamed to ``set_remote``)
    - [`GitRepo.convert_pip_url`](libvcs.git.GitRepo.convert_pip_url`) (``get_url_rev``)
    - [`GitRepo.get_revision`](libvcs.git.GitRepo.get_revision)
    - [`GitRepo.get_git_version`](libvcs.git.GitRepo.get_git_version)
"""  # NOQA: E501
from __future__ import absolute_import, print_function, unicode_literals

import logging
import os
import re
from typing import NamedTuple, Optional
from urllib import parse as urlparse

from . import exc
from .base import BaseRepo, VCSLocation, convert_pip_url as base_convert_pip_url

logger = logging.getLogger(__name__)

class GitRemote(NamedTuple):
    """Structure containing git repo information.

    Supports `collections.namedtuple._asdict()`
    """

    name: str
    fetch_url: str
    push_url: str

class GitStatus(NamedTuple):
    branch_oid: Optional[str]
    branch_head: Optional[str]
    branch_upstream: Optional[str]
    branch_ab: Optional[str]
    branch_ahead: Optional[str]
    branch_behind: Optional[str]

def extract_status(value) -> GitStatus:
    """Returns ``git status -sb --porcelain=2`` extracted to a dict

    Returns
    -------
    Dictionary of git repo's status
    """
    pattern = re.compile(
        r"""[\n\r]?
        (
            #
            \W+
            branch.oid\W+
            (?P<branch_oid>
                [a-f0-9]{40}
            )
        )?
        (
            #
            \W+
            branch.head
            [\W]+
            (?P<branch_head>
                .*
            )

        )?
        (
            #
            \W+
            branch.upstream
            [\W]+
            (?P<branch_upstream>
                .*
            )
        )?
        (
            #
            \W+
            branch.ab
            [\W]+
            (?P<branch_ab>
                \+(?P<branch_ahead>\d+)
                \W{1}
                \-(?P<branch_behind>\d+)
            )
        )?
        """,
        re.VERBOSE | re.MULTILINE,
    )
    matches = pattern.search(value)
    return GitStatus(**matches.groupdict())

def convert_pip_url(pip_url: str) -> VCSLocation:
    """
    Prefixes stub URLs like 'user@hostname:user/repo.git' with 'ssh://'.
    That's required because although they use SSH they sometimes doesn't
    work with a ssh:// scheme (e.g. Github). But we need a scheme for
    parsing. Hence we remove it again afterwards and return it as a stub.
    The manpage for git-clone(1) refers to this as the "scp-like styntax".
    """
    if '://' not in pip_url:
        assert 'file:' not in pip_url
        pip_url = pip_url.replace('git+', 'git+ssh://')
        url, rev = base_convert_pip_url(pip_url)
        url = url.replace('ssh://', '')
    elif 'github.com:' in pip_url:
        raise exc.LibVCSException(
            "Repo %s is malformatted, please use the convention %s for"
            "ssh / private GitHub repositories."
            % (pip_url, "git+https://github.com/username/repo.git")
        )
    else:
        url, rev = base_convert_pip_url(pip_url)

    return VCSLocation(url=url, rev=rev)

class GitRepo(BaseRepo):
    bin_name = 'git'
    schemes = ('git', 'git+http', 'git+https', 'git+ssh', 'git+git', 'git+file')

    def __init__(self, url, repo_dir, **kwargs):
        """A git repository.

        Parameters
        ----------
        url : str
            URL of repo

        tls_verify : bool
            Should certificate for https be checked (default False)
        """
        if 'git_shallow' not in kwargs:
            self.git_shallow = False
        if 'git_submodules' not in kwargs:
            self.git_submodules = []
        if 'tls_verify' not in kwargs:
            self.tls_verify = False

        BaseRepo.__init__(self, url, repo_dir, **kwargs)

    @classmethod
    def from_pip_url(cls, pip_url, *args, **kwargs):
        url, rev = convert_pip_url(pip_url)
        self = cls(url=url, rev=rev, *args, **kwargs)

        return self

    def get_revision(self):
        """Return current revision. Initial repositories return 'initial'."""
        try:
            return self.run(['rev-parse', '--verify', 'HEAD'])
        except exc.CommandError:
            return 'initial'

    def obtain(self):
        """Retrieve the repository, clone if doesn't exist."""
        self.ensure_dir()

        url = self.url

        cmd = ['clone', '--progress']
        if self.git_shallow:
            cmd.extend(['--depth', '1'])
        if self.tls_verify:
            cmd.extend(['-c', 'http.sslVerify=false'])
        cmd.extend([url, self.path])

        self.info('Cloning.')
        self.run(cmd, log_in_real_time=True)

        self.info('Initializing submodules.')
        self.run(['submodule', 'init'], log_in_real_time=True)
        cmd = ['submodule', 'update', '--recursive', '--init']
        cmd.extend(self.git_submodules)
        self.run(cmd, log_in_real_time=True)

    def update_repo(self):
        self.ensure_dir()

        if not os.path.isdir(os.path.join(self.path, '.git')):
            self.obtain()
            self.update_repo()
            return

        # Get requested revision or tag
        url, git_tag = self.url, getattr(self, 'rev', None)

        if not git_tag:
            self.debug("No git revision set, defaulting to origin/master")
            symref = self.run(['symbolic-ref', '--short', 'HEAD'])
            if symref:
                git_tag = symref.rstrip()
            else:
                git_tag = 'origin/master'
        self.debug("git_tag: %s" % git_tag)

        self.info("Updating to '%s'." % git_tag)

        # Get head sha
        try:
            head_sha = self.run(['rev-list', '--max-count=1', 'HEAD'])
        except exc.CommandError:
            self.error("Failed to get the hash for HEAD")
            return

        self.debug("head_sha: %s" % head_sha)

        # If a remote ref is asked for, which can possibly move around,
        # we must always do a fetch and checkout.
        show_ref_output = self.run(['show-ref', git_tag], check_returncode=False)
        self.debug("show_ref_output: %s" % show_ref_output)
        is_remote_ref = "remotes" in show_ref_output
        self.debug("is_remote_ref: %s" % is_remote_ref)

        # show-ref output is in the form "<sha> refs/remotes/<remote>/<tag>"
        # we must strip the remote from the tag.
        git_remote_name = self.get_current_remote_name()

        if "refs/remotes/%s" % git_tag in show_ref_output:
            m = re.match(
                r'^[0-9a-f]{40} refs/remotes/'
                r'(?P<git_remote_name>[^/]+)/'
                r'(?P<git_tag>.+)$',
                show_ref_output,
                re.MULTILINE,
            )
            git_remote_name = m.group('git_remote_name')
            git_tag = m.group('git_tag')
        self.debug("git_remote_name: %s" % git_remote_name)
        self.debug("git_tag: %s" % git_tag)

        # This will fail if the tag does not exist (it probably has not
        # been fetched yet).
        try:
            error_code = 0
            tag_sha = self.run(
                [
                    'rev-list',
                    '--max-count=1',
                    git_remote_name + '/' + git_tag if is_remote_ref else git_tag,
                ]
            )
        except exc.CommandError as e:
            error_code = e.returncode
            tag_sha = ""
        self.debug("tag_sha: %s" % tag_sha)

        # Is the hash checkout out what we want?
        somethings_up = (error_code, is_remote_ref, tag_sha != head_sha)
        if all(not x for x in somethings_up):
            self.info("Already up-to-date.")
            return

        try:
            process = self.run(['fetch'], log_in_real_time=True)
        except exc.CommandError:
            self.error("Failed to fetch repository '%s'" % url)
            return

        if is_remote_ref:
            # Check if stash is needed
            try:
                process = self.run(['status', '--porcelain'])
            except exc.CommandError:
                self.error("Failed to get the status")
                return
            need_stash = len(process) > 0

            # If not in clean state, stash changes in order to be able
            # to be able to perform git pull --rebase
            if need_stash:
                # If Git < 1.7.6, uses --quiet --all
                git_stash_save_options = '--quiet'
                try:
                    process = self.run(['stash', 'save', git_stash_save_options])
                except exc.CommandError:
                    self.error("Failed to stash changes")

            # Pull changes from the remote branch
            try:
                process = self.run(['rebase', git_remote_name + '/' + git_tag])
            except exc.CommandError as e:
                if 'invalid_upstream' in str(e):
                    self.error(e)
                else:
                    # Rebase failed: Restore previous state.
                    self.run(['rebase', '--abort'])
                    if need_stash:
                        self.run(['stash', 'pop', '--index', '--quiet'])

                    self.error(
                        "\nFailed to rebase in: '%s'.\n"
                        "You will have to resolve the conflicts manually" % self.path
                    )
                    return

            if need_stash:
                try:
                    process = self.run(['stash', 'pop', '--index', '--quiet'])
                except exc.CommandError:
                    # Stash pop --index failed: Try again dropping the index
                    self.run(['reset', '--hard', '--quiet'])
                    try:
                        process = self.run(['stash', 'pop', '--quiet'])
                    except exc.CommandError:
                        # Stash pop failed: Restore previous state.
                        self.run(['reset', '--hard', '--quiet', head_sha])
                        self.run(['stash', 'pop', '--index', '--quiet'])
                        self.error(
                            "\nFailed to rebase in: '%s'.\n"
                            "You will have to resolve the "
                            "conflicts manually" % self.path
                        )
                        return

        else:
            try:
                process = self.run(['checkout', git_tag])
            except exc.CommandError:
                self.error("Failed to checkout tag: '%s'" % git_tag)
                return

        cmd = ['submodule', 'update', '--recursive', '--init']
        cmd.extend(self.git_submodules)
        self.run(cmd, log_in_real_time=True)

    def remotes(self, flat=False):
        """Return remotes like git remote -v.

        Parameters
        ----------
        flat : bool
            Return a dict of ``tuple`` instead of ``dict``, default `False`.

        Returns
        -------
        dict
            dict of git upstream / remote URLs
        """
        remotes = {}

        cmd = self.run(['remote'])
        ret = filter(None, cmd.split('\n'))

        for remote_name in ret:
            remotes[remote_name] = (
                self.remote(remote_name) if flat else self.remote(remote_name)._asdict()
            )
        return remotes

    def remote(self, name, **kwargs):
        """Get the fetch and push URL for a specified remote name.

        Parameters
        ----------
        name : str
            The remote name used to define the fetch and push URL

        Returns
        -------
        libvcs.git.GitRemote
            Remote name and url in tuple form
        """

        try:
            ret = self.run(['remote', 'show', '-n', name])
            lines = ret.split('\n')
            remote_fetch_url = lines[1].replace('Fetch URL: ', '').strip()
            remote_push_url = lines[2].replace('Push  URL: ', '').strip()
            if remote_fetch_url != name and remote_push_url != name:
                return GitRemote(
                    name=name, fetch_url=remote_fetch_url, push_url=remote_push_url
                )
            else:
                return None
        except exc.LibVCSException:
            return None

    def set_remote(self, name, url, overwrite=False):
        """Set remote with name and URL like git remote add.

        Parameters
        ----------
        name : str
            defines the remote name.

        url : str
            defines the remote URL
        """

        url = self.chomp_protocol(url)

        if self.remote(name) and overwrite:
            self.run(['remote', 'set-url', name, url])
        else:
            self.run(['remote', 'add', name, url])
        return self.remote(name=name)

    @staticmethod
    def chomp_protocol(url):
        """Return clean VCS url from RFC-style url

        Parameters
        ----------
        url : str
            PIP-style url

        Returns
        -------
        str
            URL as VCS software would accept it
        """
        if '+' in url:
            url = url.split('+', 1)[1]
        scheme, netloc, path, query, frag = urlparse.urlsplit(url)
        rev = None
        if '@' in path:
            path, rev = path.rsplit('@', 1)
        url = urlparse.urlunsplit((scheme, netloc, path, query, ''))
        if url.startswith('ssh://git@github.com/'):
            url = url.replace('ssh://', 'git+ssh://')
        elif '://' not in url:
            assert 'file:' not in url
            url = url.replace('git+', 'git+ssh://')
            url = url.replace('ssh://', '')
        return url

    def get_git_version(self):
        """Return current version of git binary

        Returns
        -------
        str
            git version
        """
        VERSION_PFX = 'git version '
        version = self.run(['version'])
        if version.startswith(VERSION_PFX):
            version = version[len(VERSION_PFX) :].split()[0]
        else:
            version = ''
        return '.'.join(version.split('.')[:3])

    def status(self):
        """Retrieve status of project in dict format.

        Wraps ``git status --sb --porcelain=2``. Does not include changed files, yet.

        Returns
        -------
        dict
            Status of current checked out repository

        Examples
        --------

        >>> git_repo.status()
        {
            "branch_oid": 'de6185fde0806e5c7754ca05676325a1ea4d6348',
            "branch_head": 'fix-current-remote-name',
            "branch_upstream": 'origin/fix-current-remote-name',
            "branch_ab": '+0 -0',
            "branch_ahead": '0',
            "branch_behind": '0'
        }
        """
        return extract_status(self.run(['status', '-sb', '--porcelain=2']))

    def get_current_remote_name(self):
        """Retrieve name of the remote / upstream of currently checked out branch.

        Returns
        -------
        str
            If upstream the same, returns ``branch_name``.
            If upstream mismatches, returns ``remote_name/branch_name``.
        """
        match = self.status()

        if match.branch_upstream is None:  # no upstream set
            return match.branch_head
        return match.branch_upstream.replace('/' + match.branch_head, '')
<commit_msg>chore(git): remove git_submodules (unused)<commit_after># -*- coding: utf-8 -*-
"""Git Repo object for libvcs.

.. todo::

    From https://github.com/saltstack/salt (Apache License):

    - [`GitRepo.remote`](libvcs.git.GitRepo.remote) (renamed to ``remote``)
    - [`GitRepo.remote`](libvcs.git.GitRepo.remote_set) (renamed to ``set_remote``)

    From pip (MIT Licnese):

    - [`GitRepo.remote`](libvcs.git.GitRepo.remote_set) (renamed to ``set_remote``)
    - [`GitRepo.convert_pip_url`](libvcs.git.GitRepo.convert_pip_url`) (``get_url_rev``)
    - [`GitRepo.get_revision`](libvcs.git.GitRepo.get_revision)
    - [`GitRepo.get_git_version`](libvcs.git.GitRepo.get_git_version)
"""  # NOQA: E501
from __future__ import absolute_import, print_function, unicode_literals

import logging
import os
import re
from typing import NamedTuple, Optional
from urllib import parse as urlparse

from . import exc
from .base import BaseRepo, VCSLocation, convert_pip_url as base_convert_pip_url

logger = logging.getLogger(__name__)

class GitRemote(NamedTuple):
    """Structure containing git repo information.

    Supports `collections.namedtuple._asdict()`
    """

    name: str
    fetch_url: str
    push_url: str

class GitStatus(NamedTuple):
    branch_oid: Optional[str]
    branch_head: Optional[str]
    branch_upstream: Optional[str]
    branch_ab: Optional[str]
    branch_ahead: Optional[str]
    branch_behind: Optional[str]

def extract_status(value) -> GitStatus:
    """Returns ``git status -sb --porcelain=2`` extracted to a dict

    Returns
    -------
    Dictionary of git repo's status
    """
    pattern = re.compile(
        r"""[\n\r]?
        (
            #
            \W+
            branch.oid\W+
            (?P<branch_oid>
                [a-f0-9]{40}
            )
        )?
        (
            #
            \W+
            branch.head
            [\W]+
            (?P<branch_head>
                .*
            )

        )?
        (
            #
            \W+
            branch.upstream
            [\W]+
            (?P<branch_upstream>
                .*
            )
        )?
        (
            #
            \W+
            branch.ab
            [\W]+
            (?P<branch_ab>
                \+(?P<branch_ahead>\d+)
                \W{1}
                \-(?P<branch_behind>\d+)
            )
        )?
        """,
        re.VERBOSE | re.MULTILINE,
    )
    matches = pattern.search(value)
    return GitStatus(**matches.groupdict())

def convert_pip_url(pip_url: str) -> VCSLocation:
    """
    Prefixes stub URLs like 'user@hostname:user/repo.git' with 'ssh://'.
    That's required because although they use SSH they sometimes doesn't
    work with a ssh:// scheme (e.g. Github). But we need a scheme for
    parsing. Hence we remove it again afterwards and return it as a stub.
    The manpage for git-clone(1) refers to this as the "scp-like styntax".
    """
    if '://' not in pip_url:
        assert 'file:' not in pip_url
        pip_url = pip_url.replace('git+', 'git+ssh://')
        url, rev = base_convert_pip_url(pip_url)
        url = url.replace('ssh://', '')
    elif 'github.com:' in pip_url:
        raise exc.LibVCSException(
            "Repo %s is malformatted, please use the convention %s for"
            "ssh / private GitHub repositories."
            % (pip_url, "git+https://github.com/username/repo.git")
        )
    else:
        url, rev = base_convert_pip_url(pip_url)

    return VCSLocation(url=url, rev=rev)

class GitRepo(BaseRepo):
    bin_name = 'git'
    schemes = ('git', 'git+http', 'git+https', 'git+ssh', 'git+git', 'git+file')

    def __init__(self, url, repo_dir, **kwargs):
        """A git repository.

        Parameters
        ----------
        url : str
            URL of repo

        tls_verify : bool
            Should certificate for https be checked (default False)
        """
        if 'git_shallow' not in kwargs:
            self.git_shallow = False
        if 'tls_verify' not in kwargs:
            self.tls_verify = False

        BaseRepo.__init__(self, url, repo_dir, **kwargs)

    @classmethod
    def from_pip_url(cls, pip_url, *args, **kwargs):
        url, rev = convert_pip_url(pip_url)
        self = cls(url=url, rev=rev, *args, **kwargs)

        return self

    def get_revision(self):
        """Return current revision. Initial repositories return 'initial'."""
        try:
            return self.run(['rev-parse', '--verify', 'HEAD'])
        except exc.CommandError:
            return 'initial'

    def obtain(self):
        """Retrieve the repository, clone if doesn't exist."""
        self.ensure_dir()

        url = self.url

        cmd = ['clone', '--progress']
        if self.git_shallow:
            cmd.extend(['--depth', '1'])
        if self.tls_verify:
            cmd.extend(['-c', 'http.sslVerify=false'])
        cmd.extend([url, self.path])

        self.info('Cloning.')
        self.run(cmd, log_in_real_time=True)

        self.info('Initializing submodules.')
        self.run(['submodule', 'init'], log_in_real_time=True)
        cmd = ['submodule', 'update', '--recursive', '--init']
        self.run(cmd, log_in_real_time=True)

    def update_repo(self):
        self.ensure_dir()

        if not os.path.isdir(os.path.join(self.path, '.git')):
            self.obtain()
            self.update_repo()
            return

        # Get requested revision or tag
        url, git_tag = self.url, getattr(self, 'rev', None)

        if not git_tag:
            self.debug("No git revision set, defaulting to origin/master")
            symref = self.run(['symbolic-ref', '--short', 'HEAD'])
            if symref:
                git_tag = symref.rstrip()
            else:
                git_tag = 'origin/master'
        self.debug("git_tag: %s" % git_tag)

        self.info("Updating to '%s'." % git_tag)

        # Get head sha
        try:
            head_sha = self.run(['rev-list', '--max-count=1', 'HEAD'])
        except exc.CommandError:
            self.error("Failed to get the hash for HEAD")
            return

        self.debug("head_sha: %s" % head_sha)

        # If a remote ref is asked for, which can possibly move around,
        # we must always do a fetch and checkout.
        show_ref_output = self.run(['show-ref', git_tag], check_returncode=False)
        self.debug("show_ref_output: %s" % show_ref_output)
        is_remote_ref = "remotes" in show_ref_output
        self.debug("is_remote_ref: %s" % is_remote_ref)

        # show-ref output is in the form "<sha> refs/remotes/<remote>/<tag>"
        # we must strip the remote from the tag.
        git_remote_name = self.get_current_remote_name()

        if "refs/remotes/%s" % git_tag in show_ref_output:
            m = re.match(
                r'^[0-9a-f]{40} refs/remotes/'
                r'(?P<git_remote_name>[^/]+)/'
                r'(?P<git_tag>.+)$',
                show_ref_output,
                re.MULTILINE,
            )
            git_remote_name = m.group('git_remote_name')
            git_tag = m.group('git_tag')
        self.debug("git_remote_name: %s" % git_remote_name)
        self.debug("git_tag: %s" % git_tag)

        # This will fail if the tag does not exist (it probably has not
        # been fetched yet).
        try:
            error_code = 0
            tag_sha = self.run(
                [
                    'rev-list',
                    '--max-count=1',
                    git_remote_name + '/' + git_tag if is_remote_ref else git_tag,
                ]
            )
        except exc.CommandError as e:
            error_code = e.returncode
            tag_sha = ""
        self.debug("tag_sha: %s" % tag_sha)

        # Is the hash checkout out what we want?
        somethings_up = (error_code, is_remote_ref, tag_sha != head_sha)
        if all(not x for x in somethings_up):
            self.info("Already up-to-date.")
            return

        try:
            process = self.run(['fetch'], log_in_real_time=True)
        except exc.CommandError:
            self.error("Failed to fetch repository '%s'" % url)
            return

        if is_remote_ref:
            # Check if stash is needed
            try:
                process = self.run(['status', '--porcelain'])
            except exc.CommandError:
                self.error("Failed to get the status")
                return
            need_stash = len(process) > 0

            # If not in clean state, stash changes in order to be able
            # to be able to perform git pull --rebase
            if need_stash:
                # If Git < 1.7.6, uses --quiet --all
                git_stash_save_options = '--quiet'
                try:
                    process = self.run(['stash', 'save', git_stash_save_options])
                except exc.CommandError:
                    self.error("Failed to stash changes")

            # Pull changes from the remote branch
            try:
                process = self.run(['rebase', git_remote_name + '/' + git_tag])
            except exc.CommandError as e:
                if 'invalid_upstream' in str(e):
                    self.error(e)
                else:
                    # Rebase failed: Restore previous state.
                    self.run(['rebase', '--abort'])
                    if need_stash:
                        self.run(['stash', 'pop', '--index', '--quiet'])

                    self.error(
                        "\nFailed to rebase in: '%s'.\n"
                        "You will have to resolve the conflicts manually" % self.path
                    )
                    return

            if need_stash:
                try:
                    process = self.run(['stash', 'pop', '--index', '--quiet'])
                except exc.CommandError:
                    # Stash pop --index failed: Try again dropping the index
                    self.run(['reset', '--hard', '--quiet'])
                    try:
                        process = self.run(['stash', 'pop', '--quiet'])
                    except exc.CommandError:
                        # Stash pop failed: Restore previous state.
                        self.run(['reset', '--hard', '--quiet', head_sha])
                        self.run(['stash', 'pop', '--index', '--quiet'])
                        self.error(
                            "\nFailed to rebase in: '%s'.\n"
                            "You will have to resolve the "
                            "conflicts manually" % self.path
                        )
                        return

        else:
            try:
                process = self.run(['checkout', git_tag])
            except exc.CommandError:
                self.error("Failed to checkout tag: '%s'" % git_tag)
                return

        cmd = ['submodule', 'update', '--recursive', '--init']
        self.run(cmd, log_in_real_time=True)

    def remotes(self, flat=False):
        """Return remotes like git remote -v.

        Parameters
        ----------
        flat : bool
            Return a dict of ``tuple`` instead of ``dict``, default `False`.

        Returns
        -------
        dict
            dict of git upstream / remote URLs
        """
        remotes = {}

        cmd = self.run(['remote'])
        ret = filter(None, cmd.split('\n'))

        for remote_name in ret:
            remotes[remote_name] = (
                self.remote(remote_name) if flat else self.remote(remote_name)._asdict()
            )
        return remotes

    def remote(self, name, **kwargs):
        """Get the fetch and push URL for a specified remote name.

        Parameters
        ----------
        name : str
            The remote name used to define the fetch and push URL

        Returns
        -------
        libvcs.git.GitRemote
            Remote name and url in tuple form
        """

        try:
            ret = self.run(['remote', 'show', '-n', name])
            lines = ret.split('\n')
            remote_fetch_url = lines[1].replace('Fetch URL: ', '').strip()
            remote_push_url = lines[2].replace('Push  URL: ', '').strip()
            if remote_fetch_url != name and remote_push_url != name:
                return GitRemote(
                    name=name, fetch_url=remote_fetch_url, push_url=remote_push_url
                )
            else:
                return None
        except exc.LibVCSException:
            return None

    def set_remote(self, name, url, overwrite=False):
        """Set remote with name and URL like git remote add.

        Parameters
        ----------
        name : str
            defines the remote name.

        url : str
            defines the remote URL
        """

        url = self.chomp_protocol(url)

        if self.remote(name) and overwrite:
            self.run(['remote', 'set-url', name, url])
        else:
            self.run(['remote', 'add', name, url])
        return self.remote(name=name)

    @staticmethod
    def chomp_protocol(url):
        """Return clean VCS url from RFC-style url

        Parameters
        ----------
        url : str
            PIP-style url

        Returns
        -------
        str
            URL as VCS software would accept it
        """
        if '+' in url:
            url = url.split('+', 1)[1]
        scheme, netloc, path, query, frag = urlparse.urlsplit(url)
        rev = None
        if '@' in path:
            path, rev = path.rsplit('@', 1)
        url = urlparse.urlunsplit((scheme, netloc, path, query, ''))
        if url.startswith('ssh://git@github.com/'):
            url = url.replace('ssh://', 'git+ssh://')
        elif '://' not in url:
            assert 'file:' not in url
            url = url.replace('git+', 'git+ssh://')
            url = url.replace('ssh://', '')
        return url

    def get_git_version(self):
        """Return current version of git binary

        Returns
        -------
        str
            git version
        """
        VERSION_PFX = 'git version '
        version = self.run(['version'])
        if version.startswith(VERSION_PFX):
            version = version[len(VERSION_PFX) :].split()[0]
        else:
            version = ''
        return '.'.join(version.split('.')[:3])

    def status(self):
        """Retrieve status of project in dict format.

        Wraps ``git status --sb --porcelain=2``. Does not include changed files, yet.

        Returns
        -------
        dict
            Status of current checked out repository

        Examples
        --------

        >>> git_repo.status()
        {
            "branch_oid": 'de6185fde0806e5c7754ca05676325a1ea4d6348',
            "branch_head": 'fix-current-remote-name',
            "branch_upstream": 'origin/fix-current-remote-name',
            "branch_ab": '+0 -0',
            "branch_ahead": '0',
            "branch_behind": '0'
        }
        """
        return extract_status(self.run(['status', '-sb', '--porcelain=2']))

    def get_current_remote_name(self):
        """Retrieve name of the remote / upstream of currently checked out branch.

        Returns
        -------
        str
            If upstream the same, returns ``branch_name``.
            If upstream mismatches, returns ``remote_name/branch_name``.
        """
        match = self.status()

        if match.branch_upstream is None:  # no upstream set
            return match.branch_head
        return match.branch_upstream.replace('/' + match.branch_head, '')
<|endoftext|>


<commit_before>"""WSGI App Creator

This module is responsible for creating the basic Pylons WSGI
application (PylonsApp). It's generally assumed that it will be called
by Paste, though any WSGI server could create and call the WSGI app as
well.

"""
import inspect
import logging
import sys

import paste.registry
from routes import request_config
from webob.exc import HTTPFound, HTTPNotFound

import pylons
import pylons.legacy
import pylons.templating
from pylons.controllers.util import Request, Response
from pylons.i18n.translation import _get_translator
from pylons.util import AttribSafeContextObj, ContextObj, PylonsContext, \
    class_name_from_module_name

__all__ = ['PylonsApp']

log = logging.getLogger(__name__)

class PylonsApp(object):
    """Pylons WSGI Application

    This basic WSGI app is provided should a web developer want to
    get access to the most basic Pylons web application environment
    available. By itself, this Pylons web application does little more
    than dispatch to a controller and setup the context object, the
    request object, and the globals object.

    Additional functionality like sessions, and caching can be setup by
    altering the ``environ['pylons.environ_config']`` setting to
    indicate what key the ``session`` and ``cache`` functionality
    should come from.

    Resolving the URL and dispatching can be customized by sub-classing
    or "monkey-patching" this class. Subclassing is the preferred
    approach.

    """
    def __init__(self, **kwargs):
        """Initialize a base Pylons WSGI application

        The base Pylons WSGI application requires several keywords, the
        package name, and the globals object. If no helpers object is
        provided then h will be None.

        """
        self.config = config = pylons.config._current_obj()
        package_name = config['pylons.package']
        self.helpers = config['pylons.h']
        self.globals = config.get('pylons.app_globals') or config['pylons.g']
        self.environ_config = config['pylons.environ_config']
        self.package_name = package_name
        self.request_options = config['pylons.request_options']
        self.response_options = config['pylons.response_options']
        self.controller_classes = {}
        self.log_debug = False

        # Create the redirect function we'll use and save it
        def redirect_to(url):
            log.debug("Raising redirect to %s", url)
            raise HTTPFound(location=url)
        self.redirect_to = redirect_to

        # Initialize Buffet and all our template engines, default engine is the
        # first in the template_engines list
        if config.get('buffet.template_engines'):
            def_eng = config['buffet.template_engines'][0]
            self.buffet = pylons.templating.Buffet(
                def_eng['engine'],
                template_root=def_eng['template_root'],
                **def_eng['template_options'])
            for e in config['buffet.template_engines'][1:]:
                log.debug("Initializing additional template engine: %s", e['engine'])
                self.buffet.prepare(e['engine'], template_root=e['template_root'],
                    alias=e['alias'], **e['template_options'])

    def __call__(self, environ, start_response):
        """Setup and handle a web request

        PylonsApp splits its functionality into several methods to
        make it easier to subclass and customize core functionality.

        The methods are called in the following order:

        1. :meth:`~PylonsApp.setup_app_env`
        2. :meth:`~PylonsApp.load_test_env` (Only if operating in
           testing mode)
        3. :meth:`~PylonsApp.resolve`
        4. :meth:`~PylonsApp.dispatch`

        The response from :meth:`~PylonsApp.dispatch` is expected to be
        an iterable (valid :pep:`333` WSGI response), which is then
        sent back as the response.

        """
        # Cache the logging level for the request
        log_debug = self.log_debug = logging.DEBUG >= log.getEffectiveLevel()

        self.setup_app_env(environ, start_response)
        if 'paste.testing_variables' in environ:
            self.load_test_env(environ)
            if environ['PATH_INFO'] == '/_test_vars':
                paste.registry.restorer.save_registry_state(environ)
                start_response('200 OK', [('Content-type', 'text/plain')])
                return ['%s' % paste.registry.restorer.get_request_id(environ)]

        controller = self.resolve(environ, start_response)
        response = self.dispatch(controller, environ, start_response)

        if 'paste.testing_variables' in environ and hasattr(response,
                                                            'wsgi_response'):
            environ['paste.testing_variables']['response'] = response

        try:
            if hasattr(response, 'wsgi_response'):
                # Transform Response objects from legacy Controller
                if log_debug:
                    log.debug("Transforming legacy Response object into WSGI "
                              "response")
                return response(environ, start_response)
            elif response:
                return response

            raise Exception("No content returned by controller (Did you remember "
                            "to 'return' it?) in: %r" % controller.__name__)
        finally:
            # Help Python collect ram a bit faster by removing the reference
            # cycle that the pylons object causes
            del environ['pylons.pylons']

    def register_globals(self, environ):
        """Registers globals in the environment, called from
        :meth:`~PylonsApp.setup_app_env`

        Override this to control how the Pylons API is setup. Note that
        a custom render function will need to be used if the
        ``pylons.app_globals`` global is not available.

        """
        pylons_obj = environ['pylons.pylons']

        registry = environ['paste.registry']
        registry.register(pylons.response, pylons_obj.response)
        registry.register(pylons.request, pylons_obj.request)

        registry.register(pylons.app_globals, self.globals)
        registry.register(pylons.config, self.config)
        registry.register(pylons.h, self.helpers or \
                          pylons.legacy.load_h(self.package_name))
        registry.register(pylons.c, pylons_obj.c)
        registry.register(pylons.translator, pylons_obj.translator)

        if hasattr(pylons_obj, 'buffet'):
            registry.register(pylons.buffet, self.buffet)
        if hasattr(pylons_obj, 'session'):
            registry.register(pylons.session, pylons_obj.session)
        if hasattr(pylons_obj, 'cache'):
            registry.register(pylons.cache, pylons_obj.cache)

    def setup_app_env(self, environ, start_response):
        """Setup and register all the Pylons objects with the registry

        After creating all the global objects for use in the request,
        :meth:`~PylonsApp.register_globals` is called to register them
        in the environment.

        """
        if self.log_debug:
            log.debug("Setting up Pylons stacked object globals")

        # Setup the basic pylons global objects
        req = Request(environ)
        req.language = self.request_options['language']

        response = Response(
            content_type=self.response_options['content_type'],
            charset=self.response_options['charset'])
        response.headers.update(self.response_options['headers'])

        # Store a copy of the request/response in environ for faster access
        pylons_obj = PylonsContext()
        pylons_obj.config = self.config
        pylons_obj.request = req
        pylons_obj.response = response
        pylons_obj.g = pylons_obj.app_globals = self.globals
        pylons_obj.h = self.helpers

        if hasattr(self, 'buffet'):
            pylons_obj.buffet = self.buffet

        environ['pylons.pylons'] = pylons_obj

        environ['pylons.environ_config'] = self.environ_config

        # Setup the translator object
        pylons_obj.translator = _get_translator(self.config.get('lang'))

        if self.config['pylons.strict_c']:
            c = ContextObj()
        else:
            c = AttribSafeContextObj()
        pylons_obj.c = c

        econf = self.config['pylons.environ_config']
        if econf.get('session') and econf['session'] in environ:
            pylons_obj.session = environ[econf['session']]
        elif 'beaker.session' in environ:
            pylons_obj.session = environ['beaker.session']
        if econf.get('cache') and econf['cache'] in environ:
            pylons_obj.cache = environ[econf['cache']]
        elif 'beaker.cache' in environ:
            pylons_obj.cache = environ['beaker.cache']

        # Load the globals with the registry if around
        if 'paste.registry' in environ:
            self.register_globals(environ)

    def resolve(self, environ, start_response):
        """Uses dispatching information found in
        ``environ['wsgiorg.routing_args']`` to retrieve a controller
        name and return the controller instance from the appropriate
        controller module.

        Override this to change how the controller name is found and
        returned.

        """
        # Update the Routes config object in case we're using Routes
        config = request_config()
        config.redirect = self.redirect_to
        match = environ['wsgiorg.routing_args'][1]

        environ['pylons.routes_dict'] = match
        controller = match.get('controller')
        if not controller:
            return

        if self.log_debug:
            log.debug("Resolved URL to controller: %r", controller)
        return self.find_controller(controller)

    def find_controller(self, controller):
        """Locates a controller by attempting to import it then grab
        the SomeController instance from the imported module.

        Override this to change how the controller object is found once
        the URL has been resolved.

        """
        # Check to see if we've cached the class instance for this name
        if controller in self.controller_classes:
            return self.controller_classes[controller]

        # Pull the controllers class name, import controller
        full_module_name = self.package_name + '.controllers.' \
            + controller.replace('/', '.')

        # Hide the traceback here if the import fails (bad syntax and such)
        __traceback_hide__ = 'before_and_this'

        __import__(full_module_name)
        module_name = controller.split('/')[-1]
        class_name = class_name_from_module_name(module_name) + 'Controller'
        if self.log_debug:
            log.debug("Found controller, module: '%s', class: '%s'",
                      full_module_name, class_name)
        self.controller_classes[controller] = mycontroller = \
            getattr(sys.modules[full_module_name], class_name)
        return mycontroller

    def dispatch(self, controller, environ, start_response):
        """Dispatches to a controller, will instantiate the controller
        if necessary.

        Override this to change how the controller dispatch is handled.

        """
        log_debug = self.log_debug
        if not controller:
            if log_debug:
                log.debug("No controller found, returning 404 HTTP Not Found")
            return HTTPNotFound()(environ, start_response)

        match = environ['pylons.routes_dict']

        # If it's a class, instantiate it
        if inspect.isclass(controller):
            if log_debug:
                log.debug("Controller appears to be a class, instantiating")
            controller = controller()
            controller._pylons_log_debug = log_debug

        # Add a reference to the controller app located
        environ['pylons.controller'] = controller

        # Controller is assumed to handle a WSGI call
        if log_debug:
            log.debug("Calling controller class with WSGI interface")
        return controller(environ, start_response)

    def load_test_env(self, environ):
        """Sets up our Paste testing environment"""
        if self.log_debug:
            log.debug("Setting up paste testing environment variables")
        testenv = environ['paste.testing_variables']
        pylons_obj = environ['pylons.pylons']
        testenv['req'] = pylons_obj.request
        testenv['response'] = pylons_obj.response
        testenv['tmpl_context'] = testenv['c'] = pylons_obj.c
        testenv['app_globals'] = testenv['g'] = pylons_obj.app_globals
        testenv['h'] = self.config['pylons.h'] or pylons_obj.h
        testenv['config'] = self.config
        econf = self.config['pylons.environ_config']
        if hasattr(pylons_obj, 'session'):
            testenv['session'] = pylons_obj.session
        if hasattr(pylons_obj, 'cache'):
            testenv['cache'] = pylons_obj.cache
<commit_msg>Removed unused assignment.<commit_after>"""WSGI App Creator

This module is responsible for creating the basic Pylons WSGI
application (PylonsApp). It's generally assumed that it will be called
by Paste, though any WSGI server could create and call the WSGI app as
well.

"""
import inspect
import logging
import sys

import paste.registry
from routes import request_config
from webob.exc import HTTPFound, HTTPNotFound

import pylons
import pylons.legacy
import pylons.templating
from pylons.controllers.util import Request, Response
from pylons.i18n.translation import _get_translator
from pylons.util import AttribSafeContextObj, ContextObj, PylonsContext, \
    class_name_from_module_name

__all__ = ['PylonsApp']

log = logging.getLogger(__name__)

class PylonsApp(object):
    """Pylons WSGI Application

    This basic WSGI app is provided should a web developer want to
    get access to the most basic Pylons web application environment
    available. By itself, this Pylons web application does little more
    than dispatch to a controller and setup the context object, the
    request object, and the globals object.

    Additional functionality like sessions, and caching can be setup by
    altering the ``environ['pylons.environ_config']`` setting to
    indicate what key the ``session`` and ``cache`` functionality
    should come from.

    Resolving the URL and dispatching can be customized by sub-classing
    or "monkey-patching" this class. Subclassing is the preferred
    approach.

    """
    def __init__(self, **kwargs):
        """Initialize a base Pylons WSGI application

        The base Pylons WSGI application requires several keywords, the
        package name, and the globals object. If no helpers object is
        provided then h will be None.

        """
        self.config = config = pylons.config._current_obj()
        package_name = config['pylons.package']
        self.helpers = config['pylons.h']
        self.globals = config.get('pylons.app_globals') or config['pylons.g']
        self.environ_config = config['pylons.environ_config']
        self.package_name = package_name
        self.request_options = config['pylons.request_options']
        self.response_options = config['pylons.response_options']
        self.controller_classes = {}
        self.log_debug = False

        # Create the redirect function we'll use and save it
        def redirect_to(url):
            log.debug("Raising redirect to %s", url)
            raise HTTPFound(location=url)
        self.redirect_to = redirect_to

        # Initialize Buffet and all our template engines, default engine is the
        # first in the template_engines list
        if config.get('buffet.template_engines'):
            def_eng = config['buffet.template_engines'][0]
            self.buffet = pylons.templating.Buffet(
                def_eng['engine'],
                template_root=def_eng['template_root'],
                **def_eng['template_options'])
            for e in config['buffet.template_engines'][1:]:
                log.debug("Initializing additional template engine: %s", e['engine'])
                self.buffet.prepare(e['engine'], template_root=e['template_root'],
                    alias=e['alias'], **e['template_options'])

    def __call__(self, environ, start_response):
        """Setup and handle a web request

        PylonsApp splits its functionality into several methods to
        make it easier to subclass and customize core functionality.

        The methods are called in the following order:

        1. :meth:`~PylonsApp.setup_app_env`
        2. :meth:`~PylonsApp.load_test_env` (Only if operating in
           testing mode)
        3. :meth:`~PylonsApp.resolve`
        4. :meth:`~PylonsApp.dispatch`

        The response from :meth:`~PylonsApp.dispatch` is expected to be
        an iterable (valid :pep:`333` WSGI response), which is then
        sent back as the response.

        """
        # Cache the logging level for the request
        log_debug = self.log_debug = logging.DEBUG >= log.getEffectiveLevel()

        self.setup_app_env(environ, start_response)
        if 'paste.testing_variables' in environ:
            self.load_test_env(environ)
            if environ['PATH_INFO'] == '/_test_vars':
                paste.registry.restorer.save_registry_state(environ)
                start_response('200 OK', [('Content-type', 'text/plain')])
                return ['%s' % paste.registry.restorer.get_request_id(environ)]

        controller = self.resolve(environ, start_response)
        response = self.dispatch(controller, environ, start_response)

        if 'paste.testing_variables' in environ and hasattr(response,
                                                            'wsgi_response'):
            environ['paste.testing_variables']['response'] = response

        try:
            if hasattr(response, 'wsgi_response'):
                # Transform Response objects from legacy Controller
                if log_debug:
                    log.debug("Transforming legacy Response object into WSGI "
                              "response")
                return response(environ, start_response)
            elif response:
                return response

            raise Exception("No content returned by controller (Did you remember "
                            "to 'return' it?) in: %r" % controller.__name__)
        finally:
            # Help Python collect ram a bit faster by removing the reference
            # cycle that the pylons object causes
            del environ['pylons.pylons']

    def register_globals(self, environ):
        """Registers globals in the environment, called from
        :meth:`~PylonsApp.setup_app_env`

        Override this to control how the Pylons API is setup. Note that
        a custom render function will need to be used if the
        ``pylons.app_globals`` global is not available.

        """
        pylons_obj = environ['pylons.pylons']

        registry = environ['paste.registry']
        registry.register(pylons.response, pylons_obj.response)
        registry.register(pylons.request, pylons_obj.request)

        registry.register(pylons.app_globals, self.globals)
        registry.register(pylons.config, self.config)
        registry.register(pylons.h, self.helpers or \
                          pylons.legacy.load_h(self.package_name))
        registry.register(pylons.c, pylons_obj.c)
        registry.register(pylons.translator, pylons_obj.translator)

        if hasattr(pylons_obj, 'buffet'):
            registry.register(pylons.buffet, self.buffet)
        if hasattr(pylons_obj, 'session'):
            registry.register(pylons.session, pylons_obj.session)
        if hasattr(pylons_obj, 'cache'):
            registry.register(pylons.cache, pylons_obj.cache)

    def setup_app_env(self, environ, start_response):
        """Setup and register all the Pylons objects with the registry

        After creating all the global objects for use in the request,
        :meth:`~PylonsApp.register_globals` is called to register them
        in the environment.

        """
        if self.log_debug:
            log.debug("Setting up Pylons stacked object globals")

        # Setup the basic pylons global objects
        req = Request(environ)
        req.language = self.request_options['language']

        response = Response(
            content_type=self.response_options['content_type'],
            charset=self.response_options['charset'])
        response.headers.update(self.response_options['headers'])

        # Store a copy of the request/response in environ for faster access
        pylons_obj = PylonsContext()
        pylons_obj.config = self.config
        pylons_obj.request = req
        pylons_obj.response = response
        pylons_obj.g = pylons_obj.app_globals = self.globals
        pylons_obj.h = self.helpers

        if hasattr(self, 'buffet'):
            pylons_obj.buffet = self.buffet

        environ['pylons.pylons'] = pylons_obj

        environ['pylons.environ_config'] = self.environ_config

        # Setup the translator object
        pylons_obj.translator = _get_translator(self.config.get('lang'))

        if self.config['pylons.strict_c']:
            c = ContextObj()
        else:
            c = AttribSafeContextObj()
        pylons_obj.c = c

        econf = self.config['pylons.environ_config']
        if econf.get('session') and econf['session'] in environ:
            pylons_obj.session = environ[econf['session']]
        elif 'beaker.session' in environ:
            pylons_obj.session = environ['beaker.session']
        if econf.get('cache') and econf['cache'] in environ:
            pylons_obj.cache = environ[econf['cache']]
        elif 'beaker.cache' in environ:
            pylons_obj.cache = environ['beaker.cache']

        # Load the globals with the registry if around
        if 'paste.registry' in environ:
            self.register_globals(environ)

    def resolve(self, environ, start_response):
        """Uses dispatching information found in
        ``environ['wsgiorg.routing_args']`` to retrieve a controller
        name and return the controller instance from the appropriate
        controller module.

        Override this to change how the controller name is found and
        returned.

        """
        # Update the Routes config object in case we're using Routes
        config = request_config()
        config.redirect = self.redirect_to
        match = environ['wsgiorg.routing_args'][1]

        environ['pylons.routes_dict'] = match
        controller = match.get('controller')
        if not controller:
            return

        if self.log_debug:
            log.debug("Resolved URL to controller: %r", controller)
        return self.find_controller(controller)

    def find_controller(self, controller):
        """Locates a controller by attempting to import it then grab
        the SomeController instance from the imported module.

        Override this to change how the controller object is found once
        the URL has been resolved.

        """
        # Check to see if we've cached the class instance for this name
        if controller in self.controller_classes:
            return self.controller_classes[controller]

        # Pull the controllers class name, import controller
        full_module_name = self.package_name + '.controllers.' \
            + controller.replace('/', '.')

        # Hide the traceback here if the import fails (bad syntax and such)
        __traceback_hide__ = 'before_and_this'

        __import__(full_module_name)
        module_name = controller.split('/')[-1]
        class_name = class_name_from_module_name(module_name) + 'Controller'
        if self.log_debug:
            log.debug("Found controller, module: '%s', class: '%s'",
                      full_module_name, class_name)
        self.controller_classes[controller] = mycontroller = \
            getattr(sys.modules[full_module_name], class_name)
        return mycontroller

    def dispatch(self, controller, environ, start_response):
        """Dispatches to a controller, will instantiate the controller
        if necessary.

        Override this to change how the controller dispatch is handled.

        """
        log_debug = self.log_debug
        if not controller:
            if log_debug:
                log.debug("No controller found, returning 404 HTTP Not Found")
            return HTTPNotFound()(environ, start_response)

        match = environ['pylons.routes_dict']

        # If it's a class, instantiate it
        if inspect.isclass(controller):
            if log_debug:
                log.debug("Controller appears to be a class, instantiating")
            controller = controller()
            controller._pylons_log_debug = log_debug

        # Add a reference to the controller app located
        environ['pylons.controller'] = controller

        # Controller is assumed to handle a WSGI call
        if log_debug:
            log.debug("Calling controller class with WSGI interface")
        return controller(environ, start_response)

    def load_test_env(self, environ):
        """Sets up our Paste testing environment"""
        if self.log_debug:
            log.debug("Setting up paste testing environment variables")
        testenv = environ['paste.testing_variables']
        pylons_obj = environ['pylons.pylons']
        testenv['req'] = pylons_obj.request
        testenv['response'] = pylons_obj.response
        testenv['tmpl_context'] = testenv['c'] = pylons_obj.c
        testenv['app_globals'] = testenv['g'] = pylons_obj.app_globals
        testenv['h'] = self.config['pylons.h'] or pylons_obj.h
        testenv['config'] = self.config
        if hasattr(pylons_obj, 'session'):
            testenv['session'] = pylons_obj.session
        if hasattr(pylons_obj, 'cache'):
            testenv['cache'] = pylons_obj.cache
<|endoftext|>


<commit_before>#!/usr/bin/env python
#

import os
import pickledb
import sys
import datetime

# some global settings
monitor_path = os.path.join(os.getenv('HOME'), 'Pictures', 'iPhoto Library', 'Masters')
history_to_keep = 10

def checkInput(dir):
	if not os.path.isdir(monitor_path): raise IOError('Directory '+ dir +' does not seem to exist')

def initializeDB(db, date):
	if not db.get(0):
		db.set(0, "Database created "+ date.strftime("%Y%m%d-%H%M"))
		return True
	else:
		return True

def getNextIndex(db):
	index = 0
	while db.get(index):
		index += 1
	return index

def main():
	try:
		checkInput(monitor_path)

		date = datetime.datetime.now()
		db = pickledb.load('files.db', True)

		print db.get(0)

		initializeDB(db, date)
		free_index = getNextIndex(db)
		print "Setting index %s" % (free_index)
		db.set(free_index, "bla bla")
		#db.lcreate()

		db.dump()
	except IOError as e:
		sys.exit(e.message)
	except KeyboardInterrupt:
		print 'Aborting..'
	sys.exit(0)

if __name__ == '__main__':
	main()<commit_msg>commit before reordering<commit_after>#!/usr/bin/env python
#

import os
import datastore
from datastore.impl.filesystem import FileSystemDatastore
import sys
import datetime

#import pickle

# pickle.dump(itemlist, outfile)
# To read it back:

# itemlist = pickle.load(infile)

# some global settings
monitor_path = os.path.join(os.getenv('HOME'), 'Pictures', 'iPhoto Library', 'Masters')
history_to_keep = 10

def checkInput(dir):
	if not os.path.isdir(monitor_path): raise IOError('Directory '+ dir +' does not seem to exist')

def initializeDS(ds, date):
	initkey = datastore.Key(0)
	if not ds.contains(initkey):
		ds.put(initkey, "Database created "+ date.strftime("%Y%m%d-%H%M"))
		print ds.get(initkey)
		return True
	else:
		return True

def getNextIndex(ds):
	i = 0
	next_key = datastore.Key(i)
	while ds.contains(next_key):
		i += 1
		next_key = datastore.Key(i)
	last_key = datastore.Key(i - 1)
	return last_key, next_key

def main():
	try:
		checkInput(monitor_path)

		date = datetime.datetime.now()
		ds = FileSystemDatastore('datastore')

		initializeDS(ds, date)
		last_key, next_key = getNextIndex(ds)
		print "Last written: %s" % (last_key)
		print "Setting index %s" % (next_key)
		ds.put(next_key, ['bla', 'bla2', 'bla3'])

	except IOError as e:
		sys.exit(e.message)
	except KeyboardInterrupt:
		print 'Aborting..'
	sys.exit(0)

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


<commit_before>##########################################################################
#
#  Copyright (c) 2012, John Haddon. All rights reserved.
#  Copyright (c) 2012-2014, Image Engine Design Inc. All rights reserved.
#
#  Redistribution and use in source and binary forms, with or without
#  modification, are permitted provided that the following conditions are
#  met:
#
#      * Redistributions of source code must retain the above
#        copyright notice, this list of conditions and the following
#        disclaimer.
#
#      * Redistributions in binary form must reproduce the above
#        copyright notice, this list of conditions and the following
#        disclaimer in the documentation and/or other materials provided with
#        the distribution.
#
#      * Neither the name of John Haddon nor the names of
#        any other contributors to this software may be used to endorse or
#        promote products derived from this software without specific prior
#        written permission.
#
#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
#  IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
#  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
#  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
#  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
#  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
#  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
#  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
#  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
#  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
#  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
##########################################################################

import fnmatch
import threading
import weakref

import IECore

import Gaffer
import GafferUI

QtCore = GafferUI._qtImport( "QtCore" )

##########################################################################
# Public functions
##########################################################################

def appendMenuDefinitions( menuDefinition, prefix="" ) :

	menuDefinition.append( prefix + "/Execute Selected", { "command" : executeSelected, "shortCut" : "Ctrl+E", "active" : __selectionAvailable } )
	menuDefinition.append( prefix + "/Repeat Previous", { "command" : repeatPrevious, "shortCut" : "Ctrl+R", "active" : __previousAvailable } )

def appendNodeContextMenuDefinitions( nodeGraph, node, menuDefinition ) :

	if not hasattr( node, "execute" ) :
		return

	menuDefinition.append( "/ExecuteDivider", { "divider" : True } )
	menuDefinition.append( "/Execute", { "command" : IECore.curry( _showDispatcherWindow, [ node ] ) } )

def executeSelected( menu ) :

	scriptWindow = menu.ancestor( GafferUI.ScriptWindow )
	script = scriptWindow.scriptNode()
	_showDispatcherWindow( __selectedNodes( script ) )

def repeatPrevious( menu ) :

	scriptWindow = menu.ancestor( GafferUI.ScriptWindow )
	script = scriptWindow.scriptNode()
	_execute( __previous( script ) )

##################################################################################
# Dispatcher Window
##################################################################################

class _DispatcherWindow( GafferUI.Window ) :

	def __init__( self, **kw ) :

		GafferUI.Window.__init__( self, **kw )

		self.__dispatcher = Gaffer.Dispatcher.dispatcher( "local" )
		self.__nodes = []

		with self :

			with GafferUI.ListContainer( orientation = GafferUI.ListContainer.Orientation.Vertical, spacing = 2, borderWidth = 4 ) :

				with GafferUI.ListContainer( GafferUI.ListContainer.Orientation.Horizontal ) :
					GafferUI.Label( "Current Dispatcher" )
					dispatchersMenu = GafferUI.MultiSelectionMenu( allowMultipleSelection = False, allowEmptySelection = False )
					dispatchersMenu.append( [ IECore.CamelCase.toSpaced( x ) for x in Gaffer.Dispatcher.dispatcherNames() ] )
					dispatchersMenu.setSelection( [ "Local" ] )
					self.__dispatchersMenuSelectionChangedConnection = dispatchersMenu.selectionChangedSignal().connect( Gaffer.WeakMethod( self.__dispatcherChanged ) )

				self.__frame = GafferUI.Frame( borderStyle=GafferUI.Frame.BorderStyle.None, borderWidth=0 )
				self.__dispatchButton = GafferUI.Button( "Dispatch" )
				self.__dispatchClickedConnection = self.__dispatchButton.clickedSignal().connect( Gaffer.WeakMethod( self.__dispatchClicked ) )

		self.__update()

	def setVisible( self, visible ) :

		GafferUI.Window.setVisible( self, visible )

		if visible :
			self.__dispatchButton._qtWidget().setFocus( QtCore.Qt.OtherFocusReason )

	def dispatcher( self ) :

		return self.__dispatcher

	def setNodesToDispatch( self, nodes ) :

		self.__nodes = nodes
		self.__updateTitle()

	def __update( self ) :

		self.__frame.setChild( GafferUI.NodeUI.create( self.__dispatcher ) )
		self.__updateTitle()

	def __updateTitle( self ) :

		title = "Dispatching"
		if len(self.__nodes) :
			title += ": " + ", ".join( [ x.getName() for x in self.__nodes ] )

		self.setTitle( title )

	def __dispatchClicked( self, button ) :

		threading.Thread( target = self.__dispatch ).start()
		self.close()

	def __dispatch( self ) :

		with self.parent().scriptNode().context() :
			self.__dispatcher.dispatch( self.__nodes )
		## \todo: update _executeUILastExecuted

	def __dispatcherChanged( self, menu ) :

		self.__dispatcher = Gaffer.Dispatcher.dispatcher( IECore.CamelCase.fromSpaced( menu.getSelection()[0], IECore.CamelCase.Caps.AllExceptFirst ) )
		self.__update()

##################################################################################
# PlugValueWidget for execution - this forms the header for the ExecutableNode ui.
##################################################################################

class __RequirementPlugValueWidget( GafferUI.PlugValueWidget ) :

	def __init__( self, plug, **kw ) :

		row = GafferUI.ListContainer( GafferUI.ListContainer.Orientation.Horizontal )

		GafferUI.PlugValueWidget.__init__( self, row, plug, **kw )

		with row :

			executeButton = GafferUI.Button( "Execute" )
			executeButton.setToolTip( "Execute" )
			self.__executeClickedConnection = executeButton.clickedSignal().connect( Gaffer.WeakMethod( self.__executeClicked ) )

	def hasLabel( self ) :

		return True

	def _updateFromPlug( self ) :

		pass

	def __executeClicked( self, button ) :

		_showDispatcherWindow( [ self.getPlug().node() ] )

#################################
# PlugValueWidget for framesMode
#################################

## \todo: This can be removed once we have enums and activators driven by metadata.
class __FramesModePlugValueWidget( GafferUI.EnumPlugValueWidget ) :

	def __init__( self, plug ) :

		GafferUI.EnumPlugValueWidget.__init__(
			self, plug,
			labelsAndValues = (
				( "CurrentFrame", Gaffer.Dispatcher.FramesMode.CurrentFrame ),
				( "ScriptRange", Gaffer.Dispatcher.FramesMode.ScriptRange ),
				( "CustomRange", Gaffer.Dispatcher.FramesMode.CustomRange ),
			)
		)

	def _updateFromPlug( self ) :

		GafferUI.EnumPlugValueWidget._updateFromPlug( self )

		if self.getPlug() is None :
			return

		with self.getContext() :
			framesMode = self.getPlug().getValue()

		nodeUI = self.ancestor( GafferUI.NodeUI )
		if nodeUI :
			frameRangeWidget = nodeUI.plugValueWidget( self.getPlug().node()["frameRange"], lazy = False )
			if frameRangeWidget :
				frameRangeWidget.setEnabled( framesMode == Gaffer.Dispatcher.FramesMode.CustomRange )

##########################################################################
# Metadata, PlugValueWidgets and Nodules
##########################################################################

Gaffer.Metadata.registerPlugValue( Gaffer.ExecutableNode, "requirement", "nodeUI:section", "header" )
Gaffer.Metadata.registerPlugValue( Gaffer.ExecutableNode, "dispatcher", "nodeUI:section", "Dispatcher" )
Gaffer.Metadata.registerPlugDescription( Gaffer.Dispatcher, "framesMode", "Determines the active frame range for dispatching." )
Gaffer.Metadata.registerPlugDescription( Gaffer.Dispatcher, "frameRange", "The frame range to be used when framesMode is set to CustomRange." )
Gaffer.Metadata.registerPlugDescription( Gaffer.Dispatcher, "jobDirectory", "A directory to store temporary files used by the dispatcher." )

GafferUI.PlugValueWidget.registerCreator(
	Gaffer.Dispatcher,
	"jobDirectory",
	lambda plug : GafferUI.PathPlugValueWidget( plug,
		path = Gaffer.FileSystemPath( "/", filter = Gaffer.FileSystemPath.createStandardFilter( [ "gfr" ] ) ),
		pathChooserDialogueKeywords = {
			"leaf" : False,
		},
	)
)

GafferUI.PlugValueWidget.registerCreator( Gaffer.Dispatcher, "user", None )
GafferUI.PlugValueWidget.registerCreator( Gaffer.Dispatcher, "framesMode", __FramesModePlugValueWidget )
GafferUI.PlugValueWidget.registerCreator( Gaffer.ExecutableNode, "requirement", __RequirementPlugValueWidget )
GafferUI.PlugValueWidget.registerCreator( Gaffer.ExecutableNode, "dispatcher", GafferUI.CompoundPlugValueWidget, collapsed = None )

GafferUI.Nodule.registerNodule( Gaffer.Dispatcher, fnmatch.translate( "*" ), lambda plug : None )
GafferUI.Nodule.registerNodule( Gaffer.ExecutableNode, "dispatcher", lambda plug : None )

##########################################################################
# Implementation Details
##########################################################################

def _execute( nodes ) :

	script = nodes[0].scriptNode()
	script._executeUILastExecuted = []

	with script.context() :
		__dispatcherWindow( script ).dispatcher().dispatch( nodes )

	script._executeUILastExecuted = [ weakref.ref( node ) for node in nodes ]

__dispatcherWindowInstance = None
def __dispatcherWindow( script ) :

	global __dispatcherWindowInstance

	if __dispatcherWindowInstance is not None and __dispatcherWindowInstance() :
		window = __dispatcherWindowInstance()
	else :
		window = _DispatcherWindow()
		__dispatcherWindowInstance = weakref.ref( window )

	scriptWindow = GafferUI.ScriptWindow.acquire( script )
	scriptWindow.addChildWindow( window )

	return window

def _showDispatcherWindow( nodes ) :

	window = __dispatcherWindow( nodes[0].scriptNode() )
	window.setNodesToDispatch( nodes )
	window.setVisible( True )

def __selectedNodes( script ) :

	result = []
	for n in script.selection() :
		if hasattr( n, "execute" ) :
			result.append( n )

	return result

def __selectionAvailable( menu ) :

	scriptWindow = menu.ancestor( GafferUI.ScriptWindow )
	script = scriptWindow.scriptNode()
	return bool( __selectedNodes( script ) )

def __previous( script ) :

	if not hasattr( script, "_executeUILastExecuted" ) :
		return []

	result = []
	for w in script._executeUILastExecuted :
		n = w()
		if n is not None :
			s = n.scriptNode()
			if s is not None and s.isSame( script ) :
				result.append( n )

	return result

def __previousAvailable( menu ) :

	scriptWindow = menu.ancestor( GafferUI.ScriptWindow )
	script = scriptWindow.scriptNode()
	return bool( __previous( script ) )
<commit_msg>Removed "Current" from label and added spacing.<commit_after>##########################################################################
#
#  Copyright (c) 2012, John Haddon. All rights reserved.
#  Copyright (c) 2012-2014, Image Engine Design Inc. All rights reserved.
#
#  Redistribution and use in source and binary forms, with or without
#  modification, are permitted provided that the following conditions are
#  met:
#
#      * Redistributions of source code must retain the above
#        copyright notice, this list of conditions and the following
#        disclaimer.
#
#      * Redistributions in binary form must reproduce the above
#        copyright notice, this list of conditions and the following
#        disclaimer in the documentation and/or other materials provided with
#        the distribution.
#
#      * Neither the name of John Haddon nor the names of
#        any other contributors to this software may be used to endorse or
#        promote products derived from this software without specific prior
#        written permission.
#
#  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
#  IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
#  THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
#  PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
#  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
#  EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
#  PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
#  PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
#  LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
#  NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
#  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#
##########################################################################

import fnmatch
import threading
import weakref

import IECore

import Gaffer
import GafferUI

QtCore = GafferUI._qtImport( "QtCore" )

##########################################################################
# Public functions
##########################################################################

def appendMenuDefinitions( menuDefinition, prefix="" ) :

	menuDefinition.append( prefix + "/Execute Selected", { "command" : executeSelected, "shortCut" : "Ctrl+E", "active" : __selectionAvailable } )
	menuDefinition.append( prefix + "/Repeat Previous", { "command" : repeatPrevious, "shortCut" : "Ctrl+R", "active" : __previousAvailable } )

def appendNodeContextMenuDefinitions( nodeGraph, node, menuDefinition ) :

	if not hasattr( node, "execute" ) :
		return

	menuDefinition.append( "/ExecuteDivider", { "divider" : True } )
	menuDefinition.append( "/Execute", { "command" : IECore.curry( _showDispatcherWindow, [ node ] ) } )

def executeSelected( menu ) :

	scriptWindow = menu.ancestor( GafferUI.ScriptWindow )
	script = scriptWindow.scriptNode()
	_showDispatcherWindow( __selectedNodes( script ) )

def repeatPrevious( menu ) :

	scriptWindow = menu.ancestor( GafferUI.ScriptWindow )
	script = scriptWindow.scriptNode()
	_execute( __previous( script ) )

##################################################################################
# Dispatcher Window
##################################################################################

class _DispatcherWindow( GafferUI.Window ) :

	def __init__( self, **kw ) :

		GafferUI.Window.__init__( self, **kw )

		self.__dispatcher = Gaffer.Dispatcher.dispatcher( "local" )
		self.__nodes = []

		with self :

			with GafferUI.ListContainer( orientation = GafferUI.ListContainer.Orientation.Vertical, spacing = 2, borderWidth = 4 ) :

				with GafferUI.ListContainer( GafferUI.ListContainer.Orientation.Horizontal, spacing = 4 ) :
					GafferUI.Label( "Dispatcher" )
					dispatchersMenu = GafferUI.MultiSelectionMenu( allowMultipleSelection = False, allowEmptySelection = False )
					dispatchersMenu.append( [ IECore.CamelCase.toSpaced( x ) for x in Gaffer.Dispatcher.dispatcherNames() ] )
					dispatchersMenu.setSelection( [ "Local" ] )
					self.__dispatchersMenuSelectionChangedConnection = dispatchersMenu.selectionChangedSignal().connect( Gaffer.WeakMethod( self.__dispatcherChanged ) )

				self.__frame = GafferUI.Frame( borderStyle=GafferUI.Frame.BorderStyle.None, borderWidth=0 )
				self.__dispatchButton = GafferUI.Button( "Dispatch" )
				self.__dispatchClickedConnection = self.__dispatchButton.clickedSignal().connect( Gaffer.WeakMethod( self.__dispatchClicked ) )

		self.__update()

	def setVisible( self, visible ) :

		GafferUI.Window.setVisible( self, visible )

		if visible :
			self.__dispatchButton._qtWidget().setFocus( QtCore.Qt.OtherFocusReason )

	def dispatcher( self ) :

		return self.__dispatcher

	def setNodesToDispatch( self, nodes ) :

		self.__nodes = nodes
		self.__updateTitle()

	def __update( self ) :

		self.__frame.setChild( GafferUI.NodeUI.create( self.__dispatcher ) )
		self.__updateTitle()

	def __updateTitle( self ) :

		title = "Dispatching"
		if len(self.__nodes) :
			title += ": " + ", ".join( [ x.getName() for x in self.__nodes ] )

		self.setTitle( title )

	def __dispatchClicked( self, button ) :

		threading.Thread( target = self.__dispatch ).start()
		self.close()

	def __dispatch( self ) :

		with self.parent().scriptNode().context() :
			self.__dispatcher.dispatch( self.__nodes )
		## \todo: update _executeUILastExecuted

	def __dispatcherChanged( self, menu ) :

		self.__dispatcher = Gaffer.Dispatcher.dispatcher( IECore.CamelCase.fromSpaced( menu.getSelection()[0], IECore.CamelCase.Caps.AllExceptFirst ) )
		self.__update()

##################################################################################
# PlugValueWidget for execution - this forms the header for the ExecutableNode ui.
##################################################################################

class __RequirementPlugValueWidget( GafferUI.PlugValueWidget ) :

	def __init__( self, plug, **kw ) :

		row = GafferUI.ListContainer( GafferUI.ListContainer.Orientation.Horizontal )

		GafferUI.PlugValueWidget.__init__( self, row, plug, **kw )

		with row :

			executeButton = GafferUI.Button( "Execute" )
			executeButton.setToolTip( "Execute" )
			self.__executeClickedConnection = executeButton.clickedSignal().connect( Gaffer.WeakMethod( self.__executeClicked ) )

	def hasLabel( self ) :

		return True

	def _updateFromPlug( self ) :

		pass

	def __executeClicked( self, button ) :

		_showDispatcherWindow( [ self.getPlug().node() ] )

#################################
# PlugValueWidget for framesMode
#################################

## \todo: This can be removed once we have enums and activators driven by metadata.
class __FramesModePlugValueWidget( GafferUI.EnumPlugValueWidget ) :

	def __init__( self, plug ) :

		GafferUI.EnumPlugValueWidget.__init__(
			self, plug,
			labelsAndValues = (
				( "CurrentFrame", Gaffer.Dispatcher.FramesMode.CurrentFrame ),
				( "ScriptRange", Gaffer.Dispatcher.FramesMode.ScriptRange ),
				( "CustomRange", Gaffer.Dispatcher.FramesMode.CustomRange ),
			)
		)

	def _updateFromPlug( self ) :

		GafferUI.EnumPlugValueWidget._updateFromPlug( self )

		if self.getPlug() is None :
			return

		with self.getContext() :
			framesMode = self.getPlug().getValue()

		nodeUI = self.ancestor( GafferUI.NodeUI )
		if nodeUI :
			frameRangeWidget = nodeUI.plugValueWidget( self.getPlug().node()["frameRange"], lazy = False )
			if frameRangeWidget :
				frameRangeWidget.setEnabled( framesMode == Gaffer.Dispatcher.FramesMode.CustomRange )

##########################################################################
# Metadata, PlugValueWidgets and Nodules
##########################################################################

Gaffer.Metadata.registerPlugValue( Gaffer.ExecutableNode, "requirement", "nodeUI:section", "header" )
Gaffer.Metadata.registerPlugValue( Gaffer.ExecutableNode, "dispatcher", "nodeUI:section", "Dispatcher" )
Gaffer.Metadata.registerPlugDescription( Gaffer.Dispatcher, "framesMode", "Determines the active frame range for dispatching." )
Gaffer.Metadata.registerPlugDescription( Gaffer.Dispatcher, "frameRange", "The frame range to be used when framesMode is set to CustomRange." )
Gaffer.Metadata.registerPlugDescription( Gaffer.Dispatcher, "jobDirectory", "A directory to store temporary files used by the dispatcher." )

GafferUI.PlugValueWidget.registerCreator(
	Gaffer.Dispatcher,
	"jobDirectory",
	lambda plug : GafferUI.PathPlugValueWidget( plug,
		path = Gaffer.FileSystemPath( "/", filter = Gaffer.FileSystemPath.createStandardFilter( [ "gfr" ] ) ),
		pathChooserDialogueKeywords = {
			"leaf" : False,
		},
	)
)

GafferUI.PlugValueWidget.registerCreator( Gaffer.Dispatcher, "user", None )
GafferUI.PlugValueWidget.registerCreator( Gaffer.Dispatcher, "framesMode", __FramesModePlugValueWidget )
GafferUI.PlugValueWidget.registerCreator( Gaffer.ExecutableNode, "requirement", __RequirementPlugValueWidget )
GafferUI.PlugValueWidget.registerCreator( Gaffer.ExecutableNode, "dispatcher", GafferUI.CompoundPlugValueWidget, collapsed = None )

GafferUI.Nodule.registerNodule( Gaffer.Dispatcher, fnmatch.translate( "*" ), lambda plug : None )
GafferUI.Nodule.registerNodule( Gaffer.ExecutableNode, "dispatcher", lambda plug : None )

##########################################################################
# Implementation Details
##########################################################################

def _execute( nodes ) :

	script = nodes[0].scriptNode()
	script._executeUILastExecuted = []

	with script.context() :
		__dispatcherWindow( script ).dispatcher().dispatch( nodes )

	script._executeUILastExecuted = [ weakref.ref( node ) for node in nodes ]

__dispatcherWindowInstance = None
def __dispatcherWindow( script ) :

	global __dispatcherWindowInstance

	if __dispatcherWindowInstance is not None and __dispatcherWindowInstance() :
		window = __dispatcherWindowInstance()
	else :
		window = _DispatcherWindow()
		__dispatcherWindowInstance = weakref.ref( window )

	scriptWindow = GafferUI.ScriptWindow.acquire( script )
	scriptWindow.addChildWindow( window )

	return window

def _showDispatcherWindow( nodes ) :

	window = __dispatcherWindow( nodes[0].scriptNode() )
	window.setNodesToDispatch( nodes )
	window.setVisible( True )

def __selectedNodes( script ) :

	result = []
	for n in script.selection() :
		if hasattr( n, "execute" ) :
			result.append( n )

	return result

def __selectionAvailable( menu ) :

	scriptWindow = menu.ancestor( GafferUI.ScriptWindow )
	script = scriptWindow.scriptNode()
	return bool( __selectedNodes( script ) )

def __previous( script ) :

	if not hasattr( script, "_executeUILastExecuted" ) :
		return []

	result = []
	for w in script._executeUILastExecuted :
		n = w()
		if n is not None :
			s = n.scriptNode()
			if s is not None and s.isSame( script ) :
				result.append( n )

	return result

def __previousAvailable( menu ) :

	scriptWindow = menu.ancestor( GafferUI.ScriptWindow )
	script = scriptWindow.scriptNode()
	return bool( __previous( script ) )
<|endoftext|>


<commit_before>"""
useful links:
    - http://wiki.luajit.org/Bytecode/c728f657001eaeee7f64eb99f47dc413c8e29a56
    - http://wiki.luajit.org/Bytecode-2.0
    - https://github.com/creationix/brozula/blob/master/parser.js
    - http://lua-users.org/lists/lua-l/2012-12/msg00229.html
    - http://luajit.org/running.html#opt_b

"""
import os

from rpython.annotator.model import SomeByteArray
from rpython.rlib.rstruct.runpack import runpack
from rpython.rlib.rstruct.ieee import float_unpack
from rpython.rlib.unroll import unrolling_iterable

from pylua.opcodes import OP_DESC, ARGS_AD, ARGS_ABC
from pylua.luaframe import LuaBytecodeFrame
from pylua.helpers import debug_print
from pylua.w_objects import W_Str, W_Num

KGC_TYPES = ["CHILD", "TAB", "I64", "U64", "COMPLEX", "STR", "const_str"]
UNROLLED_KGC_TYPES = unrolling_iterable(KGC_TYPES)

"""
def decode_arg(self, type, val):
    if type == 'lit':  # literal
        return Arg(val >> 0)#?
    elif type == 'lits': # signed literal
        return Arg(0x10000 - val if (val & 0x8000) > 0 else val)
    elif type == 'pri':
        if val == 0: return Arg(None)
        elif val == 1: return Arg(False)
        elif val == 2: return Arg(True)
        else: assert 0
    elif type == 'num': return Arg(val) #return self.constants[val]
    elif type in ('str', 'tab', 'func', 'cdata'):
        return Arg(self.constants[self.num_consts-val-1])
    elif type == 'jump':
        return Arg(val - 0x8000)
    else:
     return Arg(val)
"""

class Parser(object):
    def __init__(self,  filename):
        if isinstance(filename, SomeByteArray):
            self.bytes = filename
        else:
            f = os.open(filename, os.O_RDONLY, 0777)
            self.bytes = bytes(os.read(f, 99999))
        self.pos = 0

    def next_bytes(self, l):
        if self.pos >= 0:
            v = self.bytes[self.pos:self.pos+l]
            self.pos += l
            return v

    def byte(self):
        return runpack('=B', self.next_bytes(1))

    def peek(self):
        return runpack('=B', self.bytes[self.pos])

    def h(self):
        return runpack('=H', self.next_bytes(2))

    def word(self):
        return runpack('=I', self.next_bytes(4))

    def uleb(self):
        #https://en.wikipedia.org/wiki/LEB128#Decode_unsigned_integer
        result = 0
        shift = 0
        while True:
            b = self.byte()
            result |= (b & 0x7f) << shift
            if b & 0x80 == 0:
                break
            shift += 7
        return result

    def parse(self):
        # parses a luajit bytecode file
        # see http://wiki.luajit.org/Bytecode-2.0 for format information

        # header = ESC 'L' 'J' versionB flagsU [namelenU nameB*]
        if self.byte() != 0x1b:
            raise ValueError("Expected ESC in first byte")
        if self.byte() != 0x4c:
            raise ValueError("Expected L in second byte")
        if self.byte() != 0x4a:
            raise ValueError("Expected J in third byte")
        if self.byte() != 1:
            raise ValueError("Only version 1 supported")

        # flags
        flags = self.uleb()

        # proto+
        self.frames = []
        while True:
            l = self.uleb()
            self.frames.append(self.parse_frame())
            # peek at next byte only, do not consume
            if self.peek() == 0:
                break
        # 0U and EOF
        if self.uleb() != 0:
            raise ValueError("Missing 0U at end of file")
        if self.pos < len(self.bytes):
            raise ValueError(" bytes leftover")
        return (flags, self.frames[-1])

    def parse_frame(self):
        flags = self.byte()
        num_params = self.byte()
        frame_size = self.byte()
        num_uv = self.byte()
        num_kgc = self.uleb()
        num_kn = self.uleb()
        num_bc = self.uleb()

        instructions = []
        debug_print("found "+str(num_bc)+" bc instructions")
        for i in xrange(0, num_bc):
            instructions.append(self.decode_opcode(self.word()))

        debug_print("num uv "+str(num_uv))
        uv_data = []
        for i in xrange(0, num_uv):
            uv = self.h()
            uv_data.append((uv & 0x8000, uv & 0x4000, uv & 0x3fff))

        constants = [None] * (num_kgc+num_kn)

        childc = len(self.frames)
        #TODO imlement constant parsing
        for i in xrange(0, num_kgc):
            u = self.uleb()

            # CHILD
            if u == 0:
                childc -= 1
                constants[num_kn+i] = self.frames[childc]
            else:  # string and all other things
                constants[num_kn+i] = self.const_str(u)
            """
            for t in UNROLLED_KGC_TYPES:
                if t == kgc_type:
                    meth = getattr(self, t)
            """

        for i in xrange(0, num_kn):
            debug_print("read knum")
            constants[i] = self.read_knum()

        debug_print(str(constants))
        for (ind, args) in instructions:
            debug_print(str(OP_DESC[ind].name)+" "+str(args))

        return LuaBytecodeFrame(flags, constants, instructions)

    def const_str(self, l):
        l -= 5  # Offset for STR enum
        assert l > 0
        v = self.bytes[self.pos:self.pos+l]
        self.pos += l
        return W_Str(v)

    def read_knum(self):
        isnum = self.peek() & 1
        lo = self.uleb() >> 1
        if isnum == 1:
            """
            IEEE 64 bit floating point constant
            """
            hi = self.uleb()
            hi = hi << 32
            res = float_unpack(lo | hi, 8)
            # TODO n_val can be a float or a int, can this lead
            # to problems when translating?
            return W_Num(res)
        return W_Num(lo)

    def decode_opcode(self, word):
        ind = word & 0xff
        op_desc = OP_DESC[ind]
        args_type = op_desc.args_type
        args = (0, 0, 0)
        a = (word >> 8) & 0xff
        if args_type == ARGS_AD:
            args = (a, word >> 16, 0)
        elif args_type == ARGS_ABC:
            args = (a, word >> 24, (word >> 16) & 0xff)
        else:
            raise ValueError('Invalid argument type')
        return (ind, args)
<commit_msg>Add parsing for table constants<commit_after>"""
useful links:
    - http://wiki.luajit.org/Bytecode/c728f657001eaeee7f64eb99f47dc413c8e29a56
    - http://wiki.luajit.org/Bytecode-2.0
    - https://github.com/creationix/brozula/blob/master/parser.js
    - http://lua-users.org/lists/lua-l/2012-12/msg00229.html
    - http://luajit.org/running.html#opt_b

"""
import os

from rpython.annotator.model import SomeByteArray
from rpython.rlib.rstruct.runpack import runpack
from rpython.rlib.rstruct.ieee import float_unpack
from rpython.rlib.unroll import unrolling_iterable

from pylua.opcodes import OP_DESC, ARGS_AD, ARGS_ABC
from pylua.luaframe import LuaBytecodeFrame
from pylua.helpers import debug_print
from pylua.w_objects import W_Str, W_Num, W_Table, W_Pri

KGC_TYPES = ["CHILD", "TAB", "I64", "U64", "COMPLEX", "STR", "const_str"]
UNROLLED_KGC_TYPES = unrolling_iterable(KGC_TYPES)

"""
def decode_arg(self, type, val):
    if type == 'lit':  # literal
        return Arg(val >> 0)#?
    elif type == 'lits': # signed literal
        return Arg(0x10000 - val if (val & 0x8000) > 0 else val)
    elif type == 'pri':
        if val == 0: return Arg(None)
        elif val == 1: return Arg(False)
        elif val == 2: return Arg(True)
        else: assert 0
    elif type == 'num': return Arg(val) #return self.constants[val]
    elif type in ('str', 'tab', 'func', 'cdata'):
        return Arg(self.constants[self.num_consts-val-1])
    elif type == 'jump':
        return Arg(val - 0x8000)
    else:
     return Arg(val)
"""

class Parser(object):
    def __init__(self,  filename):
        if isinstance(filename, SomeByteArray):
            self.bytes = filename
        else:
            f = os.open(filename, os.O_RDONLY, 0777)
            self.bytes = bytes(os.read(f, 99999))
        self.pos = 0

    def next_bytes(self, l):
        if self.pos >= 0:
            v = self.bytes[self.pos:self.pos+l]
            self.pos += l
            return v

    def byte(self):
        return runpack('=B', self.next_bytes(1))

    def peek(self):
        return runpack('=B', self.bytes[self.pos])

    def h(self):
        return runpack('=H', self.next_bytes(2))

    def word(self):
        return runpack('=I', self.next_bytes(4))

    def uleb(self):
        #https://en.wikipedia.org/wiki/LEB128#Decode_unsigned_integer
        result = 0
        shift = 0
        while True:
            b = self.byte()
            result |= (b & 0x7f) << shift
            if b & 0x80 == 0:
                break
            shift += 7
        return result

    def parse_tab_entry(self):
        t_type = self.uleb()
        if t_type == 0:
            # NIL
            return W_Pri(0)
        elif t_type == 1:
            # FALSE
            return W_Pri(1)
        elif t_type == 2:
            # TRUE
            return W_Pri(2)
        elif t_type == 3:
            # INT
            return W_Num(self.uleb())
        elif t_type == 4:
            return self.read_knum()
        else:
            return self.const_str(t_type)

    def parse(self):
        # parses a luajit bytecode file
        # see http://wiki.luajit.org/Bytecode-2.0 for format information

        # header = ESC 'L' 'J' versionB flagsU [namelenU nameB*]
        if self.byte() != 0x1b:
            raise ValueError("Expected ESC in first byte")
        if self.byte() != 0x4c:
            raise ValueError("Expected L in second byte")
        if self.byte() != 0x4a:
            raise ValueError("Expected J in third byte")
        if self.byte() != 1:
            raise ValueError("Only version 1 supported")

        # flags
        flags = self.uleb()

        # proto+
        self.frames = []
        while True:
            l = self.uleb()
            self.frames.append(self.parse_frame())
            # peek at next byte only, do not consume
            if self.peek() == 0:
                break
        # 0U and EOF
        if self.uleb() != 0:
            raise ValueError("Missing 0U at end of file")
        if self.pos < len(self.bytes):
            raise ValueError(" bytes leftover")
        return (flags, self.frames[-1])

    def parse_frame(self):
        flags = self.byte()
        num_params = self.byte()
        frame_size = self.byte()
        num_uv = self.byte()
        num_kgc = self.uleb()
        num_kn = self.uleb()
        num_bc = self.uleb()

        instructions = []
        debug_print("found "+str(num_bc)+" bc instructions")
        for i in xrange(0, num_bc):
            instructions.append(self.decode_opcode(self.word()))

        debug_print("num uv "+str(num_uv))
        uv_data = []
        for i in xrange(0, num_uv):
            uv = self.h()
            uv_data.append((uv & 0x8000, uv & 0x4000, uv & 0x3fff))

        constants = [None] * (num_kgc+num_kn)

        childc = len(self.frames)
        #TODO imlement constant parsing
        for i in xrange(0, num_kgc):
            u = self.uleb()
            # CHILD
            if u == 0:
                childc -= 1
                constants[num_kn+i] = self.frames[childc]
            elif u == 1:
                len_array = self.uleb()
                len_dict = self.uleb()
                w_table = W_Table()
                if len_array > 0:
                    if len_dict > 0:
                        raise RuntimeError("tables with mixed keys not supported at the moment")
                    for j in xrange(0, len_array):
                        w_table.set_val(str(j), self.parse_tab_entry())
                elif len_dict > 0:
                    if len_array > 0:
                        raise RuntimeError("tables with mixed keys not supported at the moment")
                    for j in xrange(0, len_dict):
                        key = self.parse_tab_entry()
                        w_table.set_val(key.to_str(), self.parse_tab_entry())
                constants[num_kn+i] = w_table

            else:  # string and all other things
                constants[num_kn+i] = self.const_str(u)
            """
            for t in UNROLLED_KGC_TYPES:
                if t == kgc_type:
                    meth = getattr(self, t)
            """

        for i in xrange(0, num_kn):
            debug_print("read knum")
            constants[i] = self.read_knum()

        debug_print(str(constants))
        for (ind, args) in instructions:
            debug_print(str(OP_DESC[ind].name)+" "+str(args))

        return LuaBytecodeFrame(flags, constants, instructions)

    def const_str(self, l):
        l -= 5  # Offset for STR enum
        assert l > 0
        v = self.bytes[self.pos:self.pos+l]
        self.pos += l
        return W_Str(v)

    def read_knum(self):
        isnum = self.peek() & 1
        lo = self.uleb() >> 1
        if isnum == 1:
            """
            IEEE 64 bit floating point constant
            """
            hi = self.uleb()
            hi = hi << 32
            res = float_unpack(lo | hi, 8)
            # TODO n_val can be a float or a int, can this lead
            # to problems when translating?
            return W_Num(res)
        return W_Num(lo)

    def decode_opcode(self, word):
        ind = word & 0xff
        op_desc = OP_DESC[ind]
        args_type = op_desc.args_type
        args = (0, 0, 0)
        a = (word >> 8) & 0xff
        if args_type == ARGS_AD:
            args = (a, word >> 16, 0)
        elif args_type == ARGS_ABC:
            args = (a, word >> 24, (word >> 16) & 0xff)
        else:
            raise ValueError('Invalid argument type')
        return (ind, args)
<|endoftext|>


<commit_before># --------------------------------------------------------
# (c) Copyright 2014 by Jason DeLaat.
# Licensed under BSD 3-clause licence.
# --------------------------------------------------------
from pymonad.Container import *

class Monoid(Container):
	def __init__(self, value):
		super(Monoid, self).__init__(value)

	def __add__(self, other):
		return self.mplus(other)

	@staticmethod
	def mzero():
		raise NotImplementedError

	def mplus(self, other):
		raise NotImplementedError

def mzero(monoid_value):
	try:
		return monoid_value.mzero()
	except AttributeError:
		if isinstance(monoid_value, int) or isinstance(monoid_value, float) or monoid_value == int or monoid_value == float:
			return 0
		elif isinstance(monoid_value, str) or monoid_value == str:
			return ""
		elif isinstance(monoid_value, list) or monoid_value == list:
			return []
		else:
			raise TypeError(str(monoid_value) + " is not a Monoid.")

def mconcat(monoid_list):
	result = mzero(monoid_list[0])
	for value in monoid_list:
		result += value
	return result
<commit_msg>Added docstrings to all classes, functions and methods in the Monoid module.<commit_after># --------------------------------------------------------
# (c) Copyright 2014 by Jason DeLaat.
# Licensed under BSD 3-clause licence.
# --------------------------------------------------------
from pymonad.Container import *

class Monoid(Container):
	"""
	Represents a data type which conforms to the following conditions:
	   1. Has an operation (called 'mplus') which combines two values of this type.
	   2. Has a value (called 'mzero') such that mplus(mzero, value) == mplus(value, mzero) = value.
	      In other words, mzero acts as an identity element under the mplus operation.
	   3. mplus is associative: mplus(a, mplus(b, c)) == mplus(mplus(a, b), c)

	   For instance, integers can be monoids in two ways: With mzero = 0 and mplus = + (addition)
	   or with mzero = 1 and mplus = * (multiplication).
	   In the case of strings, mzero = "" (the empty string) and mplus = + (concatenation).

	"""

	def __init__(self, value):
		""" Initializes the monoid element to 'value'.  """
		super(Monoid, self).__init__(value)

	def __add__(self, other):
		""" The 'mplus' operator.  """
		return self.mplus(other)

	@staticmethod
	def mzero():
		"""
		A static method which simply returns the identity value for the monoid type.
		This method must be overridden in subclasses to create custom monoids.
		See also: the mzero function.

		"""
		raise NotImplementedError

	def mplus(self, other):
		"""
		The defining operation of the monoid. This method must be overridden in subclasses
		and should meet the following conditions.
		   1. x + 0 == 0 + x == x
		   2. (x + y) + z == x + (y + z) == x + y + z
		Where 'x', 'y', and 'z' are monoid values, '0' is the mzero (the identity value) and '+'
		is mplus.

		"""
		raise NotImplementedError

def mzero(monoid_type):
	"""
	Returns the identity value for monoid_type. Raises TypeError if monoid_type is not a valid monoid.

	There are a number of builtin types that can operate as monoids and they can be used as such
	as is. These "natural" monoids are: int, float, str, and list.
	While thee mzero method will work on monoids derived from the Monoid class,
	this mzero function will work for *all* monoid types, including the "natural" monoids.
	For this reason it is preferable to call this function rather than calling the mzero method directly
	unless you know for sure what type of monoid you're dealing with.

	"""
	try:
		return monoid_type.mzero()
	except AttributeError:
		if isinstance(monoid_type, int) or isinstance(monoid_type, float) or monoid_type == int or monoid_type == float:
			return 0
		elif isinstance(monoid_type, str) or monoid_type == str:
			return ""
		elif isinstance(monoid_type, list) or monoid_type == list:
			return []
		else:
			raise TypeError(str(monoid_type) + " is not a Monoid.")

def mconcat(monoid_list):
	"""
	Takes a list of monoid values and reduces them to a single value by applying the
	mplus operation to each pair of elements from the list.

	"""
	result = mzero(monoid_list[0])
	for value in monoid_list:
		result += value
	return result
<|endoftext|>