tarek / pep376

code for PEP 376

Clone this repository (size: 66.5 KB): HTTPS / SSH
$ hg clone http://bitbucket.org/tarek/pep376/
commit 14: f3f46f157e05
parent 13: fbf7c9bfe142
branch: default
changed container api+reflect changes on pep 376
ta...@MacZiade
10 months ago

Changed (Δ2.4 KB):

raw changeset »

.hgignore (7 lines added, 0 lines removed)

docs/pep-0376 (105 lines added, 34 lines removed)

pkgutil.py (15 lines added, 13 lines removed)

Up to file-list .hgignore:

1
^lib$
2
^bin$
3
^include$
4
.*\.pyc$
5
^\.Python$
6
^\.hgignore$
7

Up to file-list docs/pep-0376:

@@ -115,7 +115,7 @@ To address those issues, this PEP propos
115
115
- a new `.egg-info` structure using a directory, based on the `EggFormats`
116
116
  standard from `setuptools` [#eggformats]_.
117
117
- new APIs in `pkgutil` to be able to query the information of installed
118
  projects. 
118
  projects.
119
119
- a de-facto replacement for PEP 262
120
120
- an uninstall function in Distutils.
121
121
@@ -151,10 +151,10 @@ The syntax of the egg-info directory nam
151
151
The egg-info directory name is created using a new function called
152
152
``egg_info_dirname(name, version)`` added to ``pkgutil``. ``name`` is
153
153
converted to a standard distribution name any runs of non-alphanumeric
154
characters are replaced with a single '-'. ``version`` is converted 
155
to a standard version string. Spaces become dots, and all other 
156
non-alphanumeric characters become dashes, with runs of multiple dashes 
157
condensed to a single dash. Both attributes are then converted into their 
154
characters are replaced with a single '-'. ``version`` is converted
155
to a standard version string. Spaces become dots, and all other
156
non-alphanumeric characters become dashes, with runs of multiple dashes
157
condensed to a single dash. Both attributes are then converted into their
158
158
filename-escaped form. Any '-' characters are currently replaced with '_'.
159
159
160
160
Examples::
@@ -233,23 +233,33 @@ New APIs in pkgutil
233
233
To use the `.egg-info` directory content, we need to add in the standard
234
234
library a set of APIs. The best place to put these APIs seems to be `pkgutil`.
235
235
236
The API is organized in three classes:
237
238
- ``EggInfo``: manages an `.egg-info` directory.
239
- ``EggInfoDirectory``: manages a directory that contains some `.egg-info`
240
  directories.
241
- ``EggInfoDirectories``: manages ``EggInfoDirectory`` instances.
242
236
243
EggInfo class
237
244
-------------
238
245
239
A new class called ``EggInfo`` is created, which provides the following
240
attributes:
246
A new class called ``EggInfo`` is created with a the path of the `.egg-info`
247
directory provided to the contructor. It reads the metadata contained in
248
`PKG-INFO` when it is instanciated.
249
250
``EggInfo`` provides the following attributes:
241
251
242
252
- ``name``: The name of the project
243
253
244
254
- ``metadata``: A ``DistributionMetadata`` instance loaded with the project's
245
255
  PKG-INFO file
246
256
247
The following methods are provided:
257
And following methods:
248
258
249
259
- ``get_installed_files(local=False)`` -> iterator of (path, md5, size)
250
260
251
  Iterates over the `RECORD` entries and return a tuple ``(path, md5, size)`` 
252
  for each line. If ``local`` is ``True``, the path is transformed into a 
261
  Iterates over the `RECORD` entries and return a tuple ``(path, md5, size)``
262
  for each line. If ``local`` is ``True``, the path is transformed into a
253
263
  local absolute path. Otherwise the raw value from `RECORD` is returned.
254
264
255
265
- ``uses(path)`` -> Boolean
@@ -257,30 +267,100 @@ The following methods are provided:
257
267
  Returns ``True`` if ``path`` is listed in `RECORD`. ``path``
258
268
  can be a local absolute path or a relative '/'-separated path.
259
269
260
- ``owns(path)`` -> Boolean
261
262
  Returns ``True`` if ``path`` is owned by the project.
263
  Owned means that the path is used only by this project and is not used
264
  by any other project. ``path`` can be a local absolute path or a relative
265
  '/'-separated path.
266
267
270
- ``get_file(path, binary=False)`` -> file object
268
271
269
  Returns a ``file`` instance for the file pointed by ``path``. ``path`` can be 
270
  a local absolute path or a relative '/'-separated path. If ``binary`` is 
272
  Returns a ``file`` instance for the file pointed by ``path``. ``path`` can be
273
  a local absolute path or a relative '/'-separated path. If ``binary`` is
271
274
  ``True``, opens the file in binary mode.
272
275
276
EggInfoDirectory class
277
----------------------
278
279
A new class called ``EggInfoDirectory`` is created with a path corresponding
280
to a directory. For each `.egg-info` directory founded in `path`, the class
281
creates a corresponding ``EggInfo``.
282
283
The class is iterable, and returns every ``EggInfo`` instance created.
284
285
It also provides two methods:
286
287
- ``file_users(self, path)`` -> Iterator of ``EggInfo``.
288
289
  Returns all ``EggInfo`` which uses ``path``, by calling
290
  ``EggInfo.uses(path)`` on all ``EggInfo`` instances.
291
292
- ``owner(self, path)`` -> ``EggInfo`` instance or None
293
294
  If ``path`` is used by only one ``EggInfo`` instance, returns it.
295
  Otherwise returns None.
296
297
EggInfoDirectories class
298
-------------------------
299
300
A new class called ``EggInfoDirectories`` is created. It's a collection of
301
``EggInfoDirectory`` instances. The constructor takes one optional
302
argument ``use_cache`` set to ``True`` by default. When ``True``,
303
``EggInfoDirectories`` will use a global cache to reduce the numbers of I/O
304
accesses and speed up the lookups.
305
306
The cache is a global mapping containing ``EggInfoDirectory`` instances.
307
When an ``EggInfoDirectories`` object is created, it will use the cache to
308
add an entry for each path it visits, or reuse existing entries. The cache
309
usage can be disabled at any time with the `use_cache` attribute.
310
311
The cache can also be emptied with the global ``purge_cache`` function.
312
313
The class is iterable, and returns every ``EggInfo`` instance from every
314
``EggInfoDirectory`` instance it contains.
315
316
``EggInfoDirectories`` also provides the following container and helper
317
methods::
318
319
- ``append(path)``
320
321
  Creates an ``EggInfoDirectory`` instance. for ``path`` appends it.
322
323
- ``remove(egg_info_dir)``
324
325
  Removes the ``EggInfoDirectory`` instance for the given ``path``.
326
327
- ``clear()``
328
329
  Removes all elements.
330
331
- ``load(paths)``
332
333
  Creates and adds ``EggInfoDirectory`` instances corresponding to ``paths``.
334
335
- ``reload()``
336
337
  Reloads existing entries.
338
339
- ``get_egg_infos()`` -> Iterator of ``EggInfo`` instances.
340
341
  Iterates over all ``EggInfo`` contained in ``EggInfoDirectory`` instances.
342
343
- ``get_egg_info(project_name)`` -> ``EggInfo`` or None.
344
345
  Returns an EggInfo instance for the given project name.
346
  If not found, returns None.
347
348
- ``get_file_users(path)`` -> Iterator of ``EggInfo`` instances.
349
350
  Iterates over all projects to find out which project uses the file.
351
  Returns ``EggInfo`` instances.
352
273
353
.egg-info functions
274
354
-------------------
275
355
276
356
The new functions added in the ``pkgutil`` are :
277
357
278
- ``get_egg_infos()`` -> iterator
358
- ``get_egg_infos(paths=sys.path)`` -> iterator
279
359
280
360
  Provides an iterator that looks for ``.egg-info`` directories in ``sys.path``
281
361
  and returns ``EggInfo`` instances for each one of them.
282
362
283
- ``get_egg_info(project_name)`` -> path or None
363
- ``get_egg_info(project_name, paths=sys.path)`` -> path or None
284
364
285
365
  Scans all elements in ``sys.path`` and looks for all directories ending with
286
366
  ``.egg-info``. Returns an ``EggInfo`` corresponding to the ``.egg-info``
@@ -290,23 +370,14 @@ The new functions added in the ``pkgutil
290
370
  Notice that there should be at most one result. The first result founded
291
371
  will be returned. If the directory is not found, returns None.
292
372
293
- ``get_file_users(path)`` -> iterator of ``EggInfo`` instances.
373
- ``get_file_users(path, paths=sys.path)`` -> iterator of ``EggInfo`` instances.
294
374
295
375
  Iterates over all projects to find out which project uses ``path``.
296
376
  ``path`` can be a local absolute path or a relative '/'-separated path.
297
377
298
Cache functions
299
---------------
300
301
The functions from the previous section work with a global memory cache to
302
reduce the numbers of I/O accesses and speed up the lookups.
303
304
The cache can be managed with these functions:
305
306
- ``purge_cache``: removes all entries from cache.
307
- ``cache_enabled``: returns ``True`` if the cache is enabled.
308
- ``enable_cache``: enables the cache.
309
- ``disable_cache``: disables the cache.
378
All these functions use the same global instance of ``EggInfoDirectories`` to
379
use the cache. Notice that the cache is never emptied explicitely so it keeps
380
on growing everytime it is used with new paths.
310
381
311
382
Example
312
383
-------

Up to file-list pkgutil.py:

@@ -263,31 +263,33 @@ class EggInfoDirectories(object):
263
263
    def __iter__(self):
264
264
        return iter(self._egg_info_dirs)
265
265
266
    def append(self, egg_info):
267
        self._egg_info_dirs.append(egg_info)
268
        self._paths.append(egg_info.path)
269
        if self.use_cache:
270
            _CACHED_DIRS[egg_info.path] = egg_info
266
    def append(self, path):
267
        if self.use_cache and path in _CACHED_DIRS:
268
            egg_info_dir = _CACHED_DIRS[path]
269
        else:
270
            egg_info_dir = EggInfoDirectory(path)
271
            if self.use_cache:
272
                _CACHED_DIRS[path] = egg_info_dir
273
274
        self._egg_info_dirs.append(egg_info_dir)
275
        self._paths.append(path)
271
276
272
277
    def clear(self):
273
278
        self._egg_info_dirs.clear()
274
279
        self._paths.clear()
275
280
276
    def remove(self, egg_info):
277
        self._egg_info_dirs.remove(egg_info)
278
        self._paths.remove(egg_info.path)
281
    def remove(self, egg_info_dir):
282
        self._egg_info_dirs.remove(egg_info_dir)
283
        self._paths.remove(egg_info_dir.path)
279
284
280
285
    #
281
286
    # public APIs
282
287
    #
283
288
    def load(self, paths):
284
289
        for path in paths:
285
            if path in self._paths:
290
            if path in self._paths or not os.path.isdir(path):
286
291
                continue
287
            if self.use_cache and path in _CACHED_DIRS:
288
                self.append(_CACHED_DIRS[path])
289
            elif os.path.isdir(path):
290
                self.append(EggInfoDirectory(path))
292
            self.append(path)
291
293
292
294
    def reload(self):
293
295
        paths = [d.path for d in self]