| # -*- coding: utf-8 -*- |
| """sdist tests""" |
| |
| import os |
| import shutil |
| import sys |
| import tempfile |
| import unicodedata |
| import contextlib |
| import io |
| |
| from setuptools.extern import six |
| from setuptools.extern.six.moves import map |
| |
| import pytest |
| |
| import pkg_resources |
| from setuptools.command.sdist import sdist |
| from setuptools.command.egg_info import manifest_maker |
| from setuptools.dist import Distribution |
| from setuptools.tests import fail_on_ascii |
| from .text import Filenames |
| |
| py3_only = pytest.mark.xfail(six.PY2, reason="Test runs on Python 3 only") |
| |
| SETUP_ATTRS = { |
| 'name': 'sdist_test', |
| 'version': '0.0', |
| 'packages': ['sdist_test'], |
| 'package_data': {'sdist_test': ['*.txt']}, |
| 'data_files': [("data", [os.path.join("d", "e.dat")])], |
| } |
| |
| SETUP_PY = """\ |
| from setuptools import setup |
| |
| setup(**%r) |
| """ % SETUP_ATTRS |
| |
| |
| @contextlib.contextmanager |
| def quiet(): |
| old_stdout, old_stderr = sys.stdout, sys.stderr |
| sys.stdout, sys.stderr = six.StringIO(), six.StringIO() |
| try: |
| yield |
| finally: |
| sys.stdout, sys.stderr = old_stdout, old_stderr |
| |
| |
| # Convert to POSIX path |
| def posix(path): |
| if six.PY3 and not isinstance(path, str): |
| return path.replace(os.sep.encode('ascii'), b'/') |
| else: |
| return path.replace(os.sep, '/') |
| |
| |
| # HFS Plus uses decomposed UTF-8 |
| def decompose(path): |
| if isinstance(path, six.text_type): |
| return unicodedata.normalize('NFD', path) |
| try: |
| path = path.decode('utf-8') |
| path = unicodedata.normalize('NFD', path) |
| path = path.encode('utf-8') |
| except UnicodeError: |
| pass # Not UTF-8 |
| return path |
| |
| |
| def read_all_bytes(filename): |
| with io.open(filename, 'rb') as fp: |
| return fp.read() |
| |
| |
| def latin1_fail(): |
| try: |
| desc, filename = tempfile.mkstemp(suffix=Filenames.latin_1) |
| os.close(desc) |
| os.remove(filename) |
| except Exception: |
| return True |
| |
| |
| fail_on_latin1_encoded_filenames = pytest.mark.xfail( |
| latin1_fail(), |
| reason="System does not support latin-1 filenames", |
| ) |
| |
| |
| class TestSdistTest: |
| def setup_method(self, method): |
| self.temp_dir = tempfile.mkdtemp() |
| f = open(os.path.join(self.temp_dir, 'setup.py'), 'w') |
| f.write(SETUP_PY) |
| f.close() |
| |
| # Set up the rest of the test package |
| test_pkg = os.path.join(self.temp_dir, 'sdist_test') |
| os.mkdir(test_pkg) |
| data_folder = os.path.join(self.temp_dir, "d") |
| os.mkdir(data_folder) |
| # *.rst was not included in package_data, so c.rst should not be |
| # automatically added to the manifest when not under version control |
| for fname in ['__init__.py', 'a.txt', 'b.txt', 'c.rst', |
| os.path.join(data_folder, "e.dat")]: |
| # Just touch the files; their contents are irrelevant |
| open(os.path.join(test_pkg, fname), 'w').close() |
| |
| self.old_cwd = os.getcwd() |
| os.chdir(self.temp_dir) |
| |
| def teardown_method(self, method): |
| os.chdir(self.old_cwd) |
| shutil.rmtree(self.temp_dir) |
| |
| def test_package_data_in_sdist(self): |
| """Regression test for pull request #4: ensures that files listed in |
| package_data are included in the manifest even if they're not added to |
| version control. |
| """ |
| |
| dist = Distribution(SETUP_ATTRS) |
| dist.script_name = 'setup.py' |
| cmd = sdist(dist) |
| cmd.ensure_finalized() |
| |
| with quiet(): |
| cmd.run() |
| |
| manifest = cmd.filelist.files |
| assert os.path.join('sdist_test', 'a.txt') in manifest |
| assert os.path.join('sdist_test', 'b.txt') in manifest |
| assert os.path.join('sdist_test', 'c.rst') not in manifest |
| assert os.path.join('d', 'e.dat') in manifest |
| |
| def test_defaults_case_sensitivity(self): |
| """ |
| Make sure default files (README.*, etc.) are added in a case-sensitive |
| way to avoid problems with packages built on Windows. |
| """ |
| |
| open(os.path.join(self.temp_dir, 'readme.rst'), 'w').close() |
| open(os.path.join(self.temp_dir, 'SETUP.cfg'), 'w').close() |
| |
| dist = Distribution(SETUP_ATTRS) |
| # the extension deliberately capitalized for this test |
| # to make sure the actual filename (not capitalized) gets added |
| # to the manifest |
| dist.script_name = 'setup.PY' |
| cmd = sdist(dist) |
| cmd.ensure_finalized() |
| |
| with quiet(): |
| cmd.run() |
| |
| # lowercase all names so we can test in a |
| # case-insensitive way to make sure the files |
| # are not included. |
| manifest = map(lambda x: x.lower(), cmd.filelist.files) |
| assert 'readme.rst' not in manifest, manifest |
| assert 'setup.py' not in manifest, manifest |
| assert 'setup.cfg' not in manifest, manifest |
| |
| @fail_on_ascii |
| def test_manifest_is_written_with_utf8_encoding(self): |
| # Test for #303. |
| dist = Distribution(SETUP_ATTRS) |
| dist.script_name = 'setup.py' |
| mm = manifest_maker(dist) |
| mm.manifest = os.path.join('sdist_test.egg-info', 'SOURCES.txt') |
| os.mkdir('sdist_test.egg-info') |
| |
| # UTF-8 filename |
| filename = os.path.join('sdist_test', 'smörbröd.py') |
| |
| # Must create the file or it will get stripped. |
| open(filename, 'w').close() |
| |
| # Add UTF-8 filename and write manifest |
| with quiet(): |
| mm.run() |
| mm.filelist.append(filename) |
| mm.write_manifest() |
| |
| contents = read_all_bytes(mm.manifest) |
| |
| # The manifest should be UTF-8 encoded |
| u_contents = contents.decode('UTF-8') |
| |
| # The manifest should contain the UTF-8 filename |
| if six.PY2: |
| fs_enc = sys.getfilesystemencoding() |
| filename = filename.decode(fs_enc) |
| |
| assert posix(filename) in u_contents |
| |
| @py3_only |
| @fail_on_ascii |
| def test_write_manifest_allows_utf8_filenames(self): |
| # Test for #303. |
| dist = Distribution(SETUP_ATTRS) |
| dist.script_name = 'setup.py' |
| mm = manifest_maker(dist) |
| mm.manifest = os.path.join('sdist_test.egg-info', 'SOURCES.txt') |
| os.mkdir('sdist_test.egg-info') |
| |
| filename = os.path.join(b'sdist_test', Filenames.utf_8) |
| |
| # Must touch the file or risk removal |
| open(filename, "w").close() |
| |
| # Add filename and write manifest |
| with quiet(): |
| mm.run() |
| u_filename = filename.decode('utf-8') |
| mm.filelist.files.append(u_filename) |
| # Re-write manifest |
| mm.write_manifest() |
| |
| contents = read_all_bytes(mm.manifest) |
| |
| # The manifest should be UTF-8 encoded |
| contents.decode('UTF-8') |
| |
| # The manifest should contain the UTF-8 filename |
| assert posix(filename) in contents |
| |
| # The filelist should have been updated as well |
| assert u_filename in mm.filelist.files |
| |
| @py3_only |
| def test_write_manifest_skips_non_utf8_filenames(self): |
| """ |
| Files that cannot be encoded to UTF-8 (specifically, those that |
| weren't originally successfully decoded and have surrogate |
| escapes) should be omitted from the manifest. |
| See https://bitbucket.org/tarek/distribute/issue/303 for history. |
| """ |
| dist = Distribution(SETUP_ATTRS) |
| dist.script_name = 'setup.py' |
| mm = manifest_maker(dist) |
| mm.manifest = os.path.join('sdist_test.egg-info', 'SOURCES.txt') |
| os.mkdir('sdist_test.egg-info') |
| |
| # Latin-1 filename |
| filename = os.path.join(b'sdist_test', Filenames.latin_1) |
| |
| # Add filename with surrogates and write manifest |
| with quiet(): |
| mm.run() |
| u_filename = filename.decode('utf-8', 'surrogateescape') |
| mm.filelist.append(u_filename) |
| # Re-write manifest |
| mm.write_manifest() |
| |
| contents = read_all_bytes(mm.manifest) |
| |
| # The manifest should be UTF-8 encoded |
| contents.decode('UTF-8') |
| |
| # The Latin-1 filename should have been skipped |
| assert posix(filename) not in contents |
| |
| # The filelist should have been updated as well |
| assert u_filename not in mm.filelist.files |
| |
| @fail_on_ascii |
| def test_manifest_is_read_with_utf8_encoding(self): |
| # Test for #303. |
| dist = Distribution(SETUP_ATTRS) |
| dist.script_name = 'setup.py' |
| cmd = sdist(dist) |
| cmd.ensure_finalized() |
| |
| # Create manifest |
| with quiet(): |
| cmd.run() |
| |
| # Add UTF-8 filename to manifest |
| filename = os.path.join(b'sdist_test', Filenames.utf_8) |
| cmd.manifest = os.path.join('sdist_test.egg-info', 'SOURCES.txt') |
| manifest = open(cmd.manifest, 'ab') |
| manifest.write(b'\n' + filename) |
| manifest.close() |
| |
| # The file must exist to be included in the filelist |
| open(filename, 'w').close() |
| |
| # Re-read manifest |
| cmd.filelist.files = [] |
| with quiet(): |
| cmd.read_manifest() |
| |
| # The filelist should contain the UTF-8 filename |
| if six.PY3: |
| filename = filename.decode('utf-8') |
| assert filename in cmd.filelist.files |
| |
| @py3_only |
| @fail_on_latin1_encoded_filenames |
| def test_read_manifest_skips_non_utf8_filenames(self): |
| # Test for #303. |
| dist = Distribution(SETUP_ATTRS) |
| dist.script_name = 'setup.py' |
| cmd = sdist(dist) |
| cmd.ensure_finalized() |
| |
| # Create manifest |
| with quiet(): |
| cmd.run() |
| |
| # Add Latin-1 filename to manifest |
| filename = os.path.join(b'sdist_test', Filenames.latin_1) |
| cmd.manifest = os.path.join('sdist_test.egg-info', 'SOURCES.txt') |
| manifest = open(cmd.manifest, 'ab') |
| manifest.write(b'\n' + filename) |
| manifest.close() |
| |
| # The file must exist to be included in the filelist |
| open(filename, 'w').close() |
| |
| # Re-read manifest |
| cmd.filelist.files = [] |
| with quiet(): |
| cmd.read_manifest() |
| |
| # The Latin-1 filename should have been skipped |
| filename = filename.decode('latin-1') |
| assert filename not in cmd.filelist.files |
| |
| @fail_on_ascii |
| @fail_on_latin1_encoded_filenames |
| def test_sdist_with_utf8_encoded_filename(self): |
| # Test for #303. |
| dist = Distribution(SETUP_ATTRS) |
| dist.script_name = 'setup.py' |
| cmd = sdist(dist) |
| cmd.ensure_finalized() |
| |
| filename = os.path.join(b'sdist_test', Filenames.utf_8) |
| open(filename, 'w').close() |
| |
| with quiet(): |
| cmd.run() |
| |
| if sys.platform == 'darwin': |
| filename = decompose(filename) |
| |
| if six.PY3: |
| fs_enc = sys.getfilesystemencoding() |
| |
| if sys.platform == 'win32': |
| if fs_enc == 'cp1252': |
| # Python 3 mangles the UTF-8 filename |
| filename = filename.decode('cp1252') |
| assert filename in cmd.filelist.files |
| else: |
| filename = filename.decode('mbcs') |
| assert filename in cmd.filelist.files |
| else: |
| filename = filename.decode('utf-8') |
| assert filename in cmd.filelist.files |
| else: |
| assert filename in cmd.filelist.files |
| |
| @fail_on_latin1_encoded_filenames |
| def test_sdist_with_latin1_encoded_filename(self): |
| # Test for #303. |
| dist = Distribution(SETUP_ATTRS) |
| dist.script_name = 'setup.py' |
| cmd = sdist(dist) |
| cmd.ensure_finalized() |
| |
| # Latin-1 filename |
| filename = os.path.join(b'sdist_test', Filenames.latin_1) |
| open(filename, 'w').close() |
| assert os.path.isfile(filename) |
| |
| with quiet(): |
| cmd.run() |
| |
| if six.PY3: |
| # not all windows systems have a default FS encoding of cp1252 |
| if sys.platform == 'win32': |
| # Latin-1 is similar to Windows-1252 however |
| # on mbcs filesys it is not in latin-1 encoding |
| fs_enc = sys.getfilesystemencoding() |
| if fs_enc != 'mbcs': |
| fs_enc = 'latin-1' |
| filename = filename.decode(fs_enc) |
| |
| assert filename in cmd.filelist.files |
| else: |
| # The Latin-1 filename should have been skipped |
| filename = filename.decode('latin-1') |
| filename not in cmd.filelist.files |
| else: |
| # Under Python 2 there seems to be no decoded string in the |
| # filelist. However, due to decode and encoding of the |
| # file name to get utf-8 Manifest the latin1 maybe excluded |
| try: |
| # fs_enc should match how one is expect the decoding to |
| # be proformed for the manifest output. |
| fs_enc = sys.getfilesystemencoding() |
| filename.decode(fs_enc) |
| assert filename in cmd.filelist.files |
| except UnicodeDecodeError: |
| filename not in cmd.filelist.files |
| |
| |
| def test_default_revctrl(): |
| """ |
| When _default_revctrl was removed from the `setuptools.command.sdist` |
| module in 10.0, it broke some systems which keep an old install of |
| setuptools (Distribute) around. Those old versions require that the |
| setuptools package continue to implement that interface, so this |
| function provides that interface, stubbed. See #320 for details. |
| |
| This interface must be maintained until Ubuntu 12.04 is no longer |
| supported (by Setuptools). |
| """ |
| ep_def = 'svn_cvs = setuptools.command.sdist:_default_revctrl' |
| ep = pkg_resources.EntryPoint.parse(ep_def) |
| res = ep.resolve() |
| assert hasattr(res, '__iter__') |