diff options
Diffstat (limited to 'venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_vendor/distlib/scripts.py')
-rw-r--r-- | venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_vendor/distlib/scripts.py | 415 |
1 files changed, 415 insertions, 0 deletions
diff --git a/venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_vendor/distlib/scripts.py b/venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_vendor/distlib/scripts.py new file mode 100644 index 0000000..440bd30 --- /dev/null +++ b/venv/lib/python3.7/site-packages/pip-10.0.1-py3.7.egg/pip/_vendor/distlib/scripts.py | |||
@@ -0,0 +1,415 @@ | |||
1 | # -*- coding: utf-8 -*- | ||
2 | # | ||
3 | # Copyright (C) 2013-2015 Vinay Sajip. | ||
4 | # Licensed to the Python Software Foundation under a contributor agreement. | ||
5 | # See LICENSE.txt and CONTRIBUTORS.txt. | ||
6 | # | ||
7 | from io import BytesIO | ||
8 | import logging | ||
9 | import os | ||
10 | import re | ||
11 | import struct | ||
12 | import sys | ||
13 | |||
14 | from .compat import sysconfig, detect_encoding, ZipFile | ||
15 | from .resources import finder | ||
16 | from .util import (FileOperator, get_export_entry, convert_path, | ||
17 | get_executable, in_venv) | ||
18 | |||
19 | logger = logging.getLogger(__name__) | ||
20 | |||
21 | _DEFAULT_MANIFEST = ''' | ||
22 | <?xml version="1.0" encoding="UTF-8" standalone="yes"?> | ||
23 | <assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"> | ||
24 | <assemblyIdentity version="1.0.0.0" | ||
25 | processorArchitecture="X86" | ||
26 | name="%s" | ||
27 | type="win32"/> | ||
28 | |||
29 | <!-- Identify the application security requirements. --> | ||
30 | <trustInfo xmlns="urn:schemas-microsoft-com:asm.v3"> | ||
31 | <security> | ||
32 | <requestedPrivileges> | ||
33 | <requestedExecutionLevel level="asInvoker" uiAccess="false"/> | ||
34 | </requestedPrivileges> | ||
35 | </security> | ||
36 | </trustInfo> | ||
37 | </assembly>'''.strip() | ||
38 | |||
39 | # check if Python is called on the first line with this expression | ||
40 | FIRST_LINE_RE = re.compile(b'^#!.*pythonw?[0-9.]*([ \t].*)?$') | ||
41 | SCRIPT_TEMPLATE = r'''# -*- coding: utf-8 -*- | ||
42 | if __name__ == '__main__': | ||
43 | import sys, re | ||
44 | |||
45 | def _resolve(module, func): | ||
46 | __import__(module) | ||
47 | mod = sys.modules[module] | ||
48 | parts = func.split('.') | ||
49 | result = getattr(mod, parts.pop(0)) | ||
50 | for p in parts: | ||
51 | result = getattr(result, p) | ||
52 | return result | ||
53 | |||
54 | try: | ||
55 | sys.argv[0] = re.sub(r'(-script\.pyw?|\.exe)?$', '', sys.argv[0]) | ||
56 | |||
57 | func = _resolve('%(module)s', '%(func)s') | ||
58 | rc = func() # None interpreted as 0 | ||
59 | except Exception as e: # only supporting Python >= 2.6 | ||
60 | sys.stderr.write('%%s\n' %% e) | ||
61 | rc = 1 | ||
62 | sys.exit(rc) | ||
63 | ''' | ||
64 | |||
65 | |||
66 | def _enquote_executable(executable): | ||
67 | if ' ' in executable: | ||
68 | # make sure we quote only the executable in case of env | ||
69 | # for example /usr/bin/env "/dir with spaces/bin/jython" | ||
70 | # instead of "/usr/bin/env /dir with spaces/bin/jython" | ||
71 | # otherwise whole | ||
72 | if executable.startswith('/usr/bin/env '): | ||
73 | env, _executable = executable.split(' ', 1) | ||
74 | if ' ' in _executable and not _executable.startswith('"'): | ||
75 | executable = '%s "%s"' % (env, _executable) | ||
76 | else: | ||
77 | if not executable.startswith('"'): | ||
78 | executable = '"%s"' % executable | ||
79 | return executable | ||
80 | |||
81 | |||
82 | class ScriptMaker(object): | ||
83 | """ | ||
84 | A class to copy or create scripts from source scripts or callable | ||
85 | specifications. | ||
86 | """ | ||
87 | script_template = SCRIPT_TEMPLATE | ||
88 | |||
89 | executable = None # for shebangs | ||
90 | |||
91 | def __init__(self, source_dir, target_dir, add_launchers=True, | ||
92 | dry_run=False, fileop=None): | ||
93 | self.source_dir = source_dir | ||
94 | self.target_dir = target_dir | ||
95 | self.add_launchers = add_launchers | ||
96 | self.force = False | ||
97 | self.clobber = False | ||
98 | # It only makes sense to set mode bits on POSIX. | ||
99 | self.set_mode = (os.name == 'posix') or (os.name == 'java' and | ||
100 | os._name == 'posix') | ||
101 | self.variants = set(('', 'X.Y')) | ||
102 | self._fileop = fileop or FileOperator(dry_run) | ||
103 | |||
104 | self._is_nt = os.name == 'nt' or ( | ||
105 | os.name == 'java' and os._name == 'nt') | ||
106 | |||
107 | def _get_alternate_executable(self, executable, options): | ||
108 | if options.get('gui', False) and self._is_nt: # pragma: no cover | ||
109 | dn, fn = os.path.split(executable) | ||
110 | fn = fn.replace('python', 'pythonw') | ||
111 | executable = os.path.join(dn, fn) | ||
112 | return executable | ||
113 | |||
114 | if sys.platform.startswith('java'): # pragma: no cover | ||
115 | def _is_shell(self, executable): | ||
116 | """ | ||
117 | Determine if the specified executable is a script | ||
118 | (contains a #! line) | ||
119 | """ | ||
120 | try: | ||
121 | with open(executable) as fp: | ||
122 | return fp.read(2) == '#!' | ||
123 | except (OSError, IOError): | ||
124 | logger.warning('Failed to open %s', executable) | ||
125 | return False | ||
126 | |||
127 | def _fix_jython_executable(self, executable): | ||
128 | if self._is_shell(executable): | ||
129 | # Workaround for Jython is not needed on Linux systems. | ||
130 | import java | ||
131 | |||
132 | if java.lang.System.getProperty('os.name') == 'Linux': | ||
133 | return executable | ||
134 | elif executable.lower().endswith('jython.exe'): | ||
135 | # Use wrapper exe for Jython on Windows | ||
136 | return executable | ||
137 | return '/usr/bin/env %s' % executable | ||
138 | |||
139 | def _build_shebang(self, executable, post_interp): | ||
140 | """ | ||
141 | Build a shebang line. In the simple case (on Windows, or a shebang line | ||
142 | which is not too long or contains spaces) use a simple formulation for | ||
143 | the shebang. Otherwise, use /bin/sh as the executable, with a contrived | ||
144 | shebang which allows the script to run either under Python or sh, using | ||
145 | suitable quoting. Thanks to Harald Nordgren for his input. | ||
146 | |||
147 | See also: http://www.in-ulm.de/~mascheck/various/shebang/#length | ||
148 | https://hg.mozilla.org/mozilla-central/file/tip/mach | ||
149 | """ | ||
150 | if os.name != 'posix': | ||
151 | simple_shebang = True | ||
152 | else: | ||
153 | # Add 3 for '#!' prefix and newline suffix. | ||
154 | shebang_length = len(executable) + len(post_interp) + 3 | ||
155 | if sys.platform == 'darwin': | ||
156 | max_shebang_length = 512 | ||
157 | else: | ||
158 | max_shebang_length = 127 | ||
159 | simple_shebang = ((b' ' not in executable) and | ||
160 | (shebang_length <= max_shebang_length)) | ||
161 | |||
162 | if simple_shebang: | ||
163 | result = b'#!' + executable + post_interp + b'\n' | ||
164 | else: | ||
165 | result = b'#!/bin/sh\n' | ||
166 | result += b"'''exec' " + executable + post_interp + b' "$0" "$@"\n' | ||
167 | result += b"' '''" | ||
168 | return result | ||
169 | |||
170 | def _get_shebang(self, encoding, post_interp=b'', options=None): | ||
171 | enquote = True | ||
172 | if self.executable: | ||
173 | executable = self.executable | ||
174 | enquote = False # assume this will be taken care of | ||
175 | elif not sysconfig.is_python_build(): | ||
176 | executable = get_executable() | ||
177 | elif in_venv(): # pragma: no cover | ||
178 | executable = os.path.join(sysconfig.get_path('scripts'), | ||
179 | 'python%s' % sysconfig.get_config_var('EXE')) | ||
180 | else: # pragma: no cover | ||
181 | executable = os.path.join( | ||
182 | sysconfig.get_config_var('BINDIR'), | ||
183 | 'python%s%s' % (sysconfig.get_config_var('VERSION'), | ||
184 | sysconfig.get_config_var('EXE'))) | ||
185 | if options: | ||
186 | executable = self._get_alternate_executable(executable, options) | ||
187 | |||
188 | if sys.platform.startswith('java'): # pragma: no cover | ||
189 | executable = self._fix_jython_executable(executable) | ||
190 | # Normalise case for Windows | ||
191 | executable = os.path.normcase(executable) | ||
192 | # If the user didn't specify an executable, it may be necessary to | ||
193 | # cater for executable paths with spaces (not uncommon on Windows) | ||
194 | if enquote: | ||
195 | executable = _enquote_executable(executable) | ||
196 | # Issue #51: don't use fsencode, since we later try to | ||
197 | # check that the shebang is decodable using utf-8. | ||
198 | executable = executable.encode('utf-8') | ||
199 | # in case of IronPython, play safe and enable frames support | ||
200 | if (sys.platform == 'cli' and '-X:Frames' not in post_interp | ||
201 | and '-X:FullFrames' not in post_interp): # pragma: no cover | ||
202 | post_interp += b' -X:Frames' | ||
203 | shebang = self._build_shebang(executable, post_interp) | ||
204 | # Python parser starts to read a script using UTF-8 until | ||
205 | # it gets a #coding:xxx cookie. The shebang has to be the | ||
206 | # first line of a file, the #coding:xxx cookie cannot be | ||
207 | # written before. So the shebang has to be decodable from | ||
208 | # UTF-8. | ||
209 | try: | ||
210 | shebang.decode('utf-8') | ||
211 | except UnicodeDecodeError: # pragma: no cover | ||
212 | raise ValueError( | ||
213 | 'The shebang (%r) is not decodable from utf-8' % shebang) | ||
214 | # If the script is encoded to a custom encoding (use a | ||
215 | # #coding:xxx cookie), the shebang has to be decodable from | ||
216 | # the script encoding too. | ||
217 | if encoding != 'utf-8': | ||
218 | try: | ||
219 | shebang.decode(encoding) | ||
220 | except UnicodeDecodeError: # pragma: no cover | ||
221 | raise ValueError( | ||
222 | 'The shebang (%r) is not decodable ' | ||
223 | 'from the script encoding (%r)' % (shebang, encoding)) | ||
224 | return shebang | ||
225 | |||
226 | def _get_script_text(self, entry): | ||
227 | return self.script_template % dict(module=entry.prefix, | ||
228 | func=entry.suffix) | ||
229 | |||
230 | manifest = _DEFAULT_MANIFEST | ||
231 | |||
232 | def get_manifest(self, exename): | ||
233 | base = os.path.basename(exename) | ||
234 | return self.manifest % base | ||
235 | |||
236 | def _write_script(self, names, shebang, script_bytes, filenames, ext): | ||
237 | use_launcher = self.add_launchers and self._is_nt | ||
238 | linesep = os.linesep.encode('utf-8') | ||
239 | if not use_launcher: | ||
240 | script_bytes = shebang + linesep + script_bytes | ||
241 | else: # pragma: no cover | ||
242 | if ext == 'py': | ||
243 | launcher = self._get_launcher('t') | ||
244 | else: | ||
245 | launcher = self._get_launcher('w') | ||
246 | stream = BytesIO() | ||
247 | with ZipFile(stream, 'w') as zf: | ||
248 | zf.writestr('__main__.py', script_bytes) | ||
249 | zip_data = stream.getvalue() | ||
250 | script_bytes = launcher + shebang + linesep + zip_data | ||
251 | for name in names: | ||
252 | outname = os.path.join(self.target_dir, name) | ||
253 | if use_launcher: # pragma: no cover | ||
254 | n, e = os.path.splitext(outname) | ||
255 | if e.startswith('.py'): | ||
256 | outname = n | ||
257 | outname = '%s.exe' % outname | ||
258 | try: | ||
259 | self._fileop.write_binary_file(outname, script_bytes) | ||
260 | except Exception: | ||
261 | # Failed writing an executable - it might be in use. | ||
262 | logger.warning('Failed to write executable - trying to ' | ||
263 | 'use .deleteme logic') | ||
264 | dfname = '%s.deleteme' % outname | ||
265 | if os.path.exists(dfname): | ||
266 | os.remove(dfname) # Not allowed to fail here | ||
267 | os.rename(outname, dfname) # nor here | ||
268 | self._fileop.write_binary_file(outname, script_bytes) | ||
269 | logger.debug('Able to replace executable using ' | ||
270 | '.deleteme logic') | ||
271 | try: | ||
272 | os.remove(dfname) | ||
273 | except Exception: | ||
274 | pass # still in use - ignore error | ||
275 | else: | ||
276 | if self._is_nt and not outname.endswith('.' + ext): # pragma: no cover | ||
277 | outname = '%s.%s' % (outname, ext) | ||
278 | if os.path.exists(outname) and not self.clobber: | ||
279 | logger.warning('Skipping existing file %s', outname) | ||
280 | continue | ||
281 | self._fileop.write_binary_file(outname, script_bytes) | ||
282 | if self.set_mode: | ||
283 | self._fileop.set_executable_mode([outname]) | ||
284 | filenames.append(outname) | ||
285 | |||
286 | def _make_script(self, entry, filenames, options=None): | ||
287 | post_interp = b'' | ||
288 | if options: | ||
289 | args = options.get('interpreter_args', []) | ||
290 | if args: | ||
291 | args = ' %s' % ' '.join(args) | ||
292 | post_interp = args.encode('utf-8') | ||
293 | shebang = self._get_shebang('utf-8', post_interp, options=options) | ||
294 | script = self._get_script_text(entry).encode('utf-8') | ||
295 | name = entry.name | ||
296 | scriptnames = set() | ||
297 | if '' in self.variants: | ||
298 | scriptnames.add(name) | ||
299 | if 'X' in self.variants: | ||
300 | scriptnames.add('%s%s' % (name, sys.version[0])) | ||
301 | if 'X.Y' in self.variants: | ||
302 | scriptnames.add('%s-%s' % (name, sys.version[:3])) | ||
303 | if options and options.get('gui', False): | ||
304 | ext = 'pyw' | ||
305 | else: | ||
306 | ext = 'py' | ||
307 | self._write_script(scriptnames, shebang, script, filenames, ext) | ||
308 | |||
309 | def _copy_script(self, script, filenames): | ||
310 | adjust = False | ||
311 | script = os.path.join(self.source_dir, convert_path(script)) | ||
312 | outname = os.path.join(self.target_dir, os.path.basename(script)) | ||
313 | if not self.force and not self._fileop.newer(script, outname): | ||
314 | logger.debug('not copying %s (up-to-date)', script) | ||
315 | return | ||
316 | |||
317 | # Always open the file, but ignore failures in dry-run mode -- | ||
318 | # that way, we'll get accurate feedback if we can read the | ||
319 | # script. | ||
320 | try: | ||
321 | f = open(script, 'rb') | ||
322 | except IOError: # pragma: no cover | ||
323 | if not self.dry_run: | ||
324 | raise | ||
325 | f = None | ||
326 | else: | ||
327 | first_line = f.readline() | ||
328 | if not first_line: # pragma: no cover | ||
329 | logger.warning('%s: %s is an empty file (skipping)', | ||
330 | self.get_command_name(), script) | ||
331 | return | ||
332 | |||
333 | match = FIRST_LINE_RE.match(first_line.replace(b'\r\n', b'\n')) | ||
334 | if match: | ||
335 | adjust = True | ||
336 | post_interp = match.group(1) or b'' | ||
337 | |||
338 | if not adjust: | ||
339 | if f: | ||
340 | f.close() | ||
341 | self._fileop.copy_file(script, outname) | ||
342 | if self.set_mode: | ||
343 | self._fileop.set_executable_mode([outname]) | ||
344 | filenames.append(outname) | ||
345 | else: | ||
346 | logger.info('copying and adjusting %s -> %s', script, | ||
347 | self.target_dir) | ||
348 | if not self._fileop.dry_run: | ||
349 | encoding, lines = detect_encoding(f.readline) | ||
350 | f.seek(0) | ||
351 | shebang = self._get_shebang(encoding, post_interp) | ||
352 | if b'pythonw' in first_line: # pragma: no cover | ||
353 | ext = 'pyw' | ||
354 | else: | ||
355 | ext = 'py' | ||
356 | n = os.path.basename(outname) | ||
357 | self._write_script([n], shebang, f.read(), filenames, ext) | ||
358 | if f: | ||
359 | f.close() | ||
360 | |||
361 | @property | ||
362 | def dry_run(self): | ||
363 | return self._fileop.dry_run | ||
364 | |||
365 | @dry_run.setter | ||
366 | def dry_run(self, value): | ||
367 | self._fileop.dry_run = value | ||
368 | |||
369 | if os.name == 'nt' or (os.name == 'java' and os._name == 'nt'): # pragma: no cover | ||
370 | # Executable launcher support. | ||
371 | # Launchers are from https://bitbucket.org/vinay.sajip/simple_launcher/ | ||
372 | |||
373 | def _get_launcher(self, kind): | ||
374 | if struct.calcsize('P') == 8: # 64-bit | ||
375 | bits = '64' | ||
376 | else: | ||
377 | bits = '32' | ||
378 | name = '%s%s.exe' % (kind, bits) | ||
379 | # Issue 31: don't hardcode an absolute package name, but | ||
380 | # determine it relative to the current package | ||
381 | distlib_package = __name__.rsplit('.', 1)[0] | ||
382 | result = finder(distlib_package).find(name).bytes | ||
383 | return result | ||
384 | |||
385 | # Public API follows | ||
386 | |||
387 | def make(self, specification, options=None): | ||
388 | """ | ||
389 | Make a script. | ||
390 | |||
391 | :param specification: The specification, which is either a valid export | ||
392 | entry specification (to make a script from a | ||
393 | callable) or a filename (to make a script by | ||
394 | copying from a source location). | ||
395 | :param options: A dictionary of options controlling script generation. | ||
396 | :return: A list of all absolute pathnames written to. | ||
397 | """ | ||
398 | filenames = [] | ||
399 | entry = get_export_entry(specification) | ||
400 | if entry is None: | ||
401 | self._copy_script(specification, filenames) | ||
402 | else: | ||
403 | self._make_script(entry, filenames, options=options) | ||
404 | return filenames | ||
405 | |||
406 | def make_multiple(self, specifications, options=None): | ||
407 | """ | ||
408 | Take a list of specifications and make scripts from them, | ||
409 | :param specifications: A list of specifications. | ||
410 | :return: A list of all absolute pathnames written to, | ||
411 | """ | ||
412 | filenames = [] | ||
413 | for specification in specifications: | ||
414 | filenames.extend(self.make(specification, options)) | ||
415 | return filenames | ||