def test_egg5(self): """Loading an app from an egg that has an import error in its models module raises that error""" egg_name = '%s/brokenapp.egg' % self.egg_dir with extend_sys_path(egg_name): with self.assertRaisesMessage(ImportError, 'modelz'): with self.settings(INSTALLED_APPS=['broken_app']): pass
def test_only_new_files(self): """ When calling a second time gen_filenames with only_new = True, only files from newly loaded modules should be given. """ dirname = tempfile.mkdtemp() filename = os.path.join(dirname, 'test_only_new_module.py') self.addCleanup(shutil.rmtree, dirname) with open(filename, 'w'): pass # Test uncached access self.clear_autoreload_caches() filenames = set(autoreload.gen_filenames(only_new=True)) filenames_reference = set(autoreload.gen_filenames()) self.assertEqual(filenames, filenames_reference) # Test cached access: no changes filenames = set(autoreload.gen_filenames(only_new=True)) self.assertEqual(filenames, set()) # Test cached access: add a module with extend_sys_path(dirname): import_module('test_only_new_module') filenames = set(autoreload.gen_filenames(only_new=True)) self.assertEqual(filenames, {filename})
def test_single_path(self): """ A Py3.3+ namespace package can be an app if it has only one path. """ with extend_sys_path(self.base_location): with self.settings(INSTALLED_APPS=['nsapp']): app_config = apps.get_app_config('nsapp') self.assertEqual(app_config.path, self.app_path)
def test_table_exists(self): with extend_sys_path(os.path.dirname(os.path.abspath(__file__))): with self.modify_settings( INSTALLED_APPS={'append': ['app1', 'app2']}): call_command('migrate', verbosity=0, run_syncdb=True) from app1.models import ProxyModel from app2.models import NiceModel self.assertEqual(NiceModel.objects.all().count(), 0) self.assertEqual(ProxyModel.objects.all().count(), 0)
def test_egg4(self): """Loading an app with no models from under the top-level egg package generates no error""" egg_name = '%s/omelet.egg' % self.egg_dir with extend_sys_path(egg_name): with self.settings(INSTALLED_APPS=['omelet.app_no_models']): models_module = apps.get_app_config( 'app_no_models').models_module self.assertIsNone(models_module) del apps.all_models['app_no_models']
def test_egg3(self): """Models module can be loaded from an app located under an egg's top-level package""" egg_name = '%s/omelet.egg' % self.egg_dir with extend_sys_path(egg_name): with self.settings(INSTALLED_APPS=['omelet.app_with_models']): models_module = apps.get_app_config( 'app_with_models').models_module self.assertIsNotNone(models_module) del apps.all_models['app_with_models']
def test_egg2(self): """Loading an app from an egg that has no models returns no models (and no error)""" egg_name = '%s/nomodelapp.egg' % self.egg_dir with extend_sys_path(egg_name): with self.settings(INSTALLED_APPS=['app_no_models']): models_module = apps.get_app_config( 'app_no_models').models_module self.assertIsNone(models_module) del apps.all_models['app_no_models']
def test_egg1(self): """Models module can be loaded from an app in an egg""" egg_name = '%s/modelapp.egg' % self.egg_dir with extend_sys_path(egg_name): with self.settings(INSTALLED_APPS=['app_with_models']): models_module = apps.get_app_config( 'app_with_models').models_module self.assertIsNotNone(models_module) del apps.all_models['app_with_models']
def test_multiple_paths_explicit_path(self): """ Multiple locations are ok only if app-config has explicit path. """ # Temporarily add two directories to sys.path that both contain # components of the "nsapp" package. with extend_sys_path(self.base_location, self.other_location): with self.settings(INSTALLED_APPS=['nsapp.apps.NSAppConfig']): app_config = apps.get_app_config('nsapp') self.assertEqual(app_config.path, self.app_path)
def test_discover_commands_in_eggs(self): """ Management commands can also be loaded from Python eggs. """ egg_dir = '%s/eggs' % os.path.dirname(__file__) egg_name = '%s/basic.egg' % egg_dir with extend_sys_path(egg_name): with self.settings(INSTALLED_APPS=['commandegg']): cmds = find_commands(os.path.join(apps.get_app_config('commandegg').path, 'management')) self.assertEqual(cmds, ['eggcommand'])
def test_multiple_paths(self): """ A Py3.3+ namespace package with multiple locations cannot be an app. (Because then we wouldn't know where to load its templates, static assets, etc. from.) """ # Temporarily add two directories to sys.path that both contain # components of the "nsapp" package. with extend_sys_path(self.base_location, self.other_location): with self.assertRaises(ImproperlyConfigured): with self.settings(INSTALLED_APPS=['nsapp']): pass
def test_check_errors_catches_all_exceptions(self): """ Since Python may raise arbitrary exceptions when importing code, check_errors() must catch Exception, not just some subclasses. """ dirname = tempfile.mkdtemp() filename = os.path.join(dirname, 'test_exception.py') self.addCleanup(shutil.rmtree, dirname) with open(filename, 'w') as f: f.write("raise Exception") with extend_sys_path(dirname): with self.assertRaises(Exception): autoreload.check_errors(import_module)('test_exception') self.assertFileFound(filename)
def test_check_errors_only_new(self): """ When a file containing an error is imported in a function wrapped by check_errors(), gen_filenames(only_new=True) returns it. """ dirname = tempfile.mkdtemp() filename = os.path.join(dirname, 'test_syntax_error.py') self.addCleanup(shutil.rmtree, dirname) with open(filename, 'w') as f: f.write("Ceci n'est pas du Python.") with extend_sys_path(dirname): with self.assertRaises(SyntaxError): autoreload.check_errors(import_module)('test_syntax_error') self.assertFileFoundOnlyNew(filename)
def test_deleted_removed(self): """ When a file is deleted, gen_filenames() no longer returns it. """ dirname = tempfile.mkdtemp() filename = os.path.join(dirname, 'test_deleted_removed_module.py') self.addCleanup(shutil.rmtree, dirname) with open(filename, 'w'): pass with extend_sys_path(dirname): import_module('test_deleted_removed_module') self.assertFileFound(filename) os.unlink(filename) self.assertFileNotFound(filename)
def test_shallow_loader(self): "Module existence can be tested inside eggs" egg_name = '%s/test_egg.egg' % self.egg_dir with extend_sys_path(egg_name): egg_module = import_module('egg_module') # An importable child self.assertTrue(module_has_submodule(egg_module, 'good_module')) mod = import_module('egg_module.good_module') self.assertEqual(mod.content, 'Good Module') # A child that exists, but will generate an import error if loaded self.assertTrue(module_has_submodule(egg_module, 'bad_module')) with self.assertRaises(ImportError): import_module('egg_module.bad_module') # A child that doesn't exist self.assertFalse(module_has_submodule(egg_module, 'no_such_module')) with self.assertRaises(ImportError): import_module('egg_module.no_such_module')
def test_deep_loader(self): "Modules deep inside an egg can still be tested for existence" egg_name = '%s/test_egg.egg' % self.egg_dir with extend_sys_path(egg_name): egg_module = import_module('egg_module.sub1.sub2') # An importable child self.assertTrue(module_has_submodule(egg_module, 'good_module')) mod = import_module('egg_module.sub1.sub2.good_module') self.assertEqual(mod.content, 'Deep Good Module') # A child that exists, but will generate an import error if loaded self.assertTrue(module_has_submodule(egg_module, 'bad_module')) with self.assertRaises(ImportError): import_module('egg_module.sub1.sub2.bad_module') # A child that doesn't exist self.assertFalse(module_has_submodule(egg_module, 'no_such_module')) with self.assertRaises(ImportError): import_module('egg_module.sub1.sub2.no_such_module')
def temporary_migration_module(self, app_label='migrations', module=None): """ Allows testing management commands in a temporary migrations module. Wrap all invocations to makemigrations and squashmigrations with this context manager in order to avoid creating migration files in your source tree inadvertently. Takes the application label that will be passed to makemigrations or squashmigrations and the Python path to a migrations module. The migrations module is used as a template for creating the temporary migrations module. If it isn't provided, the application's migrations module is used, if it exists. Returns the filesystem path to the temporary migrations module. """ with tempfile.TemporaryDirectory() as temp_dir: target_dir = tempfile.mkdtemp(dir=temp_dir) with open(os.path.join(target_dir, '__init__.py'), 'w'): pass target_migrations_dir = os.path.join(target_dir, 'migrations') if module is None: module = apps.get_app_config(app_label).name + '.migrations' try: source_migrations_dir = module_dir(import_module(module)) except (ImportError, ValueError): pass else: shutil.copytree(source_migrations_dir, target_migrations_dir) with extend_sys_path(temp_dir): new_module = os.path.basename(target_dir) + '.migrations' with self.settings(MIGRATION_MODULES={app_label: new_module}): yield target_migrations_dir