tarek / pep376

code for PEP 376

Clone this repository (size: 66.5 KB): HTTPS / SSH
$ hg clone http://bitbucket.org/tarek/pep376/
commit 24: db53b5a1ecf6
parent 23: a7dde278d993
branch: default
added zip support
ta...@MacZiade
9 months ago

Changed (Δ8.7 KB):

raw changeset »

pkgutil.py (61 lines added, 6 lines removed)

site-packages.zip (binary file changed)

test_pkgutil.py (54 lines added, 0 lines removed)

Up to file-list pkgutil.py:

@@ -10,6 +10,7 @@ import csv
10
10
import sys
11
11
import re
12
12
import threading
13
from zipfile import is_zipfile, ZipFile
13
14
14
15
from distutils.dist  import  DistributionMetadata
15
16
from distutils.errors import DistutilsError
@@ -150,7 +151,8 @@ class Distribution(object):
150
151
        self.container = os.path.split(path)[0]
151
152
        self.pkg_info_path = join(path, 'PKG-INFO')
152
153
        self.record_path = join(path, 'RECORD')
153
        self.metadata = _DistributionMetadata(self.pkg_info_path)
154
        pkginfo = self._open_pkginfo()
155
        self.metadata = _DistributionMetadata(pkginfo)
154
156
        self.name = self.metadata.name
155
157
        self._files = None
156
158
@@ -162,10 +164,16 @@ class Distribution(object):
162
164
        if self._files is None:
163
165
            self._files = self._read_record()
164
166
167
    def _open_pkginfo(self):
168
        return open(self.pkg_info_path)
169
170
    def _open_record(self):
171
        return open(self.record_path)
172
165
173
    def _read_record(self):
166
174
        """Reads RECORD."""
167
175
        files = []
168
        for row in csv.reader(open(self.record_path)):
176
        for row in csv.reader(self._open_record()):
169
177
            if row == []:
170
178
                continue
171
179
            location = row[0]
@@ -242,6 +250,19 @@ class Distribution(object):
242
250
                                     (path, self.info_path))
243
251
        return open(local_path, binary and 'rb' or 'r')
244
252
253
class ZippedDistribution(Distribution):
254
255
    def __init__(self, zipfile, path):
256
        self.zipfile = zipfile
257
        super(ZippedDistribution, self).__init__(path)
258
        self.container = self.zipfile.filename
259
260
    def _open_pkginfo(self):
261
        return self.zipfile.open(self.pkg_info_path)
262
263
    def _open_record(self):
264
        return self.zipfile.open(self.record_path)
265
245
266
#
246
267
# DistributionDirectory represents a directory that contains egg-info files
247
268
#
@@ -283,6 +304,35 @@ class DistributionDirectory(set):
283
304
            return users[0]
284
305
        return None
285
306
307
class ZippedDistributionDirectory(DistributionDirectory):
308
309
    def __init__(self, path):
310
        if not is_zipfile(path):
311
            raise TypeError('%s path is not a zipfile' % path)
312
        self.path = path
313
        self._zip_file = ZipFile(path)
314
        egg_infos = []
315
        # scanning the zip content
316
        for element in self._zip_file.filelist:
317
            paths = os.path.split(element.filename)
318
            if len(paths) < 2:
319
                continue
320
            if splitext(paths[0])[-1].lower() == '.egg-info':
321
                if paths[0] not in egg_infos:
322
                    self.add(ZippedDistribution(self._zip_file, paths[0]))
323
                    egg_infos.append(paths[0])
324
325
    def __repr__(self):
326
        return 'ZippedDistributionDirectory("%s")' % self.path
327
328
    def add(self, dist):
329
        """Makes sure only Distribution instances are added."""
330
        if not isinstance(dist, ZippedDistribution):
331
            raise TypeError('ZippedDistributionDirectory manage only '
332
                            'ZippedDistribution instances')
333
        super(ZippedDistributionDirectory, self).add(dist)
334
335
286
336
#
287
337
# Directories is a collection of directories, initialized with a
288
338
# list of paths.
@@ -307,9 +357,10 @@ class DistributionDirectories(dict):
307
357
        """Controls the mapping deals only with path/DistributionDirectory"""
308
358
        if not isinstance(path, str):
309
359
            raise TypeError('The key needs to be a path')
310
        if not isinstance(dir, DistributionDirectory):
360
        if not isinstance(dir, (ZippedDistributionDirectory,
361
                                DistributionDirectory)):
311
362
            raise TypeError('The value needs to be a DistributionDirectory '
312
                            'instance')
363
                            'or a ZippedDistributionDirectory instance')
313
364
        super(DistributionDirectories, self).__setitem__(path, dir)
314
365
315
366
    #
@@ -320,7 +371,10 @@ class DistributionDirectories(dict):
320
371
        if self.use_cache and path in _CACHED_DIRS:
321
372
            dist_dir = _CACHED_DIRS[path]
322
373
        else:
323
            dist_dir = DistributionDirectory(path)
374
            if is_zipfile(path):
375
                dist_dir = ZippedDistributionDirectory(path)
376
            else:
377
                dist_dir = DistributionDirectory(path)
324
378
            if self.use_cache:
325
379
                _CACHED_DIRS[path] = dist_dir
326
380
@@ -329,7 +383,8 @@ class DistributionDirectories(dict):
329
383
    def load(self, paths):
330
384
        """Loads the paths."""
331
385
        for path in paths:
332
            if path in self or not os.path.isdir(path):
386
            if path in self or not (os.path.isdir(path) or
387
                                    is_zipfile(path)):
333
388
                continue
334
389
            self.append(path)
335
390

Up to file-list site-packages.zip:

Binary file has changed or diff was empty.

Up to file-list test_pkgutil.py:

@@ -75,3 +75,57 @@ def test_egginfo_dirname():
75
75
    assert egginfo_dirname('python-ldap', '2.5'), 'python_ldap-2.5.egg-info'
76
76
    assert egginfo_dirname('python-ldap', '2.5 a---5'), 'python_ldap-2.5.a_5.egg-info'
77
77
78
79
def test_zipped_directory():
80
    dir = ZippedDistributionDirectory(SITE_PKG+'.zip')
81
82
    dist = dir.owner('mercurial/filelog.pyc')
83
    assert dist.name == 'mercurial'
84
    assert dir.owner('mercurial/filelog.py') is None
85
86
def setup_zip():
87
    sys.old = sys.path
88
    sys.path = [SITE_PKG+'.zip']
89
    pkgutil.purge_cache()
90
    pkgutil._dist_dirs = DistributionDirectories()
91
    pkgutil._dist_dirs.load([SITE_PKG+'.zip'])
92
93
@with_setup(setup_zip, teardown)
94
def test_zipped_distribution():
95
    assert_equals(get_distribution('xxx'), None)
96
97
    project = get_distribution('mercurial')
98
    assert_equals(project.name, 'mercurial')
99
100
    project = get_distribution('processing')
101
    assert_equals(project.name, 'processing')
102
103
    dist = get_distribution('mercurial')
104
    assert_equals(str(dist), "Distribution('mercurial')")
105
106
    files = list(dist.get_installed_files())
107
    assert_equals(len(files), 4)
108
    assert_equals(files[0], ('mercurial/filelog.py', '98676876876876', '12'))
109
110
    files = list(dist.get_installed_files(local=True))
111
112
    site_packages = os.path.split(dist.info_path)[0]
113
    location = os.path.join(site_packages, 'mercurial', 'filelog.py')
114
    assert_equals(files[0], (location, '98676876876876', '12'))
115
116
    f = dist.get_egginfo_file('RECORD')
117
    record = os.path.join(SITE_PKG, 'mercurial-1.0.1.egg-info', 'RECORD')
118
    assert_equals(open(record).read(), f.read())
119
120
    assert dist.uses('mercurial/filelog.py')
121
    assert not dist.uses('mercurial/filelog.sasasasa')
122
123
    assert_equals(list(dist.get_egginfo_files()),
124
                  ['mercurial-1.0.1.egg-info/PKG_INFO',
125
                   'mercurial-1.0.1.egg-info/RECORD'])
126
127
    assert_equals(list(dist.get_egginfo_files(local=True)),
128
                  [os.path.join(SITE_PKG, 'mercurial-1.0.1.egg-info/PKG_INFO'),
129
                   os.path.join(SITE_PKG, 'mercurial-1.0.1.egg-info/RECORD')])
130
131