ianb / pip (http://pip.openplans.org/)

Pip Installs Packages. For Python.

Clone this repository (size: 659.8 KB): HTTPS / SSH
$ hg clone http://bitbucket.org/ianb/pip/

Changed (Δ554 bytes):

raw changeset »

pip/req.py (43 lines added, 37 lines removed)

pip/util.py (3 lines added, 16 lines removed)

Up to file-list pip/req.py:

@@ -19,7 +19,7 @@ from pip.log import logger
19
19
from pip.util import display_path, rmtree, format_size
20
20
from pip.util import splitext, ask, backup_dir
21
21
from pip.util import url_to_filename, filename_to_url
22
from pip.util import is_url, is_filename, strip_prefix
22
from pip.util import is_url, is_filename, is_framework_layout
23
23
from pip.util import make_path_relative, is_svn_page, file_contents
24
24
from pip.util import has_leading_dir, split_leading_dir
25
25
from pip.util import get_file_content
@@ -1344,9 +1344,22 @@ class UninstallPathSet(object):
1344
1344
        self.save_dir = None
1345
1345
        self._moved_paths = []
1346
1346
1347
    def _permitted(self, path):
1348
        """
1349
        Return True if the given path is one we are permitted to remove,
1350
        False otherwise.
1351
1352
        """
1353
        ok_prefixes = [self.prefix]
1354
        # Yep, we are special casing the framework layout of MacPython here
1355
        if is_framework_layout(sys.prefix):
1356
            for location in ('/Library', '/usr/local'):
1357
                if path.startswith(location):
1358
                    ok_prefixes.append(location)
1359
        return any([path.startswith(prefix) for prefix in ok_prefixes])
1360
        
1347
1361
    def _can_uninstall(self):
1348
        prefix, stripped = strip_prefix(self.location, self.prefix)
1349
        if not stripped:
1362
        if not self._permitted(self.location):
1350
1363
            logger.notify("Not uninstalling %s at %s, outside environment %s"
1351
1364
                          % (self.dist.project_name, self.dist.location,
1352
1365
                             self.prefix))
@@ -1354,24 +1367,23 @@ class UninstallPathSet(object):
1354
1367
        return True
1355
1368
1356
1369
    def add(self, path):
1357
        path = os.path.abspath(path)
1370
        path = os.path.normcase(os.path.abspath(path))
1358
1371
        if not os.path.exists(path):
1359
1372
            return
1360
        prefix, stripped = strip_prefix(os.path.normcase(path), self.prefix)
1361
        if stripped:
1362
            self.paths.add((prefix, stripped))
1373
        if self._permitted(path):
1374
            self.paths.add(path)
1363
1375
        else:
1364
            self._refuse.add((prefix, path))
1376
            self._refuse.add(path)
1365
1377
1366
1378
    def add_pth(self, pth_file, entry):
1367
        prefix, stripped = strip_prefix(os.path.normcase(pth_file), self.prefix)
1368
        if stripped:
1379
        pth_file = os.path.normcase(pth_file)
1380
        if self._permitted(pth_file):
1369
1381
            entry = os.path.normcase(entry)
1370
            if stripped not in self.pth:
1371
                self.pth[stripped] = UninstallPthEntries(os.path.join(prefix, stripped))
1372
            self.pth[stripped].add(os.path.normcase(entry))
1382
            if pth_file not in self.pth:
1383
                self.pth[pth_file] = UninstallPthEntries(pth_file)
1384
            self.pth[pth_file].add(entry)
1373
1385
        else:
1374
            self._refuse.add((prefix, pth_file))
1386
            self._refuse.add(pth_file)
1375
1387
1376
1388
    def compact(self, paths):
1377
1389
        """Compact a path set to contain the minimal number of paths
@@ -1379,15 +1391,11 @@ class UninstallPathSet(object):
1379
1391
        /a/path/to/a/file.txt are both in the set, leave only the
1380
1392
        shorter path."""
1381
1393
        short_paths = set()
1382
        def sort_set(x, y):
1383
            prefix_x, path_x = x
1384
            prefix_y, path_y = y
1385
            return cmp(len(path_x), len(path_y))
1386
        for prefix, path in sorted(paths, sort_set):
1394
        for path in sorted(paths, lambda x, y: cmp(len(x), len(y))):
1387
1395
            if not any([(path.startswith(shortpath) and
1388
1396
                         path[len(shortpath.rstrip(os.path.sep))] == os.path.sep)
1389
                        for shortprefix, shortpath in short_paths]):
1390
                short_paths.add((prefix, path))
1397
                        for shortpath in short_paths]):
1398
                short_paths.add(path)
1391
1399
        return short_paths
1392
1400
1393
1401
    def remove(self, auto_confirm=False):
@@ -1402,22 +1410,21 @@ class UninstallPathSet(object):
1402
1410
            if auto_confirm:
1403
1411
                response = 'y'
1404
1412
            else:
1405
                for prefix, path in paths:
1406
                    logger.notify(os.path.join(prefix, path))
1413
                for path in paths:
1414
                    logger.notify(path)
1407
1415
                response = ask('Proceed (y/n)? ', ('y', 'n'))
1408
1416
            if self._refuse:
1409
1417
                logger.notify('Not removing or modifying (outside of prefix):')
1410
                for prefix, path in self.compact(self._refuse):
1411
                    logger.notify(os.path.join(prefix, path))
1418
                for path in self.compact(self._refuse):
1419
                    logger.notify(path)
1412
1420
            if response == 'y':
1413
1421
                self.save_dir = tempfile.mkdtemp('-uninstall', 'pip-')
1414
                for prefix, path in paths:
1415
                    full_path = os.path.join(prefix, path)
1416
                    new_path = os.path.join(self.save_dir, path)
1417
                    new_dir = os.path.dirname(new_path)
1418
                    logger.info('Removing file or directory %s' % full_path)
1419
                    self._moved_paths.append((prefix, path))
1420
                    os.renames(full_path, new_path)
1422
                for path in paths:
1423
                    new_path = os.path.join(self.save_dir,
1424
                                            path.lstrip(os.path.sep))
1425
                    logger.info('Removing file or directory %s' % path)
1426
                    self._moved_paths.append(path)
1427
                    os.renames(path, new_path)
1421
1428
                for pth in self.pth.values():
1422
1429
                    pth.remove()
1423
1430
                logger.notify('Successfully uninstalled %s' % self.dist.project_name)
@@ -1431,11 +1438,10 @@ class UninstallPathSet(object):
1431
1438
            logger.error("Can't roll back %s; was not uninstalled" % self.dist.project_name)
1432
1439
            return False
1433
1440
        logger.notify('Rolling back uninstall of %s' % self.dist.project_name)
1434
        for prefix, path in self._moved_paths:
1435
            tmp_path = os.path.join(self.save_dir, path)
1436
            real_path = os.path.join(prefix, path)
1437
            logger.info('Replacing %s' % real_path)
1438
            os.renames(tmp_path, real_path)
1441
        for path in self._moved_paths:
1442
            tmp_path = os.path.join(self.save_dir, path.lstrip(os.path.sep))
1443
            logger.info('Replacing %s' % path)
1444
            os.renames(tmp_path, path)
1439
1445
        for pth in self.pth:
1440
1446
            pth.rollback()
1441
1447

Up to file-list pip/util.py:

@@ -190,20 +190,6 @@ def is_filename(name):
190
190
        return False
191
191
    return True
192
192
193
def strip_prefix(path, prefix):
194
    """ If ``path`` begins with ``prefix``, return ``path`` with
195
    ``prefix`` stripped off.  Otherwise return None."""
196
    prefixes = [prefix]
197
    # Yep, we are special casing the framework layout of MacPython here
198
    if is_framework_layout(sys.prefix):
199
        for location in ('/Library', '/usr/local'):
200
            if path.startswith(location):
201
                prefixes.append(location)
202
    for prefix in prefixes:
203
        if path.startswith(prefix):
204
            return prefix, path.replace(prefix + os.path.sep, '')
205
    return None, None
206
207
193
def is_svn_page(html):
208
194
    """Returns true if the page appears to be the index page of an svn repository"""
209
195
    return (re.search(r'<title>[^<]*Revision \d+:', html)
@@ -269,11 +255,12 @@ def make_path_relative(path, rel_to):
269
255
        return '.' + os.path.sep
270
256
    return os.path.sep.join(full_parts)
271
257
272
def is_framework_layout(path):
258
def is_framework_layout(site_packages_dir):
273
259
    """Return True if the current platform is the default Python of Mac OS X
274
260
    which installs scripts in /usr/local/bin"""
275
261
    return (sys.platform[:6] == 'darwin' and
276
            (path[:9] == '/Library/' or path[:16] == '/System/Library/'))
262
            (site_packages_dir[:9] == '/Library/' or
263
             site_packages_dir[:16] == '/System/Library/'))
277
264
278
265
_scheme_re = re.compile(r'^(http|https|file):', re.I)
279
266
_url_slash_drive_re = re.compile(r'/*([a-z])\|', re.I)