diff --git a/debian/changelog b/debian/changelog
index 8436033bc7f8836b1989028ce8907b44405eb9bd..dfe2028cf407c0bcfbb0423d6bbd4c61855790d3 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -2,9 +2,11 @@ python-defaults (2.6.6-6) UNRELEASED; urgency=low
 
   * Make the error message about missing extension more clear
     (and more verbose in --verbose mode)
-  * dh_python2: install files listed in debian/package.pyinstall file
+  * dh_python2: install files listed in debian/pkg.pyinstall file
     as public modules for all requested Python versions (use dh_install's
-    package.install files for private modules)
+    package.install files for private modules); remove public modules listed
+    in debian/pkg.pyremove (glob.glob pattern and version range can be used in
+    both files)
   * pycompile: `pycompile $DESTDIR/usr/lib/python*` will recognize public
     site-packages directories and use the right interpreter instead of raising
     KeyError
diff --git a/debpython/tools.py b/debpython/tools.py
index efbcd4955697836dcf8a870ada12dc6c7422a815..0962ccfe9f7b4d0a63d3d4b5ec5f48084da9e4b7 100644
--- a/debpython/tools.py
+++ b/debpython/tools.py
@@ -22,11 +22,12 @@
 from __future__ import with_statement
 import codecs
 import logging
+import os
 import re
 from cPickle import dumps
 from glob import glob
-from os import link, makedirs, symlink
-from os.path import exists, join, split
+from os.path import exists, isdir, join, split
+from shutil import rmtree
 from debpython.version import RANGE_PATTERN, getver, get_requested_versions
 
 log = logging.getLogger(__name__)
@@ -39,6 +40,11 @@ INSTALL_RE = re.compile(r"""
     \s*  # optional version range:
     (?P<vrange>%s)?$
 """ % RANGE_PATTERN, re.VERBOSE)
+REMOVE_RE = re.compile(r"""
+    (?P<pattern>.+?)  # file pattern
+    \s*  # optional version range:
+    (?P<vrange>%s)?$
+""" % RANGE_PATTERN, re.VERBOSE)
 
 
 def sitedir(version, package=None, gdb=False):
@@ -80,7 +86,7 @@ def relpath(target, link):
 
 def relative_symlink(target, link):
     """Create relative symlink."""
-    return symlink(relpath(target, link), link)
+    return os.symlink(relpath(target, link), link)
 
 
 def shebang2pyver(fname):
@@ -131,7 +137,7 @@ class memoize(object):
 
 
 def pyinstall(package, vrange):
-    """Install local files listed in .pyinstall files as public modules."""
+    """Install local files listed in pkg.pyinstall files as public modules."""
     status = True
     srcfpath = "./debian/%s.pyinstall" % package
     if not exists(srcfpath):
@@ -151,6 +157,10 @@ def pyinstall(package, vrange):
         if details['module']:
             details['module'] = details['module'].replace('.', '/')
         myvers = versions & get_requested_versions(details['vrange'])
+        if not myvers:
+            log.debug('%s.pyinstall: no matching versions for line %s',
+                      package, line)
+            continue
         files = glob(details['pattern'])
         if not files:
             status = False
@@ -170,13 +180,65 @@ def pyinstall(package, vrange):
                 dstdir = split(dstfpath)[0]
                 if not exists(dstdir):
                     try:
-                        makedirs(dstdir)
+                        os.makedirs(dstdir)
                     except:
                         log.error('cannot create %s directory', dstdir)
                         return False
+                if exists(dstfpath):
+                    try:
+                        os.remove(dstfpath)
+                    except:
+                        status = False
+                        log.error('cannot replace %s file', dstfpath)
+                        continue
                 try:
-                    link(fpath, dstfpath)
+                    os.link(fpath, dstfpath)
                 except:
                     status = False
                     log.error('cannot copy %s file to %s', fpath, dstdir)
     return status
+
+
+def pyremove(package, vrange):
+    """Remove public modules listed in pkg.pyremove file."""
+    status = True
+    srcfpath = "./debian/%s.pyremove" % package
+    if not exists(srcfpath):
+        return status
+    versions = get_requested_versions(vrange)
+
+    for line in codecs.open(srcfpath, encoding='utf-8'):
+        if not line or line.startswith('#'):
+            continue
+        details = REMOVE_RE.match(line)
+        if not details:
+            status = False
+            log.warn('%s.pyremove: unrecognized line: %s',
+                     package, line)
+            continue
+        details = details.groupdict()
+        myvers = versions & get_requested_versions(details['vrange'])
+        if not myvers:
+            log.debug('%s.pyremove: no matching versions for line %s',
+                      package, line)
+            continue
+        for version in myvers:
+            files = glob(sitedir(version, package) + details['pattern'])
+            if not files:
+                log.debug('%s.pyremove: nothing to remove: python%d.%d, %s',
+                          package, version, details['pattern'])
+                continue
+            for fpath in files:
+                if isdir(fpath):
+                    try:
+                        rmtree(fpath)
+                    except Exception, e:
+                        status = False
+                        log.error(e)
+                else:
+                    try:
+                        os.remove(fpath)
+                    except (IOError, OSError), e:
+                        status = False
+                        log.error(e)
+    return status
diff --git a/dh_python2 b/dh_python2
index d838cb89eeb1d55d1fdb676d47603b257ca26780..9952d6b6b21d864ba62348fa668b693a98e722ea 100755
--- a/dh_python2
+++ b/dh_python2
@@ -41,7 +41,7 @@ from debpython.pydist import validate as validate_pydist, \
                              PUBLIC_DIR_RE
 from debpython.tools import sitedir, relative_symlink, \
                             shebang2pyver, clean_egg_name,\
-                            pyinstall
+                            pyinstall, pyremove
 from debpython.option import Option
 
 # initialize script
@@ -486,6 +486,8 @@ def main():
         log.debug('processing package %s...', package)
         if not pyinstall(package, options.vrange):
             exit(4)
+        if not pyremove(package, options.vrange):
+            exit(5)
         fix_locations(package)
         stats = scan(package, private_dir)
         share(package, stats, options)
diff --git a/tests/t1/debian/python-foo.pyinstall b/tests/t1/debian/python-foo.pyinstall
new file mode 100644
index 0000000000000000000000000000000000000000..0a369c395c89314f10730a639d125288cc425e1b
--- /dev/null
+++ b/tests/t1/debian/python-foo.pyinstall
@@ -0,0 +1 @@
+debian/spam.py foo 2.5-
diff --git a/tests/t1/debian/python-foo.pyremove b/tests/t1/debian/python-foo.pyremove
new file mode 100644
index 0000000000000000000000000000000000000000..e30c5852b33780e50734469ee825b89fb0d0eadd
--- /dev/null
+++ b/tests/t1/debian/python-foo.pyremove
@@ -0,0 +1,2 @@
+foo/spam.py 2.6
+foo/bar 2.7-
diff --git a/tests/t1/debian/spam.py b/tests/t1/debian/spam.py
new file mode 100644
index 0000000000000000000000000000000000000000..ac65b39840cc91fe66843101ab5d792f61800fa5
--- /dev/null
+++ b/tests/t1/debian/spam.py
@@ -0,0 +1 @@
+print 'SPAM'