test_wheelfile.py 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. import os
  2. import wheel.install
  3. import wheel.archive
  4. import hashlib
  5. try:
  6. from StringIO import StringIO
  7. except ImportError:
  8. from io import BytesIO as StringIO
  9. import codecs
  10. import zipfile
  11. import pytest
  12. import shutil
  13. import tempfile
  14. from contextlib import contextmanager
  15. @contextmanager
  16. def environ(key, value):
  17. old_value = os.environ.get(key)
  18. try:
  19. os.environ[key] = value
  20. yield
  21. finally:
  22. if old_value is None:
  23. del os.environ[key]
  24. else:
  25. os.environ[key] = old_value
  26. @contextmanager
  27. def temporary_directory():
  28. # tempfile.TemporaryDirectory doesn't exist in Python 2.
  29. tempdir = tempfile.mkdtemp()
  30. try:
  31. yield tempdir
  32. finally:
  33. shutil.rmtree(tempdir)
  34. @contextmanager
  35. def readable_zipfile(path):
  36. # zipfile.ZipFile() isn't a context manager under Python 2.
  37. zf = zipfile.ZipFile(path, 'r')
  38. try:
  39. yield zf
  40. finally:
  41. zf.close()
  42. def test_verifying_zipfile():
  43. if not hasattr(zipfile.ZipExtFile, '_update_crc'):
  44. pytest.skip('No ZIP verification. Missing ZipExtFile._update_crc.')
  45. sio = StringIO()
  46. zf = zipfile.ZipFile(sio, 'w')
  47. zf.writestr("one", b"first file")
  48. zf.writestr("two", b"second file")
  49. zf.writestr("three", b"third file")
  50. zf.close()
  51. # In default mode, VerifyingZipFile checks the hash of any read file
  52. # mentioned with set_expected_hash(). Files not mentioned with
  53. # set_expected_hash() are not checked.
  54. vzf = wheel.install.VerifyingZipFile(sio, 'r')
  55. vzf.set_expected_hash("one", hashlib.sha256(b"first file").digest())
  56. vzf.set_expected_hash("three", "blurble")
  57. vzf.open("one").read()
  58. vzf.open("two").read()
  59. try:
  60. vzf.open("three").read()
  61. except wheel.install.BadWheelFile:
  62. pass
  63. else:
  64. raise Exception("expected exception 'BadWheelFile()'")
  65. # In strict mode, VerifyingZipFile requires every read file to be
  66. # mentioned with set_expected_hash().
  67. vzf.strict = True
  68. try:
  69. vzf.open("two").read()
  70. except wheel.install.BadWheelFile:
  71. pass
  72. else:
  73. raise Exception("expected exception 'BadWheelFile()'")
  74. vzf.set_expected_hash("two", None)
  75. vzf.open("two").read()
  76. def test_pop_zipfile():
  77. sio = StringIO()
  78. zf = wheel.install.VerifyingZipFile(sio, 'w')
  79. zf.writestr("one", b"first file")
  80. zf.writestr("two", b"second file")
  81. zf.close()
  82. try:
  83. zf.pop()
  84. except RuntimeError:
  85. pass # already closed
  86. else:
  87. raise Exception("expected RuntimeError")
  88. zf = wheel.install.VerifyingZipFile(sio, 'a')
  89. zf.pop()
  90. zf.close()
  91. zf = wheel.install.VerifyingZipFile(sio, 'r')
  92. assert len(zf.infolist()) == 1
  93. def test_zipfile_timestamp():
  94. # An environment variable can be used to influence the timestamp on
  95. # TarInfo objects inside the zip. See issue #143. TemporaryDirectory is
  96. # not a context manager under Python 3.
  97. with temporary_directory() as tempdir:
  98. for filename in ('one', 'two', 'three'):
  99. path = os.path.join(tempdir, filename)
  100. with codecs.open(path, 'w', encoding='utf-8') as fp:
  101. fp.write(filename + '\n')
  102. zip_base_name = os.path.join(tempdir, 'dummy')
  103. # The earliest date representable in TarInfos, 1980-01-01
  104. with environ('SOURCE_DATE_EPOCH', '315576060'):
  105. zip_filename = wheel.archive.make_wheelfile_inner(
  106. zip_base_name, tempdir)
  107. with readable_zipfile(zip_filename) as zf:
  108. for info in zf.infolist():
  109. assert info.date_time[:3] == (1980, 1, 1)
  110. def test_zipfile_attributes():
  111. # With the change from ZipFile.write() to .writestr(), we need to manually
  112. # set member attributes.
  113. with temporary_directory() as tempdir:
  114. files = (('foo', 0o644), ('bar', 0o755))
  115. for filename, mode in files:
  116. path = os.path.join(tempdir, filename)
  117. with codecs.open(path, 'w', encoding='utf-8') as fp:
  118. fp.write(filename + '\n')
  119. os.chmod(path, mode)
  120. zip_base_name = os.path.join(tempdir, 'dummy')
  121. zip_filename = wheel.archive.make_wheelfile_inner(
  122. zip_base_name, tempdir)
  123. with readable_zipfile(zip_filename) as zf:
  124. for filename, mode in files:
  125. info = zf.getinfo(os.path.join(tempdir, filename))
  126. assert info.external_attr == (mode | 0o100000) << 16
  127. assert info.compress_type == zipfile.ZIP_DEFLATED