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

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

from __future__ import print_function

try:
    from collections import OrderedDict  # noqa
except ImportError:
    from ordereddict import OrderedDict  # noqa

import collections
import contextlib
import json
import os
import shutil
import sys
import tempfile
import traceback

from distutils import version as dist_version

from pip import req as pip_req

import argparse
import requests
import six
from six.moves import urllib

try:
    from pip import util as pip_util  # noqa
except ImportError:
    from pip import utils as pip_util  # noqa

_FINDER_URL_TPL = 'http://pypi.python.org/pypi/%s/json'

# Egg info cache and url fetch caches...
_EGGS_DETAILED = {}
_FINDER_LOOKUPS = {}
_EGGS_FAILED_DETAILED = {}
_MAX_PRIOR_VERSIONS = -1

class RequirementException(Exception):
    pass

class NotFoundException(Exception):
    pass

_MatchedRelease = collections.namedtuple('_MatchedRelease',
                                         ['string_version',
                                          'parsed_version',
                                          'origin_url',
                                          'origin_filename',
                                          'origin_size'])

def req_key(req):
    return req.req.key

@contextlib.contextmanager
def tempdir(**kwargs):
    # This seems like it was only added in python 3.2
    # Make it since its useful...
    # See: http://bugs.python.org/file12970/tempdir.patch
    tdir = tempfile.mkdtemp(**kwargs)
    try:
        yield tdir
    finally:
        shutil.rmtree(tdir)

def get_directory_details(path):
    if not os.path.isdir(path):
        raise IOError("Can not detail non-existent directory %s" % (path))
    req = pip_req.InstallRequirement.from_line(path)
    req.source_dir = path
    req.run_egg_info()
    dependencies = []
    for d in req.requirements():
        if not d.startswith("-e") and d.find("#"):
            d = d.split("#")[0]
        d = d.strip()
        if d:
            dependencies.append(d)
    details = {
        'req': req.req,
        'dependencies': dependencies,
        'name': req.name,
        'pkg_info': req.pkg_info(),
        'dependency_links': req.dependency_links,
        'version': req.installed_version,
    }
    return details

def get_archive_details(filename, filesize, options, prefix=""):
    if not os.path.isfile(filename):
        raise IOError("Can not detail non-existent file %s" % (filename))
    cache_key = "f:%s:%s" % (os.path.basename(filename), filesize)
    if cache_key in _EGGS_FAILED_DETAILED:
        exc_type, exc_value, exc_traceback = _EGGS_FAILED_DETAILED[cache_key]
        six.reraise(exc_type, exc_value, exc_traceback)
    try:
        return _EGGS_DETAILED[cache_key]
    except KeyError:
        if options.verbose:
            print("%s: Extracting egg-info from '%s'"
                  % (prefix, os.path.basename(filename)))
        with tempdir() as a_dir:
            arch_filename = os.path.join(a_dir, os.path.basename(filename))
            shutil.copyfile(filename, arch_filename)
            extract_to = os.path.join(a_dir, 'build')
            os.makedirs(extract_to)
            pip_util.unpack_file(arch_filename, extract_to,
                                 content_type='', link='')
            try:
                details = get_directory_details(extract_to)
            except Exception:
                # Don't bother saving the traceback (we don't care about it...)
                exc_type, exc_value, exc_traceback = sys.exc_info()
                _EGGS_FAILED_DETAILED[cache_key] = (exc_type, exc_value, None)
                raise
            else:
                _EGGS_DETAILED[cache_key] = details
                return details

def create_parser():
    parser = argparse.ArgumentParser()
    parser.add_argument(
        "-r", "--requirement",
        dest="requirements",
        nargs="+",
        default=[],
        metavar="<file>",
        help="Analyze all the packages listed in the given requirements file")
    parser.add_argument(
        "-s", "--scratch",
        dest="scratch",
        default=os.getcwd(),
        metavar="<path>",
        help="Scratch path (used for caching downloaded data)"
             " [default: %s]" % (os.getcwd()))
    parser.add_argument(
        "--no-verbose",
        dest="verbose",
        action='store_false',
        default=True,
        help="Disable verbose output")
    return parser

def download_url_to(url, save_path, options, size=None, prefix=""):
    if size is not None:
        kb_size = size // 1024
        if options.verbose:
            print("%s: Downloading '%s' (%skB) -> '%s'" % (prefix, url,
                                                           kb_size, save_path))
    else:
        if options.verbose:
            print("%s: Downloading '%s' -> '%s'" % (prefix, url, save_path))
    resp = requests.get(url)
    with open(save_path, 'wb') as fh:
        fh.write(resp.content)
    return resp.content

def parse_requirements(options):
    requirements = OrderedDict()
    for filename in options.requirements:
        try:
            for req in pip_req.parse_requirements(filename):
                if req_key(req) in requirements:
                    raise ValueError("Currently only one requirement for '%s'"
                                     " is allowed, merging is not currently"
                                     " supported" % req_key(req))
                requirements[req_key(req)] = req
        except Exception as ex:
            raise IOError("Cannot parse '%s': %s" % (filename, ex))
    return requirements

def find_versions(pkg_name, options, prefix=""):
    def sorter(r1, r2):
        return cmp(r1[1], r2[1])
    url = _FINDER_URL_TPL % (urllib.parse.quote(pkg_name))
    if url in _FINDER_LOOKUPS:
        return _FINDER_LOOKUPS[url]
    version_path = os.path.join(options.scratch,
                                ".versions", "%s.json" % pkg_name)
    if os.path.exists(version_path):
        with open(version_path, 'rb') as fh:
            resp_data = json.loads(fh.read())
    else:
        resp_data = json.loads(download_url_to(url,
                                               version_path,
                                               options, prefix=prefix))
    releases = []
    for v, release_infos in six.iteritems(resp_data['releases']):
        rel = rel_fn = rel_size = None
        for r in release_infos:
            if r['packagetype'] == 'sdist':
                rel = r['url']
                rel_fn = r['filename']
                rel_size = r['size']
        if not all([rel, rel_fn, rel_size]):
            print("ERROR: no sdist found for '%s==%s'"
                  % (pkg_name, v), file=sys.stderr)
            continue
        try:
            m_rel = _MatchedRelease(
                v, dist_version.LooseVersion(v),
                rel, rel_fn, rel_size)
            releases.append(m_rel)
        except ValueError:
            print("ERROR: failed parsing '%s==%s'"
                  % (pkg_name, v), file=sys.stderr)
    _FINDER_LOOKUPS[url] = sorted(releases, cmp=sorter)
    return _FINDER_LOOKUPS[url]

def fetch_details(req, options, prefix=""):
    origin_filename = req.origin_filename
    origin_url = req.origin_url
    download_path = os.path.join(options.scratch,
                                 '.download', origin_filename)
    if not os.path.exists(download_path):
        download_url_to(origin_url, download_path, options,
                        size=req.origin_size, prefix=prefix)
    return get_archive_details(download_path, req.origin_size, options,
                               prefix=prefix)

def match_available(req, available, options, prefix=""):
    looked_in = []
    useables = []
    for a in reversed(available):
        v = a.string_version
        if v in req:
            line = "%s==%s" % (req.key, v)
            m_req = pip_req.InstallRequirement.from_line(line)
            if options.verbose:
                print("%s: Found '%s' as able to satisfy '%s'"
                      % (prefix, m_req, req))
            m_req.origin_url = a.origin_url
            m_req.origin_filename = a.origin_filename
            m_req.origin_size = a.origin_size
            useables.append(m_req)
            if len(useables) == _MAX_PRIOR_VERSIONS:
                break
        else:
            looked_in.append(v)
    if not useables:
        raise NotFoundException("No requirement found that"
                                " matches '%s' (tried %s)" % (req, looked_in))
    else:
        return useables

def check_is_compatible_alongside(pkg_req, gathered,
                                  options, prefix=""):
    if options.verbose:
        print("%s: Checking if '%s' is compatible along-side:" % (prefix,
                                                                  pkg_req))
        for req_name, other_req in six.iteritems(gathered):
            if req_key(pkg_req) == req_name:
                continue
            print("%s: - %s==%s" % (prefix, req_name,
                                    other_req.details['version']))
            for other_dep in other_req.details['dependencies']:
                print("%s:  + %s" % (prefix, other_dep))
    # If we conflict with the currently gathered requirements, give up...
    for req_name, other_req in six.iteritems(gathered):
        if req_key(pkg_req) == req_name:
            if pkg_req.details['version'] not in other_req.req:
                raise RequirementException("'%s==%s' not in '%s'"
                                           % (pkg_req.details['name'],
                                              pkg_req.details['version'],
                                              other_req))

def probe(requirements, gathered, options, levels, failures):
    if not requirements:
        return gathered
    # Pick one of the requirements, get a version that works with the
    # current known siblings (other requirements that are requested along
    # side this requirement) and then recurse trying to get another
    # requirement that will work, if this is not possible, backtrack and
    # try a different version instead (and repeat)...
    chapters = []
    for c in levels:
        if not chapters:
            chapters.append([c, 1])
        else:
            if c == chapters[-1][0]:
                chapters[-1][1] += 1
            else:
                chapters.append([c, 1])
    prefix = ' %s' % (".".join("%s%s" % (c, v) for (c, v) in chapters))
    gathered = gathered.copy()
    requirements = requirements.copy()
    pkg_name, pkg_req = requirements.popitem()
    print("%s: Probing for valid match for %s" % (prefix, pkg_req))
    possibles = match_available(pkg_req.req,
                                find_versions(pkg_name, options,
                                              prefix=prefix),
                                options,
                                prefix=prefix)
    for m in possibles:
        if m.req in failures:
            continue
        prior_failures = failures.copy()
        if not hasattr(m, 'details'):
            try:
                m.details = fetch_details(m, options, prefix=prefix)
            except Exception as e:
                print("ERROR: failed detailing '%s'"
                      % (m), file=sys.stderr)
                e_blob = str(e)
                for line in e_blob.splitlines():
                    print("%s" % (line), file=sys.stderr)
        if not hasattr(m, 'details'):
            continue
        if pkg_name in gathered:
            if m.details['version'] not in gathered[pkg_name].req:
                continue
        gathered[pkg_name] = m
        try:
            check_is_compatible_alongside(m, gathered, options,
                                          prefix=prefix)
            # Search the versions of this package which will work and now
            # deeply expand there dependencies to see if any of those
            # cause issues...
            if m.details['dependencies']:
                deep_requirements = OrderedDict()
                print("%s: Checking if '%s' dependencies are compatible..."
                      % (prefix, m))
                # NOTE: not including the active requirements in this
                # list does limit the scan-space, and may discount
                # various combinations (especially on failures) but this
                # should be ok enough...
                for dep in m.details['dependencies']:
                    d_req = pip_req.InstallRequirement.from_line(dep)
                    deep_requirements[req_key(d_req)] = d_req
                levels.append('d')
                try:
                    probe(deep_requirements, gathered,
                          options, levels, prior_failures)
                finally:
                    levels.pop()
            levels.append('p')
            try:
                result = probe(requirements, gathered,
                               options, levels, prior_failures)
            finally:
                levels.pop()
        except RequirementException as e:
            if options.verbose:
                print("%s: Undoing decision to select '%s'"
                      " due to: %s" % (prefix, m, e))
            gathered.pop(pkg_name)
            prior_failures.add(m.req)
        else:
            gathered.update(result)
            return gathered
    raise RequirementException("No working requirement found for '%s'"
                               % pkg_req)

def main():
    def req_cmp(a, b):
        return cmp(req_key(a), req_key(b))
    parser = create_parser()
    options = parser.parse_args()
    if not options.requirements:
        parser.error("At least one requirement file must be provided")
    initial = parse_requirements(options)
    for d in ['.download', '.versions']:
        scratch_path = os.path.join(options.scratch, d)
        if not os.path.isdir(scratch_path):
            os.makedirs(scratch_path)
    print("+ Initial package set:")
    for r in sorted(list(six.itervalues(initial)), cmp=req_cmp):
        print(" - %s" % r)
    print("+ Probing for a valid set...")
    matches = OrderedDict()
    levels = ['p']
    failures = set()
    try:
        matches = probe(initial, matches, options, levels, failures)
    except Exception:
        traceback.print_exc(file=sys.stdout)
    else:
        print("+ Expanded package set:")
        for r in sorted(list(six.itervalues(matches)), cmp=req_cmp):
            print(" - %s" % r)

if __name__ == "__main__":
    main()
<commit_msg>Show 'comes_from' information for dependencies<commit_after># -*- coding: utf-8 -*-

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

from __future__ import print_function

try:
    from collections import OrderedDict  # noqa
except ImportError:
    from ordereddict import OrderedDict  # noqa

import collections
import contextlib
import json
import os
import shutil
import sys
import tempfile
import traceback

from distutils import version as dist_version

from pip import req as pip_req

import argparse
import requests
import six
from six.moves import urllib

try:
    from pip import util as pip_util  # noqa
except ImportError:
    from pip import utils as pip_util  # noqa

_FINDER_URL_TPL = 'http://pypi.python.org/pypi/%s/json'

# Egg info cache and url fetch caches...
_EGGS_DETAILED = {}
_FINDER_LOOKUPS = {}
_EGGS_FAILED_DETAILED = {}
_MAX_PRIOR_VERSIONS = -1

class RequirementException(Exception):
    pass

class NotFoundException(Exception):
    pass

_MatchedRelease = collections.namedtuple('_MatchedRelease',
                                         ['string_version',
                                          'parsed_version',
                                          'origin_url',
                                          'origin_filename',
                                          'origin_size'])

def req_key(req):
    return req.req.key

@contextlib.contextmanager
def tempdir(**kwargs):
    # This seems like it was only added in python 3.2
    # Make it since its useful...
    # See: http://bugs.python.org/file12970/tempdir.patch
    tdir = tempfile.mkdtemp(**kwargs)
    try:
        yield tdir
    finally:
        shutil.rmtree(tdir)

def get_directory_details(path):
    if not os.path.isdir(path):
        raise IOError("Can not detail non-existent directory %s" % (path))
    req = pip_req.InstallRequirement.from_line(path)
    req.source_dir = path
    req.run_egg_info()
    dependencies = []
    for d in req.requirements():
        if not d.startswith("-e") and d.find("#"):
            d = d.split("#")[0]
        d = d.strip()
        if d:
            dependencies.append(d)
    details = {
        'req': req.req,
        'dependencies': dependencies,
        'name': req.name,
        'pkg_info': req.pkg_info(),
        'dependency_links': req.dependency_links,
        'version': req.installed_version,
    }
    return details

def get_archive_details(filename, filesize, options, prefix=""):
    if not os.path.isfile(filename):
        raise IOError("Can not detail non-existent file %s" % (filename))
    cache_key = "f:%s:%s" % (os.path.basename(filename), filesize)
    if cache_key in _EGGS_FAILED_DETAILED:
        exc_type, exc_value, exc_traceback = _EGGS_FAILED_DETAILED[cache_key]
        six.reraise(exc_type, exc_value, exc_traceback)
    try:
        return _EGGS_DETAILED[cache_key]
    except KeyError:
        if options.verbose:
            print("%s: Extracting egg-info from '%s'"
                  % (prefix, os.path.basename(filename)))
        with tempdir() as a_dir:
            arch_filename = os.path.join(a_dir, os.path.basename(filename))
            shutil.copyfile(filename, arch_filename)
            extract_to = os.path.join(a_dir, 'build')
            os.makedirs(extract_to)
            pip_util.unpack_file(arch_filename, extract_to,
                                 content_type='', link='')
            try:
                details = get_directory_details(extract_to)
            except Exception:
                # Don't bother saving the traceback (we don't care about it...)
                exc_type, exc_value, exc_traceback = sys.exc_info()
                _EGGS_FAILED_DETAILED[cache_key] = (exc_type, exc_value, None)
                raise
            else:
                _EGGS_DETAILED[cache_key] = details
                return details

def create_parser():
    parser = argparse.ArgumentParser()
    parser.add_argument(
        "-r", "--requirement",
        dest="requirements",
        nargs="+",
        default=[],
        metavar="<file>",
        help="Analyze all the packages listed in the given requirements file")
    parser.add_argument(
        "-s", "--scratch",
        dest="scratch",
        default=os.getcwd(),
        metavar="<path>",
        help="Scratch path (used for caching downloaded data)"
             " [default: %s]" % (os.getcwd()))
    parser.add_argument(
        "--no-verbose",
        dest="verbose",
        action='store_false',
        default=True,
        help="Disable verbose output")
    return parser

def download_url_to(url, save_path, options, size=None, prefix=""):
    if size is not None:
        kb_size = size // 1024
        if options.verbose:
            print("%s: Downloading '%s' (%skB) -> '%s'" % (prefix, url,
                                                           kb_size, save_path))
    else:
        if options.verbose:
            print("%s: Downloading '%s' -> '%s'" % (prefix, url, save_path))
    resp = requests.get(url)
    with open(save_path, 'wb') as fh:
        fh.write(resp.content)
    return resp.content

def parse_requirements(options):
    requirements = OrderedDict()
    for filename in options.requirements:
        try:
            for req in pip_req.parse_requirements(filename):
                if req_key(req) in requirements:
                    raise ValueError("Currently only one requirement for '%s'"
                                     " is allowed, merging is not currently"
                                     " supported" % req_key(req))
                requirements[req_key(req)] = req
        except Exception as ex:
            raise IOError("Cannot parse '%s': %s" % (filename, ex))
    return requirements

def find_versions(pkg_name, options, prefix=""):
    def sorter(r1, r2):
        return cmp(r1[1], r2[1])
    url = _FINDER_URL_TPL % (urllib.parse.quote(pkg_name))
    if url in _FINDER_LOOKUPS:
        return _FINDER_LOOKUPS[url]
    version_path = os.path.join(options.scratch,
                                ".versions", "%s.json" % pkg_name)
    if os.path.exists(version_path):
        with open(version_path, 'rb') as fh:
            resp_data = json.loads(fh.read())
    else:
        resp_data = json.loads(download_url_to(url,
                                               version_path,
                                               options, prefix=prefix))
    releases = []
    for v, release_infos in six.iteritems(resp_data['releases']):
        rel = rel_fn = rel_size = None
        for r in release_infos:
            if r['packagetype'] == 'sdist':
                rel = r['url']
                rel_fn = r['filename']
                rel_size = r['size']
        if not all([rel, rel_fn, rel_size]):
            print("ERROR: no sdist found for '%s==%s'"
                  % (pkg_name, v), file=sys.stderr)
            continue
        try:
            m_rel = _MatchedRelease(
                v, dist_version.LooseVersion(v),
                rel, rel_fn, rel_size)
            releases.append(m_rel)
        except ValueError:
            print("ERROR: failed parsing '%s==%s'"
                  % (pkg_name, v), file=sys.stderr)
    _FINDER_LOOKUPS[url] = sorted(releases, cmp=sorter)
    return _FINDER_LOOKUPS[url]

def fetch_details(req, options, prefix=""):
    origin_filename = req.origin_filename
    origin_url = req.origin_url
    download_path = os.path.join(options.scratch,
                                 '.download', origin_filename)
    if not os.path.exists(download_path):
        download_url_to(origin_url, download_path, options,
                        size=req.origin_size, prefix=prefix)
    return get_archive_details(download_path, req.origin_size, options,
                               prefix=prefix)

def match_available(req, available, options, prefix=""):
    looked_in = []
    useables = []
    for a in reversed(available):
        v = a.string_version
        if v in req:
            line = "%s==%s" % (req.key, v)
            m_req = pip_req.InstallRequirement.from_line(line)
            if options.verbose:
                print("%s: Found '%s' as able to satisfy '%s'"
                      % (prefix, m_req, req))
            m_req.origin_url = a.origin_url
            m_req.origin_filename = a.origin_filename
            m_req.origin_size = a.origin_size
            useables.append(m_req)
            if len(useables) == _MAX_PRIOR_VERSIONS:
                break
        else:
            looked_in.append(v)
    if not useables:
        raise NotFoundException("No requirement found that"
                                " matches '%s' (tried %s)" % (req, looked_in))
    else:
        return useables

def check_is_compatible_alongside(pkg_req, gathered,
                                  options, prefix=""):
    if options.verbose:
        print("%s: Checking if '%s' is compatible along-side:" % (prefix,
                                                                  pkg_req))
        for req_name, other_req in six.iteritems(gathered):
            if req_key(pkg_req) == req_name:
                continue
            print("%s: - %s==%s" % (prefix, req_name,
                                    other_req.details['version']))
            for other_dep in other_req.details['dependencies']:
                print("%s:  + %s" % (prefix, other_dep))
    # If we conflict with the currently gathered requirements, give up...
    for req_name, other_req in six.iteritems(gathered):
        if req_key(pkg_req) == req_name:
            if pkg_req.details['version'] not in other_req.req:
                raise RequirementException("'%s==%s' not in '%s'"
                                           % (pkg_req.details['name'],
                                              pkg_req.details['version'],
                                              other_req))

def probe(requirements, gathered, options, levels, failures):
    if not requirements:
        return gathered
    # Pick one of the requirements, get a version that works with the
    # current known siblings (other requirements that are requested along
    # side this requirement) and then recurse trying to get another
    # requirement that will work, if this is not possible, backtrack and
    # try a different version instead (and repeat)...
    chapters = []
    for c in levels:
        if not chapters:
            chapters.append([c, 1])
        else:
            if c == chapters[-1][0]:
                chapters[-1][1] += 1
            else:
                chapters.append([c, 1])
    prefix = ' %s' % (".".join("%s%s" % (c, v) for (c, v) in chapters))
    gathered = gathered.copy()
    requirements = requirements.copy()
    pkg_name, pkg_req = requirements.popitem()
    print("%s: Probing for valid match for %s" % (prefix, pkg_req))
    possibles = match_available(pkg_req.req,
                                find_versions(pkg_name, options,
                                              prefix=prefix),
                                options,
                                prefix=prefix)
    for m in possibles:
        if m.req in failures:
            continue
        prior_failures = failures.copy()
        if not hasattr(m, 'details'):
            try:
                m.details = fetch_details(m, options, prefix=prefix)
            except Exception as e:
                print("ERROR: failed detailing '%s'"
                      % (m), file=sys.stderr)
                e_blob = str(e)
                for line in e_blob.splitlines():
                    print("%s" % (line), file=sys.stderr)
        if not hasattr(m, 'details'):
            continue
        if pkg_name in gathered:
            if m.details['version'] not in gathered[pkg_name].req:
                continue
        gathered[pkg_name] = m
        try:
            check_is_compatible_alongside(m, gathered, options,
                                          prefix=prefix)
            # Search the versions of this package which will work and now
            # deeply expand there dependencies to see if any of those
            # cause issues...
            if m.details['dependencies']:
                deep_requirements = OrderedDict()
                print("%s: Checking if '%s' dependencies are compatible..."
                      % (prefix, m))
                # NOTE: not including the active requirements in this
                # list does limit the scan-space, and may discount
                # various combinations (especially on failures) but this
                # should be ok enough...
                for i, dep in enumerate(m.details['dependencies']):
                    d_req = pip_req.InstallRequirement.from_line(
                        dep,
                        comes_from="dependency of %s (entry %s)" % (m, i + 1))
                    deep_requirements[req_key(d_req)] = d_req
                levels.append('d')
                try:
                    probe(deep_requirements, gathered,
                          options, levels, prior_failures)
                finally:
                    levels.pop()
            levels.append('p')
            try:
                result = probe(requirements, gathered,
                               options, levels, prior_failures)
            finally:
                levels.pop()
        except RequirementException as e:
            if options.verbose:
                print("%s: Undoing decision to select '%s'"
                      " due to: %s" % (prefix, m, e))
            gathered.pop(pkg_name)
            prior_failures.add(m.req)
        else:
            gathered.update(result)
            return gathered
    raise RequirementException("No working requirement found for '%s'"
                               % pkg_req)

def main():
    def req_cmp(a, b):
        return cmp(req_key(a), req_key(b))
    parser = create_parser()
    options = parser.parse_args()
    if not options.requirements:
        parser.error("At least one requirement file must be provided")
    initial = parse_requirements(options)
    for d in ['.download', '.versions']:
        scratch_path = os.path.join(options.scratch, d)
        if not os.path.isdir(scratch_path):
            os.makedirs(scratch_path)
    print("+ Initial package set:")
    for r in sorted(list(six.itervalues(initial)), cmp=req_cmp):
        print(" - %s" % r)
    print("+ Probing for a valid set...")
    matches = OrderedDict()
    levels = ['p']
    failures = set()
    try:
        matches = probe(initial, matches, options, levels, failures)
    except Exception:
        traceback.print_exc(file=sys.stdout)
    else:
        print("+ Expanded package set:")
        for r in sorted(list(six.itervalues(matches)), cmp=req_cmp):
            print(" - %s" % r)

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


<commit_before>import os
import dbLib
import time

class Session:
    def __init__(self):
        self.conn = dbLib.ConnectionHandler()
        self.conf = self.getConfig()

    def getConfig(self):
        conf = {}
        with open(".scrybe.conf", "r") as configFile:
            for line in configFile.readlines():
                if(line.strip() and line.strip()[0] != "#"):
                    try:
                        key, val = line.split(":")
                    except:
                        continue
                    conf[key.strip()] = val.strip()
        return(conf)

    def start(self):
        self.choice = ""
        self.displayHelp()
        while(self.choice.lower() != "q"):
            self.choice = raw_input("->")
            self.parseInput(self.choice)

    def displayHelp(self, choiceList=None):
            print("Options as follows:")
            print("h/H : Display this message")
            print("l/L;[c(urrent - default), a(rchived), b(oth)] : List notes")
            print("s/S;search-string;[c(urrent - default), a(rchived), b(oth)] : Search notes")
            print("f/F;tag1[,tag2,...] : List only notes with all tags given")
            print("a/A;title;[tags - comma-separated] : add a note")
#            print("i/I;title;/path/to/file;[tags] : import a note from file")
            print("arch;note-id;[in - default/out] : move a note into or out of archive")
            print("d/D;note-id : delete a note forever")
            print("e/E;note-id;[title];[tags] : Edit a note that already exists")
            print("g/G;note-id : View a specific note")
            print("c/C: Clear screen")
            print("q/Q : Quit Scrybe")

    def parseInput(self, choiceString):
        if(not choiceString):#user accidentally pressed enter
            return
        choiceList = []
        for choice in choiceString.split(";"):
            choiceList.append(choice.strip())
        choiceList[0] = choiceList[0].lower()
        choiceFuncs = {"h":self.displayHelp,
                       "l":self.listNotes,
                       "s":self.searchNotes,
                       "a":self.addNote,
                       "i":self.importNote,
                       "arch":self.archiveHandler,
                       "d":self.deleteNote,
                       "e":self.editNote,
                       "g":self.getNote,
                       "c":self.clear,
                       "f":self.filter,
                       "q":self.quit}
        if(choiceList[0] not in choiceFuncs.keys()):
            print("Sorry, " + choiceList[0] + " isn't a valid command (enter 'h' for help)")
            return
        choiceFuncs[choiceList[0]](choiceList)

    def listNotes(self, choiceList):#list all notes in db
        modeSwitchDict = {"c":"current", "a":"archived", "b":"all"}
        if(len(choiceList) < 2):
            choiceList.append("c")
        if(choiceList[1] not in modeSwitchDict.keys()):
            print("Sorry, " + choiceList[1] + " isn't a valid option for list")
            return
        requestMode = modeSwitchDict[choiceList[1]]
        notesList = self.conn.getNotes(mode=requestMode)
        printString = ""
        if(not notesList):
            printString = "No notes found"
        else:
            for note in notesList:
                printString += self.oneLineStringGen(note)
        print(printString)

    def oneLineStringGen(self, note, maxChars=60):#one line repr of passed note
        noteId = note.id
        title = note.title
        body = note.body
        createTimeString = time.strftime("%d/%m/%Y %H:%M", time.localtime(note.createTime))
        archived = ["Current", "Achived"][note.archived]
        appendString = " | " + createTimeString + " | " + archived + "\n"
        printString = ""
        printString += str(noteId) + " | "
        printString += title + " | "
        printString += body
        printString = printString.replace("\n", " ")
        if(len(printString + appendString) > maxChars):
            appendString = "..." + appendString
            maxLengthBeforeAppend = maxChars - len(appendString)
            printString = printString[0:maxLengthBeforeAppend]
        printString += appendString
        return(printString)

    def searchNotes(self, choiceList):#weighted search of title, body and tags
        if(len(choiceList) < 2):
            print("You need to specify a search string")
            return
        searchTerm = choiceList[1].lower()
        if(len(choiceList) < 3):
            choiceList.append("c")
        if(choiceList[2] not in ["a","b","c"]):
            print("Sorry, " + choiceList[2] + " isn't a valid search option")
            return
        selectorMode = {"c":"current", "a":"archived", "b":"all"}[choiceList[2]]
        matchingNotes = []
        for note in self.conn.getNotes(mode=selectorMode):
            matchValue = 0
            if(searchTerm in note.title.lower()):
                matchValue += 2
            matchValue += note.body.lower().count(searchTerm)
            matchValue += note.tags.count(searchTerm) * 3
            if(time.time() - note.createTime < 7*24*60*60):
                matchValue += 1
            if(matchValue > 0):
                matchingNotes.append((note, matchValue))
        matchingNotes.sort(key=lambda x: x[1], reverse=True)
        printString = ""
        for noteTuple in matchingNotes:
            printString += self.oneLineStringGen(noteTuple[0])
        if(printString):
            print(printString)
        else:
            print("Nothing found matching those search terms, sorry")

    def addNote(self, choiceList):#adds a note to the db
        if(len(choiceList) > 2 and choiceList[1]):
            title = choiceList[1]
        else:
            print("Notes require a title")
            return
        tags = ""
        if(len(choiceList) > 2):
            tags = choiceList[2]
        os.system(self.conf["editor"] + " .scrybe.tmp")
        with open(".scrybe.tmp", "r") as tmpFile:
            body = ""
            for line in tmpFile.readlines():
                if(line.strip() and line.strip()[0] != "#"):
                    body += line
        os.remove(".scrybe.tmp")
        if(not body):
            print("Note addition cancelled due to empty body")
        self.conn.addNote(title, body, tags)
        print("Note added")

    def fullStringGen(self, note):#generates a string repr of an entire note
        noteId = note.id
        title = note.title
        body = note.body
        tagString = "Tags: | "
        for tag in note.tags:
            tagString += tag + " | "
        createTimeString = time.strftime("%d/%m/%Y %H:%M", time.localtime(note.createTime))
        archived = ["Current", "Archived"][note.archived]
        printString = ""
        printString += title + "\n"
        printString += str(noteId) + " | " + createTimeString + " | " + archived  + "\n"
        printString += tagString + "\n"
        printString += body
        return(printString)

    def archiveHandler(self, choiceList):
        if(len(choiceList) < 2):
            print("You need to specify a note to archive")
            return
        try:
            noteId = int(choiceList[1])
        except:
            print("Sorry, note ids need to be an integer")
            return
        archive = 1
        if(len(choiceList) > 2):
            if(choiceList[2].lower() not in ["in", "out"]):
                print("Sorry, " + choiceList + " isn't an option for archive")
                return
            archive = {"out":0, "in":1}[choiceList[2]]
        self.conn.archiveNote(noteId, archive)
        print("Note archived")

    def deleteNote(self, choiceList):
        if(len(choiceList) < 2):
            print("You must specify a note to be deleted")
            return
        try:
            noteId = int(choiceList[1])
        except:
            print("Note index must be an integer")
            return
        verify = raw_input("Are you sure (y/n)?")
        if(verify.strip().lower() == "y"):
            self.conn.deleteNote(noteId)
            print("Note deleted")
        else:
            print("Deletion cancelled")

    def editNote(self, choiceList):
        if(len(choiceList) < 2):
            print("You need to sepcify a note by id")
            return
        try:
            noteId = int(choiceList[1])
        except:
            print("Note id must be an integer")
            return
        try:
            note = self.conn.getNote(noteId)
        except:
            print("No note found of that id (did you delete it?)")
            return
        newTitle = note.title
        if(len(choiceList) > 2 and choiceList[2]):
            newTitle = choiceList[2]
        newBody = note.body
        newTags = ",".join(note.tags)
        if(len(choiceList) > 3 and choiceList[3]):
            newTags = choiceList[3]
        with open(".scrybe.tmp", "w") as tmpFile:
            tmpFile.write(newBody)
        os.system(self.conf["editor"] + " .scrybe.tmp")
        with open(".scrybe.tmp", "r") as tmpFile:
            newBody = ""
            for line in tmpFile.readlines():
                if(line.strip() and line.strip()[0] != "#"):
                    newBody += line
        os.remove(".scrybe.tmp")
        self.conn.editNote(noteId, newTitle, newBody, newTags)
        print("Note " + str(noteId) + " edited")

    def getNote(self, choiceList):
        if(len(choiceList) < 2):
            print("You need to specify a note by it's id (number on the left)")
            return
        try:
            requestId = int(choiceList[1])
        except:
            print("Note ids need to be integers")
            return
        try:
            note = self.conn.getNote(requestId)
        except IndexError:
            print("Sorry, there's no note with that id (did you delete it?)")
            return
        print(self.fullStringGen(note))

    def quit(self, choiceList):
        self.choice = "q"

    def clear(self, choiceList):
        os.system("clear")

    def importNote(self, choiceList):
        if(len(choiceList) < 3):
            print("You must specify title and path when importing a file")
            return
        title = choiceList[1]
        path = choiceList[2]
        tags = ""
        if(len(choiceList) > 3):
            tags = choiceList[3]
        try:
            with open(path, "r") as noteFile:
                body = noteFile.read()
        except:
            print("Sorry, failed to read file. Did you get the path right?")
            return
        self.conn.addNote(title, body, tags)
        print("Note imported")

    def filter(self, choiceList):
        if(len(choiceList) < 2 or not choiceList[1]):
            print("You need to supply atleast one tag to filter by")
        tagList = choiceList[1].split(",")
        tagList = map(lambda x: x.strip(), tagList)
        notes = self.conn.getNotes()
        printString = ""
        for note in notes:
            matchingTags = [tag for tag in tagList if tag in note.tags]
            if(len(matchingTags) == len(tagList)):
                printString += (self.oneLineStringGen(note))
        if(not printString):
            printString = "Sorry, nothing matching that filter found"
        print(printString)

<commit_msg>Added key to help message explaining markup, fixed help function indentation<commit_after>import os
import dbLib
import time

class Session:
    def __init__(self):
        self.conn = dbLib.ConnectionHandler()
        self.conf = self.getConfig()

    def getConfig(self):
        conf = {}
        with open(".scrybe.conf", "r") as configFile:
            for line in configFile.readlines():
                if(line.strip() and line.strip()[0] != "#"):
                    try:
                        key, val = line.split(":")
                    except:
                        continue
                    conf[key.strip()] = val.strip()
        return(conf)

    def start(self):
        self.choice = ""
        self.displayHelp()
        while(self.choice.lower() != "q"):
            self.choice = raw_input("->")
            self.parseInput(self.choice)

    def displayHelp(self, choiceList=None):
        print("Key: mandatory, [optional], (explanatory - do not include)")
        print("Options as follows:")
        print("h/H : Display this message")
        print("l/L;[c(urrent - default), a(rchived), b(oth)] : List notes")
        print("s/S;search-string;[c(urrent - default), a(rchived), b(oth)] : Search notes")
        print("f/F;tag1[,tag2,...] : List only notes with all tags given")
        print("a/A;title;[tags - comma-separated] : add a note")
#        print("i/I;title;/path/to/file;[tags] : import a note from file")
        print("arch;note-id;[in - default/out] : move a note into or out of archive")
        print("d/D;note-id : delete a note forever")
        print("e/E;note-id;[title];[tags] : Edit a note that already exists")
        print("g/G;note-id : View a specific note")
        print("c/C: Clear screen")
        print("q/Q : Quit Scrybe")

    def parseInput(self, choiceString):
        if(not choiceString):#user accidentally pressed enter
            return
        choiceList = []
        for choice in choiceString.split(";"):
            choiceList.append(choice.strip())
        choiceList[0] = choiceList[0].lower()
        choiceFuncs = {"h":self.displayHelp,
                       "l":self.listNotes,
                       "s":self.searchNotes,
                       "a":self.addNote,
                       "i":self.importNote,
                       "arch":self.archiveHandler,
                       "d":self.deleteNote,
                       "e":self.editNote,
                       "g":self.getNote,
                       "c":self.clear,
                       "f":self.filter,
                       "q":self.quit}
        if(choiceList[0] not in choiceFuncs.keys()):
            print("Sorry, " + choiceList[0] + " isn't a valid command (enter 'h' for help)")
            return
        choiceFuncs[choiceList[0]](choiceList)

    def listNotes(self, choiceList):#list all notes in db
        modeSwitchDict = {"c":"current", "a":"archived", "b":"all"}
        if(len(choiceList) < 2):
            choiceList.append("c")
        if(choiceList[1] not in modeSwitchDict.keys()):
            print("Sorry, " + choiceList[1] + " isn't a valid option for list")
            return
        requestMode = modeSwitchDict[choiceList[1]]
        notesList = self.conn.getNotes(mode=requestMode)
        printString = ""
        if(not notesList):
            printString = "No notes found"
        else:
            for note in notesList:
                printString += self.oneLineStringGen(note)
        print(printString)

    def oneLineStringGen(self, note, maxChars=60):#one line repr of passed note
        noteId = note.id
        title = note.title
        body = note.body
        createTimeString = time.strftime("%d/%m/%Y %H:%M", time.localtime(note.createTime))
        archived = ["Current", "Achived"][note.archived]
        appendString = " | " + createTimeString + " | " + archived + "\n"
        printString = ""
        printString += str(noteId) + " | "
        printString += title + " | "
        printString += body
        printString = printString.replace("\n", " ")
        if(len(printString + appendString) > maxChars):
            appendString = "..." + appendString
            maxLengthBeforeAppend = maxChars - len(appendString)
            printString = printString[0:maxLengthBeforeAppend]
        printString += appendString
        return(printString)

    def searchNotes(self, choiceList):#weighted search of title, body and tags
        if(len(choiceList) < 2):
            print("You need to specify a search string")
            return
        searchTerm = choiceList[1].lower()
        if(len(choiceList) < 3):
            choiceList.append("c")
        if(choiceList[2] not in ["a","b","c"]):
            print("Sorry, " + choiceList[2] + " isn't a valid search option")
            return
        selectorMode = {"c":"current", "a":"archived", "b":"all"}[choiceList[2]]
        matchingNotes = []
        for note in self.conn.getNotes(mode=selectorMode):
            matchValue = 0
            if(searchTerm in note.title.lower()):
                matchValue += 2
            matchValue += note.body.lower().count(searchTerm)
            matchValue += note.tags.count(searchTerm) * 3
            if(time.time() - note.createTime < 7*24*60*60):
                matchValue += 1
            if(matchValue > 0):
                matchingNotes.append((note, matchValue))
        matchingNotes.sort(key=lambda x: x[1], reverse=True)
        printString = ""
        for noteTuple in matchingNotes:
            printString += self.oneLineStringGen(noteTuple[0])
        if(printString):
            print(printString)
        else:
            print("Nothing found matching those search terms, sorry")

    def addNote(self, choiceList):#adds a note to the db
        if(len(choiceList) > 2 and choiceList[1]):
            title = choiceList[1]
        else:
            print("Notes require a title")
            return
        tags = ""
        if(len(choiceList) > 2):
            tags = choiceList[2]
        os.system(self.conf["editor"] + " .scrybe.tmp")
        with open(".scrybe.tmp", "r") as tmpFile:
            body = ""
            for line in tmpFile.readlines():
                if(line.strip() and line.strip()[0] != "#"):
                    body += line
        os.remove(".scrybe.tmp")
        if(not body):
            print("Note addition cancelled due to empty body")
        self.conn.addNote(title, body, tags)
        print("Note added")

    def fullStringGen(self, note):#generates a string repr of an entire note
        noteId = note.id
        title = note.title
        body = note.body
        tagString = "Tags: | "
        for tag in note.tags:
            tagString += tag + " | "
        createTimeString = time.strftime("%d/%m/%Y %H:%M", time.localtime(note.createTime))
        archived = ["Current", "Archived"][note.archived]
        printString = ""
        printString += title + "\n"
        printString += str(noteId) + " | " + createTimeString + " | " + archived  + "\n"
        printString += tagString + "\n"
        printString += body
        return(printString)

    def archiveHandler(self, choiceList):
        if(len(choiceList) < 2):
            print("You need to specify a note to archive")
            return
        try:
            noteId = int(choiceList[1])
        except:
            print("Sorry, note ids need to be an integer")
            return
        archive = 1
        if(len(choiceList) > 2):
            if(choiceList[2].lower() not in ["in", "out"]):
                print("Sorry, " + choiceList + " isn't an option for archive")
                return
            archive = {"out":0, "in":1}[choiceList[2]]
        self.conn.archiveNote(noteId, archive)
        print("Note archived")

    def deleteNote(self, choiceList):
        if(len(choiceList) < 2):
            print("You must specify a note to be deleted")
            return
        try:
            noteId = int(choiceList[1])
        except:
            print("Note index must be an integer")
            return
        verify = raw_input("Are you sure (y/n)?")
        if(verify.strip().lower() == "y"):
            self.conn.deleteNote(noteId)
            print("Note deleted")
        else:
            print("Deletion cancelled")

    def editNote(self, choiceList):
        if(len(choiceList) < 2):
            print("You need to sepcify a note by id")
            return
        try:
            noteId = int(choiceList[1])
        except:
            print("Note id must be an integer")
            return
        try:
            note = self.conn.getNote(noteId)
        except:
            print("No note found of that id (did you delete it?)")
            return
        newTitle = note.title
        if(len(choiceList) > 2 and choiceList[2]):
            newTitle = choiceList[2]
        newBody = note.body
        newTags = ",".join(note.tags)
        if(len(choiceList) > 3 and choiceList[3]):
            newTags = choiceList[3]
        with open(".scrybe.tmp", "w") as tmpFile:
            tmpFile.write(newBody)
        os.system(self.conf["editor"] + " .scrybe.tmp")
        with open(".scrybe.tmp", "r") as tmpFile:
            newBody = ""
            for line in tmpFile.readlines():
                if(line.strip() and line.strip()[0] != "#"):
                    newBody += line
        os.remove(".scrybe.tmp")
        self.conn.editNote(noteId, newTitle, newBody, newTags)
        print("Note " + str(noteId) + " edited")

    def getNote(self, choiceList):
        if(len(choiceList) < 2):
            print("You need to specify a note by it's id (number on the left)")
            return
        try:
            requestId = int(choiceList[1])
        except:
            print("Note ids need to be integers")
            return
        try:
            note = self.conn.getNote(requestId)
        except IndexError:
            print("Sorry, there's no note with that id (did you delete it?)")
            return
        print(self.fullStringGen(note))

    def quit(self, choiceList):
        self.choice = "q"

    def clear(self, choiceList):
        os.system("clear")

    def importNote(self, choiceList):
        if(len(choiceList) < 3):
            print("You must specify title and path when importing a file")
            return
        title = choiceList[1]
        path = choiceList[2]
        tags = ""
        if(len(choiceList) > 3):
            tags = choiceList[3]
        try:
            with open(path, "r") as noteFile:
                body = noteFile.read()
        except:
            print("Sorry, failed to read file. Did you get the path right?")
            return
        self.conn.addNote(title, body, tags)
        print("Note imported")

    def filter(self, choiceList):
        if(len(choiceList) < 2 or not choiceList[1]):
            print("You need to supply atleast one tag to filter by")
        tagList = choiceList[1].split(",")
        tagList = map(lambda x: x.strip(), tagList)
        notes = self.conn.getNotes()
        printString = ""
        for note in notes:
            matchingTags = [tag for tag in tagList if tag in note.tags]
            if(len(matchingTags) == len(tagList)):
                printString += (self.oneLineStringGen(note))
        if(not printString):
            printString = "Sorry, nothing matching that filter found"
        print(printString)

<|endoftext|>


<commit_before>
class Player:
    VERSION = "Default Python folding player"

    low_cards = ["2", "3", "4", "5", "6", "7", "8"]
    hand = []
    odds = {"hand": [
            [{"rank": "A"}, {"rank": "A"},  {"odds": 4}, {"suit": False}],
            [{"rank": "K"}, {"rank": "K"},  {"odds": 4}, {"suit": False}],
            [{"rank": "Q"}, {"rank": "Q"},  {"odds": 4}, {"suit": False}],
            [{"rank": "J"}, {"rank": "J"},  {"odds": 3}, {"suit": False}],
            [{"rank": "10"}, {"rank": "10"},  {"odds": 3}, {"suit": False}],
            [{"rank": "9"}, {"rank": "9"},  {"odds": 3}, {"suit": False}],
            [{"rank": "8"}, {"rank": "8"},  {"odds": 2}, {"suit": False}],
            [{"rank": "7"}, {"rank": "7"},  {"odds": 2}, {"suit": False}],
            [{"rank": "6"}, {"rank": "6"},  {"odds": 2}, {"suit": False}],
            [{"rank": "5"}, {"rank": "5"},  {"odds": 2}, {"suit": False}],
            [{"rank": "4"}, {"rank": "4"},  {"odds": 2}, {"suit": False}],
            [{"rank": "3"}, {"rank": "3"},  {"odds": 1}, {"suit": False}],
            [{"rank": "2"}, {"rank": "2"},  {"odds": 1}, {"suit": False}],
            [{"rank": "A"}, {"rank": "K"},  {"odds": 3}, {"suit": False}],
            [{"rank": "A"}, {"rank": "Q"},  {"odds": 3}, {"suit": False}],
            [{"rank": "A"}, {"rank": "J"},  {"odds": 2}, {"suit": False}],
            [{"rank": "A"}, {"rank": "10"},  {"odds": 2}, {"suit": False}],
            [{"rank": "A"}, {"rank": "9"},  {"odds": 2}, {"suit": False}],
            [{"rank": "A"}, {"rank": "8"},  {"odds": 2}, {"suit": False}],
            [{"rank": "A"}, {"rank": "7"},  {"odds": 1}, {"suit": False}],
            [{"rank": "A"}, {"rank": "6"},  {"odds": 1}, {"suit": False}],
            [{"rank": "A"}, {"rank": "5"},  {"odds": 1}, {"suit": False}],
            [{"rank": "A"}, {"rank": "4"},  {"odds": 1}, {"suit": False}],
            [{"rank": "A"}, {"rank": "3"},  {"odds": 1}, {"suit": False}],
            [{"rank": "A"}, {"rank": "2"},  {"odds": 1}, {"suit": False}],
            [{"rank": "A"}, {"rank": "K"},  {"odds": 4}, {"suit": True}],
            [{"rank": "A"}, {"rank": "Q"},  {"odds": 3}, {"suit": True}],
            [{"rank": "A"}, {"rank": "J"},  {"odds": 3}, {"suit": True}],
            [{"rank": "A"}, {"rank": "10"},  {"odds": 2}, {"suit": True}],
            [{"rank": "A"}, {"rank": "9"},  {"odds": 2}, {"suit": True}],
            [{"rank": "A"}, {"rank": "8"},  {"odds": 2}, {"suit": True}],
            [{"rank": "A"}, {"rank": "7"},  {"odds": 2}, {"suit": True}],
            [{"rank": "A"}, {"rank": "6"},  {"odds": 1}, {"suit": True}],
            [{"rank": "A"}, {"rank": "5"},  {"odds": 1}, {"suit": True}],
            [{"rank": "A"}, {"rank": "4"},  {"odds": 1}, {"suit": True}],
            [{"rank": "A"}, {"rank": "3"},  {"odds": 1}, {"suit": True}],
            [{"rank": "A"}, {"rank": "2"},  {"odds": 1}, {"suit": True}],
            [{"rank": "K"}, {"rank": "Q"},  {"odds": 2}, {"suit": True}],
            [{"rank": "K"}, {"rank": "J"},  {"odds": 2}, {"suit": True}],
            [{"rank": "K"}, {"rank": "10"},  {"odds": 1}, {"suit": True}],
            [{"rank": "K"}, {"rank": "9"},  {"odds": 1}, {"suit": True}],
            [{"rank": "K"}, {"rank": "8"},  {"odds": 1}, {"suit": True}],
            [{"rank": "K"}, {"rank": "7"},  {"odds": 1}, {"suit": True}],
            [{"rank": "K"}, {"rank": "6"},  {"odds": 1}, {"suit": True}],
            [{"rank": "K"}, {"rank": "5"},  {"odds": 1}, {"suit": True}],
            [{"rank": "K"}, {"rank": "4"},  {"odds": 1}, {"suit": True}],
            [{"rank": "K"}, {"rank": "3"},  {"odds": 1}, {"suit": True}],
            [{"rank": "Q"}, {"rank": "J"},  {"odds": 1}, {"suit": True}],
            [{"rank": "Q"}, {"rank": "10"},  {"odds": 1}, {"suit": True}],
            [{"rank": "Q"}, {"rank": "9"},  {"odds": 1}, {"suit": True}],
            [{"rank": "Q"}, {"rank": "8"},  {"odds": 1}, {"suit": True}],
            [{"rank": "Q"}, {"rank": "7"},  {"odds": 1}, {"suit": True}],
            [{"rank": "J"}, {"rank": "10"},  {"odds": 1}, {"suit": True}],
            [{"rank": "J"}, {"rank": "9"},  {"odds": 1}, {"suit": True}],
            [{"rank": "J"}, {"rank": "8"},  {"odds": 1}, {"suit": True}],
            [{"rank": "J"}, {"rank": "7"},  {"odds": 1}, {"suit": True}]
            ]}

    def betRequest(self, game_state):
        return self.think(game_state)

    def showdown(self, game_state):
        pass

    def think(self, game_state):
        if len(game_state["community_cards"]) == 0:
            bet = 1000
        for player in game_state["players"]:
            if player["name"] == "piton":
                for cards in player["hole_cards"]:
                    self.hand.append(cards)
                    for card, value in cards.items():
                        if card == "rank":
                            if value in self.low_cards:
                                bet = 0
        if self.hand[0]["rank"] == self.hand[1]["rank"]:
            bet = 1000
        return bet

        # for odd in self.odds:
        #     possible_hand = []
        #     for card in odd[:2]:
        #         possible_hand.append(card)
        #     if (self.hand[0]["rank"] == possible_hand[0]["rank"] and self.hand[1]["rank"] == possible_hand[1]["rank"] or self.hand[1]["rank"] == possible_hand[0]["rank"] and self.hand[0]["rank"] == possible_hand[1]["rank"]) and ((possible_hand[0]["suit"] == possible_hand[1]["suit"]) == suit_check()):
        #             if int(possible_hand[0]["odds"]) > 2:
        #                 bet = 6000
        #                 break
        #             else:
        #                 bet = 50
        #                 break
        #     return bet
        # if len(game_state["community_cards"]) == 0:
    #
    # def suit_check(self):
    #     return self.hand[0]["suit"] == self.hand[1]["suit"]
    #
    # def handreturn(self, game_state):
    #     self.hand = []
    #     for player in game_state["players"]:
    #         if player["name"] == "piton":
    #             for cards in player["hole_cards"]:
    #                 self.hand.append(cards)
    #
    # def communreturn(self, game_state):
    #     self.commun_cards = []
    #     for cards in game_state["community_cards"]:
    #         self.commun_cards.append(cards)
    #
    # def checkpair(self, game_state):
    #     for card in self.hand:
    #         for flop in self.commun_cards:
    #             if card["rank"] == flop["rank"]:
    #                 self.state.append(card)
    #                 self.state.append(flop)
    #     return len(self.state)
    #
    # def ifsuit(self):
    #     if self.hand[0]["suit"] == self.hand[1]["suit"]:
    #         for card in self.hand:
    #             for flop in self.commun_cards:
    #                 if card["suit"] == flop["suit"]:
    #                     self.suit.append(card)
    #     if len(self.suit) >= 5:
    #         return True
<commit_msg>nemar<commit_after>a = {
    "tournament_id":"550d1d68cd7bd10003000003",     # Id of the current tournament

    "game_id":"550da1cb2d909006e90004b1",           # Id of the current sit'n'go game. You can use this to link a
                                                    # sequence of game states together for logging purposes, or to
                                                    # make sure that the same strategy is played for an entire game

    "round":0,                                      # Index of the current round within a sit'n'go

    "bet_index":0,                                  # Index of the betting opportunity within a round

    "small_blind": 10,                              # The small blind in the current round. The big blind is twice the
                                                    #     small blind

    "current_buy_in": 320,                          # The amount of the largest current bet from any one player

    "pot": 400,                                     # The size of the pot (sum of the player bets)

    "minimum_raise": 240,                           # Minimum raise amount. To raise you have to return at least:
                                                    #     current_buy_in - players[in_action][bet] + minimum_raise

    "dealer": 1,                                    # The index of the player on the dealer button in this round
                                                    #     The first player is (dealer+1)%(players.length)

    "orbits": 7,                                    # Number of orbits completed. (The number of times the dealer
                                                    #     button returned to the same player.)

    "in_action": 1,                                 # The index of your player, in the players array

    "players": [                                    # An array of the players. The order stays the same during the
        {                                           #     entire tournament

            "id": 0,                                # Id of the player (same as the index)

            "name": "Albert",                       # Name specified in the tournament config

            "status": "active",                     # Status of the player:
                                                    #   - active: the player can make bets, and win the current pot
                                                    #   - folded: the player folded, and gave up interest in
                                                    #       the current pot. They can return in the next round.
                                                    #   - out: the player lost all chips, and is out of this sit'n'go

            "version": "Default random player",     # Version identifier returned by the player

            "stack": 1010,                          # Amount of chips still available for the player. (Not including
                                                    #     the chips the player bet in this round.)

            "bet": 320                              # The amount of chips the player put into the pot
        },
        {
            "id": 1,                                # Your own player looks similar, with one extension.
            "name": "piton",
            "status": "active",
            "version": "Default random player",
            "stack": 1590,
            "bet": 80,
            "hole_cards": [                         # The cards of the player. This is only visible for your own player
                                                    #     except after showdown, when cards revealed are also included.
                {
                    "rank": "6",                    # Rank of the card. Possible values are numbers 2-10 and J,Q,K,A
                    "suit": "hearts"                # Suit of the card. Possible values are: clubs,spades,hearts,diamonds
                },
                {
                    "rank": "K",
                    "suit": "hearts"
                }
            ]
        },
        {
            "id": 2,
            "name": "Chuck",
            "status": "out",
            "version": "Default random player",
            "stack": 0,
            "bet": 0
        }
    ],
    "community_cards": [                            # Finally the array of community cards.
        {
            "rank": "4",
            "suit": "hearts"
        },
        {
            "rank": "A",
            "suit": "hearts"
        },
        {
            "rank": "6",
            "suit": "hearts"
        }
    ]
}

class Player:
    VERSION = "Default Python folding player"

# <<<<<<< Updated upstream
    low_cards = ["2", "3", "4", "5", "6", "7", "8"]
    hand = []
#     odds = {"hand": [
#             [{"rank": "A"}, {"rank": "A"},  {"odds": 4}, {"suit": False}],
#             [{"rank": "K"}, {"rank": "K"},  {"odds": 4}, {"suit": False}],
#             [{"rank": "Q"}, {"rank": "Q"},  {"odds": 4}, {"suit": False}],
#             [{"rank": "J"}, {"rank": "J"},  {"odds": 3}, {"suit": False}],
#             [{"rank": "10"}, {"rank": "10"},  {"odds": 3}, {"suit": False}],
#             [{"rank": "9"}, {"rank": "9"},  {"odds": 3}, {"suit": False}],
#             [{"rank": "8"}, {"rank": "8"},  {"odds": 2}, {"suit": False}],
#             [{"rank": "7"}, {"rank": "7"},  {"odds": 2}, {"suit": False}],
#             [{"rank": "6"}, {"rank": "6"},  {"odds": 2}, {"suit": False}],
#             [{"rank": "5"}, {"rank": "5"},  {"odds": 2}, {"suit": False}],
#             [{"rank": "4"}, {"rank": "4"},  {"odds": 2}, {"suit": False}],
#             [{"rank": "3"}, {"rank": "3"},  {"odds": 1}, {"suit": False}],
#             [{"rank": "2"}, {"rank": "2"},  {"odds": 1}, {"suit": False}],
#             [{"rank": "A"}, {"rank": "K"},  {"odds": 3}, {"suit": False}],
#             [{"rank": "A"}, {"rank": "Q"},  {"odds": 3}, {"suit": False}],
#             [{"rank": "A"}, {"rank": "J"},  {"odds": 2}, {"suit": False}],
#             [{"rank": "A"}, {"rank": "10"},  {"odds": 2}, {"suit": False}],
#             [{"rank": "A"}, {"rank": "9"},  {"odds": 2}, {"suit": False}],
#             [{"rank": "A"}, {"rank": "8"},  {"odds": 2}, {"suit": False}],
#             [{"rank": "A"}, {"rank": "7"},  {"odds": 1}, {"suit": False}],
#             [{"rank": "A"}, {"rank": "6"},  {"odds": 1}, {"suit": False}],
#             [{"rank": "A"}, {"rank": "5"},  {"odds": 1}, {"suit": False}],
#             [{"rank": "A"}, {"rank": "4"},  {"odds": 1}, {"suit": False}],
#             [{"rank": "A"}, {"rank": "3"},  {"odds": 1}, {"suit": False}],
#             [{"rank": "A"}, {"rank": "2"},  {"odds": 1}, {"suit": False}],
#             [{"rank": "A"}, {"rank": "K"},  {"odds": 4}, {"suit": True}],
#             [{"rank": "A"}, {"rank": "Q"},  {"odds": 3}, {"suit": True}],
#             [{"rank": "A"}, {"rank": "J"},  {"odds": 3}, {"suit": True}],
#             [{"rank": "A"}, {"rank": "10"},  {"odds": 2}, {"suit": True}],
#             [{"rank": "A"}, {"rank": "9"},  {"odds": 2}, {"suit": True}],
#             [{"rank": "A"}, {"rank": "8"},  {"odds": 2}, {"suit": True}],
#             [{"rank": "A"}, {"rank": "7"},  {"odds": 2}, {"suit": True}],
#             [{"rank": "A"}, {"rank": "6"},  {"odds": 1}, {"suit": True}],
#             [{"rank": "A"}, {"rank": "5"},  {"odds": 1}, {"suit": True}],
#             [{"rank": "A"}, {"rank": "4"},  {"odds": 1}, {"suit": True}],
#             [{"rank": "A"}, {"rank": "3"},  {"odds": 1}, {"suit": True}],
#             [{"rank": "A"}, {"rank": "2"},  {"odds": 1}, {"suit": True}],
#             [{"rank": "K"}, {"rank": "Q"},  {"odds": 2}, {"suit": True}],
#             [{"rank": "K"}, {"rank": "J"},  {"odds": 2}, {"suit": True}],
#             [{"rank": "K"}, {"rank": "10"},  {"odds": 1}, {"suit": True}],
#             [{"rank": "K"}, {"rank": "9"},  {"odds": 1}, {"suit": True}],
#             [{"rank": "K"}, {"rank": "8"},  {"odds": 1}, {"suit": True}],
#             [{"rank": "K"}, {"rank": "7"},  {"odds": 1}, {"suit": True}],
#             [{"rank": "K"}, {"rank": "6"},  {"odds": 1}, {"suit": True}],
#             [{"rank": "K"}, {"rank": "5"},  {"odds": 1}, {"suit": True}],
#             [{"rank": "K"}, {"rank": "4"},  {"odds": 1}, {"suit": True}],
#             [{"rank": "K"}, {"rank": "3"},  {"odds": 1}, {"suit": True}],
#             [{"rank": "Q"}, {"rank": "J"},  {"odds": 1}, {"suit": True}],
#             [{"rank": "Q"}, {"rank": "10"},  {"odds": 1}, {"suit": True}],
#             [{"rank": "Q"}, {"rank": "9"},  {"odds": 1}, {"suit": True}],
#             [{"rank": "Q"}, {"rank": "8"},  {"odds": 1}, {"suit": True}],
#             [{"rank": "Q"}, {"rank": "7"},  {"odds": 1}, {"suit": True}],
#             [{"rank": "J"}, {"rank": "10"},  {"odds": 1}, {"suit": True}],
#             [{"rank": "J"}, {"rank": "9"},  {"odds": 1}, {"suit": True}],
#             [{"rank": "J"}, {"rank": "8"},  {"odds": 1}, {"suit": True}],
#             [{"rank": "J"}, {"rank": "7"},  {"odds": 1}, {"suit": True}]
#             ]}

    def betRequest(self, game_state):
        return self.think(game_state)
# =======
#     low_cards = ["2", "3", "4", "5", "6", "7", "8", "9"]
#     hand = []
#     commun_cards = []
#     handcard1 = []
#     handcard2 = []
#     suit = []
#
#     def betRequest(self, game_state):
#         self.handreturn(game_state)
#         self.communreturn(game_state)
# >>>>>>> Stashed changes
#
#     def showdown(self, game_state):
#        pass
#
#     def handreturn(self, game_state):
#         self.hand = []
#         for player in game_state["players"]:
#             if player["name"] == "piton":
#                 for cards in player["hole_cards"]:
#                     self.hand.append(cards)
#
#     def communreturn(self, game_state):
#         self.commun_cards = []
#         for cards in game_state["community_cards"]:
#             self.commun_cards.append(cards)
#
#     def checkifhandpair(self):
#         if self.hand[0]["rank"] == self.hand[1]["rank"]:
#             return True
#
#     def checkifpair(self):
#         for flop in self.commun_cards:
#             if self.hand[0]["rank"] == flop["rank"]:
#                 self.handcard1.append(flop["rank"])
#             # for card in self.hand:
#             #     for flop in self.commun_cards:
#             #         if card["rank"] == flop["rank"]:
#             #             self.state.append(card)
#             #             self.state.append(flop)
#             # return len(self.state)
#
#     def ifsuit(self):
#         if self.hand[0]["suit"] == self.hand[1]["suit"]:
#             for card in self.hand:
#                 for flop in self.commun_cards:
#                     if card["suit"] == flop["suit"]:
#                         self.suit.append(card)
#         if len(self.suit) >= 5:
#             return True
#
#
#     # def think(self):
#     #     for player in a["players"]:  # PlayerService().game_state
#     #         if player["name"] == "piton":
#     #             for cards in player["hole_cards"]:
#     #                 for card, value in cards.items():
#     #                     if card == "rank":
#     #                         if value in self.low_cards:
#     #                             bet = 0
#     #
#     #     return bet
#
# <<<<<<< Updated upstream
    def think(self, game_state):
        if len(game_state["community_cards"]) == 0:
            bet = 1000
        for player in game_state["players"]:
            if player["name"] == "piton":
                for cards in player["hole_cards"]:
                    self.hand.append(cards)
                    for card, value in cards.items():
                        if card == "rank":
                            if value in self.low_cards:
                                bet = 0
        if self.hand[0]["rank"] == self.hand[1]["rank"]:
            bet = 1000
        return bet

        # for odd in self.odds:
        #     possible_hand = []
        #     for card in odd[:2]:
        #         possible_hand.append(card)
        #     if (self.hand[0]["rank"] == possible_hand[0]["rank"] and self.hand[1]["rank"] == possible_hand[1]["rank"] or self.hand[1]["rank"] == possible_hand[0]["rank"] and self.hand[0]["rank"] == possible_hand[1]["rank"]) and ((possible_hand[0]["suit"] == possible_hand[1]["suit"]) == suit_check()):
        #             if int(possible_hand[0]["odds"]) > 2:
        #                 bet = 6000
        #                 break
        #             else:
        #                 bet = 50
        #                 break
        #     return bet
        # if len(game_state["community_cards"]) == 0:
    #
    # def suit_check(self):
    #     return self.hand[0]["suit"] == self.hand[1]["suit"]
    #
    # def handreturn(self, game_state):
    #     self.hand = []
    #     for player in game_state["players"]:
    #         if player["name"] == "piton":
    #             for cards in player["hole_cards"]:
    #                 self.hand.append(cards)
    #
    # def communreturn(self, game_state):
    #     self.commun_cards = []
    #     for cards in game_state["community_cards"]:
    #         self.commun_cards.append(cards)
    #
    # def checkpair(self, game_state):
    #     for card in self.hand:
    #         for flop in self.commun_cards:
    #             if card["rank"] == flop["rank"]:
    #                 self.state.append(card)
    #                 self.state.append(flop)
    #     return len(self.state)
    #
    # def ifsuit(self):
    #     if self.hand[0]["suit"] == self.hand[1]["suit"]:
    #         for card in self.hand:
    #             for flop in self.commun_cards:
    #                 if card["suit"] == flop["suit"]:
    #                     self.suit.append(card)
    #     if len(self.suit) >= 5:
    #         return True
# =======
# x = Player()
# x.betRequest(a)
# print(x.ifsuit())
# >>>>>>> Stashed changes
<|endoftext|>