From 2f51ae58f7e24a3bc73c24e97d9b37a2b2e8c476 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Piotr=20O=C5=BCarowski?= <piotr@debian.org>
Date: Sat, 30 Jun 2012 00:42:55 +0200
Subject: [PATCH] translate Python version numbers into Debian ones for those
 require.txt items that have a pydist file with (uscan like) rules or PEP386
 flag (Closes: #653740)

---
 debian/changelog                 |  3 ++
 debpython/pydist.py              | 72 +++++++++++++++++++++++++++-----
 tests/t1/Makefile                |  1 +
 tests/t1/debian/pydist-overrides |  2 +-
 tests/t1/debian/rules            |  2 +-
 5 files changed, 68 insertions(+), 12 deletions(-)

diff --git a/debian/changelog b/debian/changelog
index 3fa39dd..2d6a160 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -13,6 +13,9 @@ python-defaults (2.7.3-1) unstable; urgency=low
       (example: --shebang /usr/bin/python2.6)
     - no longer generates python2.X | python2.Y depenendies for public modules
       (Closes: 625740)
+    - translate Python version numbers into Debian ones for those
+      require.txt items that have a pydist file with (uscan like) rules
+      or PEP386 flag (Closes: #653740)
   * pyversions, dh_python2, pycompile: allow to override system's list of
     supported Python versions via DEBPYTHON_SUPPORTED and default Python
     version via DEBPYTHON_DEFAULT env. variables
diff --git a/debpython/pydist.py b/debpython/pydist.py
index 1549196..2db8372 100644
--- a/debpython/pydist.py
+++ b/debpython/pydist.py
@@ -24,6 +24,7 @@ import logging
 import os
 import re
 from os.path import exists, isdir, join
+from string import maketrans
 from subprocess import PIPE, Popen
 from debpython.version import vrepr, getver, get_requested_versions
 from debpython.tools import memoize
@@ -41,7 +42,7 @@ PYDIST_RE = re.compile(r"""
         ;\s*
         (?P<standard>PEP386)?                    # PEP-386 mode
         \s*
-        (?P<rules>s/.*)?                         # translator rules
+        (?P<rules>(?:s|tr|y).*)?                 # translator rules
     )?
     """, re.VERBOSE)
 REQUIRES_RE = re.compile(r'''
@@ -117,13 +118,13 @@ def guess_dependency(req, version=None):
     req = safe_name(name) + rest
 
     data = load()
-    req_dict = REQUIRES_RE.match(req)
-    if not req_dict:
+    req_d = REQUIRES_RE.match(req)
+    if not req_d:
         log.info('please ask dh_python2 author to fix REQUIRES_RE '
                  'or your upstream author to fix requires.txt')
         raise Exception('requirement is not valid: %s' % req)
-    req_dict = req_dict.groupdict()
-    name = req_dict['name']
+    req_d = req_d.groupdict()
+    name = req_d['name']
     details = data.get(name.lower())
     if details:
         for item in details:
@@ -134,12 +135,13 @@ def guess_dependency(req, version=None):
             if not item['dependency']:
                 return  # this requirement should be ignored
             if item['dependency'].endswith(')'):
-                # no need to translate versions if version is hardcoded in Debian
-                # dependency
-                return item['dependency']
-            if req_dict['version']:
-                # FIXME: translate it (rules, versions)
+                # no need to translate versions if version is hardcoded in
+                # Debian dependency
                 return item['dependency']
+            if req_d['version'] and (item['standard'] or item['rules']) and\
+               req_d['operator'] not in (None, '=='):
+                v = _translate(req_d['version'], item['rules'], item['standard'])
+                return "%s (%s %s)" % (item['dependency'], req_d['operator'], v)
             else:
                 return item['dependency']
 
@@ -231,3 +233,53 @@ def sensible_pname(egg_name):
 def ci_regexp(name):
     """Return case insensitive dpkg -S regexp."""
     return ''.join("[%s%s]" % (i.upper(), i) if i.isalpha() else i for i in name.lower())
+
+
+PRE_VER_RE = re.compile(r'[-.]?(alpha|beta|rc|dev|a|b|c)')
+GROUP_RE = re.compile(r'\$(\d+)')
+
+
+def _pl2py(pattern):
+    """Convert Perl RE patterns used in uscan to Python's
+
+    >>> print _pl2py('foo$3')
+    foo\g<3>
+    """
+    return GROUP_RE.sub(r'\\g<\1>', pattern)
+
+
+def _translate(version, rules, standard):
+    """Translate Python version into Debian one.
+
+    >>> _translate('1.C2betac', ['s/c//gi'], None)
+    '1.2beta'
+    >>> _translate('5-fooa1.2beta3-fooD',
+    ...     ['s/^/1:/', 's/-foo//g', 's:([A-Z]):+$1:'], 'PEP386')
+    '1:5~a1.2~beta3+D'
+    >>> _translate('x.y.x.z', ['tr/xy/ab/', 'y,z,Z,'], None)
+    'a.b.a.Z'
+    """
+    for rule in rules:
+        # uscan supports s, tr and y operations
+        if rule.startswith(('tr', 'y')):
+            # Note: no support for escaped separator in the pattern
+            pos = 1 if rule.startswith('y') else 2
+            tmp = rule[pos + 1:].split(rule[pos])
+            version = version.translate(maketrans(tmp[0], tmp[1]))
+        elif rule.startswith('s'):
+            # uscan supports: g, u and x flags
+            tmp = rule[2:].split(rule[1])
+            pattern = re.compile(tmp[0])
+            count = 1
+            if tmp[2:]:
+                flags = tmp[2]
+                if 'g' in flags:
+                    count = 0
+                if 'i' in flags:
+                    pattern = re.compile(tmp[0], re.I)
+            version = pattern.sub(_pl2py(tmp[1]), version, count)
+        else:
+            log.warn('unknown rule ignored: %s', rule)
+    if standard == 'PEP386':
+        version = PRE_VER_RE.sub('~\g<1>', version)
+    return version
diff --git a/tests/t1/Makefile b/tests/t1/Makefile
index b66038c..585f962 100644
--- a/tests/t1/Makefile
+++ b/tests/t1/Makefile
@@ -5,6 +5,7 @@ DPY=$(DEBPYTHON_DEFAULT)
 
 check:
 	grep -q "Depends: .*python-mako" debian/python-foo/DEBIAN/control
+	grep -q 'python-foo (>= 2:0.1~rc2)' debian/python-foo/DEBIAN/control
 	test -f debian/python-foo/usr/lib/python2.6/dist-packages/foo/__init__.py
 	test ! -f debian/python-foo/usr/lib/python2.6/dist-packages/foo/spam.py
 	grep -q "Depends: .*python (<<" debian/python-foo/DEBIAN/control
diff --git a/tests/t1/debian/pydist-overrides b/tests/t1/debian/pydist-overrides
index fd6c9d7..6957b0b 100644
--- a/tests/t1/debian/pydist-overrides
+++ b/tests/t1/debian/pydist-overrides
@@ -1,5 +1,5 @@
 Mako python-mako (>= 0.2)
 SQLAlchemy python-sqlalchemy (>= 0.6)
-Foo python-foo
+Foo python-foo; PEP386 s/^/2:/
 Bar python-bar
 Baz
diff --git a/tests/t1/debian/rules b/tests/t1/debian/rules
index 9372870..d0cc758 100755
--- a/tests/t1/debian/rules
+++ b/tests/t1/debian/rules
@@ -12,5 +12,5 @@ override_dh_pysupport:
 	DH_VERBOSE=1 ../../dh_python2\
 		--depends 'SQLAlchemy >= 0.6.1'\
 		--recommends Mako\
-		--suggests 'Foo >= 0.1'\
+		--suggests 'Foo >= 0.1rc2'\
 		--suggests 'bar >= 1.0'
-- 
GitLab