|
1 | 1 | # -*- coding: utf-8 -*- |
2 | 2 |
|
3 | | -from ast import parse |
4 | | -from operator import attrgetter, itemgetter |
5 | | -from os import path |
6 | | -import sys |
| 3 | +""" |
| 4 | +setup.py implementation, interesting because it parsed the first __init__.py and |
| 5 | + extracts the `__author__` and `__version__` |
| 6 | +""" |
7 | 7 |
|
| 8 | +from ast import Assign, Name, parse |
| 9 | +from functools import partial |
| 10 | +from operator import attrgetter |
| 11 | +from os import listdir, path |
| 12 | +from os.path import extsep |
| 13 | +import sys |
8 | 14 | from setuptools import find_packages, setup |
9 | 15 |
|
10 | 16 | if sys.version_info[:2] >= (3, 12): |
@@ -74,57 +80,121 @@ class DistutilsPlatformError(Exception): |
74 | 80 | "I don't know where Python installs its library " |
75 | 81 | "on platform '%s'" % os.name |
76 | 82 | ) |
77 | | - |
| 83 | + from ast import Del as Str |
78 | 84 | else: |
79 | 85 | from distutils.sysconfig import get_python_lib |
| 86 | + from ast import Str |
80 | 87 |
|
81 | 88 | if sys.version_info[0] == 2: |
82 | 89 | from itertools import ifilter as filter |
83 | 90 | from itertools import imap as map |
84 | 91 |
|
85 | | -if __name__ == "__main__": |
86 | | - package_name = "offshell" |
| 92 | +if sys.version_info[:2] > (3, 7): |
| 93 | + from ast import Constant |
| 94 | +else: |
| 95 | + from ast import expr |
| 96 | + |
| 97 | + # Constant. Will never be used in Python =< 3.8 |
| 98 | + Constant = type("Constant", (expr,), {}) |
| 99 | + |
| 100 | + |
| 101 | +package_name_verbatim = "offshell" |
| 102 | +package_name = package_name_verbatim.replace("-", "_") |
| 103 | + |
| 104 | +with open( |
| 105 | + path.join(path.dirname(__file__), "README{extsep}md".format(extsep=extsep)), |
| 106 | + "rt", |
| 107 | +) as fh: |
| 108 | + long_description = fh.read() |
| 109 | + |
| 110 | + |
| 111 | +def to_funcs(*paths): |
| 112 | + """ |
| 113 | + Produce function tuples that produce the local and install dir, respectively. |
| 114 | +
|
| 115 | + :param paths: one or more str, referring to relative folder names |
| 116 | + :type paths: ```*paths``` |
| 117 | +
|
| 118 | + :return: 2 functions |
| 119 | + :rtype: ```Tuple[Callable[Optional[List[str]], str], Callable[Optional[List[str]], str]]``` |
| 120 | + """ |
| 121 | + return ( |
| 122 | + partial(path.join, path.dirname(__file__), package_name, *paths), |
| 123 | + partial(path.join, get_python_lib(prefix=""), package_name, *paths), |
| 124 | + ) |
| 125 | + |
| 126 | + |
| 127 | +def main(): |
| 128 | + """Main function for setup.py; this actually does the installation""" |
| 129 | + with open( |
| 130 | + path.join( |
| 131 | + path.abspath(path.dirname(__file__)), |
| 132 | + package_name, |
| 133 | + "__init__{extsep}py".format(extsep=extsep), |
| 134 | + ) |
| 135 | + ) as f: |
| 136 | + parsed_init = parse(f.read()) |
87 | 137 |
|
88 | | - with open(path.join(package_name, "__init__.py")) as f: |
89 | | - __author__, __version__ = map( |
90 | | - lambda const: const.value if hasattr(const, "value") else const.s, |
| 138 | + __author__, __version__, __description__ = map( |
| 139 | + lambda node: node.value if isinstance(node, Constant) else node.s, |
| 140 | + filter( |
| 141 | + lambda node: isinstance(node, (Constant, Str)), |
91 | 142 | map( |
92 | 143 | attrgetter("value"), |
93 | | - map( |
94 | | - itemgetter(0), |
95 | | - map( |
96 | | - attrgetter("body"), |
97 | | - map( |
98 | | - parse, |
99 | | - filter( |
100 | | - lambda line: line.startswith("__version__") |
101 | | - or line.startswith("__author__"), |
102 | | - f, |
| 144 | + filter( |
| 145 | + lambda node: isinstance(node, Assign) |
| 146 | + and any( |
| 147 | + filter( |
| 148 | + lambda name: isinstance(name, Name) and name.id |
| 149 | + in frozenset( |
| 150 | + ("__author__", "__version__", "__description__") |
103 | 151 | ), |
104 | | - ), |
| 152 | + node.targets, |
| 153 | + ) |
105 | 154 | ), |
| 155 | + parsed_init.body, |
106 | 156 | ), |
107 | 157 | ), |
108 | | - ) |
| 158 | + ), |
| 159 | + ) |
109 | 160 |
|
110 | 161 | setup( |
111 | | - name=package_name, |
| 162 | + name=package_name_verbatim, |
112 | 163 | author=__author__, |
113 | 164 | version=__version__, |
114 | | - description="offshell opens an interactive shell into your offscale node. It uses paramiko, a Python implementation of the SSHv2 protocol. It can also output in OpenSSH (e.g.: ~/.ssh/config) format.", |
| 165 | + description=__description__, |
115 | 166 | classifiers=[ |
116 | 167 | "Development Status :: 7 - Inactive", |
117 | 168 | "Intended Audience :: Developers", |
118 | 169 | "Topic :: Software Development", |
119 | 170 | "Topic :: Software Development :: Libraries :: Python Modules", |
120 | | - "License :: OSI Approved :: MIT License", |
| 171 | + "License :: CC0 1.0 Universal (CC0 1.0) Public Domain Dedication", |
121 | 172 | "License :: OSI Approved :: Apache Software License", |
| 173 | + "License :: OSI Approved :: MIT License", |
122 | 174 | "Programming Language :: Python", |
| 175 | + "Programming Language :: Python :: 2", |
123 | 176 | "Programming Language :: Python :: 2.7", |
124 | 177 | "Programming Language :: Python :: 3", |
| 178 | + "Programming Language :: Python :: 3.5", |
| 179 | + "Programming Language :: Python :: 3.6", |
| 180 | + "Programming Language :: Python :: 3.7", |
| 181 | + "Programming Language :: Python :: 3.8", |
| 182 | + "Programming Language :: Python :: 3.9", |
| 183 | + "Programming Language :: Python :: 3.10", |
| 184 | + "Programming Language :: Python :: 3.11", |
| 185 | + "Programming Language :: Python :: 3.12", |
125 | 186 | ], |
126 | 187 | install_requires=["pyyaml"], |
127 | 188 | test_suite="{}{}tests".format(package_name, path.sep), |
128 | 189 | packages=find_packages(), |
129 | 190 | package_dir={package_name: package_name}, |
130 | 191 | ) |
| 192 | + |
| 193 | + |
| 194 | +def setup_py_main(): |
| 195 | + """Calls main if `__name__ == '__main__'`""" |
| 196 | + if __name__ == "__main__": |
| 197 | + main() |
| 198 | + |
| 199 | + |
| 200 | +setup_py_main() |
0 commit comments