1# -*- coding: utf-8 -*-
2"""
3Decides if vendor bundles are used or not.
4Setup python path accordingly.
5"""
6
7from __future__ import absolute_import, print_function
8import os.path
9import sys
10
11# -----------------------------------------------------------------------------
12# DEFINES:
13# -----------------------------------------------------------------------------
14HERE = os.path.dirname(__file__)
15TASKS_VENDOR_DIR = os.path.join(HERE, "_vendor")
16INVOKE_BUNDLE = os.path.join(TASKS_VENDOR_DIR, "invoke.zip")
17INVOKE_BUNDLE_VERSION = "1.2.0"
18
19DEBUG_SYSPATH = False
20
21
22# -----------------------------------------------------------------------------
23# EXCEPTIONS:
24# -----------------------------------------------------------------------------
25class VersionRequirementError(SystemExit):
26    pass
27
28
29# -----------------------------------------------------------------------------
30# FUNCTIONS:
31# -----------------------------------------------------------------------------
32def setup_path(invoke_minversion=None):
33    """Setup python search and add ``TASKS_VENDOR_DIR`` (if available)."""
34    # print("INVOKE.tasks: setup_path")
35    if not os.path.isdir(TASKS_VENDOR_DIR):
36        print("SKIP: TASKS_VENDOR_DIR=%s is missing" % TASKS_VENDOR_DIR)
37        return
38    elif os.path.abspath(TASKS_VENDOR_DIR) in sys.path:
39        # -- SETUP ALREADY DONE:
40        # return
41        pass
42
43    use_vendor_bundles = os.environ.get("INVOKE_TASKS_USE_VENDOR_BUNDLES", "no")
44    if need_vendor_bundles(invoke_minversion):
45        use_vendor_bundles = "yes"
46
47    if use_vendor_bundles == "yes":
48        syspath_insert(0, os.path.abspath(TASKS_VENDOR_DIR))
49        if setup_path_for_bundle(INVOKE_BUNDLE, pos=1):
50            import invoke
51            bundle_path = os.path.relpath(INVOKE_BUNDLE, os.getcwd())
52            print("USING: %s (version: %s)" % (bundle_path, invoke.__version__))
53    else:
54        # -- BEST-EFFORT: May rescue something
55        syspath_append(os.path.abspath(TASKS_VENDOR_DIR))
56        setup_path_for_bundle(INVOKE_BUNDLE, pos=len(sys.path))
57
58    if DEBUG_SYSPATH:
59        for index, p in enumerate(sys.path):
60            print("  %d.  %s" % (index, p))
61
62
63def require_invoke_minversion(min_version, verbose=False):
64    """Ensures that :mod:`invoke` has at the least the :param:`min_version`.
65    Otherwise,
66
67    :param min_version: Minimal acceptable invoke version (as string).
68    :param verbose:     Indicates if invoke.version should be shown.
69    :raises: VersionRequirementError=SystemExit if requirement fails.
70    """
71    # -- REQUIRES: sys.path is setup and contains invoke
72    try:
73        import invoke
74        invoke_version = invoke.__version__
75    except ImportError:
76        invoke_version = "__NOT_INSTALLED"
77
78    if invoke_version < min_version:
79        message = "REQUIRE: invoke.version >= %s (but was: %s)" % \
80                  (min_version, invoke_version)
81        message += "\nUSE: pip install invoke>=%s" % min_version
82        raise VersionRequirementError(message)
83
84    # pylint: disable=invalid-name
85    INVOKE_VERSION = os.environ.get("INVOKE_VERSION", None)
86    if verbose and not INVOKE_VERSION:
87        os.environ["INVOKE_VERSION"] = invoke_version
88        print("USING: invoke.version=%s" % invoke_version)
89
90
91def need_vendor_bundles(invoke_minversion=None):
92    invoke_minversion = invoke_minversion or "0.0.0"
93    need_vendor_answers = []
94    need_vendor_answers.append(need_vendor_bundle_invoke(invoke_minversion))
95    # -- REQUIRE: path.py
96    try:
97        import path
98        need_bundle = False
99    except ImportError:
100        need_bundle = True
101    need_vendor_answers.append(need_bundle)
102
103    # -- DIAG: print("INVOKE: need_bundle=%s" % need_bundle1)
104    # return need_bundle1 or need_bundle2
105    return any(need_vendor_answers)
106
107
108def need_vendor_bundle_invoke(invoke_minversion="0.0.0"):
109    # -- REQUIRE: invoke
110    try:
111        import invoke
112        need_bundle = invoke.__version__ < invoke_minversion
113        if need_bundle:
114            del sys.modules["invoke"]
115            del invoke
116    except ImportError:
117        need_bundle = True
118    except Exception:   # pylint: disable=broad-except
119        need_bundle = True
120    return need_bundle
121
122
123# -----------------------------------------------------------------------------
124# UTILITY FUNCTIONS:
125# -----------------------------------------------------------------------------
126def setup_path_for_bundle(bundle_path, pos=0):
127    if os.path.exists(bundle_path):
128        syspath_insert(pos, os.path.abspath(bundle_path))
129        return True
130    return False
131
132
133def syspath_insert(pos, path):
134    if path in sys.path:
135        sys.path.remove(path)
136    sys.path.insert(pos, path)
137
138
139def syspath_append(path):
140    if path in sys.path:
141        sys.path.remove(path)
142    sys.path.append(path)
143
144