def _pep370_fix_path_unix(): """If ~/.local/bin is not in $PATH, automatically add them Do this only with the user's consent. And do not run this check more than once (i.e., run only when PyPM is *first run*). """ if sys.platform.startswith('win'): return # MSI does this on Windows # Proceed only when the terminal is interactive and was never run before isatty = (sys.stdin.isatty() and sys.stdout.isatty()) firstrun_file = P.join(application.locations.user_cache_dir, '.firstrun-pep370') if (not isatty) or P.exists(firstrun_file): return import site from datetime import datetime pathenv = [ P.abspath(x.strip()) for x in os.environ.get('PATH', '').split(':') ] binpath = P.abspath(P.join(site.USER_BASE, 'bin')) profile = P.expanduser('~/.profile' if sys.platform == 'darwin' else '~/.bashrc') profile_lines = [ '# PEP 370 PATH added by PyPM on %s' % datetime.now(), 'export PATH=%s:$PATH' % binpath, ] already_in_profile = P.exists(profile) and profile_lines[1] in [ l.strip() for l in open(profile).readlines() ] # Proceed only if ~/.local/bin is neither in $PATH, nor added to profile if binpath in pathenv or already_in_profile: return # Add to profile on the user's consent msg = ('Packages will install their script files to "%s" ' '(as per PEP 370). This directory is not yet in your $PATH. ' 'Would you like PyPM to add it by appending to "%s"?') % ( concise_path(binpath), concise_path(profile)) if textui.askyesno(wrapped(msg, '*** '), default=True): if P.exists(profile): sh.cp(profile, profile + '.bak') # take a backup first with open(profile, 'a') as f: f.write('\n%s\n' % '\n'.join(profile_lines)) print('You may now reopen your shell for the changes to take effect.') sh.mkdirs(P.dirname(firstrun_file)) with open(firstrun_file, 'w') as f: pass # prevent future runs
def _pep370_fix_path_unix(): """If ~/.local/bin is not in $PATH, automatically add them Do this only with the user's consent. And do not run this check more than once (i.e., run only when PyPM is *first run*). """ if sys.platform.startswith('win'): return # MSI does this on Windows # Proceed only when the terminal is interactive and was never run before isatty = (sys.stdin.isatty() and sys.stdout.isatty()) firstrun_file = P.join(application.locations.user_cache_dir, '.firstrun-pep370') if (not isatty) or P.exists(firstrun_file): return import site from datetime import datetime pathenv = [P.abspath(x.strip()) for x in os.environ.get('PATH', '').split(':')] binpath = P.abspath(P.join(site.USER_BASE, 'bin')) profile = P.expanduser('~/.profile' if sys.platform == 'darwin' else '~/.bashrc') profile_lines = [ '# PEP 370 PATH added by PyPM on %s' % datetime.now(), 'export PATH=%s:$PATH' % binpath, ] already_in_profile = P.exists(profile) and profile_lines[1] in [ l.strip() for l in open(profile).readlines() ] # Proceed only if ~/.local/bin is neither in $PATH, nor added to profile if binpath in pathenv or already_in_profile: return # Add to profile on the user's consent msg = ( 'Packages will install their script files to "%s" ' '(as per PEP 370). This directory is not yet in your $PATH. ' 'Would you like PyPM to add it by appending to "%s"?' ) % (concise_path(binpath), concise_path(profile)) if textui.askyesno(wrapped(msg, '*** '), default=True): if P.exists(profile): sh.cp(profile, profile+'.bak') # take a backup first with open(profile, 'a') as f: f.write('\n%s\n' % '\n'.join(profile_lines)) print ('You may now reopen your shell for the changes to take effect.') sh.mkdirs(P.dirname(firstrun_file)) with open(firstrun_file, 'w') as f: pass # prevent future runs
def printable_location(self): """A printable string for this Python's location A concise representation of the path to this Python's directory that is meant for displaying to the end user. """ return '"{0}" ({1})'.format(concise_path(self.base_dir), self.pyver)
def _fix_script(script_path, python_exe): """replace old #! path with ``python_exe``""" if P.islink(script_path): # ignore symlinks (including broken ones) return # read in 'byte' mode and see if the first two chars are #!; if not # return immediately to avoid failing with binary files. with open(script_path, 'rb') as file: if file.read(2) != b'#!': return with open(script_path, encoding='utf-8') as file: content = file.read() if not content: return first_line = content.splitlines()[0].strip() assert first_line.startswith('#!') # HACK: guess Python shebang (apart from, say, /bin/sh) if 'python' in first_line.lower(): shebang = '#!' + python_exe LOG.info('Fixing script %s', concise_path(script_path)) LOG.debug('Replacing shebang "%s" with "%s"', first_line, shebang) content = content.replace(first_line, shebang) with open(script_path, 'w', encoding='utf-8') as file: file.write(content)
def _fix_script(script_path, python_exe): """replace old #! path with ``python_exe``""" # read in 'byte' mode and see if the first two chars are #!; if not # return immediately to avoid failing with binary files. with open(script_path, 'rb') as file: if file.read(2) != b'#!': return with open(script_path, encoding='utf-8') as file: content = file.read() if not content: return first_line = content.splitlines()[0].strip() assert first_line.startswith('#!') # HACK: guess Python shebang (apart from, say, /bin/sh) if 'python' in first_line.lower(): shebang = '#!' + python_exe LOG.info('Fixing script %s', concise_path(script_path)) LOG.debug('Replacing shebang "%s" with "%s"', first_line, shebang) content = content.replace(first_line, shebang) with open(script_path, 'w', encoding='utf-8') as file: file.write(content)
def printable_location(self): """A printable string for this Python's location A concise representation of the path to this Python's directory that is meant for displaying to the end user. """ return '"{0}" ({1})'.format( concise_path(self.base_dir), self.pyver)
def do_files(self, subcmd, opts, name): """${cmd_name}: Display the list of files in an installed package ${cmd_usage} ${cmd_option_list} """ with self.bootstrapped(): pyenv = self.pypmenv.pyenv name = pje_let_go(name) pkg = self.pypmenv.installed_store.find_only_package(name) for f in sorted(pkg.get_files_list()): display_f = f = join(pyenv.base_dir, f) if not opts.full_path: display_f = concise_path(f) LOG.info('%s %s', display_f, '' if exists(f) else '(missing)')
def do_files(self, subcmd, opts, name): """${cmd_name}: Display the list of files in an installed package ${cmd_usage} ${cmd_option_list} """ with self.bootstrapped(): pyenv = self.pypmenv.pyenv pkg = self.pypmenv.installed_store.find_only_package(name) files = list(sorted(pkg.get_files_list())) for f in files: display_f = f = P.join(pyenv.base_dir, f) if not opts.full_path: display_f = concise_path(f) LOG.info('%s %s', display_f, '' if P.exists(f) else '(missing)') return files
def do_info(self, subcmd, opts): """${cmd_name}: Show version and other diagnostic details ${cmd_usage} ${cmd_option_list} """ with self.bootstrapped(): pyenv = python.GlobalPythonEnvironment() if opts.full is False: LOG.info('PyPM {0} ({1[product_type]} {2})'.format( pypm.__version__, activestate.version_info, activestate.version)) LOG.info('Installation target: {0}'.format( self.pypmenv.pyenv.printable_location)) LOG.info('(type "pypm info --full" for detailed information)') else: LOG.info('PyPM: %s', pypm.__version__) LOG.info('ActivePython %s built on %s <%s>', activestate.version, activestate.version_info['build_time'], concise_path(sys.prefix)) LOG.info('Installation target: Python %s <%s>', self.pypmenv.pyenv.pyver, concise_path(self.pypmenv.pyenv.site_packages_dir)) LOG.info('Platform: %s', PLATNAME) LOG.info('Repositories:\n %s', '\n '.join([ r.url for r in self.pypmenv.repo_store.repository_list])) LOG.info('Business Edition: %s', licensing.user_has_be_license()) LOG.info('Config file:\n (current) %s\n (global) %s', concise_path(self.options.configfile), concise_path(CONF_FILE_GLOBAL)) LOG.info('Log file: %s', concise_path(application.locations.log_file_path)) LOG.info('Repository cache: %s', concise_path(IDX_PATH)) LOG.info('Download cache: %s', concise_path(DOWNLOAD_CACHE)) LOG.info('Install database: %s', concise_path(self.pypmenv.installed_store.storepath)) import applib, appdirs, sqlalchemy, zclockfile LOG.info('Imports:\n %s', '\n '.join([ concise_path(pypm.__file__), concise_path(applib.__file__), concise_path(appdirs.__file__), concise_path(six.__file__), concise_path(zclockfile.__file__), concise_path(sqlalchemy.__file__)])) return self.pypmenv
def do_info(self, subcmd, opts): """${cmd_name}: Show version and other diagnostic details ${cmd_usage} ${cmd_option_list} """ with self.bootstrapped(): pyenv = python.GlobalPythonEnvironment() if opts.full is False: LOG.info('PyPM {0} ({1[product_type]} {2})'.format( pypm.__version__, activestate.version_info, activestate.version)) LOG.info('Installation target: {0}'.format( self.pypmenv.pyenv.printable_location)) LOG.info('(type "pypm info --full" for detailed information)') else: LOG.info('PyPM: %s', pypm.__version__) LOG.info('ActivePython %s built on %s <%s>', activestate.version, activestate.version_info['build_time'], concise_path(sys.prefix)) LOG.info('Installation target: Python %s <%s>', self.pypmenv.pyenv.pyver, concise_path(self.pypmenv.pyenv.site_packages_dir)) LOG.info('Platform: %s', PLATNAME) LOG.info( 'Repositories:\n %s', '\n '.join([ r.url for r in self.pypmenv.repo_store.repository_list ])) LOG.info('Business Edition: %s', licensing.user_has_be_license()) LOG.info('Config file:\n (current) %s\n (global) %s', concise_path(self.options.configfile), concise_path(CONF_FILE_GLOBAL)) LOG.info('Log file: %s', concise_path(application.locations.log_file_path)) LOG.info('Repository cache: %s', concise_path(IDX_PATH)) LOG.info('Download cache: %s', concise_path(DOWNLOAD_CACHE)) LOG.info('Install database: %s', concise_path(self.pypmenv.installed_store.storepath)) import applib, appdirs, sqlalchemy, zclockfile LOG.info( 'Imports:\n %s', '\n '.join([ concise_path(pypm.__file__), concise_path(applib.__file__), concise_path(appdirs.__file__), concise_path(six.__file__), concise_path(zclockfile.__file__), concise_path(sqlalchemy.__file__) ])) return self.pypmenv
def _extract_to_install_scheme(self, bpkgfile, name): pyenv = self.pypmenv.pyenv # Install scheme used by the build environment (i.e., pyenv used by # pypm-builder on our build machines). as_build_scheme = { 'win': { 'purelib': 'lib/site-packages', 'stdlib': 'lib', 'scripts': 'scripts', }, 'unix': { 'purelib': 'lib/python{0}/site-packages'.format(pyenv.pyver), 'stdlib': 'lib/python{0}'.format(pyenv.pyver), 'scripts': 'bin', }, } plat = PLATNAME.startswith('win') and 'win' or 'unix' # Scheme used by pyenv pyenv_scheme = { 'purelib': self._pyenv_scheme_path('purelib'), 'stdlib': self._pyenv_scheme_path('stdlib'), 'scripts': self._pyenv_scheme_path('scripts'), } files_to_overwrite = [] force_overwrite = self.pypmenv.options['force'] # Hack #1: Don't check for distribute and pip, as virtualenvs usually # already have a copy of them installed. if name in ('distribute', 'setuptools', 'pip'): force_overwrite = True with bpkgfile.extract_over2(pyenv.base_dir) as tf: for tinfo in tf.getmembers(): # Replace AS build virtualenv scheme with the user's scheme # Eg: lib/site-packages/XYZ -> %APPDATA%/Python/Python26/XYZ for name, prefix in as_build_scheme[plat].items(): if tinfo.name.lower().startswith(prefix): old = tinfo.name new = pyenv_scheme[name] + old[len(prefix):] if new != old: LOG.debug('fs:extract: transforming "%s" to "%s"', old, new) tinfo.name = new # Check for overwrites if os.path.lexists( tinfo.name) and not os.path.isdir(tinfo.name): # Hack #2: allow overwriting of *.pth files (setuptools # hackishness) eg: [...]/site-packages/setuptools.pth if not tinfo.name.endswith('.pth'): files_to_overwrite.append(tinfo.name) if files_to_overwrite: LOG.debug( 'install requires overwriting of %d files:\n%s', len(files_to_overwrite), '\n'.join([ os.path.join(pyenv.base_dir, f) for f in files_to_overwrite ])) if force_overwrite: LOG.warn('overwriting %d files' % len(files_to_overwrite)) else: errmsg = [ 'cannot overwrite "%s"' % concise_path( os.path.join(pyenv.base_dir, files_to_overwrite[0])) ] if len(files_to_overwrite) > 1: errmsg.append(' (and %d other files)' % (len(files_to_overwrite) - 1, )) errmsg.append( '; run pypm as "pypm --force ..." to overwrite anyway') if len(files_to_overwrite) > 1: errmsg.append( '; run "pypm log" to see the full list of files to be overwritten' ) raise IOError(wrapped(''.join(errmsg))) return tf.getnames()
def _extract_to_install_scheme(self, bpkgfile, name): pyenv = self.pypmenv.pyenv # Install scheme used by the build environment (i.e., pyenv used by # pypm-builder on our build machines). as_build_scheme = { 'win': { 'purelib': 'lib/site-packages', 'stdlib': 'lib', 'scripts': 'scripts', }, 'unix': { 'purelib': 'lib/python{0}/site-packages'.format(pyenv.pyver), 'stdlib': 'lib/python{0}'.format(pyenv.pyver), 'scripts': 'bin', }, } plat = PLATNAME.startswith('win') and 'win' or 'unix' # Scheme used by pyenv pyenv_scheme = { 'purelib': self._pyenv_scheme_path('purelib'), 'stdlib': self._pyenv_scheme_path('stdlib'), 'scripts': self._pyenv_scheme_path('scripts'), } files_to_overwrite = [] force_overwrite = self.pypmenv.options['force'] # Hack #1: Don't check for distribute and pip, as virtualenvs usually # already have a copy of them installed. if name in ('distribute', 'setuptools', 'pip'): force_overwrite = True with bpkgfile.extract_over2(pyenv.base_dir) as tf: for tinfo in tf.getmembers(): # Replace AS build virtualenv scheme with the user's scheme # Eg: lib/site-packages/XYZ -> %APPDATA%/Python/Python26/XYZ for name, prefix in as_build_scheme[plat].items(): if tinfo.name.lower().startswith(prefix): old = tinfo.name new = pyenv_scheme[name] + old[len(prefix):] if new != old: LOG.debug('fs:extract: transforming "%s" to "%s"', old, new) tinfo.name = new # Check for overwrites if os.path.exists(tinfo.name) and not os.path.isdir(tinfo.name): # Hack #2: allow overwriting of *.pth files (setuptools # hackishness) eg: [...]/site-packages/setuptools.pth if not tinfo.name.endswith('.pth'): files_to_overwrite.append(tinfo.name) if files_to_overwrite: LOG.debug( 'install requires overwriting of %d files:\n%s', len(files_to_overwrite), '\n'.join([os.path.join(pyenv.base_dir, f) for f in files_to_overwrite])) if force_overwrite: LOG.warn('overwriting %d files' % len(files_to_overwrite)) else: errmsg = ['cannot overwrite "%s"' % concise_path(os.path.join( pyenv.base_dir, files_to_overwrite[0]))] if len(files_to_overwrite) > 1: errmsg.append(' (and %d other files)' % (len(files_to_overwrite)-1,)) errmsg.append('; run pypm as "pypm --force ..." to overwrite anyway') if len(files_to_overwrite) > 1: errmsg.append('; run "pypm log" to see the full list of files to be overwritten') raise IOError(wrapped(''.join(errmsg))) return tf.getnames()