cpython: 47788c90f80b (original) (raw)
--- a/Lib/distutils/filelist.py +++ b/Lib/distutils/filelist.py @@ -210,6 +210,7 @@ class FileList: Return 1 if files are found. """
# XXX docstring lying about what the special chars are?[](#l1.7) files_found = 0[](#l1.8) pattern_re = translate_pattern(pattern, anchor, prefix, is_regex)[](#l1.9) self.debug_print("include_pattern: applying regex r'%s'" %[](#l1.10)
@@ -297,11 +298,14 @@ def glob_to_re(pattern): # IMHO is wrong -- '?' and '*' aren't supposed to match slash in Unix, # and by extension they shouldn't match such "special characters" under # any OS. So change all non-escaped dots in the RE to match any
character except the special characters.
XXX currently the "special characters" are just slash -- i.e. this is
Unix-only.
- pattern_re = re.sub(r'((?<!\)(\\)*).', r'\1[^/]', pattern_re)
character except the special characters (currently: just os.sep).
- sep = os.sep
- if os.sep == '\':
# we're using a regex to manipulate a regex, so we need[](#l1.23)
# to escape the backslash twice[](#l1.24)
sep = r'\\\\'[](#l1.25)
- escaped = r'\1[^%s]' % sep
- pattern_re = re.sub(r'((?<!\)(\\)*).', escaped, pattern_re) return pattern_re @@ -328,8 +332,10 @@ def translate_pattern(pattern, anchor=1, # ditch end of pattern character empty_pattern = glob_to_re('') prefix_re = glob_to_re(prefix)[:-len(empty_pattern)]
# paths should always use / in manifest templates[](#l1.35)
pattern_re = "^%s/.*%s" % (prefix_re, pattern_re)[](#l1.36)
sep = os.sep[](#l1.37)
if os.sep == '\\':[](#l1.38)
sep = r'\\'[](#l1.39)
else: # no prefix -- respect anchor flag if anchor: pattern_re = "^" + pattern_repattern_re = "^" + sep.join((prefix_re, ".*" + pattern_re))[](#l1.40)
--- a/Lib/distutils/tests/test_filelist.py +++ b/Lib/distutils/tests/test_filelist.py @@ -1,4 +1,5 @@ """Tests for distutils.filelist.""" +import os import re import unittest from distutils import debug @@ -14,6 +15,7 @@ include ok include xo exclude xo include foo.tmp +include buildout.cfg global-include *.x global-include *.txt global-exclude *.tmp @@ -24,6 +26,11 @@ prune dir3 """ +def make_local_path(s):
+ + class FileListTestCase(support.LoggingSilencer, unittest.TestCase): @@ -36,41 +43,60 @@ class FileListTestCase(support.LoggingSi self.clear_logs() def test_glob_to_re(self):
# simple cases[](#l2.33)
self.assertEqual(glob_to_re('foo*'), 'foo[^/]*\\Z(?ms)')[](#l2.34)
self.assertEqual(glob_to_re('foo?'), 'foo[^/]\\Z(?ms)')[](#l2.35)
self.assertEqual(glob_to_re('foo??'), 'foo[^/][^/]\\Z(?ms)')[](#l2.36)
sep = os.sep[](#l2.37)
if os.sep == '\\':[](#l2.38)
sep = re.escape(os.sep)[](#l2.39)
# special cases[](#l2.41)
self.assertEqual(glob_to_re(r'foo\\*'), r'foo\\\\[^/]*\Z(?ms)')[](#l2.42)
self.assertEqual(glob_to_re(r'foo\\\*'), r'foo\\\\\\[^/]*\Z(?ms)')[](#l2.43)
self.assertEqual(glob_to_re('foo????'), r'foo[^/][^/][^/][^/]\Z(?ms)')[](#l2.44)
self.assertEqual(glob_to_re(r'foo\\??'), r'foo\\\\[^/][^/]\Z(?ms)')[](#l2.45)
for glob, regex in ([](#l2.46)
# simple cases[](#l2.47)
('foo*', r'foo[^%(sep)s]*\Z(?ms)'),[](#l2.48)
('foo?', r'foo[^%(sep)s]\Z(?ms)'),[](#l2.49)
('foo??', r'foo[^%(sep)s][^%(sep)s]\Z(?ms)'),[](#l2.50)
# special cases[](#l2.51)
(r'foo\\*', r'foo\\\\[^%(sep)s]*\Z(?ms)'),[](#l2.52)
(r'foo\\\*', r'foo\\\\\\[^%(sep)s]*\Z(?ms)'),[](#l2.53)
('foo????', r'foo[^%(sep)s][^%(sep)s][^%(sep)s][^%(sep)s]\Z(?ms)'),[](#l2.54)
(r'foo\\??', r'foo\\\\[^%(sep)s][^%(sep)s]\Z(?ms)')):[](#l2.55)
regex = regex % {'sep': sep}[](#l2.56)
self.assertEqual(glob_to_re(glob), regex)[](#l2.57)
def test_process_template_line(self): # testing all MANIFEST.in template patterns file_list = FileList()
l = make_local_path[](#l2.62)
# simulated file list file_list.allfiles = ['foo.tmp', 'ok', 'xo', 'four.txt',
'global/one.txt',[](#l2.66)
'global/two.txt',[](#l2.67)
'global/files.x',[](#l2.68)
'global/here.tmp',[](#l2.69)
'f/o/f.oo',[](#l2.70)
'dir/graft-one',[](#l2.71)
'dir/dir2/graft2',[](#l2.72)
'dir3/ok',[](#l2.73)
'dir3/sub/ok.txt',[](#l2.74)
][](#l2.75)
'buildout.cfg',[](#l2.76)
# filelist does not filter out VCS directories,[](#l2.77)
# it's sdist that does[](#l2.78)
l('.hg/last-message.txt'),[](#l2.79)
l('global/one.txt'),[](#l2.80)
l('global/two.txt'),[](#l2.81)
l('global/files.x'),[](#l2.82)
l('global/here.tmp'),[](#l2.83)
l('f/o/f.oo'),[](#l2.84)
l('dir/graft-one'),[](#l2.85)
l('dir/dir2/graft2'),[](#l2.86)
l('dir3/ok'),[](#l2.87)
l('dir3/sub/ok.txt'),[](#l2.88)
][](#l2.89)
for line in MANIFEST_IN.split('\n'): if line.strip() == '': continue file_list.process_template_line(line)
wanted = ['ok', 'four.txt', 'global/one.txt', 'global/two.txt',[](#l2.96)
'f/o/f.oo', 'dir/graft-one', 'dir/dir2/graft2'][](#l2.97)
wanted = ['ok',[](#l2.98)
'buildout.cfg',[](#l2.99)
'four.txt',[](#l2.100)
l('.hg/last-message.txt'),[](#l2.101)
l('global/one.txt'),[](#l2.102)
l('global/two.txt'),[](#l2.103)
l('f/o/f.oo'),[](#l2.104)
l('dir/graft-one'),[](#l2.105)
l('dir/dir2/graft2'),[](#l2.106)
][](#l2.107)
self.assertEqual(file_list.files, wanted) @@ -158,6 +184,7 @@ class FileListTestCase(support.LoggingSi self.assertEqual(file_list.allfiles, ['a.py', 'b.txt']) def test_process_template(self):
l = make_local_path[](#l2.115) # invalid lines[](#l2.116) file_list = FileList()[](#l2.117) for action in ('include', 'exclude', 'global-include',[](#l2.118)
@@ -168,7 +195,7 @@ class FileListTestCase(support.LoggingSi # include file_list = FileList()
file_list.set_allfiles(['a.py', 'b.txt', 'd/c.py'])[](#l2.123)
file_list.set_allfiles(['a.py', 'b.txt', l('d/c.py')])[](#l2.124)
file_list.process_template_line('include *.py') self.assertEqual(file_list.files, ['a.py']) @@ -180,31 +207,31 @@ class FileListTestCase(support.LoggingSi # exclude file_list = FileList()
file_list.files = ['a.py', 'b.txt', 'd/c.py'][](#l2.132)
file_list.files = ['a.py', 'b.txt', l('d/c.py')][](#l2.133)
file_list.process_template_line('exclude *.py')
self.assertEqual(file_list.files, ['b.txt', 'd/c.py'])[](#l2.136)
self.assertEqual(file_list.files, ['b.txt', l('d/c.py')])[](#l2.137) self.assertNoWarnings()[](#l2.138)
file_list.process_template_line('exclude *.rb')
self.assertEqual(file_list.files, ['b.txt', 'd/c.py'])[](#l2.141)
self.assertEqual(file_list.files, ['b.txt', l('d/c.py')])[](#l2.142) self.assertWarnings()[](#l2.143)
# global-include file_list = FileList()
file_list.set_allfiles(['a.py', 'b.txt', 'd/c.py'])[](#l2.147)
file_list.set_allfiles(['a.py', 'b.txt', l('d/c.py')])[](#l2.148)
file_list.process_template_line('global-include *.py')
self.assertEqual(file_list.files, ['a.py', 'd/c.py'])[](#l2.151)
self.assertEqual(file_list.files, ['a.py', l('d/c.py')])[](#l2.152) self.assertNoWarnings()[](#l2.153)
file_list.process_template_line('global-include *.rb')
self.assertEqual(file_list.files, ['a.py', 'd/c.py'])[](#l2.156)
self.assertEqual(file_list.files, ['a.py', l('d/c.py')])[](#l2.157) self.assertWarnings()[](#l2.158)
# global-exclude file_list = FileList()
file_list.files = ['a.py', 'b.txt', 'd/c.py'][](#l2.162)
file_list.files = ['a.py', 'b.txt', l('d/c.py')][](#l2.163)
file_list.process_template_line('global-exclude *.py') self.assertEqual(file_list.files, ['b.txt']) @@ -216,50 +243,52 @@ class FileListTestCase(support.LoggingSi # recursive-include file_list = FileList()
file_list.set_allfiles(['a.py', 'd/b.py', 'd/c.txt', 'd/d/e.py'])[](#l2.171)
file_list.set_allfiles(['a.py', l('d/b.py'), l('d/c.txt'),[](#l2.172)
l('d/d/e.py')])[](#l2.173)
file_list.process_template_line('recursive-include d *.py')
self.assertEqual(file_list.files, ['d/b.py', 'd/d/e.py'])[](#l2.176)
self.assertEqual(file_list.files, [l('d/b.py'), l('d/d/e.py')])[](#l2.177) self.assertNoWarnings()[](#l2.178)
file_list.process_template_line('recursive-include e *.py')
self.assertEqual(file_list.files, ['d/b.py', 'd/d/e.py'])[](#l2.181)
self.assertEqual(file_list.files, [l('d/b.py'), l('d/d/e.py')])[](#l2.182) self.assertWarnings()[](#l2.183)
# recursive-exclude file_list = FileList()
file_list.files = ['a.py', 'd/b.py', 'd/c.txt', 'd/d/e.py'][](#l2.187)
file_list.files = ['a.py', l('d/b.py'), l('d/c.txt'), l('d/d/e.py')][](#l2.188)
file_list.process_template_line('recursive-exclude d *.py')
self.assertEqual(file_list.files, ['a.py', 'd/c.txt'])[](#l2.191)
self.assertEqual(file_list.files, ['a.py', l('d/c.txt')])[](#l2.192) self.assertNoWarnings()[](#l2.193)
file_list.process_template_line('recursive-exclude e *.py')
self.assertEqual(file_list.files, ['a.py', 'd/c.txt'])[](#l2.196)
self.assertEqual(file_list.files, ['a.py', l('d/c.txt')])[](#l2.197) self.assertWarnings()[](#l2.198)
# graft file_list = FileList()
file_list.set_allfiles(['a.py', 'd/b.py', 'd/d/e.py', 'f/f.py'])[](#l2.202)
file_list.set_allfiles(['a.py', l('d/b.py'), l('d/d/e.py'),[](#l2.203)
l('f/f.py')])[](#l2.204)
file_list.process_template_line('graft d')
self.assertEqual(file_list.files, ['d/b.py', 'd/d/e.py'])[](#l2.207)
self.assertEqual(file_list.files, [l('d/b.py'), l('d/d/e.py')])[](#l2.208) self.assertNoWarnings()[](#l2.209)
file_list.process_template_line('graft e')
self.assertEqual(file_list.files, ['d/b.py', 'd/d/e.py'])[](#l2.212)
self.assertEqual(file_list.files, [l('d/b.py'), l('d/d/e.py')])[](#l2.213) self.assertWarnings()[](#l2.214)
# prune file_list = FileList()
file_list.files = ['a.py', 'd/b.py', 'd/d/e.py', 'f/f.py'][](#l2.218)
file_list.files = ['a.py', l('d/b.py'), l('d/d/e.py'), l('f/f.py')][](#l2.219)
file_list.process_template_line('prune d')
self.assertEqual(file_list.files, ['a.py', 'f/f.py'])[](#l2.222)
self.assertEqual(file_list.files, ['a.py', l('f/f.py')])[](#l2.223) self.assertNoWarnings()[](#l2.224)
file_list.process_template_line('prune e')
self.assertEqual(file_list.files, ['a.py', 'f/f.py'])[](#l2.227)
self.assertEqual(file_list.files, ['a.py', l('f/f.py')])[](#l2.228) self.assertWarnings()[](#l2.229)
--- a/Lib/distutils/tests/test_sdist.py +++ b/Lib/distutils/tests/test_sdist.py @@ -42,6 +42,7 @@ setup(name='fake') MANIFEST = """[](#l3.4)
file GENERATED by distutils, do NOT edit
README +buildout.cfg inroot.txt setup.py data%(sep)sdata.dt @@ -150,7 +151,7 @@ class SDistTestCase(PyPIRCCommandTestCas dist_folder = join(self.tmp_dir, 'dist') result = os.listdir(dist_folder) result.sort()
self.assertEqual(result, ['fake-1.0.tar', 'fake-1.0.tar.gz'] )[](#l3.15)
self.assertEqual(result, ['fake-1.0.tar', 'fake-1.0.tar.gz'])[](#l3.16)
os.remove(join(dist_folder, 'fake-1.0.tar')) os.remove(join(dist_folder, 'fake-1.0.tar.gz')) @@ -209,11 +210,18 @@ class SDistTestCase(PyPIRCCommandTestCas self.write_file((data_dir, 'data.dt'), '#') some_dir = join(self.tmp_dir, 'some') os.mkdir(some_dir)
# make sure VCS directories are pruned (#14004)[](#l3.24)
hg_dir = join(self.tmp_dir, '.hg')[](#l3.25)
os.mkdir(hg_dir)[](#l3.26)
self.write_file((hg_dir, 'last-message.txt'), '#')[](#l3.27)
# a buggy regex used to prevent this from working on windows (#6884)[](#l3.28)
self.write_file((self.tmp_dir, 'buildout.cfg'), '#')[](#l3.29) self.write_file((self.tmp_dir, 'inroot.txt'), '#')[](#l3.30) self.write_file((some_dir, 'file.txt'), '#')[](#l3.31) self.write_file((some_dir, 'other_file.txt'), '#')[](#l3.32)
dist.data_files = [('data', ['data/data.dt',
'buildout.cfg',[](#l3.35) 'inroot.txt',[](#l3.36) 'notexisting']),[](#l3.37) 'some/file.txt',[](#l3.38)
@@ -243,15 +251,15 @@ class SDistTestCase(PyPIRCCommandTestCas zip_file.close() # making sure everything was added
self.assertEqual(len(content), 11)[](#l3.43)
self.assertEqual(len(content), 12)[](#l3.44)
# checking the MANIFEST f = open(join(self.tmp_dir, 'MANIFEST')) try: manifest = f.read()
self.assertEqual(manifest, MANIFEST % {'sep': os.sep})[](#l3.50) finally:[](#l3.51) f.close()[](#l3.52)
self.assertEqual(manifest, MANIFEST % {'sep': os.sep})[](#l3.53)
@unittest.skipUnless(zlib, "requires zlib") def test_metadata_check_option(self):
--- a/Misc/NEWS +++ b/Misc/NEWS @@ -98,6 +98,9 @@ Core and Builtins Library ------- +- Issue #6884: Fix long-standing bugs with MANIFEST.in parsing in distutils
- Issue #8033: sqlite3: Fix 64-bit integer handling in user functions on 32-bit architectures. Initial patch by Philippe Devalkeneer. @@ -250,8 +253,6 @@ Library
- Issues #1745761, #755670, #13357, #12629, #1200313: HTMLParser now correctly handles non-valid attributes, including adjacent and unquoted attributes. -- Issue #13193: Fix distutils.filelist.FileList under Windows. -
- Issue #13373: multiprocessing.Queue.get() could sometimes block indefinitely when called with a timeout. Patch by Arnaud Ysmal.