1import difflib 2import glob 3import gzip 4import os 5import tempfile 6 7import Cython.Build.Dependencies 8import Cython.Utils 9from Cython.TestUtils import CythonTest 10 11 12class TestCyCache(CythonTest): 13 14 def setUp(self): 15 CythonTest.setUp(self) 16 self.temp_dir = tempfile.mkdtemp( 17 prefix='cycache-test', 18 dir='TEST_TMP' if os.path.isdir('TEST_TMP') else None) 19 self.src_dir = tempfile.mkdtemp(prefix='src', dir=self.temp_dir) 20 self.cache_dir = tempfile.mkdtemp(prefix='cache', dir=self.temp_dir) 21 22 def cache_files(self, file_glob): 23 return glob.glob(os.path.join(self.cache_dir, file_glob)) 24 25 def fresh_cythonize(self, *args, **kwargs): 26 Cython.Utils.clear_function_caches() 27 Cython.Build.Dependencies._dep_tree = None # discard method caches 28 Cython.Build.Dependencies.cythonize(*args, **kwargs) 29 30 def test_cycache_switch(self): 31 content1 = 'value = 1\n' 32 content2 = 'value = 2\n' 33 a_pyx = os.path.join(self.src_dir, 'a.pyx') 34 a_c = a_pyx[:-4] + '.c' 35 36 open(a_pyx, 'w').write(content1) 37 self.fresh_cythonize(a_pyx, cache=self.cache_dir) 38 self.fresh_cythonize(a_pyx, cache=self.cache_dir) 39 self.assertEqual(1, len(self.cache_files('a.c*'))) 40 a_contents1 = open(a_c).read() 41 os.unlink(a_c) 42 43 open(a_pyx, 'w').write(content2) 44 self.fresh_cythonize(a_pyx, cache=self.cache_dir) 45 a_contents2 = open(a_c).read() 46 os.unlink(a_c) 47 48 self.assertNotEqual(a_contents1, a_contents2, 'C file not changed!') 49 self.assertEqual(2, len(self.cache_files('a.c*'))) 50 51 open(a_pyx, 'w').write(content1) 52 self.fresh_cythonize(a_pyx, cache=self.cache_dir) 53 self.assertEqual(2, len(self.cache_files('a.c*'))) 54 a_contents = open(a_c).read() 55 self.assertEqual( 56 a_contents, a_contents1, 57 msg='\n'.join(list(difflib.unified_diff( 58 a_contents.split('\n'), a_contents1.split('\n')))[:10])) 59 60 def test_cycache_uses_cache(self): 61 a_pyx = os.path.join(self.src_dir, 'a.pyx') 62 a_c = a_pyx[:-4] + '.c' 63 open(a_pyx, 'w').write('pass') 64 self.fresh_cythonize(a_pyx, cache=self.cache_dir) 65 a_cache = os.path.join(self.cache_dir, os.listdir(self.cache_dir)[0]) 66 gzip.GzipFile(a_cache, 'wb').write('fake stuff'.encode('ascii')) 67 os.unlink(a_c) 68 self.fresh_cythonize(a_pyx, cache=self.cache_dir) 69 a_contents = open(a_c).read() 70 self.assertEqual(a_contents, 'fake stuff', 71 'Unexpected contents: %s...' % a_contents[:100]) 72 73 def test_multi_file_output(self): 74 a_pyx = os.path.join(self.src_dir, 'a.pyx') 75 a_c = a_pyx[:-4] + '.c' 76 a_h = a_pyx[:-4] + '.h' 77 a_api_h = a_pyx[:-4] + '_api.h' 78 open(a_pyx, 'w').write('cdef public api int foo(int x): return x\n') 79 self.fresh_cythonize(a_pyx, cache=self.cache_dir) 80 expected = [a_c, a_h, a_api_h] 81 for output in expected: 82 self.assertTrue(os.path.exists(output), output) 83 os.unlink(output) 84 self.fresh_cythonize(a_pyx, cache=self.cache_dir) 85 for output in expected: 86 self.assertTrue(os.path.exists(output), output) 87 88 def test_options_invalidation(self): 89 hash_pyx = os.path.join(self.src_dir, 'options.pyx') 90 hash_c = hash_pyx[:-len('.pyx')] + '.c' 91 92 open(hash_pyx, 'w').write('pass') 93 self.fresh_cythonize(hash_pyx, cache=self.cache_dir, cplus=False) 94 self.assertEqual(1, len(self.cache_files('options.c*'))) 95 96 os.unlink(hash_c) 97 self.fresh_cythonize(hash_pyx, cache=self.cache_dir, cplus=True) 98 self.assertEqual(2, len(self.cache_files('options.c*'))) 99 100 os.unlink(hash_c) 101 self.fresh_cythonize(hash_pyx, cache=self.cache_dir, cplus=False, show_version=False) 102 self.assertEqual(2, len(self.cache_files('options.c*'))) 103 104 os.unlink(hash_c) 105 self.fresh_cythonize(hash_pyx, cache=self.cache_dir, cplus=False, show_version=True) 106 self.assertEqual(2, len(self.cache_files('options.c*'))) 107