Ejemplo n.º 1
0
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
Ejemplo n.º 2
0
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
Ejemplo n.º 3
0
    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)
Ejemplo n.º 4
0
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)
Ejemplo n.º 5
0
 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)
Ejemplo n.º 6
0
    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)
Ejemplo n.º 7
0
    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)')
Ejemplo n.º 8
0
    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
Ejemplo n.º 9
0
    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
Ejemplo n.º 10
0
    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
Ejemplo n.º 11
0
    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()
Ejemplo n.º 12
0
    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()