[Python-modules-commits] [python-requirements-detector] 01/02: Imported Upstream version 0.4

Daniel Stender danstender-guest at moszumanska.debian.org
Sun Jun 21 13:59:11 UTC 2015


This is an automated email from the git hooks/post-receive script.

danstender-guest pushed a commit to branch master
in repository python-requirements-detector.

commit 8c55f2d702fdf78aed1c4ecee474d96d7098eee2
Author: Daniel Stender <debian at danielstender.com>
Date:   Sun Jun 21 15:53:09 2015 +0200

    Imported Upstream version 0.4
---
 .coveragerc                                        |   3 +
 .gitignore                                         |  35 +++
 .landscape.yaml                                    |   4 +
 .travis.yml                                        |  13 +
 LICENSE                                            |  21 ++
 MANIFEST.in                                        |   1 +
 README.md                                          |  49 ++++
 bin/detect-requirements                            |   3 +
 requirements_detector/__init__.py                  |   1 +
 requirements_detector/detect.py                    | 264 +++++++++++++++++++++
 requirements_detector/formatters.py                |  13 +
 requirements_detector/requirement.py               | 148 ++++++++++++
 requirements_detector/run.py                       |  36 +++
 setup.py                                           |  38 +++
 tests/__init__.py                                  |   0
 tests/detection/syntax_error/setup.py              |  10 +
 tests/detection/test1/requirements.txt             |   6 +
 tests/detection/test2/requirements/base.txt        |   2 +
 tests/detection/test2/requirements/webui.pip       |   2 +
 tests/detection/test3/pip_requirements.txt         |   1 +
 tests/detection/test3/reqs.txt                     |   1 +
 tests/detection/test3/requirements_base.txt        |   1 +
 tests/detection/test3/requirements_test.txt        |   1 +
 tests/detection/test3/test_requirements.txt        |   1 +
 tests/detection/test4/callable.py                  |  13 +
 tests/detection/test4/in_file.py                   |  12 +
 tests/detection/test4/simple.py                    |  10 +
 tests/detection/test4/subscript_assign.py          |  19 ++
 tests/detection/test4/tuple.py                     |  10 +
 tests/detection/test4/uses_requires.py             |  10 +
 .../test4/uses_requires_and_install_requires.py    |  12 +
 tests/detection/test4/utf8.py                      |  11 +
 tests/detection/test5/invalid_requirements.txt     |   5 +
 tests/detection/test6/requirements.txt             |  82 +++++++
 tests/test_detection.py                            |  93 ++++++++
 tests/test_failure_cases.py                        |  10 +
 tests/test_parsing.py                              |  80 +++++++
 tox.ini                                            |   6 +
 38 files changed, 1027 insertions(+)

diff --git a/.coveragerc b/.coveragerc
new file mode 100644
index 0000000..0382f85
--- /dev/null
+++ b/.coveragerc
@@ -0,0 +1,3 @@
+
+[run]
+source=requirements_detector
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..d2d6f36
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,35 @@
+*.py[cod]
+
+# C extensions
+*.so
+
+# Packages
+*.egg
+*.egg-info
+dist
+build
+eggs
+parts
+bin
+var
+sdist
+develop-eggs
+.installed.cfg
+lib
+lib64
+
+# Installer logs
+pip-log.txt
+
+# Unit test / coverage reports
+.coverage
+.tox
+nosetests.xml
+
+# Translations
+*.mo
+
+# Mr Developer
+.mr.developer.cfg
+.project
+.pydevproject
diff --git a/.landscape.yaml b/.landscape.yaml
new file mode 100644
index 0000000..e509d42
--- /dev/null
+++ b/.landscape.yaml
@@ -0,0 +1,4 @@
+doc-warnings: no
+strictness: veryhigh
+max-line-length: 120
+
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 0000000..5e4e364
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,13 @@
+language: python
+python:
+  - "2.6"
+  - "2.7"
+  - "3.3"
+  - "3.4"
+install:
+  - "pip install --use-mirrors nose coverage coveralls"
+  - "pip install --use-mirrors --editable ."
+script: 
+  nosetests -s --with-coverage --cover-package requirements_detector --cover-inclusive
+after_success:
+  coveralls
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..46eb817
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+The MIT License
+
+Copyright (c) 2010-2012 Google, Inc. http://angularjs.org
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 0000000..a09e60e
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1 @@
+include bin/detect-requirements
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..1b46bbb
--- /dev/null
+++ b/README.md
@@ -0,0 +1,49 @@
+# Requirements Detector
+
+## Status
+
+[![Latest Version](https://pypip.in/version/requirements-detector/badge.svg?text=version&style=flat)](https://pypi.python.org/pypi/requirements-detector)
+[![Build Status](https://travis-ci.org/landscapeio/requirements-detector.png?branch=master)](https://travis-ci.org/landscapeio/requirements-detector)
+[![Health](https://landscape.io/github/landscapeio/requirements-detector/master/landscape.svg?style=flat)](https://landscape.io/github/landscapeio/requirements-detector/master)
+[![Coverage Status](https://img.shields.io/coveralls/landscapeio/requirements-detector.svg?style=flat)](https://coveralls.io/r/landscapeio/requirements-detector)
+[![Documentation](https://readthedocs.org/projects/requirements-detector/badge/?version=master)](https://readthedocs.org/projects/requirements-detector/)
+
+## About
+
+`requirements-detector` is a simple Python tool which attempts to find and list the requirements of a Python project. 
+
+When run from the root of a Python project, it will try to ascertain which libraries and the versions of those libraries that the project depends on.
+
+It uses the following methods in order, in the root of the project:
+
+1. Parse `setup.py` (if this is successful, the remaining steps are skipped)
+2. Parse `requirements.txt` or `requirements.pip`
+3. Parse all `*.txt` and `*.pip` files inside a folder called `requirements`
+4. Parse all files in the root folder matching `*requirements*.txt` or `reqs.txt` (so for example, `pip_requirements.txt` would match, as would `requirements_common.txt`)
+
+### Usage
+
+```
+detect-requirements [path]
+```
+If `path` is not specified, the current working directory will be used.
+
+### Output
+
+The output will be plaintext, and match that of a [pip requirements file](http://www.pip-installer.org/en/latest/logic.html), for example:
+
+```
+Django==1.5.2
+South>=0.8
+anyjson
+celery>=2.2,<3
+```
+
+### Usage From Python
+
+```
+>>> import os
+>>> from requirements_detector import find_requirements
+>>> find_requirements(os.getcwd())
+[DetectedRequirement:Django==1.5.2, DetectedRequirement:South>=0.8, ...]
+```
diff --git a/bin/detect-requirements b/bin/detect-requirements
new file mode 100755
index 0000000..854ed3b
--- /dev/null
+++ b/bin/detect-requirements
@@ -0,0 +1,3 @@
+#!/usr/bin/env python
+from requirements_detector.run import run
+run()
\ No newline at end of file
diff --git a/requirements_detector/__init__.py b/requirements_detector/__init__.py
new file mode 100644
index 0000000..2a4de15
--- /dev/null
+++ b/requirements_detector/__init__.py
@@ -0,0 +1 @@
+from requirements_detector.detect import find_requirements
\ No newline at end of file
diff --git a/requirements_detector/detect.py b/requirements_detector/detect.py
new file mode 100644
index 0000000..e289497
--- /dev/null
+++ b/requirements_detector/detect.py
@@ -0,0 +1,264 @@
+import re
+import os
+import sys
+from astroid.builder import AstroidBuilder
+from astroid import MANAGER, CallFunc, Name, Assign, Keyword, List, Tuple, Const, AssName
+from requirements_detector.requirement import DetectedRequirement
+
+
+__all__ = ['find_requirements',
+           'RequirementsNotFound',
+           'CouldNotParseRequirements']
+
+
+# PEP263, see http://legacy.python.org/dev/peps/pep-0263/
+_ENCODING_REGEXP = re.compile(r'coding[:=]\s*([-\w.]+)')
+
+
+_PY3K = sys.version_info >= (3, 0)
+
+
+_PIP_OPTIONS = (
+    '-i', '--index-url',
+    '--extra-index-url',
+    '--no-index',
+    '-f', '--find-links',
+    '-r'
+)
+
+
+class RequirementsNotFound(Exception):
+    pass
+
+
+class CouldNotParseRequirements(Exception):
+    pass
+
+
+def _load_file_contents(filepath):
+    # This function is a bit of a tedious workaround (AKA 'hack').
+    # Astroid calls 'compile' under the hood, which refuses to accept a Unicode
+    # object which contains a PEP-263 encoding definition. However if we give
+    # Astroid raw bytes, it'll assume ASCII. Therefore we need to detect the encoding
+    # here, convert the file contents to a Unicode object, *and also strip the encoding
+    # declaration* to avoid the compile step breaking.
+    with open(filepath) as f:
+        if _PY3K:
+            return f.read()
+        
+        contents = f.readlines()
+
+        result = []
+        encoding_lines = contents[0:2]
+        encoding = 'utf-8'
+        for line in encoding_lines:
+            match = _ENCODING_REGEXP.search(line)
+            if match is None:
+                result.append(line)
+            else:
+                encoding = match.group(1)
+
+        result += contents[2:]
+        result = '\n'.join(result)
+        return result.decode(encoding)
+
+
+def find_requirements(path):
+    """
+    This method tries to determine the requirements of a particular project
+    by inspecting the possible places that they could be defined.
+
+    It will attempt, in order:
+
+    1) to parse setup.py in the root for an install_requires value
+    2) to read a requirements.txt file or a requirements.pip in the root
+    3) to read all .txt files in a folder called 'requirements' in the root
+    4) to read files matching "*requirements*.txt" and "*reqs*.txt" in the root,
+       excluding any starting or ending with 'test'
+
+    If one of these succeeds, then a list of pkg_resources.Requirement's
+    will be returned. If none can be found, then a RequirementsNotFound
+    will be raised
+    """
+    requirements = []
+
+    setup_py = os.path.join(path, 'setup.py')
+    if os.path.exists(setup_py) and os.path.isfile(setup_py):
+        try:
+            requirements = from_setup_py(setup_py)
+            requirements.sort()
+            return requirements
+        except CouldNotParseRequirements:
+            pass
+
+    for reqfile_name in ('requirements.txt', 'requirements.pip'):
+        reqfile_path = os.path.join(path, reqfile_name)
+        if os.path.exists(reqfile_path) and os.path.isfile(reqfile_path):
+            try:
+                requirements += from_requirements_txt(reqfile_path)
+            except CouldNotParseRequirements as e:
+                pass
+
+    requirements_dir = os.path.join(path, 'requirements')
+    if os.path.exists(requirements_dir) and os.path.isdir(requirements_dir):
+        from_dir = from_requirements_dir(requirements_dir)
+        if from_dir is not None:
+            requirements += from_dir
+
+    from_blob = from_requirements_blob(path)
+    if from_blob is not None:
+        requirements += from_blob
+
+    requirements = list(set(requirements))
+    if len(requirements) > 0:
+        requirements.sort()
+        return requirements
+
+    raise RequirementsNotFound
+
+
+class SetupWalker(object):
+
+    def __init__(self, ast):
+        self._ast = ast
+        self._setup_call = None
+        self._top_level_assigns = {}
+        self.walk()
+
+    def walk(self, node=None):
+        top = node is None
+        node = node or self._ast
+
+        # test to see if this is a call to setup()
+        if isinstance(node, CallFunc):
+            for child_node in node.get_children():
+                if isinstance(child_node, Name) and child_node.name == 'setup':
+                    # TODO: what if this isn't actually the distutils setup?
+                    self._setup_call = node
+
+        for child_node in node.get_children():
+            if top and isinstance(child_node, Assign):
+                for target in child_node.targets:
+                    if isinstance(target, AssName):
+                        self._top_level_assigns[target.name] = child_node.value
+            self.walk(child_node)
+
+    def _get_list_value(self, list_node):
+        values = []
+        for child_node in list_node.get_children():
+            if not isinstance(child_node, Const):
+                # we can't handle anything fancy, only constant values
+                raise CouldNotParseRequirements
+            values.append(child_node.value)
+        return values
+
+    def get_requires(self):
+        # first, if we have a call to setup, then we can see what its "install_requires" argument is
+        if not self._setup_call:
+            raise CouldNotParseRequirements
+
+        found_requirements = []
+
+        for child_node in self._setup_call.get_children():
+            if not isinstance(child_node, Keyword):
+                # do we want to try to handle positional arguments?
+                continue
+
+            if child_node.arg not in ('install_requires', 'requires'):
+                continue
+
+            if isinstance(child_node.value, (List, Tuple)):
+                # joy! this is a simple list or tuple of requirements
+                # this is a Keyword -> List or Keyword -> Tuple
+                found_requirements += self._get_list_value(child_node.value)
+                continue
+
+            if isinstance(child_node.value, Name):
+                # otherwise, it's referencing a value defined elsewhere
+                # this will be a Keyword -> Name
+                try:
+                    reqs = self._top_level_assigns[child_node.value.name]
+                except KeyError:
+                    raise CouldNotParseRequirements
+                else:
+                    if isinstance(reqs, (List, Tuple)):
+                        found_requirements += self._get_list_value(reqs)
+                        continue
+
+            # otherwise it's something funky and we can't handle it
+            raise CouldNotParseRequirements
+
+        # if we've fallen off the bottom with nothing in our list of requirements,
+        #  we simply didn't find anything useful
+        if len(found_requirements) > 0:
+            return found_requirements
+        raise CouldNotParseRequirements
+
+
+def from_setup_py(setup_file):
+    try:
+        contents = _load_file_contents(setup_file)
+        ast = AstroidBuilder(MANAGER).string_build(contents)
+    except SyntaxError:
+        # if the setup file is broken, we can't do much about that...
+        raise CouldNotParseRequirements
+
+    walker = SetupWalker(ast)
+
+    requirements = []
+    for req in walker.get_requires():
+        requirements.append(DetectedRequirement.parse(req, setup_file))
+
+    return requirements
+
+
+def from_requirements_txt(requirements_file):
+    # see http://www.pip-installer.org/en/latest/logic.html
+    requirements = []
+    with open(requirements_file) as f:
+        for req in f.readlines():
+            if req.strip() == '':
+                # empty line
+                continue
+            if req.strip().startswith('#'):
+                # this is a comment
+                continue
+            if req.strip().split()[0] in _PIP_OPTIONS:
+                # this is a pip option
+                continue
+            detected = DetectedRequirement.parse(req, requirements_file)
+            if detected is None:
+                continue
+            requirements.append(detected)
+
+    return requirements
+
+
+def from_requirements_dir(path):
+    requirements = []
+    for entry in os.listdir(path):
+        filepath = os.path.join(path, entry)
+        if not os.path.isfile(filepath):
+            continue
+        if entry.endswith('.txt') or entry.endswith('.pip'):
+            # TODO: deal with duplicates
+            requirements += from_requirements_txt(filepath)
+
+    return requirements
+
+
+def from_requirements_blob(path):
+    requirements = []
+
+    for entry in os.listdir(path):
+        filepath = os.path.join(path, entry)
+        if not os.path.isfile(filepath):
+            continue
+        m = re.match(r'^(\w*)req(uirement)?s(\w*)\.txt$', entry)
+        if m is None:
+            continue
+        if m.group(1).startswith('test') or m.group(3).endswith('test'):
+            continue
+        requirements += from_requirements_txt(filepath)
+
+    return requirements
diff --git a/requirements_detector/formatters.py b/requirements_detector/formatters.py
new file mode 100644
index 0000000..415cf60
--- /dev/null
+++ b/requirements_detector/formatters.py
@@ -0,0 +1,13 @@
+
+import sys
+
+
+def requirements_file(requirements_list):
+    for requirement in requirements_list:
+        sys.stdout.write(requirement.pip_format())
+        sys.stdout.write('\n')
+
+
+FORMATTERS = {
+    'requirements_file': requirements_file
+}
diff --git a/requirements_detector/requirement.py b/requirements_detector/requirement.py
new file mode 100644
index 0000000..40d2c95
--- /dev/null
+++ b/requirements_detector/requirement.py
@@ -0,0 +1,148 @@
+"""
+This module represents the various types of requirement that can be specified for
+a project. It is somewhat redundant to re-implement here as we could use
+`pip.req.InstallRequirement`, but that would require depending on pip which is not
+easy to do since it will usually be installed by the user at a specific version.
+Additionally, the pip implementation has a lot of extra features that we don't need -
+we don't expect relative file paths to exist, for example. Note that the parsing here
+is also intentionally more lenient - it is not our job to validate the requirements
+list.
+"""
+import os
+import re
+from pkg_resources import Requirement
+
+try:
+    import urlparse
+except ImportError:
+    # python3
+    from urllib import parse as urlparse
+
+
+def _is_filepath(req):
+    # this is (probably) a file
+    return os.path.sep in req or req.startswith('.')
+
+
+def _parse_egg_name(url_fragment):
+    """
+    >>> _parse_egg_name('egg=fish&cake=lala')
+    fish
+    >>> _parse_egg_name('something_spurious')
+    None
+    """
+    if '=' not in url_fragment:
+        return None
+    parts = urlparse.parse_qs(url_fragment)
+    if 'egg' not in parts:
+        return None
+    return parts['egg'][0]  # taking the first value mimics pip's behaviour
+
+
+def _strip_fragment(urlparts):
+    new_urlparts = (
+        urlparts.scheme,
+        urlparts.netloc,
+        urlparts.path,
+        urlparts.params,
+        urlparts.query,
+        None
+    )
+    return urlparse.urlunparse(new_urlparts)
+
+
+class DetectedRequirement(object):
+
+    def __init__(self, name=None, url=None, requirement=None, location_defined=None):
+        if requirement is not None:
+            self.name = requirement.key
+            self.requirement = requirement
+            self.version_specs = requirement.specs
+            self.url = None
+        else:
+            self.name = name
+            self.version_specs = []
+            self.url = url
+            self.requirement = None
+        self.location_defined = location_defined
+
+    def _format_specs(self):
+        return ','.join(['%s%s' % (comp, version) for comp, version in self.version_specs])
+
+    def pip_format(self):
+        if self.url:
+            if self.name:
+                return '%s#egg=%s' % (self.url, self.name)
+            return self.url
+        if self.name:
+            if self.version_specs:
+                return "%s%s" % (self.name, self._format_specs())
+            return self.name
+
+    def __str__(self):
+        rep = self.name or 'Unknown'
+        if self.version_specs:
+            specs = ','.join(['%s%s' % (comp, version) for comp, version in self.version_specs])
+            rep = '%s%s' % (rep, specs)
+        if self.url:
+            rep = '%s (%s)' % (rep, self.url)
+        return rep
+
+    def __hash__(self):
+        return hash(str(self.name) + str(self.url) + str(self.version_specs))
+
+    def __repr__(self):
+        return 'DetectedRequirement:%s' % str(self)
+
+    def __eq__(self, other):
+        return self.name == other.name and self.url == other.url and self.version_specs == other.version_specs
+
+    def __gt__(self, other):
+        return (self.name or "") > (other.name or "")
+
+    @staticmethod
+    def parse(line, location_defined=None):
+        # the options for a Pip requirements file are:
+        #
+        # 1) <dependency_name>
+        # 2) <dependency_name><version_spec>
+        # 3) <vcs_url>(#egg=<dependency_name>)?
+        # 4) <url_to_archive>(#egg=<dependency_name>)?
+        # 5) <path_to_dir>
+        # 6) (-e|--editable) <path_to_dir>(#egg=<dependency_name)?
+        # 7) (-e|--editable) <vcs_url>#egg=<dependency_name>
+        line = line.strip()
+
+        # strip the editable flag
+        line = re.sub('^(-e|--editable) ', '', line)
+
+        url = urlparse.urlparse(line)
+
+        # if it is a VCS URL, then we want to strip off the protocol as urlparse
+        # might not handle it correctly
+        vcs_scheme = None
+        if '+' in url.scheme or url.scheme in ('git',):
+            if url.scheme == 'git':
+                vcs_scheme = 'git+git'
+            else:
+                vcs_scheme = url.scheme
+            url = urlparse.urlparse(re.sub(r'^%s://' % re.escape(url.scheme), '', line))
+
+        if vcs_scheme is None and url.scheme == '' and not _is_filepath(line):
+            # if we are here, it is a simple dependency
+            try:
+                req = Requirement.parse(line)
+            except ValueError:
+                # this happens if the line is invalid
+                return None
+            else:
+                return DetectedRequirement(requirement=req, location_defined=location_defined)
+
+        # otherwise, this is some kind of URL
+        name = _parse_egg_name(url.fragment)
+        url = _strip_fragment(url)
+
+        if vcs_scheme:
+            url = '%s://%s' % (vcs_scheme, url)
+
+        return DetectedRequirement(name=name, url=url, location_defined=location_defined)
diff --git a/requirements_detector/run.py b/requirements_detector/run.py
new file mode 100644
index 0000000..cbb4080
--- /dev/null
+++ b/requirements_detector/run.py
@@ -0,0 +1,36 @@
+import os
+import sys
+from requirements_detector.detect import RequirementsNotFound
+from requirements_detector.formatters import FORMATTERS
+from requirements_detector import find_requirements
+
+
+def _die(message):
+    sys.stderr.write("%s\n" % message)
+    sys.exit(1)
+
+
+def run():
+    if len(sys.argv) > 1:
+        path = sys.argv[1]
+    else:
+        path = os.getcwd()
+
+    if not os.path.exists(path):
+        _die("%s does not exist" % path)
+
+    if not os.path.isdir(path):
+        _die("%s is not a directory" % path)
+
+    try:
+        requirements = find_requirements(path)
+    except RequirementsNotFound:
+        _die("Unable to find requirements at %s" % path)
+
+    format_name = 'requirements_file'  # TODO: other output formats such as JSON
+    FORMATTERS[format_name](requirements)
+    sys.exit(0)
+
+
+if __name__ == '__main__':
+    run()
diff --git a/setup.py b/setup.py
new file mode 100644
index 0000000..eeedd54
--- /dev/null
+++ b/setup.py
@@ -0,0 +1,38 @@
+# -*- coding: UTF-8 -*-
+from distutils.core import setup
+from setuptools import find_packages
+
+
+_version = "0.4"
+_packages = find_packages(exclude=["*.tests", "*.tests.*", "tests.*", "tests"])
+
+_short_description = "Python tool to find and list requirements of a Python project"
+
+_CLASSIFIERS = (
+    'Development Status :: 5 - Production/Stable',
+    'Environment :: Console',
+    'Intended Audience :: Developers',
+    'Operating System :: Unix',
+    'Topic :: Software Development :: Quality Assurance',
+    'Programming Language :: Python :: 2.6',
+    'Programming Language :: Python :: 2.7',
+    'Programming Language :: Python :: 3.3',
+    'License :: OSI Approved :: '
+    'GNU General Public License v2 or later (GPLv2+)',
+)
+
+
+setup(
+    name='requirements-detector',
+    url='https://github.com/landscapeio/requirements-detector',
+    author='landscape.io',
+    author_email='code at landscape.io',
+    description=_short_description,
+    version=_version,
+    scripts=['bin/detect-requirements'],
+    install_requires=['astroid>=1.0.0'],
+    packages=_packages,
+    license='MIT',
+    keywords='python requirements detector',
+    classifiers=_CLASSIFIERS,
+)
diff --git a/tests/__init__.py b/tests/__init__.py
new file mode 100644
index 0000000..e69de29
diff --git a/tests/detection/syntax_error/setup.py b/tests/detection/syntax_error/setup.py
new file mode 100644
index 0000000..cc5a9d6
--- /dev/null
+++ b/tests/detection/syntax_error/setup.py
@@ -0,0 +1,10 @@
+from distutils.core import setup
+
+setup(
+    name='prospector-test-1',
+    version='0.0.1',
+    install_requires=[
+        'Django==1.5.0',
+        'django-gubbins==1.1.2'
+    ] narm narm narm
+)
\ No newline at end of file
diff --git a/tests/detection/test1/requirements.txt b/tests/detection/test1/requirements.txt
new file mode 100644
index 0000000..764434b
--- /dev/null
+++ b/tests/detection/test1/requirements.txt
@@ -0,0 +1,6 @@
+-i https://example.com/custom/pypi
+Django>=1.5.0
+South==0.8.2
+amqp!=1.0.13
+# we want six too
+six<1.4,>=1.3.0
\ No newline at end of file
diff --git a/tests/detection/test2/requirements/base.txt b/tests/detection/test2/requirements/base.txt
new file mode 100644
index 0000000..54f1b29
--- /dev/null
+++ b/tests/detection/test2/requirements/base.txt
@@ -0,0 +1,2 @@
+amqp==1.0.13
+anyjson==0.3.3
diff --git a/tests/detection/test2/requirements/webui.pip b/tests/detection/test2/requirements/webui.pip
new file mode 100644
index 0000000..14ae0d2
--- /dev/null
+++ b/tests/detection/test2/requirements/webui.pip
@@ -0,0 +1,2 @@
+Django==1.5.2
+South==0.8.2
\ No newline at end of file
diff --git a/tests/detection/test3/pip_requirements.txt b/tests/detection/test3/pip_requirements.txt
new file mode 100644
index 0000000..b9ba1e0
--- /dev/null
+++ b/tests/detection/test3/pip_requirements.txt
@@ -0,0 +1 @@
+anyjson==0.3.3
diff --git a/tests/detection/test3/reqs.txt b/tests/detection/test3/reqs.txt
new file mode 100644
index 0000000..ac7d70c
--- /dev/null
+++ b/tests/detection/test3/reqs.txt
@@ -0,0 +1 @@
+django-gubbins==1.1.2
\ No newline at end of file
diff --git a/tests/detection/test3/requirements_base.txt b/tests/detection/test3/requirements_base.txt
new file mode 100644
index 0000000..dca95d6
--- /dev/null
+++ b/tests/detection/test3/requirements_base.txt
@@ -0,0 +1 @@
+amqp==1.0.13
diff --git a/tests/detection/test3/requirements_test.txt b/tests/detection/test3/requirements_test.txt
new file mode 100644
index 0000000..2cdfeb3
--- /dev/null
+++ b/tests/detection/test3/requirements_test.txt
@@ -0,0 +1 @@
+South==0.8.2
\ No newline at end of file
diff --git a/tests/detection/test3/test_requirements.txt b/tests/detection/test3/test_requirements.txt
new file mode 100644
index 0000000..7dd404b
--- /dev/null
+++ b/tests/detection/test3/test_requirements.txt
@@ -0,0 +1 @@
+Django==1.5.2
\ No newline at end of file
diff --git a/tests/detection/test4/callable.py b/tests/detection/test4/callable.py
new file mode 100644
index 0000000..0232e5c
--- /dev/null
+++ b/tests/detection/test4/callable.py
@@ -0,0 +1,13 @@
+from distutils.core import setup
+
+def _install_requires():
+    return [
+        'Django==1.5.0',
+        'django-gubbins==1.1.2'
+    ]
+
+setup(
+    name='prospector-test-2',
+    version='0.0.1',
+    install_requires=_install_requires()
+)
\ No newline at end of file
diff --git a/tests/detection/test4/in_file.py b/tests/detection/test4/in_file.py
new file mode 100644
index 0000000..7d11615
--- /dev/null
+++ b/tests/detection/test4/in_file.py
@@ -0,0 +1,12 @@
+from distutils.core import setup
+
+_install_requires = [
+    'Django==1.5.0',
+    'django-gubbins==1.1.2'
+]
+
+setup(
+    name='prospector-test-2',
+    version='0.0.1',
+    install_requires=_install_requires
+)
\ No newline at end of file
diff --git a/tests/detection/test4/simple.py b/tests/detection/test4/simple.py
new file mode 100644
index 0000000..793c915
--- /dev/null
+++ b/tests/detection/test4/simple.py
@@ -0,0 +1,10 @@
+from distutils.core import setup
+
+setup(
+    name='prospector-test-1',
+    version='0.0.1',
+    install_requires=[
+        'Django==1.5.0',
+        'django-gubbins==1.1.2'
+    ]
+)
\ No newline at end of file
diff --git a/tests/detection/test4/subscript_assign.py b/tests/detection/test4/subscript_assign.py
new file mode 100644
index 0000000..e12aeb3
--- /dev/null
+++ b/tests/detection/test4/subscript_assign.py
@@ -0,0 +1,19 @@
+"""
+This test is to verify that top level subscript assigns (x[y]) don't break the
+parser. For version <=0.1, a subscript assign would break the setup.py AST walker
+completely.
+"""
+
+from distutils.core import setup
+
+something = dict()
+something['fish'] = ['a', 'b', 'c']
+
+setup(
+    name='prospector-test-1',
+    version='0.0.1',
+    install_requires=(
+        'Django==1.5.0',
+        'django-gubbins==1.1.2'
+    )
+)
\ No newline at end of file
diff --git a/tests/detection/test4/tuple.py b/tests/detection/test4/tuple.py
new file mode 100644
index 0000000..af441dc
--- /dev/null
+++ b/tests/detection/test4/tuple.py
@@ -0,0 +1,10 @@
+from distutils.core import setup
+
+setup(
+    name='prospector-test-1',
+    version='0.0.1',
+    install_requires=(
+        'Django==1.5.0',
+        'django-gubbins==1.1.2'
+    )
+)
\ No newline at end of file
diff --git a/tests/detection/test4/uses_requires.py b/tests/detection/test4/uses_requires.py
new file mode 100644
index 0000000..5ed80f3
--- /dev/null
+++ b/tests/detection/test4/uses_requires.py
@@ -0,0 +1,10 @@
+from distutils.core import setup
+
+setup(
+    name='prospector-test-1',
+    version='0.0.1',
+    requires=[
+        'Django==1.5.0',
+        'django-gubbins==1.1.2'
+    ]
+)
\ No newline at end of file
diff --git a/tests/detection/test4/uses_requires_and_install_requires.py b/tests/detection/test4/uses_requires_and_install_requires.py
new file mode 100644
index 0000000..32ccb0d
--- /dev/null
+++ b/tests/detection/test4/uses_requires_and_install_requires.py
@@ -0,0 +1,12 @@
+from distutils.core import setup
+
+setup(
+    name='prospector-test-1',
+    version='0.0.1',
+    requires=[
+        'Django==1.5.0',
+    ],
+    install_requires=[
+        'django-gubbins==1.1.2'
+    ]
+)
\ No newline at end of file
diff --git a/tests/detection/test4/utf8.py b/tests/detection/test4/utf8.py
new file mode 100644
index 0000000..37ea216
--- /dev/null
+++ b/tests/detection/test4/utf8.py
@@ -0,0 +1,11 @@
+# -*- coding: UTF-8 -*-
+from distutils.core import setup
+
+setup(
+    name=u'prospector-test-4-üéø',
+    version='0.0.1',
+    install_requires=[
+        'Django==1.5.0',
+        'django-gubbins==1.1.2'
... 318 lines suppressed ...

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/python-modules/packages/python-requirements-detector.git



More information about the Python-modules-commits mailing list