"""Tests for distutils.filelist."""
|
|
import logging
|
import os
|
import re
|
from distutils import debug, filelist
|
from distutils.errors import DistutilsTemplateError
|
from distutils.filelist import FileList, glob_to_re, translate_pattern
|
|
import jaraco.path
|
import pytest
|
|
from .compat import py39 as os_helper
|
|
MANIFEST_IN = """\
|
include ok
|
include xo
|
exclude xo
|
include foo.tmp
|
include buildout.cfg
|
global-include *.x
|
global-include *.txt
|
global-exclude *.tmp
|
recursive-include f *.oo
|
recursive-exclude global *.x
|
graft dir
|
prune dir3
|
"""
|
|
|
def make_local_path(s):
|
"""Converts '/' in a string to os.sep"""
|
return s.replace('/', os.sep)
|
|
|
class TestFileList:
|
def assertNoWarnings(self, caplog):
|
warnings = [rec for rec in caplog.records if rec.levelno == logging.WARNING]
|
assert not warnings
|
caplog.clear()
|
|
def assertWarnings(self, caplog):
|
warnings = [rec for rec in caplog.records if rec.levelno == logging.WARNING]
|
assert warnings
|
caplog.clear()
|
|
def test_glob_to_re(self):
|
sep = os.sep
|
if os.sep == '\\':
|
sep = re.escape(os.sep)
|
|
for glob, regex in (
|
# simple cases
|
('foo*', r'(?s:foo[^%(sep)s]*)\Z'),
|
('foo?', r'(?s:foo[^%(sep)s])\Z'),
|
('foo??', r'(?s:foo[^%(sep)s][^%(sep)s])\Z'),
|
# special cases
|
(r'foo\\*', r'(?s:foo\\\\[^%(sep)s]*)\Z'),
|
(r'foo\\\*', r'(?s:foo\\\\\\[^%(sep)s]*)\Z'),
|
('foo????', r'(?s:foo[^%(sep)s][^%(sep)s][^%(sep)s][^%(sep)s])\Z'),
|
(r'foo\\??', r'(?s:foo\\\\[^%(sep)s][^%(sep)s])\Z'),
|
):
|
regex = regex % {'sep': sep}
|
assert glob_to_re(glob) == regex
|
|
def test_process_template_line(self):
|
# testing all MANIFEST.in template patterns
|
file_list = FileList()
|
mlp = make_local_path
|
|
# simulated file list
|
file_list.allfiles = [
|
'foo.tmp',
|
'ok',
|
'xo',
|
'four.txt',
|
'buildout.cfg',
|
# filelist does not filter out VCS directories,
|
# it's sdist that does
|
mlp('.hg/last-message.txt'),
|
mlp('global/one.txt'),
|
mlp('global/two.txt'),
|
mlp('global/files.x'),
|
mlp('global/here.tmp'),
|
mlp('f/o/f.oo'),
|
mlp('dir/graft-one'),
|
mlp('dir/dir2/graft2'),
|
mlp('dir3/ok'),
|
mlp('dir3/sub/ok.txt'),
|
]
|
|
for line in MANIFEST_IN.split('\n'):
|
if line.strip() == '':
|
continue
|
file_list.process_template_line(line)
|
|
wanted = [
|
'ok',
|
'buildout.cfg',
|
'four.txt',
|
mlp('.hg/last-message.txt'),
|
mlp('global/one.txt'),
|
mlp('global/two.txt'),
|
mlp('f/o/f.oo'),
|
mlp('dir/graft-one'),
|
mlp('dir/dir2/graft2'),
|
]
|
|
assert file_list.files == wanted
|
|
def test_debug_print(self, capsys, monkeypatch):
|
file_list = FileList()
|
file_list.debug_print('xxx')
|
assert capsys.readouterr().out == ''
|
|
monkeypatch.setattr(debug, 'DEBUG', True)
|
file_list.debug_print('xxx')
|
assert capsys.readouterr().out == 'xxx\n'
|
|
def test_set_allfiles(self):
|
file_list = FileList()
|
files = ['a', 'b', 'c']
|
file_list.set_allfiles(files)
|
assert file_list.allfiles == files
|
|
def test_remove_duplicates(self):
|
file_list = FileList()
|
file_list.files = ['a', 'b', 'a', 'g', 'c', 'g']
|
# files must be sorted beforehand (sdist does it)
|
file_list.sort()
|
file_list.remove_duplicates()
|
assert file_list.files == ['a', 'b', 'c', 'g']
|
|
def test_translate_pattern(self):
|
# not regex
|
assert hasattr(translate_pattern('a', anchor=True, is_regex=False), 'search')
|
|
# is a regex
|
regex = re.compile('a')
|
assert translate_pattern(regex, anchor=True, is_regex=True) == regex
|
|
# plain string flagged as regex
|
assert hasattr(translate_pattern('a', anchor=True, is_regex=True), 'search')
|
|
# glob support
|
assert translate_pattern('*.py', anchor=True, is_regex=False).search(
|
'filelist.py'
|
)
|
|
def test_exclude_pattern(self):
|
# return False if no match
|
file_list = FileList()
|
assert not file_list.exclude_pattern('*.py')
|
|
# return True if files match
|
file_list = FileList()
|
file_list.files = ['a.py', 'b.py']
|
assert file_list.exclude_pattern('*.py')
|
|
# test excludes
|
file_list = FileList()
|
file_list.files = ['a.py', 'a.txt']
|
file_list.exclude_pattern('*.py')
|
assert file_list.files == ['a.txt']
|
|
def test_include_pattern(self):
|
# return False if no match
|
file_list = FileList()
|
file_list.set_allfiles([])
|
assert not file_list.include_pattern('*.py')
|
|
# return True if files match
|
file_list = FileList()
|
file_list.set_allfiles(['a.py', 'b.txt'])
|
assert file_list.include_pattern('*.py')
|
|
# test * matches all files
|
file_list = FileList()
|
assert file_list.allfiles is None
|
file_list.set_allfiles(['a.py', 'b.txt'])
|
file_list.include_pattern('*')
|
assert file_list.allfiles == ['a.py', 'b.txt']
|
|
def test_process_template(self, caplog):
|
mlp = make_local_path
|
# invalid lines
|
file_list = FileList()
|
for action in (
|
'include',
|
'exclude',
|
'global-include',
|
'global-exclude',
|
'recursive-include',
|
'recursive-exclude',
|
'graft',
|
'prune',
|
'blarg',
|
):
|
with pytest.raises(DistutilsTemplateError):
|
file_list.process_template_line(action)
|
|
# include
|
file_list = FileList()
|
file_list.set_allfiles(['a.py', 'b.txt', mlp('d/c.py')])
|
|
file_list.process_template_line('include *.py')
|
assert file_list.files == ['a.py']
|
self.assertNoWarnings(caplog)
|
|
file_list.process_template_line('include *.rb')
|
assert file_list.files == ['a.py']
|
self.assertWarnings(caplog)
|
|
# exclude
|
file_list = FileList()
|
file_list.files = ['a.py', 'b.txt', mlp('d/c.py')]
|
|
file_list.process_template_line('exclude *.py')
|
assert file_list.files == ['b.txt', mlp('d/c.py')]
|
self.assertNoWarnings(caplog)
|
|
file_list.process_template_line('exclude *.rb')
|
assert file_list.files == ['b.txt', mlp('d/c.py')]
|
self.assertWarnings(caplog)
|
|
# global-include
|
file_list = FileList()
|
file_list.set_allfiles(['a.py', 'b.txt', mlp('d/c.py')])
|
|
file_list.process_template_line('global-include *.py')
|
assert file_list.files == ['a.py', mlp('d/c.py')]
|
self.assertNoWarnings(caplog)
|
|
file_list.process_template_line('global-include *.rb')
|
assert file_list.files == ['a.py', mlp('d/c.py')]
|
self.assertWarnings(caplog)
|
|
# global-exclude
|
file_list = FileList()
|
file_list.files = ['a.py', 'b.txt', mlp('d/c.py')]
|
|
file_list.process_template_line('global-exclude *.py')
|
assert file_list.files == ['b.txt']
|
self.assertNoWarnings(caplog)
|
|
file_list.process_template_line('global-exclude *.rb')
|
assert file_list.files == ['b.txt']
|
self.assertWarnings(caplog)
|
|
# recursive-include
|
file_list = FileList()
|
file_list.set_allfiles(['a.py', mlp('d/b.py'), mlp('d/c.txt'), mlp('d/d/e.py')])
|
|
file_list.process_template_line('recursive-include d *.py')
|
assert file_list.files == [mlp('d/b.py'), mlp('d/d/e.py')]
|
self.assertNoWarnings(caplog)
|
|
file_list.process_template_line('recursive-include e *.py')
|
assert file_list.files == [mlp('d/b.py'), mlp('d/d/e.py')]
|
self.assertWarnings(caplog)
|
|
# recursive-exclude
|
file_list = FileList()
|
file_list.files = ['a.py', mlp('d/b.py'), mlp('d/c.txt'), mlp('d/d/e.py')]
|
|
file_list.process_template_line('recursive-exclude d *.py')
|
assert file_list.files == ['a.py', mlp('d/c.txt')]
|
self.assertNoWarnings(caplog)
|
|
file_list.process_template_line('recursive-exclude e *.py')
|
assert file_list.files == ['a.py', mlp('d/c.txt')]
|
self.assertWarnings(caplog)
|
|
# graft
|
file_list = FileList()
|
file_list.set_allfiles(['a.py', mlp('d/b.py'), mlp('d/d/e.py'), mlp('f/f.py')])
|
|
file_list.process_template_line('graft d')
|
assert file_list.files == [mlp('d/b.py'), mlp('d/d/e.py')]
|
self.assertNoWarnings(caplog)
|
|
file_list.process_template_line('graft e')
|
assert file_list.files == [mlp('d/b.py'), mlp('d/d/e.py')]
|
self.assertWarnings(caplog)
|
|
# prune
|
file_list = FileList()
|
file_list.files = ['a.py', mlp('d/b.py'), mlp('d/d/e.py'), mlp('f/f.py')]
|
|
file_list.process_template_line('prune d')
|
assert file_list.files == ['a.py', mlp('f/f.py')]
|
self.assertNoWarnings(caplog)
|
|
file_list.process_template_line('prune e')
|
assert file_list.files == ['a.py', mlp('f/f.py')]
|
self.assertWarnings(caplog)
|
|
|
class TestFindAll:
|
@os_helper.skip_unless_symlink
|
def test_missing_symlink(self, temp_cwd):
|
os.symlink('foo', 'bar')
|
assert filelist.findall() == []
|
|
def test_basic_discovery(self, temp_cwd):
|
"""
|
When findall is called with no parameters or with
|
'.' as the parameter, the dot should be omitted from
|
the results.
|
"""
|
jaraco.path.build({'foo': {'file1.txt': ''}, 'bar': {'file2.txt': ''}})
|
file1 = os.path.join('foo', 'file1.txt')
|
file2 = os.path.join('bar', 'file2.txt')
|
expected = [file2, file1]
|
assert sorted(filelist.findall()) == expected
|
|
def test_non_local_discovery(self, tmp_path):
|
"""
|
When findall is called with another path, the full
|
path name should be returned.
|
"""
|
jaraco.path.build({'file1.txt': ''}, tmp_path)
|
expected = [str(tmp_path / 'file1.txt')]
|
assert filelist.findall(tmp_path) == expected
|
|
@os_helper.skip_unless_symlink
|
def test_symlink_loop(self, tmp_path):
|
jaraco.path.build(
|
{
|
'link-to-parent': jaraco.path.Symlink('.'),
|
'somefile': '',
|
},
|
tmp_path,
|
)
|
files = filelist.findall(tmp_path)
|
assert len(files) == 1
|