[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

New debhelper update



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 <hertzog@debian.org>  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 <hertzog@debian.org>  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 <hertzog@debian.org>  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%;s%#DIRLIST#%@dirlist%");
+				$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 <joss@debian.org>
+Raphael Hertzog <hertzog@debian.org>
 
 most ideas stolen from Brendan O'Dea <bod@debian.org>
 

Reply to: