##// END OF EJS Templates
Refactor with Pathlib for setupbase.py
kevin1kevin1k -
Show More
@@ -1,214 +1,213
1 1 # encoding: utf-8
2 2 """
3 3 This module defines the things that are used in setup.py for building IPython
4 4
5 5 This includes:
6 6
7 7 * The basic arguments to setup
8 8 * Functions for finding things like packages, package data, etc.
9 9 * A function for checking dependencies.
10 10 """
11 11
12 12 # Copyright (c) IPython Development Team.
13 13 # Distributed under the terms of the Modified BSD License.
14 14
15 15 import os
16 from pathlib import Path
16 17 import re
17 18 import sys
18 19 from glob import glob
19 20 from logging import log
20 21
21 22 from setuptools import Command
22 23 from setuptools.command.build_py import build_py
23 24
24 25 from setuptools.command.install import install
25 26 from setuptools.command.install_scripts import install_scripts
26 27
27 28
28 29 #-------------------------------------------------------------------------------
29 30 # Useful globals and utility functions
30 31 #-------------------------------------------------------------------------------
31 32
32 33 # A few handy globals
33 isfile = os.path.isfile
34 pjoin = os.path.join
35 repo_root = os.path.dirname(os.path.abspath(__file__))
34 repo_root = Path(__file__).resolve().parent
36 35
37 36 def execfile(fname, globs, locs=None):
38 37 locs = locs or globs
39 38 with open(fname, encoding="utf-8") as f:
40 39 exec(compile(f.read(), fname, "exec"), globs, locs)
41 40
42 41 #---------------------------------------------------------------------------
43 42 # Basic project information
44 43 #---------------------------------------------------------------------------
45 44
46 45 # release.py contains version, authors, license, url, keywords, etc.
47 execfile(pjoin(repo_root, 'IPython','core','release.py'), globals())
46 with open(repo_root / "IPython" / "core" / "release.py", encoding="utf-8") as f:
47 exec(f.read(), globals())
48 48
49 49 # Create a dict with the basic information
50 50 # This dict is eventually passed to setup after additional keys are added.
51 51 setup_args = dict(
52 52 author = author,
53 53 author_email = author_email,
54 54 license = license,
55 55 )
56 56
57 57 #---------------------------------------------------------------------------
58 58 # Check package data
59 59 #---------------------------------------------------------------------------
60 60
61 61 def check_package_data(package_data):
62 62 """verify that package_data globs make sense"""
63 63 print("checking package data")
64 64 for pkg, data in package_data.items():
65 pkg_root = pjoin(*pkg.split('.'))
65 pkg_root = Path(*pkg.split("."))
66 66 for d in data:
67 path = pjoin(pkg_root, d)
67 path = pkg_root / d
68 68 if '*' in path:
69 69 assert len(glob(path)) > 0, "No files match pattern %s" % path
70 70 else:
71 assert os.path.exists(path), "Missing package data: %s" % path
71 assert path.exists(), f"Missing package data: {path}"
72 72
73 73
74 74 def check_package_data_first(command):
75 75 """decorator for checking package_data before running a given command
76 76
77 77 Probably only needs to wrap build_py
78 78 """
79 79 class DecoratedCommand(command):
80 80 def run(self):
81 81 check_package_data(self.package_data)
82 82 command.run(self)
83 83 return DecoratedCommand
84 84
85 85
86 86 #---------------------------------------------------------------------------
87 87 # Find data files
88 88 #---------------------------------------------------------------------------
89 89
90 90 def find_data_files():
91 91 """
92 92 Find IPython's data_files.
93 93
94 94 Just man pages at this point.
95 95 """
96 96
97 97 if "freebsd" in sys.platform:
98 manpagebase = pjoin('man', 'man1')
98 manpagebase = Path("man") / "man1"
99 99 else:
100 manpagebase = pjoin('share', 'man', 'man1')
100 manpagebase = Path("share") / "man" / "man1"
101 101
102 102 # Simple file lists can be made by hand
103 manpages = [f for f in glob(pjoin('docs','man','*.1.gz')) if isfile(f)]
103 manpages = [f for f in Path("docs/man").glob("*.1.gz") if f.is_file()]
104 104 if not manpages:
105 105 # When running from a source tree, the manpages aren't gzipped
106 manpages = [f for f in glob(pjoin('docs','man','*.1')) if isfile(f)]
106 manpages = [f for f in Path("docs/man").glob("*.1") if f.is_file()]
107 107
108 108 # And assemble the entire output list
109 data_files = [ (manpagebase, manpages) ]
109 data_files = [(str(manpagebase), [str(f) for f in manpages])]
110 110
111 111 return data_files
112 112
113 113
114 114 # The two functions below are copied from IPython.utils.path, so we don't need
115 115 # to import IPython during setup, which fails on Python 3.
116 116
117 117 def target_outdated(target,deps):
118 118 """Determine whether a target is out of date.
119 119
120 120 target_outdated(target,deps) -> 1/0
121 121
122 122 deps: list of filenames which MUST exist.
123 123 target: single filename which may or may not exist.
124 124
125 125 If target doesn't exist or is older than any file listed in deps, return
126 126 true, otherwise return false.
127 127 """
128 128 try:
129 target_time = os.path.getmtime(target)
130 except os.error:
129 target_time = Path(target).stat().st_mtime
130 except FileNotFoundError:
131 131 return 1
132 132 for dep in deps:
133 dep_time = os.path.getmtime(dep)
133 dep_time = Path(dep).stat().st_mtime
134 134 if dep_time > target_time:
135 135 # print("For target",target,"Dep failed:",dep) # dbg
136 136 # print("times (dep,tar):",dep_time,target_time) # dbg
137 137 return 1
138 138 return 0
139 139
140 140
141 141 def target_update(target,deps,cmd):
142 142 """Update a target with a given command given a list of dependencies.
143 143
144 144 target_update(target,deps,cmd) -> runs cmd if target is outdated.
145 145
146 146 This is just a wrapper around target_outdated() which calls the given
147 147 command if target is outdated."""
148 148
149 149 if target_outdated(target,deps):
150 150 os.system(cmd)
151 151
152 152 #---------------------------------------------------------------------------
153 153 # VCS related
154 154 #---------------------------------------------------------------------------
155 155
156 156 def git_prebuild(pkg_dir, build_cmd=build_py):
157 157 """Return extended build or sdist command class for recording commit
158 158
159 159 records git commit in IPython.utils._sysinfo.commit
160 160
161 161 for use in IPython.utils.sysinfo.sys_info() calls after installation.
162 162 """
163 163
164 164 class MyBuildPy(build_cmd):
165 165 ''' Subclass to write commit data into installation tree '''
166 166 def run(self):
167 167 # loose as `.dev` is suppose to be invalid
168 168 print("check version number")
169 169 loose_pep440re = re.compile(r'^(\d+)\.(\d+)\.(\d+((a|b|rc)\d+)?)(\.post\d+)?(\.dev\d*)?$')
170 170 if not loose_pep440re.match(version):
171 171 raise ValueError("Version number '%s' is not valid (should match [N!]N(.N)*[{a|b|rc}N][.postN][.devN])" % version)
172 172
173 173
174 174 build_cmd.run(self)
175 175 # this one will only fire for build commands
176 176 if hasattr(self, 'build_lib'):
177 177 self._record_commit(self.build_lib)
178 178
179 179 def make_release_tree(self, base_dir, files):
180 180 # this one will fire for sdist
181 181 build_cmd.make_release_tree(self, base_dir, files)
182 182 self._record_commit(base_dir)
183 183
184 184 def _record_commit(self, base_dir):
185 185 import subprocess
186 186 proc = subprocess.Popen('git rev-parse --short HEAD',
187 187 stdout=subprocess.PIPE,
188 188 stderr=subprocess.PIPE,
189 189 shell=True)
190 190 repo_commit, _ = proc.communicate()
191 191 repo_commit = repo_commit.strip().decode("ascii")
192 192
193 out_pth = pjoin(base_dir, pkg_dir, 'utils', '_sysinfo.py')
194 if os.path.isfile(out_pth) and not repo_commit:
193 out_pth = Path(base_dir) / pkg_dir / "utils" / "_sysinfo.py"
194 if out_pth.is_file() and not repo_commit:
195 195 # nothing to write, don't clobber
196 196 return
197 197
198 print("writing git commit '%s' to %s" % (repo_commit, out_pth))
198 print(f"writing git commit '{repo_commit}' to {out_pth}")
199 199
200 200 # remove to avoid overwriting original via hard link
201 201 try:
202 os.remove(out_pth)
203 except (IOError, OSError):
202 out_pth.unlink()
203 except FileNotFoundError:
204 204 pass
205 with open(out_pth, "w", encoding="utf-8") as out_file:
205 with out_pth.open("w", encoding="utf-8") as out_file:
206 206 out_file.writelines(
207 207 [
208 208 "# GENERATED BY setup.py\n",
209 'commit = "%s"\n' % repo_commit,
209 f'commit = "{repo_commit}"\n',
210 210 ]
211 211 )
212 212
213 213 return MyBuildPy
214
General Comments 0
You need to be logged in to leave comments. Login now