Hello community, here is the log from the commit of package python-requirements-detector for openSUSE:Factory checked in at 2015-05-16 19:01:50 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Comparing /work/SRC/openSUSE:Factory/python-requirements-detector (Old) and /work/SRC/openSUSE:Factory/.python-requirements-detector.new (New) ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Package is "python-requirements-detector" Changes: -------- --- /work/SRC/openSUSE:Factory/python-requirements-detector/python-requirements-detector.changes 2014-01-23 15:54:42.000000000 +0100 +++ /work/SRC/openSUSE:Factory/.python-requirements-detector.new/python-requirements-detector.changes 2015-05-16 19:01:50.000000000 +0200 @@ -1,0 +2,6 @@ +Tue May 12 14:22:20 UTC 2015 - benoit.mo...@gmx.fr + +- update to version 0.4: + * no changelog available + +------------------------------------------------------------------- Old: ---- requirements-detector-0.1.1.tar.gz New: ---- requirements-detector-0.4.tar.gz ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ Other differences: ------------------ ++++++ python-requirements-detector.spec ++++++ --- /var/tmp/diff_new_pack.wDojQR/_old 2015-05-16 19:01:51.000000000 +0200 +++ /var/tmp/diff_new_pack.wDojQR/_new 2015-05-16 19:01:51.000000000 +0200 @@ -1,7 +1,7 @@ # # spec file for package python-requirements-detector # -# Copyright (c) 2014 SUSE LINUX Products GmbH, Nuernberg, Germany. +# Copyright (c) 2015 SUSE LINUX GmbH, Nuernberg, Germany. # # All modifications and additions to the file contributed by third parties # remain the property of their copyright owners, unless otherwise agreed @@ -17,7 +17,7 @@ Name: python-requirements-detector -Version: 0.1.1 +Version: 0.4 Release: 0 Summary: Python tool to find and list requirements of a Python project License: MIT ++++++ requirements-detector-0.1.1.tar.gz -> requirements-detector-0.4.tar.gz ++++++ diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/requirements-detector-0.1.1/PKG-INFO new/requirements-detector-0.4/PKG-INFO --- old/requirements-detector-0.1.1/PKG-INFO 2013-10-26 17:50:56.000000000 +0200 +++ new/requirements-detector-0.4/PKG-INFO 2015-03-24 07:41:47.000000000 +0100 @@ -1,6 +1,6 @@ -Metadata-Version: 1.0 +Metadata-Version: 1.1 Name: requirements-detector -Version: 0.1.1 +Version: 0.4 Summary: Python tool to find and list requirements of a Python project Home-page: https://github.com/landscapeio/requirements-detector Author: landscape.io @@ -9,3 +9,12 @@ Description: UNKNOWN Keywords: python requirements detector Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Console +Classifier: Intended Audience :: Developers +Classifier: Operating System :: Unix +Classifier: Topic :: Software Development :: Quality Assurance +Classifier: Programming Language :: Python :: 2.6 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3.3 +Classifier: License :: OSI Approved :: GNU General Public License v2 or later (GPLv2+) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/requirements-detector-0.1.1/requirements_detector/detect.py new/requirements-detector-0.4/requirements_detector/detect.py --- old/requirements-detector-0.1.1/requirements_detector/detect.py 2013-10-26 17:48:58.000000000 +0200 +++ new/requirements-detector-0.4/requirements_detector/detect.py 2015-03-23 23:06:42.000000000 +0100 @@ -1,14 +1,21 @@ 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_dependencies', +__all__ = ['find_requirements', 'RequirementsNotFound', - 'CouldNotParseRequirements', - 'get_prospector_profiles'] + '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 = ( @@ -28,6 +35,34 @@ 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 @@ -36,7 +71,7 @@ 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 in the root + 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' @@ -45,29 +80,38 @@ 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: - return from_setup_py(setup_py) + requirements = from_setup_py(setup_py) + requirements.sort() + return requirements except CouldNotParseRequirements: pass - requirements_txt = os.path.join(path, 'requirements.txt') - if os.path.exists(requirements_txt) and os.path.isfile(requirements_txt): - try: - return from_requirements_txt(requirements_txt) - 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): - requirements = from_requirements_dir(requirements_dir) - if requirements: - return requirements - - requirements = from_requirements_blob(path) - if requirements: + 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 @@ -108,23 +152,26 @@ values.append(child_node.value) return values - def get_install_requires(self): + 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 != 'install_requires': + 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 - return self._get_list_value(child_node.value) + found_requirements += self._get_list_value(child_node.value) + continue if isinstance(child_node.value, Name): # otherwise, it's referencing a value defined elsewhere @@ -135,26 +182,33 @@ raise CouldNotParseRequirements else: if isinstance(reqs, (List, Tuple)): - return self._get_list_value(reqs) + 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, we simply didn't find anything useful + # 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): - with open(setup_file) as f: - ast = AstroidBuilder(MANAGER).string_build(f.read()) + 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_install_requires(): - requirements.append(DetectedRequirement.parse(req)) + for req in walker.get_requires(): + requirements.append(DetectedRequirement.parse(req, setup_file)) - requirements.sort(key=lambda r: r.name) return requirements @@ -172,9 +226,11 @@ if req.strip().split()[0] in _PIP_OPTIONS: # this is a pip option continue - requirements.append(DetectedRequirement.parse(req)) + detected = DetectedRequirement.parse(req, requirements_file) + if detected is None: + continue + requirements.append(detected) - requirements.sort(key=lambda r: r.name) return requirements @@ -182,11 +238,12 @@ requirements = [] for entry in os.listdir(path): filepath = os.path.join(path, entry) - if os.path.isfile(filepath) and entry.endswith('.txt'): + if not os.path.isfile(filepath): + continue + if entry.endswith('.txt') or entry.endswith('.pip'): # TODO: deal with duplicates requirements += from_requirements_txt(filepath) - requirements.sort(key=lambda r: r.name) return requirements @@ -204,5 +261,4 @@ continue requirements += from_requirements_txt(filepath) - requirements.sort(key=lambda r: r.name) - return requirements \ No newline at end of file + return requirements diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/requirements-detector-0.1.1/requirements_detector/formatters.py new/requirements-detector-0.4/requirements_detector/formatters.py --- old/requirements-detector-0.1.1/requirements_detector/formatters.py 2013-10-17 11:04:16.000000000 +0200 +++ new/requirements-detector-0.4/requirements_detector/formatters.py 2014-08-21 19:28:36.000000000 +0200 @@ -4,7 +4,7 @@ def requirements_file(requirements_list): for requirement in requirements_list: - sys.stdout.write(str(requirement)) + sys.stdout.write(requirement.pip_format()) sys.stdout.write('\n') diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/requirements-detector-0.1.1/requirements_detector/requirement.py new/requirements-detector-0.4/requirements_detector/requirement.py --- old/requirements-detector-0.1.1/requirements_detector/requirement.py 2013-10-19 16:33:02.000000000 +0200 +++ new/requirements-detector-0.4/requirements_detector/requirement.py 2015-03-23 23:11:24.000000000 +0100 @@ -10,9 +10,14 @@ """ import os import re -import urlparse 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 @@ -48,7 +53,7 @@ class DetectedRequirement(object): - def __init__(self, name=None, url=None, requirement=None): + def __init__(self, name=None, url=None, requirement=None, location_defined=None): if requirement is not None: self.name = requirement.key self.requirement = requirement @@ -59,6 +64,20 @@ 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' @@ -69,14 +88,20 @@ 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): + def parse(line, location_defined=None): # the options for a Pip requirements file are: # # 1) <dependency_name> @@ -96,14 +121,22 @@ # 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: - vcs_scheme = url.scheme + 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 - req = Requirement.parse(line) - return DetectedRequirement(requirement=req) + 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) @@ -112,4 +145,4 @@ if vcs_scheme: url = '%s://%s' % (vcs_scheme, url) - return DetectedRequirement(name=name, url=url) \ No newline at end of file + return DetectedRequirement(name=name, url=url, location_defined=location_defined) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/requirements-detector-0.1.1/requirements_detector.egg-info/PKG-INFO new/requirements-detector-0.4/requirements_detector.egg-info/PKG-INFO --- old/requirements-detector-0.1.1/requirements_detector.egg-info/PKG-INFO 2013-10-26 17:50:56.000000000 +0200 +++ new/requirements-detector-0.4/requirements_detector.egg-info/PKG-INFO 2015-03-24 07:41:47.000000000 +0100 @@ -1,6 +1,6 @@ -Metadata-Version: 1.0 +Metadata-Version: 1.1 Name: requirements-detector -Version: 0.1.1 +Version: 0.4 Summary: Python tool to find and list requirements of a Python project Home-page: https://github.com/landscapeio/requirements-detector Author: landscape.io @@ -9,3 +9,12 @@ Description: UNKNOWN Keywords: python requirements detector Platform: UNKNOWN +Classifier: Development Status :: 5 - Production/Stable +Classifier: Environment :: Console +Classifier: Intended Audience :: Developers +Classifier: Operating System :: Unix +Classifier: Topic :: Software Development :: Quality Assurance +Classifier: Programming Language :: Python :: 2.6 +Classifier: Programming Language :: Python :: 2.7 +Classifier: Programming Language :: Python :: 3.3 +Classifier: License :: OSI Approved :: GNU General Public License v2 or later (GPLv2+) diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/requirements-detector-0.1.1/requirements_detector.egg-info/requires.txt new/requirements-detector-0.4/requirements_detector.egg-info/requires.txt --- old/requirements-detector-0.1.1/requirements_detector.egg-info/requires.txt 2013-10-26 17:50:56.000000000 +0200 +++ new/requirements-detector-0.4/requirements_detector.egg-info/requires.txt 2015-03-24 07:41:47.000000000 +0100 @@ -1 +1 @@ -astroid>=1.0.0 \ No newline at end of file +astroid>=1.0.0 diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' '--exclude=.svnignore' old/requirements-detector-0.1.1/setup.py new/requirements-detector-0.4/setup.py --- old/requirements-detector-0.1.1/setup.py 2013-10-26 17:50:46.000000000 +0200 +++ new/requirements-detector-0.4/setup.py 2015-03-23 23:11:11.000000000 +0100 @@ -3,11 +3,24 @@ from setuptools import find_packages -_version = "0.1.1" +_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', @@ -21,4 +34,5 @@ packages=_packages, license='MIT', keywords='python requirements detector', + classifiers=_CLASSIFIERS, )