summaryrefslogtreecommitdiff
path: root/src/setuptools_scm/__init__.py
blob: eebd9bb5089f54bfdd342efb6210c5ecb13d1c53 (plain)
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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
"""
:copyright: 2010-2015 by Ronny Pfannschmidt
:license: MIT
"""
import os
import warnings

from .config import (
    Configuration,
    DEFAULT_VERSION_SCHEME,
    DEFAULT_LOCAL_SCHEME,
    DEFAULT_TAG_REGEX,
)
from .utils import function_has_arg, string_types
from .version import format_version, meta
from .discover import iter_matching_entrypoints

PRETEND_KEY = "SETUPTOOLS_SCM_PRETEND_VERSION"

TEMPLATES = {
    ".py": """\
# coding: utf-8
# file generated by setuptools_scm
# don't change, don't track in version control
version = {version!r}
""",
    ".txt": "{version}",
}


def version_from_scm(root):
    warnings.warn(
        "version_from_scm is deprecated please use get_version",
        category=DeprecationWarning,
    )
    config = Configuration()
    config.root = root
    # TODO: Is it API?
    return _version_from_entrypoints(config)


def _call_entrypoint_fn(root, config, fn):
    if function_has_arg(fn, "config"):
        return fn(root, config=config)
    else:
        warnings.warn(
            "parse functions are required to provide a named argument"
            " 'config' in the future.",
            category=PendingDeprecationWarning,
            stacklevel=2,
        )
        return fn(root)


def _version_from_entrypoints(config, fallback=False):
    if fallback:
        entrypoint = "setuptools_scm.parse_scm_fallback"
        root = config.fallback_root
    else:
        entrypoint = "setuptools_scm.parse_scm"
        root = config.absolute_root
    for ep in iter_matching_entrypoints(root, entrypoint):
        version = _call_entrypoint_fn(root, config, ep.load())

        if version:
            return version


def dump_version(root, version, write_to, template=None):
    assert isinstance(version, string_types)
    if not write_to:
        return
    target = os.path.normpath(os.path.join(root, write_to))
    ext = os.path.splitext(target)[1]
    template = template or TEMPLATES.get(ext)

    if template is None:
        raise ValueError(
            "bad file format: '{}' (of {}) \nonly *.txt and *.py are supported".format(
                os.path.splitext(target)[1], target
            )
        )
    with open(target, "w") as fp:
        fp.write(template.format(version=version))


def _do_parse(config):
    pretended = os.environ.get(PRETEND_KEY)
    if pretended:
        # we use meta here since the pretended version
        # must adhere to the pep to begin with
        return meta(tag=pretended, preformatted=True, config=config)

    if config.parse:
        parse_result = _call_entrypoint_fn(config.absolute_root, config, config.parse)
        if isinstance(parse_result, string_types):
            raise TypeError(
                "version parse result was a string\nplease return a parsed version"
            )
        version = parse_result or _version_from_entrypoints(config, fallback=True)
    else:
        # include fallbacks after dropping them from the main entrypoint
        version = _version_from_entrypoints(config) or _version_from_entrypoints(
            config, fallback=True
        )

    if version:
        return version

    raise LookupError(
        "setuptools-scm was unable to detect version for %r.\n\n"
        "Make sure you're either building from a fully intact git repository "
        "or PyPI tarballs. Most other sources (such as GitHub's tarballs, a "
        "git checkout without the .git folder) don't contain the necessary "
        "metadata and will not work.\n\n"
        "For example, if you're using pip, instead of "
        "https://github.com/user/proj/archive/master.zip "
        "use git+https://github.com/user/proj.git#egg=proj" % config.absolute_root
    )


def get_version(
    root=".",
    version_scheme=DEFAULT_VERSION_SCHEME,
    local_scheme=DEFAULT_LOCAL_SCHEME,
    write_to=None,
    write_to_template=None,
    relative_to=None,
    tag_regex=DEFAULT_TAG_REGEX,
    fallback_version=None,
    fallback_root=".",
    parse=None,
    git_describe_command=None,
):
    """
    If supplied, relative_to should be a file from which root may
    be resolved. Typically called by a script or module that is not
    in the root of the repository to direct setuptools_scm to the
    root of the repository by supplying ``__file__``.
    """

    config = Configuration(**locals())
    return _get_version(config)


def _get_version(config):
    parsed_version = _do_parse(config)

    if parsed_version:
        version_string = format_version(
            parsed_version,
            version_scheme=config.version_scheme,
            local_scheme=config.local_scheme,
        )
        dump_version(
            root=config.root,
            version=version_string,
            write_to=config.write_to,
            template=config.write_to_template,
        )

        return version_string