예제 #1
0
파일: package.py 프로젝트: olevole/py2deb
    def debian_version(self):
        """
        The version of the Debian package (a string).

        Reformats :attr:`python_version` using
        :func:`.normalize_package_version()`.
        """
        return normalize_package_version(self.python_version)
예제 #2
0
    def debian_version(self):
        """
        The version of the Debian package (a string).

        Reformats :attr:`python_version` using
        :func:`.normalize_package_version()`.
        """
        return normalize_package_version(self.python_version)
예제 #3
0
    def debian_version(self):
        """
        The version of the Debian package (a string).

        Reformats the version of the Python package using
        :py:func:`.normalize_package_version()`.
        """
        return normalize_package_version(self.requirement.version)
예제 #4
0
    def debian_version(self):
        """
        The version of the Debian package (a string).

        Reformats :attr:`python_version` using
        :func:`.normalize_package_version()`.
        """
        return normalize_package_version(
            self.python_version,
            prerelease_workaround=self.converter.prerelease_workaround)
예제 #5
0
파일: tests.py 프로젝트: trousev/py2deb
 def test_version_reformatting(self):
     """Test reformatting of Python version strings."""
     assert normalize_package_version('1.5_42') == '1.5-42'
     assert normalize_package_version('1.5-whatever') == '1.5-whatever-1'
     # PEP 440 pre-release versions (specific handling added in release 1.0).
     assert normalize_package_version('1.0a2') == '1.0~a2'
     assert normalize_package_version('1.0b2') == '1.0~b2'
     assert normalize_package_version('1.0c2') == '1.0~rc2'
     assert normalize_package_version('1.0rc2') == '1.0~rc2'
     # New versus old behavior (the option to control backwards compatibility was added in release 2.1).
     assert normalize_package_version(
         '1.0a2', prerelease_workaround=True) == '1.0~a2'
     assert normalize_package_version(
         '1.0a2', prerelease_workaround=False) == '1.0a2'
예제 #6
0
 def test_version_reformatting(self):
     """Test reformatting of Python version strings."""
     assert normalize_package_version('1.5_42') == '1.5-42'
     assert normalize_package_version('1.5-whatever') == '1.5-whatever-1'
     # PEP 440 pre-release versions.
     assert normalize_package_version('1.0a2') == '1.0~a2'
     assert normalize_package_version('1.0b2') == '1.0~b2'
     assert normalize_package_version('1.0c2') == '1.0~rc2'
     assert normalize_package_version('1.0rc2') == '1.0~rc2'
예제 #7
0
    def debian_dependencies(self):
        """
        Find Debian dependencies of Python package.

        Converts `Python version specifiers`_ to `Debian package
        relationships`_.

        :returns: A list with Debian package relationships (strings) in the
                  format of the ``Depends:`` line of a Debian package
                  ``control`` file. Based on :py:data:`python_requirements`.

        .. _Python version specifiers: http://www.python.org/dev/peps/pep-0440/#version-specifiers
        .. _Debian package relationships: https://www.debian.org/doc/debian-policy/ch-relationships.html
        """
        dependencies = set()
        for requirement in self.python_requirements:
            debian_package_name = self.converter.transform_name(requirement.project_name, *requirement.extras)
            if requirement.specs:
                for constraint, version in requirement.specs:
                    version = normalize_package_version(version)
                    if version == 'dev':
                        # Requirements like 'pytz > dev' (celery==3.1.16) don't
                        # seem to really mean anything to pip (based on my
                        # reading of the 1.4.x source code) but Debian will
                        # definitely complain because version strings should
                        # start with a digit. In this case we'll just fall
                        # back to a dependency without a version specification
                        # so we don't drop the dependency.
                        dependencies.add(debian_package_name)
                    elif constraint == '==':
                        dependencies.add('%s (= %s)' % (debian_package_name, version))
                    elif constraint == '!=':
                        values = (debian_package_name, version, debian_package_name, version)
                        dependencies.add('%s (<< %s) | %s (>> %s)' % values)
                    elif constraint == '<':
                        dependencies.add('%s (<< %s)' % (debian_package_name, version))
                    elif constraint == '>':
                        dependencies.add('%s (>> %s)' % (debian_package_name, version))
                    elif constraint in ('<=', '>='):
                        dependencies.add('%s (%s %s)' % (debian_package_name, constraint, version))
                    else:
                        msg = "Conversion specifier not supported! (%r used by Python package %s)"
                        raise Exception(msg % (constraint, self.python_name))
            else:
                dependencies.add(debian_package_name)
        dependencies = sorted(dependencies)
        logger.debug("Debian dependencies of %s: %r", self, dependencies)
        return dependencies
예제 #8
0
    def transform_version(self, package_to_convert, python_requirement_name, python_requirement_version):
        """
        Transform a Python requirement version to a Debian version number.

        :param package_to_convert: The :class:`.PackageToConvert` whose
                                   requirement is being transformed.
        :param python_requirement_name: The name of a Python package
                                        as found on PyPI (a string).
        :param python_requirement_version: The required version of the
                                           Python package (a string).
        :returns: The transformed version (a string).

        This method is a wrapper for :func:`.normalize_package_version()` that
        takes care of one additional quirk to ensure compatibility with
        :pypi:`pip`. Explaining this quirk requires a bit of context:

        - When package A requires package B (via ``install_requires``) and
          package A absolutely pins the required version of package B using one
          or more trailing zeros (e.g. ``B==1.0.0``) but the actual version
          number of package B (embedded in the metadata of package B) contains
          less trailing zeros (e.g. ``1.0``) then :pypi:`pip` will not complain
          but silently fetch version ``1.0`` of package B to satisfy the
          requirement.

        - However this doesn't change the absolutely pinned version in the
          ``install_requires`` metadata of package A.

        - When py2deb converts the resulting requirement set, the dependency of
          package A is converted as ``B (= 1.0.0)``. The resulting packages
          will not be installable because :man:`apt` considers ``1.0`` to be
          different from ``1.0.0``.

        This method analyzes the requirement set to identify occurrences of
        this quirk and strip trailing zeros in ``install_requires`` metadata
        that would otherwise result in converted packages that cannot be
        installed.
        """
        matching_packages = [
            pkg for pkg in self.packages_to_convert
            if package_names_match(pkg.python_name, python_requirement_name)
        ]
        if len(matching_packages) > 1:
            # My assumption while writing this code is that this should never
            # happen. This check is to make sure that if it does happen it will
            # be noticed because the last thing I want is for this `hack' to
            # result in packages that are silently wrongly converted.
            normalized_name = normalize_package_name(python_requirement_name)
            num_matches = len(matching_packages)
            raise Exception(compact("""
                Expected requirement set to contain exactly one Python package
                whose name can be normalized to {name} but encountered {count}
                packages instead! (matching packages: {matches})
            """, name=normalized_name, count=num_matches, matches=matching_packages))
        elif matching_packages:
            # Check whether the version number included in the requirement set
            # matches the version number in a package's requirements.
            requirement_to_convert = matching_packages[0]
            if python_requirement_version != requirement_to_convert.python_version:
                logger.debug("Checking whether to strip trailing zeros from required version ..")
                # Check whether the version numbers share the same prefix.
                required_version = tokenize_version(python_requirement_version)
                included_version = tokenize_version(requirement_to_convert.python_version)
                common_length = min(len(required_version), len(included_version))
                required_prefix = required_version[:common_length]
                included_prefix = included_version[:common_length]
                prefixes_match = (required_prefix == included_prefix)
                logger.debug("Prefix of required version: %s", required_prefix)
                logger.debug("Prefix of included version: %s", included_prefix)
                logger.debug("Prefixes match? %s", prefixes_match)
                # Check if 1) only the required version has a suffix and 2) this
                # suffix consists only of trailing zeros.
                required_suffix = required_version[common_length:]
                included_suffix = included_version[common_length:]
                logger.debug("Suffix of required version: %s", required_suffix)
                logger.debug("Suffix of included version: %s", included_suffix)
                if prefixes_match and required_suffix and not included_suffix:
                    # Check whether the suffix of the required version contains
                    # only zeros, i.e. pip considers the version numbers the same
                    # although apt would not agree.
                    if all(re.match('^0+$', t) for t in required_suffix if t.isdigit()):
                        modified_version = ''.join(required_prefix)
                        logger.warning("Stripping superfluous trailing zeros from required"
                                       " version of %s required by %s! (%s -> %s)",
                                       python_requirement_name, package_to_convert.python_name,
                                       python_requirement_version, modified_version)
                        python_requirement_version = modified_version
        return normalize_package_version(python_requirement_version, prerelease_workaround=self.prerelease_workaround)
예제 #9
0
 def test_version_reformatting(self):
     """
     Test reformatting of Python version strings.
     """
     assert normalize_package_version('1.5_42') == '1.5-42'
     assert normalize_package_version('1.5-whatever') == '1.5-whatever-1'
예제 #10
0
    def transform_version(self, package_to_convert, python_requirement_name, python_requirement_version):
        """
        Transform a Python requirement version to a Debian version number.

        :param package_to_convert: The :class:`.PackageToConvert` whose
                                   requirement is being transformed.
        :param python_requirement_name: The name of a Python package
                                        as found on PyPI (a string).
        :param python_requirement_version: The required version of the
                                           Python package (a string).
        :returns: The transformed version (a string).

        This method is a wrapper for :func:`.normalize_package_version()` that
        takes care of one additional quirk to ensure compatibility with pip.
        Explaining this quirk requires a bit of context:

        - When package A requires package B (via ``install_requires``) and
          package A absolutely pins the required version of package B using one
          or more trailing zeros (e.g. ``B==1.0.0``) but the actual version
          number of package B (embedded in the metadata of package B) contains
          less trailing zeros (e.g. ``1.0``) then pip will not complain but
          silently fetch version ``1.0`` of package B to satisfy the
          requirement.

        - However this doesn't change the absolutely pinned version in the
          ``install_requires`` metadata of package A.

        - When py2deb converts the resulting requirement set, the dependency of
          package A is converted as ``B (= 1.0.0)``. The resulting packages
          will not be installable because ``apt`` considers ``1.0`` to be
          different from ``1.0.0``.

        This method analyzes the requirement set to identify occurrences of
        this quirk and strip trailing zeros in ``install_requires`` metadata
        that would otherwise result in converted packages that cannot be
        installed.
        """
        matching_packages = [p for p in self.packages_to_convert
                             if package_names_match(p.python_name, python_requirement_name)]
        if len(matching_packages) != 1:
            # My assumption while writing this code is that this should never
            # happen. This check is to make sure that if it does happen it will
            # be noticed because the last thing I want is for this `hack' to
            # result in packages that are silently wrongly converted.
            normalized_name = normalize_package_name(python_requirement_name)
            num_matches = len(matching_packages)
            raise Exception(compact("""
                Expected requirement set to contain exactly one Python package
                whose name can be normalized to {name} but encountered {count}
                packages instead! (matching packages: {matches})
            """, name=normalized_name, count=num_matches, matches=matching_packages))
        # Check whether the version number included in the requirement set
        # matches the version number in a package's requirements.
        requirement_to_convert = matching_packages[0]
        if python_requirement_version != requirement_to_convert.python_version:
            logger.debug("Checking whether to strip trailing zeros from required version ..")
            # Check whether the version numbers share the same prefix.
            required_version = tokenize_version(python_requirement_version)
            included_version = tokenize_version(requirement_to_convert.python_version)
            common_length = min(len(required_version), len(included_version))
            required_prefix = required_version[:common_length]
            included_prefix = included_version[:common_length]
            prefixes_match = (required_prefix == included_prefix)
            logger.debug("Prefix of required version: %s", required_prefix)
            logger.debug("Prefix of included version: %s", included_prefix)
            logger.debug("Prefixes match? %s", prefixes_match)
            # Check if 1) only the required version has a suffix and 2) this
            # suffix consists only of trailing zeros.
            required_suffix = required_version[common_length:]
            included_suffix = included_version[common_length:]
            logger.debug("Suffix of required version: %s", required_suffix)
            logger.debug("Suffix of included version: %s", included_suffix)
            if prefixes_match and required_suffix and not included_suffix:
                # Check whether the suffix of the required version contains
                # only zeros, i.e. pip considers the version numbers the same
                # although apt would not agree.
                if all(re.match('^0+$', t) for t in required_suffix if t.isdigit()):
                    modified_version = ''.join(required_prefix)
                    logger.warning("Stripping superfluous trailing zeros from required"
                                   " version of %s required by %s! (%s -> %s)",
                                   python_requirement_name, package_to_convert.python_name,
                                   python_requirement_version, modified_version)
                    python_requirement_version = modified_version
        return normalize_package_version(python_requirement_version)
예제 #11
0
파일: tests.py 프로젝트: chuckyblack/py2deb
 def test_version_reformatting(self):
     """
     Test reformatting of Python version strings.
     """
     assert normalize_package_version('1.5_42') == '1.5-42'
     assert normalize_package_version('1.5-whatever') == '1.5-whatever-1'