Hi,

I've uploaded a NMU 5.0.37.3 in DELAYED/1-day fixing several issues
concerning dh_python:

   * Update of dh_python
     - when buidling for a non-standard Python version, generate more
       reasonable Depends like "python (>= X.Y) | pythonX.Y"
       Closes: #375576
     - fix handling of private extensions. Closes: #375948
     - fix parsing of XS-Python-Version, it didn't work if only fixed versions
       were listed in XS-Python-Version.
     - fix use of unitialized value. Closes: #374776
     - fix typos in POD documentation. Closes: #375936

Please find attached the cumulative patch of the last 3 uploads (5.0.37 -> 
5.0.37.3).

You can also find a copy of the package here:
http://people.debian.org/~hertzog/python/
http://people.debian.org/~hertzog/python/debhelper_5.0.37.3_all.deb
http://people.debian.org/~hertzog/python/debhelper_5.0.37.3.dsc

Dear debian-python readers, please test this updated dh_python
and report any problem to me.

Cheers,
-- 
Raphaël Hertzog

Premier livre français sur Debian GNU/Linux :
http://www.ouaza.com/livre/admin-debian/
diff -Nru debhelper-5.0.37/debian/changelog debhelper-5.0.37.3/debian/changelog
--- debhelper-5.0.37/debian/changelog   2006-06-12 03:33:28.000000000 +0200
+++ debhelper-5.0.37.3/debian/changelog 2006-07-10 13:27:50.000000000 +0200
@@ -1,3 +1,52 @@
+debhelper (5.0.37.3) unstable; urgency=low
+
+  * Non-maintainer upload.
+  * Update of dh_python
+    - when buidling for a non-standard Python version, generate more
+      reasonable Depends like "python (>= X.Y) | pythonX.Y"
+      Closes: #375576
+    - fix handling of private extensions. Closes: #375948
+    - fix parsing of XS-Python-Version, it didn't work if only fixed versions
+      were listed in XS-Python-Version.
+    - fix use of unitialized value. Closes: #374776
+    - fix typos in POD documentation. Closes: #375936
+
+ -- Raphael Hertzog <[EMAIL PROTECTED]>  Mon, 10 Jul 2006 13:20:06 +0200
+
+debhelper (5.0.37.2) unstable; urgency=low
+
+  * Non-maintainer upload.
+  * Update of dh_python
+    - vastly refactored, easier to understand, and the difference
+      between old policy and new policy is easier to grasp
+    - it supports an -X option which can be used to not scan some files
+    - uses debian/pyversions as reference source of information for
+      dependencies but also parse the XS-Python-Version header as fallback.
+    - ${python:Versions}'s default value is XS-Python-Version's value
+      instead of "all" when the package doesn't depend on a
+      specific python version. Closes: #373853
+    - always generate ${python:Provides} and leave the responsibility to the
+      maintainer to not use ${python:Provides} if he doesn't want the
+      provides.
+    - uses debian/pycompat or DH_PYCOMPAT as reference field to run in new
+      policy mode. The presence of XS-Python-Version will also trigger the
+      new policy mode (this is for short-term compatibility, it may be removed 
in
+      the not too-distant future).
+      DH_PYCOMPAT=1 is the default mode and is compatible to the old policy.
+      DH_PYCOMPAT=2 is the new mode and is compatible with the new policy.
+  * Use "grep ^Version:" instead of "grep Version:" on the output of
+    dpkg-parsechangelog since the above changelog entry matched "Version:" and
+    thus made the build fail.
+
+ -- Raphael Hertzog <[EMAIL PROTECTED]>  Sat, 17 Jun 2006 20:44:29 +0200
+
+debhelper (5.0.37.1) unstable; urgency=low
+
+  * Non-maintainer upload.
+  * Integrate the new dh_python implementing the new Python policy. Closes: 
#370833
+
+ -- Raphael Hertzog <[EMAIL PROTECTED]>  Mon, 12 Jun 2006 08:58:22 +0200
+
 debhelper (5.0.37) unstable; urgency=low
 
   * dh_installmodules: depmod -a is no longer run during boot, so if a module
diff -Nru debhelper-5.0.37/debian/rules debhelper-5.0.37.3/debian/rules
--- debhelper-5.0.37/debian/rules       2006-06-03 23:06:41.000000000 +0200
+++ debhelper-5.0.37.3/debian/rules     2006-06-19 18:31:06.000000000 +0200
@@ -26,7 +26,7 @@
                }'
 
 # Figure out the `current debhelper version.
-VERSION=$(shell expr "`dpkg-parsechangelog |grep Version:`" : '.*Version: 
\(.*\)')
+VERSION=$(shell expr "`dpkg-parsechangelog |grep ^Version:`" : '.*Version: 
\(.*\)')
 
 PERLLIBDIR=$(shell perl -MConfig -e 'print $$Config{vendorlib}')
 
diff -Nru debhelper-5.0.37/dh_python debhelper-5.0.37.3/dh_python
--- debhelper-5.0.37/dh_python  2006-04-24 22:09:12.000000000 +0200
+++ debhelper-5.0.37.3/dh_python        2006-07-10 14:14:53.000000000 +0200
@@ -12,7 +12,7 @@
 
 =head1 SYNOPSIS
 
-B<dh_python> [S<I<debhelper options>>] [B<-n>] [B<-V> I<version>] [S<I<module 
dirs ...>>]
+B<dh_python> [S<I<debhelper options>>] [B<-n>] [B<-X>I<item>] [B<-V> 
I<version>] [S<I<module dirs ...>>]
 
 =head1 DESCRIPTION
 
@@ -21,7 +21,40 @@
 will also add a postinst and a prerm script if required.
 
 The program will look at python scripts and modules in your package, and
-will use this information to generate a dependency on python, with the
+will use this information to generate adequate dependencies. Additionally,
+it will also use debian/pyversions as a list of python versions that the
+package is known to support. That file contains a single line with a
+comma delimited list of python versions in the format "X.Y". You can
+specify ranges of python versions with "W.X-Y.Z". You can omit one side of
+the range like in "X.Y-" or "-X.Y" which will respectively indicate all
+python versions greater or equal to "X.Y" or smaller or equal to "X.Y".
+
+In order to support two different Python policies, dh_python looks for
+an environment variable DH_PYCOMPAT or the file debian/pycompat. The
+content of those is a single number representing the compatibility mode
+that will be used by dh_python. This number can be 1 or 2. 
+
+In both modes, the ${python:Depends} variable contains appropriate
+dependencies on python or pythonX.Y.
+
+=head2 Compatibility level 2 (new policy)
+
+dh_python will only generate substitution variables, it won't take care of
+the byte-compilation of modules any more.
+
+The ${python:Provides} variable will contain versioned provides of the
+package (if the package's name starts with "python-"). A python-foo
+package could provide "python2.3-foo" and "python2.4-foo" at the same
+time. Python extensions have to provide those whereas it's only option
+for pure python modules.
+
+The ${python:Versions} variable can be used to provide an optional
+XB-Python-Version field listing the python versions supported by the
+package.
+
+=head2 Compatibility level 1 (old policy)
+
+It looks at scripts and modules in your package and adds a dependency on 
python, with the
 current major version, or on pythonX.Y if your scripts or modules need a
 specific python version. The dependency will be substituted into your
 package's control file wherever you place the token "${python:Depends}".
@@ -32,6 +65,8 @@
 
 If you use this program, your package should build-depend on python.
 
+Note: in the old policy, /usr/lib/site-python is also scanned for modules.
+
 =head1 OPTIONS
 
 =over 4
@@ -40,11 +75,10 @@
 
 If your package installs python modules in non-standard directories, you
 can make dh_python check those directories by passing their names on the
-command line. By default, it will check /usr/lib/site-python,
-/usr/lib/$PACKAGE, /usr/share/$PACKAGE, /usr/lib/games/$PACKAGE,
+command line. By default, it will check /usr/lib/$PACKAGE, 
/usr/share/$PACKAGE, /usr/lib/games/$PACKAGE,
 /usr/share/games/$PACKAGE and /usr/lib/python?.?/site-packages.
 
-Note: only /usr/lib/site-python, /usr/lib/python?.?/site-packages and the
+Note: only /usr/lib/python?.?/site-packages and the
 extra names on the command line are searched for binary (.so) modules.
 
 =item B<-V> I<version>
@@ -53,17 +87,26 @@
 pythonX.Y version, you can use this option to specify the desired version,
 such as 2.3. Do not use if you ship modules in /usr/lib/site-python.
 
+With the new policy, this option is mostly deprecated. Use the
+XS-Python-Field to indicate that you're using a specific python version.
+
 =item B<-n>, B<--noscripts>
 
 Do not modify postinst/postrm scripts.
 
+=item B<-X>I<item>, B<--exclude=>I<item>
+
+Exclude files that contain "item" anywhere in their filename from being
+taken into account to generate the python dependency. You may use this
+option multiple times to build up a list of things to exclude.
+
 =back
 
 =head1 CONFORMS TO
 
-Debian policy, version 3.5.7
+Debian policy, version 3.7.2
 
-Python policy, version 0.3.7
+Python policy, version 0.4.1 (2006-06-20)
 
 =cut
 
@@ -85,10 +128,10 @@
 }
 
 # The next python version
-my $python_nextversion = $python_version + 0.1;
+my $python_nextversion = next_minor_version($python_version);
 my $python_nextmajor = $python_major + 1;
 
-my @python_allversions = ('1.5','2.1','2.2','2.3','2.4');
+my @python_allversions = ('1.5','2.1','2.2','2.3','2.4','2.5');
 foreach (@python_allversions) {
        s/^/python/;
 }
@@ -109,39 +152,96 @@
        s#^/##;
 }
 
+# Check the compatibilty mode to use
+my $compat_mode = "";
+my $pyversions_field = "";
+my $python_header = "";
+{
+       local $/ = ""; # Grab until empty line
+       open(CONTROL, "debian/control"); # Can't fail, dh_testdir has already 
been called
+       my $source = <CONTROL>;
+       close(CONTROL);
+       if ($source =~ m/^XS-Python-Version: \s*(.*)$/m) {
+               $python_header = $1;
+               chomp($python_header);
+               $pyversions_field = convert_python_header($python_header);
+               $compat_mode = 2 unless $compat_mode; #
+       }
+}
+if (defined($ENV{DH_PYCOMPAT})) {
+       $compat_mode = $ENV{DH_PYCOMPAT};
+} elsif (-e "debian/pycompat") {
+       open (COMPAT_IN, "debian/pycompat") || error "debian/pycompat: $!";
+       my $l=<COMPAT_IN>;
+       chomp $l;
+       if ($l) {
+               $compat_mode = $l
+       } else {
+               warning("debian/pycompat is empty, assuming dh_python 
compatibility level 1");
+       }
+}
+$compat_mode = 1 unless $compat_mode; # Default to old policy
+error("Maximum dh_python compatibility mode supported is 2.") if $compat_mode 
> 2;
+
+# pyversions describes the list of python versions that this package can
+# work with
+if (-e "debian/pyversions") {
+       open(PYVERS, "debian/pyversions") || error("Can't open 
debian/pyversions: $!");
+       $pyversions_field = <PYVERS>;
+       chomp($pyversions_field);
+       close(PYVERS);
+}
+verbose_print("Pyversions field: $pyversions_field");
+
+# Extract a list of officially supported Python versions
+my %officially_supported;
+if (-e "/usr/share/python/debian_defaults") {
+       open(DEFAULTS, "/usr/share/python/debian_defaults") ||
+           error("Can't open /usr/share/python/debian_defaults: $!");
+       foreach (<DEFAULTS>) {
+               if (/^supported-versions\s*=\s*(.*)$/) {
+                       my $supported = $1;
+                       foreach my $version (split(/\s+/, $supported)) {
+                               if ($version =~ /python([\d\.]+)/) {
+                                   $officially_supported{$1} = 1;
+                               }
+                       }
+                       last;
+               }
+       }
+       close(DEFAULTS);
+}
+
 # dependency types
 use constant PROGRAM   => 1;
-use constant PY_MODULE => 2;
-use constant PY_MODULE_NONSTANDARD => 4;
-use constant SO_MODULE => 8;
-use constant SO_MODULE_NONSTANDARD => 16;
+use constant PY_PRIVATE_MODULE => 2;
+use constant PY_PUBLIC_MODULE => 4;
+use constant PY_OFFICIAL_MODULE => 8;
+use constant PY_UNKNOWN_MODULE => 16;
+use constant SO_PRIVATE_MODULE => 32;
+use constant SO_PUBLIC_MODULE => 64;
+use constant SO_OFFICIAL_MODULE => 128;
+use constant SO_UNKNOWN_MODULE => 256;
 
 foreach my $package (@{$dh{DOPACKAGES}}) {
        my $tmp = tmpdir($package);
 
-       delsubstvar($package, "python:Depends");
-
-       my @dirs = ("usr/lib/site-python", "usr/lib/$package", 
"usr/share/$package", "usr/lib/games/$package", "usr/share/games/$package", 
@ARGV );
-       my @dirs_so = ("usr/lib/site-python", @ARGV );
+       my @dirs = ("usr/lib/$package", "usr/share/$package", 
"usr/lib/games/$package", "usr/share/games/$package", @ARGV );
+       my @dirs_so = (@ARGV);
 
        my $dep_on_python = 0;
        my $strong_dep = 0;
-       my $look_for_pythonXY = 1;
 
-       # First, the case of python-foo and pythonX.Y-foo
-       if ($package =~ /^python-/) {
-               $dep_on_python = 1;
-               $strong_dep = 1;
-               my $pack = $package;
-               $pack =~ s/^python/python$python_version/;
-               if (grep { "$_" eq "$pack" } getpackages()) {
-                       addsubstvar($package, "python:Depends", $pack);
-               }
-       }
-       if ($package !~ /^python[0-9].[0-9]-/) {
-               push @dirs, "usr/lib/$usepython/site-packages";
-               push @dirs_so, "usr/lib/$usepython/site-packages";
-               $look_for_pythonXY = 0;
+       # Fail early if the package use usr/lib/site-python
+       if (-d "$tmp/usr/lib/site-python") {
+               if ($compat_mode >= 2) {
+                       # Error only on new policy
+                       error("The package $package puts files in 
/usr/lib/site-python: forbidden by policy");
+               } else {
+                       # Old policy allowed that directory, so scan it
+                       push @dirs, "usr/lib/site-python";
+                       push @dirs_so, "usr/lib/site-python";
+               }
        }
 
        @dirs = grep -d, map "$tmp/$_", @dirs;
@@ -153,114 +253,514 @@
                $verdeps{$_} = 0;
        }
 
-       # Find scripts
+       # Global scan
+       my $private_pydirs_regex = join('|', map { "\Q$_\E" } @dirs);
+       my $private_sodirs_regex = join('|', map { "\Q$_\E" } @dirs_so);
+       my %private_dirs_list;
+       my %pyversions_found;
        find sub {
-               return unless -f and (-x or /\.py$/);
-               local *F;
-               return unless open F, $_;
-               if (read F, local $_, 32 and 
m%^#!\s*/usr/bin/(env\s+)?(python(\d+\.\d+)?)\s%) {
-                       if ( "python" eq $2 ) {
-                               $deps |= PROGRAM;
-                       } elsif(defined $verdeps{$2}) {
-                               $verdeps{$2} |= PROGRAM;
+               return unless -f;
+               # See if we were asked to exclude this file.
+               # Note that we have to test on the full filename, including 
directory.
+               my $fn="$File::Find::dir/$_";
+               if (excludefile($fn)) {
+                       verbose_print("Ignoring $fn");
+                       return;
+               }
+               # Find scripts
+               if (-x or /\.py$/) {
+                       local *F;
+                       return unless open F, $_;
+                       if (read F, local $_, 32 and 
m%^#!\s*/usr/bin/(env\s+)?(python(\d+\.\d+)?)\s%) {
+                               verbose_print("Found program using $2: $fn");
+                               if ( "python" eq $2 ) {
+                                       $deps |= PROGRAM;
+                               } elsif(defined $verdeps{$2}) {
+                                       $verdeps{$2} |= PROGRAM;
+                               }
                        }
+                       close F;
                }
-               close F;
-       }, $tmp;
+               # Continue only with .py or .so
+               return unless (/\.py$/ or /\.so$/);
 
-       # Look for python modules
-       my $dirlist="";
-       if (@dirs) {
-               foreach my $curdir (@dirs) {
-                       my $has_module = 0;
-                       $curdir =~ s%^$tmp/%%;
-                       find sub {
-                               return unless -f;
-                               if (/\.py$/) {
-                                       $has_module = 1;
-                                       doit(("rm","-f",$_."c",$_."o"));
+               # Remove any byte-compiled file
+               doit(("rm","-f",$_."c",$_."o")) if /\.py$/;
+               
+               # Classify the file in the right category
+               if (/\.py$/ and $private_pydirs_regex and $fn =~ 
m/(?:$private_pydirs_regex)/) {
+                       # Private python module
+                       verbose_print("Found private module: $fn");
+                       my $dir;
+                       foreach $dir (@dirs) {
+                               if ($fn =~ m/\Q$dir\E/) {
+                                       $dir =~ s/^$tmp//;
+                                       verbose_print("Memorizing dir to 
byte-compile: $dir");
+                                       $private_dirs_list{"$dir"} = 1;
+                               }
+                       }
+                       if ($dh{V_FLAG_SET}) {
+                               $verdeps{$usepython} |= PY_PRIVATE_MODULE;
+                       } else {
+                               $deps |= PY_PRIVATE_MODULE;
+                       }
+               } elsif (/\.so$/ and $private_sodirs_regex and $fn =~ 
m/(?:$private_sodirs_regex)/) {
+                       # Private python extension
+                       verbose_print("Found private extension: $fn");
+                       if ($dh{V_FLAG_SET}) {
+                               $verdeps{$usepython} |= SO_PRIVATE_MODULE;
+                       } else {
+                               $deps |= SO_PRIVATE_MODULE;
+                       }
+               } elsif ($fn =~ m|$tmp/usr/lib/python([\d\.]+)/site-packages/|) 
{
+                       $pyversions_found{$1} = 1;
+                       my $v = $1;
+                       if (/\.py$/) {
+                               verbose_print("Found public module: $fn");
+                               $verdeps{"python$v"} |= PY_PUBLIC_MODULE;
+                       } else {
+                               verbose_print("Found public extension: $fn");
+                               $verdeps{"python$v"} |= SO_PUBLIC_MODULE;
+                       }
+               } elsif ($fn =~ m|$tmp/usr/lib/python([\d\.]+)/|) {
+                       $pyversions_found{$1} = 1;
+                       my $v = $1;
+                       if (/\.py$/) {
+                               verbose_print("Found official module: $fn");
+                               $verdeps{"python$v"} |= PY_OFFICIAL_MODULE;
+                       } else {
+                               verbose_print("Found official extension: $fn");
+                               $verdeps{"python$v"} |= SO_OFFICIAL_MODULE;
+                       }
+               } elsif ($fn =~ 
m|$tmp/usr/lib/python-support/[^/]+/python([\d\.]+)/|) {
+                       $pyversions_found{$1} = 1;
+                       my $v = $1;
+                       if (/\.py$/) {
+                               verbose_print("Found public module: $fn");
+                               $verdeps{"python$v"} |= PY_PUBLIC_MODULE;
+                       } else {
+                               verbose_print("Found public extension: $fn");
+                               $verdeps{"python$v"} |= SO_PUBLIC_MODULE;
+                       }
+               } elsif ($fn =~ 
m{$tmp(?:/usr/share/pycentral/|/usr/share/python-support/)}) {
+                       if (/\.py$/) {
+                               verbose_print("Found public module: $fn");
+                               $deps |= PY_PUBLIC_MODULE;
+                       } # No extensions here
+               } elsif ($fn =~ m|$tmp/usr/lib/python([\d\.]+)/site-packages/|) 
{
+                       
+               } elsif ($fn =~ m|$tmp/usr/share/doc/|) {
+                       # Ignore .py files in doc directory
+               } else {
+                       # Unknown pyfiles
+                       if (/\.py$/) {
+                               verbose_print("Found unclassified module: $fn");
+                               if ($dh{V_FLAG_SET}) {
+                                       $verdeps{$usepython} |= 
PY_UNKNOWN_MODULE;
+                               } else {
+                                       $deps |= PY_UNKNOWN_MODULE;
                                }
-                       }, "$tmp/$curdir" ;
-                       if ($has_module) {
+                       } else {
+                               verbose_print("Found unclassified extension: 
$fn");
                                if ($dh{V_FLAG_SET}) {
-                                       $verdeps{$usepython} |= 
PY_MODULE_NONSTANDARD;
+                                       $verdeps{$usepython} |= 
SO_UNKNOWN_MODULE;
                                } else {
-                                       $deps |= PY_MODULE;
+                                       $deps |= SO_UNKNOWN_MODULE;
                                }
-                               $dirlist="$dirlist /$curdir";
                        }
                }
+       }, $tmp;
+
+       # Common dependency handling
+       foreach my $pyver (keys %verdeps) {
+               # Always add pythonX.Y dependency if a script uses that 
interpreter
+               if ($verdeps{$pyver} & PROGRAM) {
+                       addsubstvar($package, "python:Depends", $pyver);
+               }
+               # Always add pythonX.Y dependency if some private modules are
+               # byte-compiled with it (or if extensions are
+               # byte-compiled with it)
+               if ($verdeps{$pyver} & (PY_PRIVATE_MODULE|SO_PRIVATE_MODULE)) {
+                       addsubstvar($package, "python:Depends", $pyver);
+               }
        }
-       if (@dirs_so) {
-               foreach my $curdir (@dirs_so) {
-                       my $has_module = 0;
-                       $curdir =~ s%^$tmp/%%;
-                       find sub {
-                               return unless -f;
-                               $has_module = 1 if /\.so$/;
-                       }, "$tmp/$curdir" ;
-                       if ($has_module) {
-                               if ($dh{V_FLAG_SET}) {
-                                       $verdeps{$usepython} |= 
SO_MODULE_NONSTANDARD;
+
+       if ($compat_mode == 2) {
+               #
+               # NEW POLICY
+               #
+               # Generate the depends to accept all python
+               # versions that this package effectively provides
+               my ($min_version, $max_version, @versions) = 
analyze_pyversions($pyversions_field);
+               my $stop_version = "";
+               $stop_version = next_minor_version($max_version) if 
$max_version;
+               my $versions_field = "";
+               verbose_print("Pyversions analysis gives: min=$min_version, 
max=$max_version (@versions)");
+               
+               # Reset again, analysis using new policy follows
+               $dep_on_python = 0;
+
+               # Private extensions, must be rebuilt for each python version
+               if ($deps & SO_PRIVATE_MODULE) {
+                       $dep_on_python++;
+                       # Packages using a private extension can only
+                       # support one version
+                       # Unless min/max are the same we put $python_version
+                       if ($min_version and ($min_version eq $max_version)) {
+                               $versions_field = $min_version;
+                       } else {
+                               if (compare_version($min_version, 
$python_version) > 0) {
+                                       # The current version is unsupported, 
use the min version instead
+                                       $versions_field = $min_version;
+                               } else {
+                                       # Use the current version by default
+                                       $versions_field = $python_version;
+                                       $min_version = $python_version;
                                }
-                               else {
-                                       $deps |= SO_MODULE;
+                               $stop_version = 
next_minor_version($versions_field);
+                       }
+               } 
+
+               # Private modules 
+               if ($deps & PY_PRIVATE_MODULE) {
+                       # Package with private modules can only support one 
version at a time
+                       # (even if the modules can be automatically 
byte-compiled for any new
+                       # python version).
+                       unless ($versions_field) {
+                               # Unless min/max are the same we put "current"
+                               if ($min_version and ($min_version eq 
$max_version)) {
+                                       $versions_field = $min_version;
+                               } else {
+                                       $versions_field = "current";
                                }
                        }
                }
-       }
-
-       # Dependencies on current python
-       $dep_on_python = 1 if $deps;
-       $strong_dep = 1 if($deps & (PY_MODULE|SO_MODULE));
 
-       if ($dep_on_python) {
-               addsubstvar($package, "python:Depends", $python, ">= 
$python_version");
-               if ($strong_dep) {
-                       addsubstvar($package, "python:Depends", $python, "<< 
$python_nextversion");
+               # Python scripts & public modules
+               if ($deps & (PROGRAM|PY_PUBLIC_MODULE)) {
+                       $dep_on_python++;
+               }
+               
+               # Public extensions
+               if (scalar keys %pyversions_found) {
+                       # Extensions will always trigger this (as well as public
+                       # modules not handled by python-support/python-central)
+                       $dep_on_python++;
+                       if (scalar grep { $python_version eq $_ } keys 
%pyversions_found) {
+                               # Current versions is supported by the package
+                               # It's safe to depends on the corresponding
+                               # interval of python versions
+                               $min_version = min(keys %pyversions_found);
+                               unless ($stop_version) {
+                                   $max_version = max(keys %pyversions_found);
+                                   $stop_version = 
next_minor_version($max_version);
+                               }
+                       }
+                       # Generate the Python-Version field
+                       foreach (keys %pyversions_found) {
+                               addsubstvar($package, "python:Versions", $_);
+                       }
+                       # Generate provides for the python2.X-foo packages that 
we emulate
+                       if ($package =~ /^python-/) {
+                               foreach (keys %pyversions_found) {
+                                       my $virtual = $package;
+                                       $virtual =~ s/^python-/$python$_-/;
+                                       addsubstvar($package, 
"python:Provides", $virtual);
+                               }
+                       }
                } else {
-                       addsubstvar($package, "python:Depends", $python, "<< 
$python_nextmajor");
+                       # Still try to describe the versions that the package 
support
+                       $versions_field = $python_header unless $versions_field;
+                       $versions_field = "all" unless $versions_field;
+                       addsubstvar($package, "python:Versions", 
$versions_field);
+
+                       # Generate provides for all python versions supported
+                       if ($package =~ /^python-/) {
+                               foreach (grep { $officially_supported{$_} } 
@versions) {
+                                       my $virtual = $package;
+                                       $virtual =~ s/^python-/$python$_-/;
+                                       addsubstvar($package, 
"python:Provides", $virtual);
+                               }
+                       }
+               }
+               
+               if ($dep_on_python) {
+                       # At least a script has been detected
+                       if ($min_version) {
+                               if (compare_version($min_version, 
$python_version) <= 0 ) {
+                                       # Min-version is less or equal to 
current version
+                                       addsubstvar($package, "python:Depends", 
$python, ">= $min_version");
+                               } else {
+                                       # Min version is greater to current 
version
+                                       # Supposition: new stuff working only 
with the latest python, 
+                                       # depend on that specific python version
+                                       addsubstvar($package, "python:Depends", 
+                                                   "python (>= $min_version) | 
python$min_version");
+                               }
+                       }
+                       # If a stronger dependency is needed
+                       if ($stop_version) {
+                               if (compare_version($stop_version, 
$python_version) > 0 ) {
+                                       # Stop version is strictly bigger than 
current version
+                                       addsubstvar($package, "python:Depends", 
$python, "<< $stop_version");
+                               } else {
+                                       # Only works with old versions,
+                                       # package is mostly deprecated,
+                                       # no need for a python dependency
+                               }
+                       }
+                       # Let's depend on python anyway
+                       addsubstvar($package, "python:Depends", $python) unless 
($min_version or $stop_version);
                }
-       }
 
-       my $need_prerm = 0;
+       } elsif ($compat_mode == 1) {
+               #
+               # OLD POLICY
+               #
+
+               # First, the case of python-foo and pythonX.Y-foo
+               if ($package =~ /^python-/) {
+                       $dep_on_python = 1;
+                       $strong_dep = 1;
+                       # This adds a dependency to python<current>-foo in 
python-foo
+                       my $pack = $package;
+                       $pack =~ s/^python/python$python_version/;
+                       if (grep { "$_" eq "$pack" } getpackages()) {
+                               addsubstvar($package, "python:Depends", $pack);
+                       }
+               }
 
-       # Look for specific pythonX.Y modules
-       foreach my $pyver (@python_allversions) {
-               my $pydir="/usr/lib/$pyver/site-packages";
-               if ($look_for_pythonXY) {
-                       if (grep -d,"$tmp$pydir") {
-                               find sub {
-                                       return unless -f;
-                                       if (/\.py$/) {
-                                               $verdeps{$pyver} |= PY_MODULE;
-                                               doit(("rm","-f",$_."c",$_."o"));
-                                       }
-                                       $verdeps{$pyver} |= SO_MODULE if 
/\.so$/;
-                               }, "$tmp$pydir";
-                       }
-               }
-       
-               # Go for the dependencies
-               addsubstvar($package, "python:Depends", $pyver) if 
$verdeps{$pyver};
-
-               # And now, the postinst and prerm stuff
-               if ($pyver eq "$usepython") {
-                       if ($verdeps{$pyver} & PY_MODULE) {
-                               $pydir = $pydir.$dirlist;
+               # Dependencies on current python
+               $dep_on_python = 1 if ($deps &
+                   
(PROGRAM|PY_PUBLIC_MODULE|SO_PUBLIC_MODULE|PY_PRIVATE_MODULE|SO_PRIVATE_MODULE));
+               $strong_dep = 1 if ($deps & 
(PY_PUBLIC_MODULE|SO_PUBLIC_MODULE));
+
+               # Add python dependencies
+               if ($dep_on_python) {
+                       addsubstvar($package, "python:Depends", $python, ">= 
$python_version");
+                       if ($strong_dep) {
+                               addsubstvar($package, "python:Depends", 
$python, "<< $python_nextversion");
                        } else {
-                               $pydir = $dirlist;
+                               addsubstvar($package, "python:Depends", 
$python, "<< $python_nextmajor");
                        }
-                       $verdeps{$pyver} |= PY_MODULE if($deps & PY_MODULE);
                }
-               if ($verdeps{$pyver} & (PY_MODULE|PY_MODULE_NONSTANDARD) && ! 
$dh{NOSCRIPTS}) {
-                       
autoscript($package,"postinst","postinst-python","s%#PYVER#%$pyver%;s%#DIRLIST#%$pydir%");
-                       $need_prerm = 1;
+
+               # Add postinst/preinst
+               my $need_prerm = 0;
+               foreach my $pyver (@python_allversions) {
+                       my $pydir="/usr/lib/$pyver/site-packages";
+                       my @dirlist = (); # List of directories to byte-compile
+
+                       # If we have public modules, byte-compile 
/usr/lib/pythonX.Y/site-packages/
+                       if ($verdeps{$pyver} & PY_PUBLIC_MODULE) {
+                               push @dirlist, $pydir;
+                       }
+                       
+                       # Byte-compile private modules with current python
+                       # (or the one indicated by -V)
+                       if (($pyver eq "$usepython") and scalar(keys 
%private_dirs_list)) {
+                               push @dirlist, keys %private_dirs_list;
+                       }
+
+                       # If we have something to byte-compile, add the
+                       # corresponding postinst snippet
+                       if (scalar(@dirlist) && ! $dh{NOSCRIPTS}) {
+                               
autoscript($package,"postinst","postinst-python","s%#PYVER#%$pyver%;[EMAIL 
PROTECTED]");
+                               $need_prerm = 1;
+                       }
+               }
+               if ($need_prerm && ! $dh{NOSCRIPTS}) {
+                       
autoscript($package,"prerm","prerm-python","s%#PACKAGE#%$package%");
                }
        }
-       if ($need_prerm && ! $dh{NOSCRIPTS}) {
-               
autoscript($package,"prerm","prerm-python","s%#PACKAGE#%$package%");
+}
+
+sub next_minor_version {
+    my $version = shift;
+    # Handles 2.10 -> 2.11 gracefully
+    my @items = split(/\./, $version);
+    $items[1] += 1;
+    $version = join(".", @items);
+    return $version;
+}
+
+sub compare_version {
+    my ($a, $b) = @_;
+    my @A = split(/\./, $a);
+    my @B = split(/\./, $b);
+    my $diff = 0;
+    for (my $i = 0; $i <= $#A; $i++) {
+       $diff = $A[$i] - $B[$i];
+       return $diff if $diff; 
+    }
+    # They are the same
+    return 0;
+}
+
+sub max {
+    my $max = shift;
+    foreach (@_) {
+       $max = $_ if (compare_version($_, $max) > 0);
+    }
+    return $max;
+}
+
+sub min {
+    my $min = shift;
+    foreach (@_) {
+       $min = $_ if (compare_version($_, $min) < 0);
+    }
+    return $min;
+}
+
+# Extract min, max and list of versions from
+# a string like this "-1.2,1.4-1.6,2.0,2.3-"
+sub analyze_pyversions {
+    my $field = shift;
+    my ($min, $max, @versions);
+
+    my @all_versions = ("0.0");
+    foreach (@python_allversions) {
+       if (m/python([\d\.]+)/) {
+           push @all_versions, $1;
+       }
+    }
+    push @all_versions, "100.0";
+
+    foreach my $range (split /,/, $field) {
+       if ($range =~ /^([\d\.]+)?-([\d\.]+)?$/) {
+           $min = defined($1) && $1 ? $1 : "0.0";
+           $max = defined($2) && $2 ? $2 : "100.0";
+           push @versions, grep { 
+               compare_version($_, $min) >= 0 and 
+               compare_version($_, $max) <= 0 } @all_versions;
+       } else {
+           push @versions, $range if (grep { $range eq $_ } @all_versions);
+       }
+    }
+
+    $min = min(@versions);
+    $max = max(@versions);
+    $min = "" if (!defined($min) or $min eq "0.0");
+    $max = "" if (!defined($max) or $max eq "100.0");
+
+    @versions = grep { $_ ne "0.0" and $_ ne "100.0" } @versions;
+
+    return ($min, $max, @versions);
+}
+
+# Convert the Python-Version field in a standardized "pyversions" field
+sub convert_python_header {
+    my $header = shift;
+    my %pyversions_expected;
+    my %pyversions_additional;
+    my $use_additional = 0;
+    my @all_versions = ();
+    foreach (@python_allversions) {
+       if (m/python([\d\.]+)/) {
+           $pyversions_additional{$1} = 1;
+           push @all_versions, $1;
+       }
+    }
+    # Add two fakes minima, maxima to check if we have limits
+    $pyversions_additional{"0.0"} = 1;
+    $pyversions_additional{"100.0"} = 1;
+
+    # Compute the list of versions that are supported
+    foreach my $check (split(/,\s*/, $header)) {
+       if ($check =~ /^=?\s*([\d\.]+)/) {
+           # Add any fixed version
+           $pyversions_expected{$1} = 1 if (grep { $_ eq $1 } @all_versions);
+           next;
+       }
+       # Deactivate versions which do not match the other
+       # criteria
+       if ($check =~ /^<<\s*([\d\.]+)/) {
+           my $v = $1;
+           $use_additional = 1;
+           foreach (keys %pyversions_additional) {
+               $pyversions_additional{$_} = 0 unless (compare_version($_, $v) 
< 0);
+           }
+       } elsif ($check =~ /^<=\s*([\d\.]+)/) {
+           my $v = $1;
+           $use_additional = 1;
+           foreach (keys %pyversions_additional) {
+               $pyversions_additional{$_} = 0 unless (compare_version($_, $v) 
<= 0);
+           }
+       } elsif ($check =~ /^>=\s*([\d\.]+)/) {
+           my $v = $1;
+           $use_additional = 1;
+           foreach (keys %pyversions_additional) {
+               $pyversions_additional{$_} = 0 unless (compare_version($_, $v) 
>= 0);
+           }
+       }
+    }
+    if ($use_additional) {
+       foreach (grep { $pyversions_additional{$_} } keys 
%pyversions_additional) {
+           $pyversions_expected{$_} = 1;
+       }
+    }
+
+    my @supported = sort { compare_version($a, $b) } 
+                   grep { $pyversions_expected{$_} == 1 } keys 
%pyversions_expected;
+    
+    verbose_print("List of versions supported according to XS-Python-Version: 
@supported");
+
+    # Generate the corresponding information in standardized "pyversions" 
format
+    # XXX: I go to great length to generate the shortest pyversions
+    # syntax but it may not be necessary for the usage that we make of it.
+    my ($result, $index, $range_is_open, $last_real_version) = ("", 0, 0, "");
+    foreach (@supported) {
+       if ($_ eq "0.0") {
+           $result .= "-"; # No lower limit
+           $range_is_open = 1;
+       } elsif ($_ eq "100.0") {
+           $result .= "-" unless $range_is_open; # No upper limit
+           $range_is_open = 0; # Proper end
+       } else {
+           $last_real_version = $_;
+           if ($range_is_open) {
+               # Range is open
+               if ($index <= $#all_versions and $all_versions[$index] eq $_) {
+                   # This version still in the range
+               } else {
+                   # Previous version ended the range
+                   $result .= $all_versions[$index-1] . ",$_";
+                   $range_is_open = 0;
+               }
+               # Find the current version in all_versions
+               while ($index <= $#all_versions) {
+                   last if ($all_versions[$index] eq $_);
+                   $index++;
+               }
+           } else {
+               # There's no range yet
+               if ($result) {
+                   if ($index <= $#all_versions and $all_versions[$index] eq 
$_) {
+                       # This version still match, we can start a range
+                       $result .= "-";
+                       $range_is_open = 1;
+                   } else {
+                       # Next version doesn't match, no range but a list
+                       $result .= ",$_";
+                   }
+               } else {
+                   # Empty field, start with something!
+                   $result = "$_";
+               }
+               while ($index <= $#all_versions) {
+                   last if ($all_versions[$index] eq $_);
+                   $index++;
+               }
+           }
+           $index++;
        }
+    }
+    if ($range_is_open) {
+       # Close the range properly
+       $result .= $last_real_version;
+    }
+    return $result;
 }
 
 =head1 SEE ALSO
@@ -269,9 +769,10 @@
 
 This program is a part of debhelper.
 
-=head1 AUTHOR
+=head1 AUTHORS
 
 Josselin Mouette <[EMAIL PROTECTED]>
+Raphael Hertzog <[EMAIL PROTECTED]>
 
 most ideas stolen from Brendan O'Dea <[EMAIL PROTECTED]>
 

Reply via email to