import sys

import pytest
from snakeoil import modules


class TestModules:
    @pytest.fixture(autouse=True)
    def _setup(self, tmp_path):
        # set up some test modules for our use
        packdir = tmp_path / "mod_testpack"
        packdir.mkdir()
        # create an empty file
        (packdir / "__init__.py").touch()

        for directory in (tmp_path, packdir):
            for i in range(3):
                (directory / f"mod_test{i}.py").write_text("def foo(): pass\n")
            (directory / "mod_horked.py").write_text("1/0\n")
        # append them to path
        sys.path.insert(0, str(tmp_path))

        yield

        # pop the test module dir from path
        sys.path.pop(0)
        # make sure we don't keep the sys.modules entries around
        for i in range(3):
            sys.modules.pop("mod_test%s" % i, None)
            sys.modules.pop("mod_testpack.mod_test%s" % i, None)
        sys.modules.pop("mod_testpack", None)
        sys.modules.pop("mod_horked", None)
        sys.modules.pop("mod_testpack.mod_horked", None)

    def test_load_module(self):
        # import an already-imported module
        assert modules.load_module("snakeoil.modules") is modules
        # and a system one, just for kicks
        assert modules.load_module("sys") is sys
        # non-existing module from an existing package
        with pytest.raises(modules.FailedImport):
            modules.load_module("snakeoil.__not_there")
        # (hopefully :) non-existing top-level module/package
        with pytest.raises(modules.FailedImport):
            modules.load_module("__not_there")

        # "Unable to import"
        # pylint: disable=F0401

        # unimported toplevel module
        modtest1 = modules.load_module("mod_test1")
        import mod_test1

        assert mod_test1 is modtest1
        # unimported in-package module
        packtest2 = modules.load_module("mod_testpack.mod_test2")
        from mod_testpack import mod_test2

        assert mod_test2 is packtest2

    def test_load_attribute(self):
        # already imported
        assert modules.load_attribute("sys.path") is sys.path
        # unimported
        myfoo = modules.load_attribute("mod_testpack.mod_test2.foo")

        # "Unable to import"
        # pylint: disable=F0401

        from mod_testpack.mod_test2 import foo

        assert foo is myfoo
        # nonexisting attribute
        with pytest.raises(modules.FailedImport):
            modules.load_attribute("snakeoil.froznicator")
        # nonexisting top-level
        with pytest.raises(modules.FailedImport):
            modules.load_attribute("spork_does_not_exist.foo")
        # not an attr
        with pytest.raises(modules.FailedImport):
            modules.load_attribute("sys")
        # not imported yet
        with pytest.raises(modules.FailedImport):
            modules.load_attribute("mod_testpack.mod_test3")

    def test_load_any(self):
        # import an already-imported module
        assert modules.load_any("snakeoil.modules") is modules
        # attribute of an already imported module
        assert modules.load_any("sys.path") is sys.path
        # already imported toplevel.
        assert sys is modules.load_any("sys")
        # unimported
        myfoo = modules.load_any("mod_testpack.mod_test2.foo")

        # "Unable to import"
        # pylint: disable=F0401

        from mod_testpack.mod_test2 import foo

        assert foo is myfoo
        # nonexisting attribute
        with pytest.raises(modules.FailedImport):
            modules.load_any("snakeoil.froznicator")
        # nonexisting top-level
        with pytest.raises(modules.FailedImport):
            modules.load_any("spork_does_not_exist.foo")
        with pytest.raises(modules.FailedImport):
            modules.load_any("spork_does_not_exist")
        # not imported yet
        with pytest.raises(modules.FailedImport):
            modules.load_any("mod_testpack.mod_test3")

    def test_broken_module(self):
        for func in [modules.load_module, modules.load_any]:
            with pytest.raises(modules.FailedImport):
                func("mod_testpack.mod_horked")
            assert "mod_testpack.mod_horked" not in sys.modules
