diff options
Diffstat (limited to '')
-rw-r--r-- | portato/eix/parser.py | 252 |
1 files changed, 248 insertions, 4 deletions
diff --git a/portato/eix/parser.py b/portato/eix/parser.py index 6e12973..2a6658b 100644 --- a/portato/eix/parser.py +++ b/portato/eix/parser.py @@ -10,7 +10,15 @@ # # Written by René 'Necoro' Neumann <necoro@necoro.net> +""" +The cache file supports different types of data. +In this module (nearly) all of these types have a corresponding function. + +For the exact way all the functions work, have a look at the eix format description. +""" + from __future__ import absolute_import, with_statement +__docformat__ = "restructuredtext" import os import struct @@ -19,7 +27,29 @@ from functools import partial from ..helper import debug from .exceptions import EndOfFileException +# +# Helper +# + def _get_bytes (file, length, expect_list = False): + """ + Return a number of bytes. + + :Parameters: + + file : file + The file to read from. + + length : int + The number of bytes to read. + + expect_list : bool + In case ``length`` is 1, only a single byte is returned. If ``expect_list`` is true, then a list is also returned in this case. + + :rtype: int or int[] + :raises EndOfFileException: if EOF is reached during execution + """ + s = file.read(length) if len(s) != length: @@ -30,7 +60,25 @@ def _get_bytes (file, length, expect_list = False): else: return struct.unpack("%sB" % length, s) +# +# Base Types +# + def number (file, skip = False): + """ + Returns a number. + + :Parameters: + + file : file + The file to read from. + + skip : bool + Do not return the actual value, but just skip to the next datum. + + :rtype: int + """ + n = _get_bytes(file, 1) if n < 0xFF: @@ -63,6 +111,27 @@ def number (file, skip = False): return value def vector (file, get_type, skip = False, nelems = None): + """ + Returns a vector of elements. + + :Parameters: + + file : file + The file to read from. + + get_type : function(file, bool) + The function determining type of the elements. + + skip : bool + Do not return the actual value, but just skip to the next datum. + + nelems : int + Normally the eix-Vector has the number of elements as the first argument. + If for some reason this is not the case, you can pass it in here. + + :rtype: list + """ + if nelems is None: nelems = number(file) @@ -73,12 +142,42 @@ def vector (file, get_type, skip = False, nelems = None): return [get_type(file) for i in range(nelems)] def typed_vector(type, nelems = None): + """ + Shortcut to create a function for a special type of vector. + + :Parameters: + + type : function(file, bool) + The function determining type of the elements. + + nelems : int + Normally the eix-Vector has the number of elements as the first argument. + If for some reason this is not the case, you can pass it in here. + Do not return the actual value, but just skip to the next datum. + + :rtype: function(file, bool) + :see: `vector` + """ + if nelems is None: return partial(vector, get_type = type) else: return partial(vector, get_type = type, nelems = nelems) def string (file, skip = False): + """ + Returns a string. + + :Parameters: + + file : file + The file to read from. + + skip : bool + Do not return the actual value, but just skip to the next datum. + + :rtype: str + """ nelems = number(file) if skip: @@ -92,10 +191,29 @@ def string (file, skip = False): return s +# +# Complex Types +# + class LazyElement (object): + """ + This class models a value in the cache, which is only read on access. + + If not accessed directly, only the position inside the file is stored. + """ __slots__ = ("file", "get_type", "_value", "pos") def __init__ (self, get_type, file): + """ + :Parameters: + + get_type : function(file, bool) + The function determining type of the elements. + + file : file + The file to read from. + """ + self.file = file self.get_type = get_type self._value = None @@ -105,6 +223,10 @@ class LazyElement (object): @property def value (self): + """ + The value of the element. + """ + if self._value is None: old_pos = self.file.tell() self.file.seek(self.pos, os.SEEK_SET) @@ -114,20 +236,86 @@ class LazyElement (object): return self._value def __call__ (self): + """ + Convenience function. Also returns the value. + """ return self.value class overlay (object): + """ + Represents an overlay object. + + :IVariables: + + path : `LazyElement` <string> + The path to the overlay + + label : `LazyElement` <string> + The label/name of the overlay + """ __slots__ = ("path", "label") def __init__ (self, file, skip = False): + """ + :Parameters: + + file : file + The file to read from. + + skip : bool + Do not return the actual value, but just skip to the next datum. + """ + self.path = LazyElement(string, file) self.label = LazyElement(string, file) class header (object): + """ + Represents the header of the cache. + + :IVariables: + + version : `LazyElement` <int> + The version of the cache file. + + ncats : `LazyElement` <int> + The number of categories. + + overlays : `LazyElement` <`overlay` []> + The list of overlays. + + provide : `LazyElement` <string[]> + A list of "PROVIDE" values. + + licenses : `LazyElement` <string[]> + The list of licenses. + + keywords : `LazyElement` <string[]> + The list of keywords. + + useflags : `LazyElement` <string[]> + The list of useflags. + + slots : `LazyElement` <string[]> + The list of slots different from "0". + + sets : `LazyElement` <string[]> + The names of world sets are the names (without leading @) of the world sets stored in /var/lib/portage/world_sets. + If SAVE_WORLD=false, the list is empty. + """ __slots__ = ("version", "ncats", "overlays", "provide", "licenses", "keywords", "useflags", "slots", "sets") def __init__ (self, file, skip = False): + """ + :Parameters: + + file : file + The file to read from. + + skip : bool + Do not return the actual value, but just skip to the next datum. + """ def LE (t): return LazyElement(t, file) @@ -142,14 +330,50 @@ class header (object): self.sets = LE(typed_vector(string)) class package (object): - __slots__ = ("offset","name", "description", - "provide", "homepage", "license", "useflags") + """ + The representation of one package. + + Currently, version information is not parsed and stored. + So you can gain general infos only. + + :IVariables: + + name : `LazyElement` <string> + The name of the package. + + description : `LazyElement` <string> + Description of the package. + + homepage : `LazyElement` <string> + The homepage of the package. + + provide : `LazyElement` <int[]> + The indices of `header.provide` representing the PROVIDE value of the package. + + license : `LazyElement` <int> + The index of `header.licenses` representing the license of the package. + + useflags : `LazyElement` <int[]> + The indices of `header.useflags` representing the IUSE value of the package. + """ + + __slots__ = ("name", "description", "provide", + "homepage", "license", "useflags") def __init__ (self, file, skip = False): + """ + :Parameters: + + file : file + The file to read from. + + skip : bool + Do not return the actual value, but just skip to the next datum. + """ def LE (t): return LazyElement(t, file) - self.offset = number(file) + self._offset = number(file) after_offset = file.tell() @@ -162,11 +386,31 @@ class package (object): # self.versions = LE(typed_vector(version)) # for the moment just skip the versions - file.seek(self.offset - (file.tell() - after_offset), os.SEEK_CUR) + file.seek(self._offset - (file.tell() - after_offset), os.SEEK_CUR) class category (object): + """ + Represents a whole category. + + :IVariables: + + name : `LazyElement` <string> + The category name. + + packages : `LazyElement` <`package` []> + All the packages of the category. + """ __slots__ = ("name", "packages") def __init__ (self, file, skip = False): + """ + :Parameters: + + file : file + The file to read from. + + skip : bool + Do not return the actual value, but just skip to the next datum. + """ self.name = LazyElement(string, file) self.packages = LazyElement(typed_vector(package), file) |