File: loader.py

package info (click to toggle)
python-invoke 2.2.0-2
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 1,856 kB
  • sloc: python: 15,986; makefile: 24
file content (123 lines) | stat: -rw-r--r-- 4,432 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
import os
import sys
from importlib.util import spec_from_file_location
from types import ModuleType
from pathlib import Path

from pytest import raises

from invoke import Config
from invoke.loader import Loader, FilesystemLoader as FSLoader
from invoke.exceptions import CollectionNotFound

from _util import support


class _BasicLoader(Loader):
    """
    Tests top level Loader behavior with basic finder stub.

    Used when we want to make sure we're testing Loader.load and not e.g.
    FilesystemLoader's specific implementation.
    """

    def find(self, name):
        path = os.path.join(support, name)
        if os.path.exists(f"{path}.py"):
            path = f"{path}.py"
        elif os.path.exists(path):
            path = os.path.join(path, "__init__.py")
        spec = spec_from_file_location(name, path)
        return spec


class Loader_:
    def exhibits_default_config_object(self):
        loader = _BasicLoader()
        assert isinstance(loader.config, Config)
        assert loader.config.tasks.collection_name == "tasks"

    def returns_module_and_location(self):
        mod, path = _BasicLoader().load("namespacing")
        assert isinstance(mod, ModuleType)
        assert path == support

    def may_configure_config_via_constructor(self):
        config = Config({"tasks": {"collection_name": "mytasks"}})
        loader = _BasicLoader(config=config)
        assert loader.config.tasks.collection_name == "mytasks"

    def adds_module_parent_dir_to_sys_path(self):
        # Crummy doesn't-explode test.
        _BasicLoader().load("namespacing")

    def doesnt_duplicate_parent_dir_addition(self):
        _BasicLoader().load("namespacing")
        _BasicLoader().load("namespacing")
        # If the bug is present, this will be 2 at least (and often more, since
        # other tests will pollute it (!).
        assert sys.path.count(support) == 1

    def can_load_package(self):
        loader = _BasicLoader()
        # Load itself doesn't explode (tests 'from . import xxx' internally)
        mod, enclosing_dir = loader.load("package")
        # Properties of returned values look as expected
        # (enclosing dir is always the one above the module-or-package)
        assert enclosing_dir == support
        assert mod.__file__ == str(Path(support) / "package" / "__init__.py")

    def load_name_defaults_to_config_tasks_collection_name(self):
        "load() name defaults to config.tasks.collection_name"

        class MockLoader(_BasicLoader):
            def find(self, name):
                # Sanity
                assert name == "simple_ns_list"
                return super().find(name)

        config = Config({"tasks": {"collection_name": "simple_ns_list"}})
        loader = MockLoader(config=config)
        # More sanity: expect simple_ns_list.py (not tasks.py)
        mod, path = loader.load()
        assert mod.__file__ == os.path.join(support, "simple_ns_list.py")


class FilesystemLoader_:
    def setup_method(self):
        self.loader = FSLoader(start=support)

    def discovery_start_point_defaults_to_cwd(self):
        assert FSLoader().start == os.getcwd()

    def exposes_start_point_as_attribute(self):
        assert FSLoader().start == os.getcwd()

    def start_point_is_configurable_via_kwarg(self):
        start = "/tmp"
        assert FSLoader(start=start).start == start

    def start_point_is_configurable_via_config(self):
        config = Config({"tasks": {"search_root": "nowhere"}})
        assert FSLoader(config=config).start == "nowhere"

    def raises_CollectionNotFound_if_not_found(self):
        with raises(CollectionNotFound):
            self.loader.load("nope")

    def raises_ImportError_if_found_collection_cannot_be_imported(self):
        # Instead of masking with a CollectionNotFound
        with raises(ModuleNotFoundError):
            self.loader.load("oops")

    # TODO: Need CollectionImportError here

    def searches_towards_root_of_filesystem(self):
        # Loaded while root is in same dir as .py
        directly = self.loader.load("foo")
        # Loaded while root is multiple dirs deeper than the .py
        deep = os.path.join(support, "ignoreme", "ignoremetoo")
        indirectly = FSLoader(start=deep).load("foo")
        assert directly[0].__file__ == indirectly[0].__file__
        assert directly[0].__spec__ == indirectly[0].__spec__
        assert directly[1] == indirectly[1]