From b8f45f026ace3df864efac5d49a62be4a0fc9eae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20=27Necoro=27=20Neumann?= Date: Fri, 24 Jul 2009 21:30:40 +0200 Subject: First eix stuff --- portato/eix/__init__.py | 0 portato/eix/eix_utils.pxd | 47 +++++++++++++++++++++++++++++++ portato/eix/eix_utils.pyx | 72 +++++++++++++++++++++++++++++++++++++++++++++++ portato/eix/libeix.pyx | 14 +++++++++ 4 files changed, 133 insertions(+) create mode 100644 portato/eix/__init__.py create mode 100644 portato/eix/eix_utils.pxd create mode 100644 portato/eix/eix_utils.pyx create mode 100644 portato/eix/libeix.pyx (limited to 'portato') diff --git a/portato/eix/__init__.py b/portato/eix/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/portato/eix/eix_utils.pxd b/portato/eix/eix_utils.pxd new file mode 100644 index 0000000..a85a65e --- /dev/null +++ b/portato/eix/eix_utils.pxd @@ -0,0 +1,47 @@ +cdef extern from *: + ctypedef int size_t + +cdef extern from "errno.h": + int errno + +cdef extern from "string.h": + char* strerror (int errno) + size_t strlen (char* s) + char* strcpy (char* dest, char* src) + +cdef extern from "stdio.h": + + ctypedef struct FILE: + pass + + FILE* c_fopen "fopen" (char* path, char* mode) + int c_fclose "fclose" (FILE* f) + int c_feof "feof" (FILE* f) + int fread (void* buf, size_t size, size_t n, FILE* f) + + enum WHENCE: + SEEK_SET + SEEK_CUR + SEEK_END + + int fseek (FILE* stream, long offset, WHENCE whence) + +cdef extern from "Python.h": + ctypedef struct PyObject: + pass + + void* PyMem_Malloc (size_t n) + void PyMem_Free (void* p) + +cdef: + struct File: + FILE* file + char* name + + File* fopen (char* path, char* mode) except NULL + void fclose (File* f) + + void ffree (void* p) + char* fget (File* f, size_t n) except NULL + + char* strdup (char* other) except NULL diff --git a/portato/eix/eix_utils.pyx b/portato/eix/eix_utils.pyx new file mode 100644 index 0000000..57fa5d7 --- /dev/null +++ b/portato/eix/eix_utils.pyx @@ -0,0 +1,72 @@ +class EndOfFileError (IOError): + + def __init__ (self, filename = None): + self.message = "End of file reached while not expecting it" + self.filename = filename + + def __str__ (self): + if self.filename is not None: + return "%s: %s" % (self.message, self.filename) + else: + return self.message + +cdef char* strdup (char * other) except NULL: + cdef size_t len + cdef char* new + + if other is NULL: + return NULL + + len = strlen(other) + new = PyMem_Malloc(len+1) + + if new is NULL: + raise MemoryError, "Malloc of new string copy" + return NULL + + return strcpy(new, other) + + +cdef File* fopen (char* path, char* mode) except NULL: + cdef File* f + + f = PyMem_Malloc(sizeof(File)) + + if f is NULL: + raise MemoryError, "Malloc of File" + return NULL + + f.file = c_fopen(path, mode) + + if f.file is NULL: + raise IOError, (errno, strerror(errno), path) + return NULL + + f.name = strdup(path) + + if f.name is NULL: + return NULL + + return f + +cdef void fclose (File* f): + c_fclose(f.file) + ffree(f.name) + PyMem_Free(f) + +cdef void ffree (void* p): + PyMem_Free(p) + +cdef char* fget (File* f, size_t n) except NULL: + cdef char* buf + buf = PyMem_Malloc(n) + + if buf is NULL: + raise MemoryError, "Malloc" + return NULL + + if (fread(buf, 1, n, f.file) != n): + PyMem_Free(buf) + raise EndOfFileError, f.name + + return buf diff --git a/portato/eix/libeix.pyx b/portato/eix/libeix.pyx new file mode 100644 index 0000000..2b3ebb0 --- /dev/null +++ b/portato/eix/libeix.pyx @@ -0,0 +1,14 @@ +from portato.eix.eix_utils cimport File, fopen, fclose, ffree, fget + +def test (f): + cdef File* cf + cdef char* buf + + print "Trying to open: ", f + cf = fopen(f, "r") + try: + buf = fget(cf, 20) + print "The first two chars:", buf[0], buf[1] + ffree(buf) + finally: + fclose(cf) -- cgit v1.2.3 From 5a5c7536d307aa12c040f89b4d5048fcd8b1a1f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20=27Necoro=27=20Neumann?= Date: Sun, 26 Jul 2009 02:54:41 +0200 Subject: Get rid of the self-implemented file-stuff. Use the normal python-API instead --- portato/eix/eix_utils.pxd | 30 ------------------------- portato/eix/eix_utils.pyx | 57 ----------------------------------------------- 2 files changed, 87 deletions(-) (limited to 'portato') diff --git a/portato/eix/eix_utils.pxd b/portato/eix/eix_utils.pxd index a85a65e..39f60a9 100644 --- a/portato/eix/eix_utils.pxd +++ b/portato/eix/eix_utils.pxd @@ -9,39 +9,9 @@ cdef extern from "string.h": size_t strlen (char* s) char* strcpy (char* dest, char* src) -cdef extern from "stdio.h": - - ctypedef struct FILE: - pass - - FILE* c_fopen "fopen" (char* path, char* mode) - int c_fclose "fclose" (FILE* f) - int c_feof "feof" (FILE* f) - int fread (void* buf, size_t size, size_t n, FILE* f) - - enum WHENCE: - SEEK_SET - SEEK_CUR - SEEK_END - - int fseek (FILE* stream, long offset, WHENCE whence) - cdef extern from "Python.h": - ctypedef struct PyObject: - pass - void* PyMem_Malloc (size_t n) void PyMem_Free (void* p) cdef: - struct File: - FILE* file - char* name - - File* fopen (char* path, char* mode) except NULL - void fclose (File* f) - - void ffree (void* p) - char* fget (File* f, size_t n) except NULL - char* strdup (char* other) except NULL diff --git a/portato/eix/eix_utils.pyx b/portato/eix/eix_utils.pyx index 57fa5d7..0ea9603 100644 --- a/portato/eix/eix_utils.pyx +++ b/portato/eix/eix_utils.pyx @@ -1,15 +1,3 @@ -class EndOfFileError (IOError): - - def __init__ (self, filename = None): - self.message = "End of file reached while not expecting it" - self.filename = filename - - def __str__ (self): - if self.filename is not None: - return "%s: %s" % (self.message, self.filename) - else: - return self.message - cdef char* strdup (char * other) except NULL: cdef size_t len cdef char* new @@ -25,48 +13,3 @@ cdef char* strdup (char * other) except NULL: return NULL return strcpy(new, other) - - -cdef File* fopen (char* path, char* mode) except NULL: - cdef File* f - - f = PyMem_Malloc(sizeof(File)) - - if f is NULL: - raise MemoryError, "Malloc of File" - return NULL - - f.file = c_fopen(path, mode) - - if f.file is NULL: - raise IOError, (errno, strerror(errno), path) - return NULL - - f.name = strdup(path) - - if f.name is NULL: - return NULL - - return f - -cdef void fclose (File* f): - c_fclose(f.file) - ffree(f.name) - PyMem_Free(f) - -cdef void ffree (void* p): - PyMem_Free(p) - -cdef char* fget (File* f, size_t n) except NULL: - cdef char* buf - buf = PyMem_Malloc(n) - - if buf is NULL: - raise MemoryError, "Malloc" - return NULL - - if (fread(buf, 1, n, f.file) != n): - PyMem_Free(buf) - raise EndOfFileError, f.name - - return buf -- cgit v1.2.3 From 30a5b66bb52b3116d0fa1aa47b76210b2613062d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20=27Necoro=27=20Neumann?= Date: Sun, 26 Jul 2009 02:55:08 +0200 Subject: Implement reading a number in the EixReader --- portato/eix/libeix.pyx | 116 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 102 insertions(+), 14 deletions(-) (limited to 'portato') diff --git a/portato/eix/libeix.pyx b/portato/eix/libeix.pyx index 2b3ebb0..2a1c7df 100644 --- a/portato/eix/libeix.pyx +++ b/portato/eix/libeix.pyx @@ -1,14 +1,102 @@ -from portato.eix.eix_utils cimport File, fopen, fclose, ffree, fget - -def test (f): - cdef File* cf - cdef char* buf - - print "Trying to open: ", f - cf = fopen(f, "r") - try: - buf = fget(cf, 20) - print "The first two chars:", buf[0], buf[1] - ffree(buf) - finally: - fclose(cf) +class EixError (Exception): + message = "Unknown error." + + def __str__ (self): + return self.message + +class EndOfFileException (EixError): + + def __init__ (self, filename): + self.message = "End of file reached though it was not expected: '%s'" % filename + +class EixReaderClosed (EixError): + message = "EixReader is already closed." + +class UnsupportedVersionError (EixError): + + def __init__ (self, version): + self.message = "Version '%s' is not supported." % version + +cdef class EixReader (object): + cdef object file + cdef char closed + + cdef readonly object filename + cdef readonly object version + + supported_versions = (28, ) + + def __init__ (self, filename): + self.filename = filename + self.file = open(filename, "r") + self.closed = 0 + + self.version = self._get_number() + + if self.version not in self.supported_versions: + raise UnsupportedVersionError(self.version) + + cdef unsigned long _get_number (self) except *: + cdef unsigned char n + cdef short count, i + cdef unsigned long value + cdef char* rest + cdef object orest + + n = self._get_one_byte() + + if n < 0xFF: + value = n + else: + count = 0 + + while (n == 0xFF): + count += 1 + n = self._get_one_byte() + + if n == 0: + n = 0xFF # 0xFF is encoded as 0xFF 0x00 + count -= 1 + + value = n << (count*8) + + if count > 0: + orest = self.get_string(count) + rest = orest # cast to char* + + for 0 <= i < count: + value += (rest[i]) << ((count - i - 1)*8) + + return value + + cdef unsigned char _get_one_byte (self) except? 0: + s = self.file.read(1) + + if len(s) != 1: + raise EndOfFileException, self.filename + + return ord(s) + + cdef object _get_string (self, length): + if self.closed: + raise EixReaderClosed + + s = self.file.read(length) + + if len(s) != length: + raise EndOfFileException, self.filename + + return s + + def get_number (self): + if self.closed: + raise EixReaderClosed + + return self._get_number() + + def close (self): + if self.closed: + raise EixReaderClosed + + self.file.close() + self.closed = 1 -- cgit v1.2.3 From 40344af86dbe9c7ad2f38b18f31f0f8a4917bda9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20=27Necoro=27=20Neumann?= Date: Mon, 27 Jul 2009 00:38:41 +0200 Subject: Moved from Cython to normal Python --- portato/eix.py | 113 ++++++++++++++++++++++++++++++++++++++++++++++ portato/eix/__init__.py | 0 portato/eix/eix_utils.pxd | 17 ------- portato/eix/eix_utils.pyx | 15 ------ portato/eix/libeix.pyx | 102 ----------------------------------------- 5 files changed, 113 insertions(+), 134 deletions(-) create mode 100644 portato/eix.py delete mode 100644 portato/eix/__init__.py delete mode 100644 portato/eix/eix_utils.pxd delete mode 100644 portato/eix/eix_utils.pyx delete mode 100644 portato/eix/libeix.pyx (limited to 'portato') diff --git a/portato/eix.py b/portato/eix.py new file mode 100644 index 0000000..927870e --- /dev/null +++ b/portato/eix.py @@ -0,0 +1,113 @@ +# -*- coding: utf-8 -*- +# +# File: portato/eix.py +# This file is part of the Portato-Project, a graphical portage-frontend. +# +# Copyright (C) 2006-2009 René 'Necoro' Neumann +# This is free software. You may redistribute copies of it under the terms of +# the GNU General Public License version 2. +# There is NO WARRANTY, to the extent permitted by law. +# +# Written by René 'Necoro' Neumann + +from __future__ import absolute_import, with_statement + +import struct +from functools import wraps + +from .helper import debug + +class EixError (Exception): + message = "Unknown error." + + def __str__ (self): + return self.message + +class EndOfFileException (EixError): + + def __init__ (self, filename): + self.message = "End of file reached though it was not expected: '%s'" % filename + +class EixReaderClosed (EixError): + message = "EixReader is already closed." + +class UnsupportedVersionError (EixError): + + def __init__ (self, version): + self.message = "Version '%s' is not supported." % version + +class EixReader (object): + supported_versions = (28, ) + + def __init__ (self, filename): + self.filename = filename + self.file = open(filename, "r") + self.closed = 0 + + try: + self.version = self.get_number() + + if self.version not in self.supported_versions: + raise UnsupportedVersionError(self.version) + + debug("Started EixReader for version %s.", self.version) + except: + self.close() + raise + + def check_closed (f): + @wraps(f) + def wrapper (self, *args, **kwargs): + if self.closed: + raise EixReaderClosed + + return f(self, *args, **kwargs) + return wrapper + + @check_closed + def get_number (self): + n = self._get_bytes(1) + + if n < 0xFF: + value = n + else: + count = 0 + + while (n == 0xFF): + count += 1 + n = self._get_bytes(1) + + if n == 0: + n = 0xFF # 0xFF is encoded as 0xFF 0x00 + count -= 1 + + value = n << (count*8) + + if count > 0: + rest = self._get_bytes(count, expect_list = True) + + for i, r in enumerate(rest): + value += r << ((count - i - 1)*8) + + return value + + def _get_bytes (self, length, expect_list = False): + s = self.file.read(length) + + if len(s) != length: + raise EndOfFileException, self.filename + + if length == 1 and not expect_list: + return ord(s) # is faster than unpack and we have a scalar + else: + return struct.unpack("%sB" % length, s) + + @check_closed + def close (self): + if self.closed: + raise EixReaderClosed + + self.file.close() + self.closed = 1 + + debug("EixReader closed.") diff --git a/portato/eix/__init__.py b/portato/eix/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/portato/eix/eix_utils.pxd b/portato/eix/eix_utils.pxd deleted file mode 100644 index 39f60a9..0000000 --- a/portato/eix/eix_utils.pxd +++ /dev/null @@ -1,17 +0,0 @@ -cdef extern from *: - ctypedef int size_t - -cdef extern from "errno.h": - int errno - -cdef extern from "string.h": - char* strerror (int errno) - size_t strlen (char* s) - char* strcpy (char* dest, char* src) - -cdef extern from "Python.h": - void* PyMem_Malloc (size_t n) - void PyMem_Free (void* p) - -cdef: - char* strdup (char* other) except NULL diff --git a/portato/eix/eix_utils.pyx b/portato/eix/eix_utils.pyx deleted file mode 100644 index 0ea9603..0000000 --- a/portato/eix/eix_utils.pyx +++ /dev/null @@ -1,15 +0,0 @@ -cdef char* strdup (char * other) except NULL: - cdef size_t len - cdef char* new - - if other is NULL: - return NULL - - len = strlen(other) - new = PyMem_Malloc(len+1) - - if new is NULL: - raise MemoryError, "Malloc of new string copy" - return NULL - - return strcpy(new, other) diff --git a/portato/eix/libeix.pyx b/portato/eix/libeix.pyx deleted file mode 100644 index 2a1c7df..0000000 --- a/portato/eix/libeix.pyx +++ /dev/null @@ -1,102 +0,0 @@ -class EixError (Exception): - message = "Unknown error." - - def __str__ (self): - return self.message - -class EndOfFileException (EixError): - - def __init__ (self, filename): - self.message = "End of file reached though it was not expected: '%s'" % filename - -class EixReaderClosed (EixError): - message = "EixReader is already closed." - -class UnsupportedVersionError (EixError): - - def __init__ (self, version): - self.message = "Version '%s' is not supported." % version - -cdef class EixReader (object): - cdef object file - cdef char closed - - cdef readonly object filename - cdef readonly object version - - supported_versions = (28, ) - - def __init__ (self, filename): - self.filename = filename - self.file = open(filename, "r") - self.closed = 0 - - self.version = self._get_number() - - if self.version not in self.supported_versions: - raise UnsupportedVersionError(self.version) - - cdef unsigned long _get_number (self) except *: - cdef unsigned char n - cdef short count, i - cdef unsigned long value - cdef char* rest - cdef object orest - - n = self._get_one_byte() - - if n < 0xFF: - value = n - else: - count = 0 - - while (n == 0xFF): - count += 1 - n = self._get_one_byte() - - if n == 0: - n = 0xFF # 0xFF is encoded as 0xFF 0x00 - count -= 1 - - value = n << (count*8) - - if count > 0: - orest = self.get_string(count) - rest = orest # cast to char* - - for 0 <= i < count: - value += (rest[i]) << ((count - i - 1)*8) - - return value - - cdef unsigned char _get_one_byte (self) except? 0: - s = self.file.read(1) - - if len(s) != 1: - raise EndOfFileException, self.filename - - return ord(s) - - cdef object _get_string (self, length): - if self.closed: - raise EixReaderClosed - - s = self.file.read(length) - - if len(s) != length: - raise EndOfFileException, self.filename - - return s - - def get_number (self): - if self.closed: - raise EixReaderClosed - - return self._get_number() - - def close (self): - if self.closed: - raise EixReaderClosed - - self.file.close() - self.closed = 1 -- cgit v1.2.3 From 8e4a8d8b3971490d60aa5b26ec465a8a4ab50cf8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20=27Necoro=27=20Neumann?= Date: Mon, 27 Jul 2009 01:14:43 +0200 Subject: Implemented vectors and so --- portato/eix.py | 37 +++++++++++++++++++++++++++++++++++-- 1 file changed, 35 insertions(+), 2 deletions(-) (limited to 'portato') diff --git a/portato/eix.py b/portato/eix.py index 927870e..5262bf2 100644 --- a/portato/eix.py +++ b/portato/eix.py @@ -12,6 +12,7 @@ from __future__ import absolute_import, with_statement +import os import struct from functools import wraps @@ -45,7 +46,7 @@ class EixReader (object): self.closed = 0 try: - self.version = self.get_number() + self.version = self.number() if self.version not in self.supported_versions: raise UnsupportedVersionError(self.version) @@ -65,7 +66,7 @@ class EixReader (object): return wrapper @check_closed - def get_number (self): + def number (self): n = self._get_bytes(1) if n < 0xFF: @@ -91,6 +92,38 @@ class EixReader (object): return value + @check_closed + def vector (self, get_type, skip = False): + nelems = self.number() + + if skip: + for i in range(nelems): + get_type(skip = True) + else: + return (get_type() for i in range(nelems)) + + @check_closed + def string (self, skip = False): + nelems = self.number() + + if skip: + self.file.seek(nelems, os.SEEK_CUR) + else: + s = self.file.read(nelems) + + if len(s) != nelems: + raise EndOfFileException, self.filename + + return s + + @check_closed + def overlay (self, skip = False): + if skip: + self.file.seek(self.number(), os.SEEK_CUR) # path + self.file.seek(self.number(), os.SEEK_CUR) # label + else: + return (self.string(), self.string()) + def _get_bytes (self, length, expect_list = False): s = self.file.read(length) -- cgit v1.2.3 From 3f70553a078aafbb029782ec092c8d037d9ea45f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20=27Necoro=27=20Neumann?= Date: Mon, 27 Jul 2009 23:25:21 +0200 Subject: Moved eix stuff to its own module. Also make all the parsing stuff being functions -- will be easier to deal with for the LazyElements --- portato/eix.py | 146 ---------------------------------------------- portato/eix/__init__.py | 40 +++++++++++++ portato/eix/exceptions.py | 29 +++++++++ portato/eix/parser.py | 86 +++++++++++++++++++++++++++ 4 files changed, 155 insertions(+), 146 deletions(-) delete mode 100644 portato/eix.py create mode 100644 portato/eix/__init__.py create mode 100644 portato/eix/exceptions.py create mode 100644 portato/eix/parser.py (limited to 'portato') diff --git a/portato/eix.py b/portato/eix.py deleted file mode 100644 index 5262bf2..0000000 --- a/portato/eix.py +++ /dev/null @@ -1,146 +0,0 @@ -# -*- coding: utf-8 -*- -# -# File: portato/eix.py -# This file is part of the Portato-Project, a graphical portage-frontend. -# -# Copyright (C) 2006-2009 René 'Necoro' Neumann -# This is free software. You may redistribute copies of it under the terms of -# the GNU General Public License version 2. -# There is NO WARRANTY, to the extent permitted by law. -# -# Written by René 'Necoro' Neumann - -from __future__ import absolute_import, with_statement - -import os -import struct -from functools import wraps - -from .helper import debug - -class EixError (Exception): - message = "Unknown error." - - def __str__ (self): - return self.message - -class EndOfFileException (EixError): - - def __init__ (self, filename): - self.message = "End of file reached though it was not expected: '%s'" % filename - -class EixReaderClosed (EixError): - message = "EixReader is already closed." - -class UnsupportedVersionError (EixError): - - def __init__ (self, version): - self.message = "Version '%s' is not supported." % version - -class EixReader (object): - supported_versions = (28, ) - - def __init__ (self, filename): - self.filename = filename - self.file = open(filename, "r") - self.closed = 0 - - try: - self.version = self.number() - - if self.version not in self.supported_versions: - raise UnsupportedVersionError(self.version) - - debug("Started EixReader for version %s.", self.version) - except: - self.close() - raise - - def check_closed (f): - @wraps(f) - def wrapper (self, *args, **kwargs): - if self.closed: - raise EixReaderClosed - - return f(self, *args, **kwargs) - return wrapper - - @check_closed - def number (self): - n = self._get_bytes(1) - - if n < 0xFF: - value = n - else: - count = 0 - - while (n == 0xFF): - count += 1 - n = self._get_bytes(1) - - if n == 0: - n = 0xFF # 0xFF is encoded as 0xFF 0x00 - count -= 1 - - value = n << (count*8) - - if count > 0: - rest = self._get_bytes(count, expect_list = True) - - for i, r in enumerate(rest): - value += r << ((count - i - 1)*8) - - return value - - @check_closed - def vector (self, get_type, skip = False): - nelems = self.number() - - if skip: - for i in range(nelems): - get_type(skip = True) - else: - return (get_type() for i in range(nelems)) - - @check_closed - def string (self, skip = False): - nelems = self.number() - - if skip: - self.file.seek(nelems, os.SEEK_CUR) - else: - s = self.file.read(nelems) - - if len(s) != nelems: - raise EndOfFileException, self.filename - - return s - - @check_closed - def overlay (self, skip = False): - if skip: - self.file.seek(self.number(), os.SEEK_CUR) # path - self.file.seek(self.number(), os.SEEK_CUR) # label - else: - return (self.string(), self.string()) - - def _get_bytes (self, length, expect_list = False): - s = self.file.read(length) - - if len(s) != length: - raise EndOfFileException, self.filename - - if length == 1 and not expect_list: - return ord(s) # is faster than unpack and we have a scalar - else: - return struct.unpack("%sB" % length, s) - - @check_closed - def close (self): - if self.closed: - raise EixReaderClosed - - self.file.close() - self.closed = 1 - - debug("EixReader closed.") diff --git a/portato/eix/__init__.py b/portato/eix/__init__.py new file mode 100644 index 0000000..3c1958e --- /dev/null +++ b/portato/eix/__init__.py @@ -0,0 +1,40 @@ +# -*- coding: utf-8 -*- +# +# File: portato/eix/__init__.py +# This file is part of the Portato-Project, a graphical portage-frontend. +# +# Copyright (C) 2006-2009 René 'Necoro' Neumann +# This is free software. You may redistribute copies of it under the terms of +# the GNU General Public License version 2. +# There is NO WARRANTY, to the extent permitted by law. +# +# Written by René 'Necoro' Neumann + +from __future__ import absolute_import, with_statement + +from . import parser +from . import exceptions as ex + +from ..helper import debug + +class EixReader(object): + supported_version = (28,) + + def __init__ (self, filename): + self.filename = filename + self.file = open(filename, "r") + + try: + self.version = parser.number(self.file) + + if self.version not in self.supported_versions: + raise ex.UnsupportedVersionError(self.version) + + debug("Started EixReader for version %s.", self.version) + except: + self.close() + raise + + def close (self): + self.file.close() + debug("EixReader closed.") diff --git a/portato/eix/exceptions.py b/portato/eix/exceptions.py new file mode 100644 index 0000000..fd72dcf --- /dev/null +++ b/portato/eix/exceptions.py @@ -0,0 +1,29 @@ +# -*- coding: utf-8 -*- +# +# File: portato/eix/exceptions.py +# This file is part of the Portato-Project, a graphical portage-frontend. +# +# Copyright (C) 2006-2009 René 'Necoro' Neumann +# This is free software. You may redistribute copies of it under the terms of +# the GNU General Public License version 2. +# There is NO WARRANTY, to the extent permitted by law. +# +# Written by René 'Necoro' Neumann + +from __future__ import absolute_import, with_statement + +class EixError (Exception): + message = _("Unknown error.") + + def __str__ (self): + return self.message + +class EndOfFileException (EixError): + + def __init__ (self, filename): + self.message = _("End of file reached though it was not expected: '%s'") % filename + +class UnsupportedVersionError (EixError): + + def __init__ (self, version): + self.message = _("Version '%s' is not supported.") % version diff --git a/portato/eix/parser.py b/portato/eix/parser.py new file mode 100644 index 0000000..e89bffe --- /dev/null +++ b/portato/eix/parser.py @@ -0,0 +1,86 @@ +# -*- coding: utf-8 -*- +# +# File: portato/eix/parser.py +# This file is part of the Portato-Project, a graphical portage-frontend. +# +# Copyright (C) 2006-2009 René 'Necoro' Neumann +# This is free software. You may redistribute copies of it under the terms of +# the GNU General Public License version 2. +# There is NO WARRANTY, to the extent permitted by law. +# +# Written by René 'Necoro' Neumann + +from __future__ import absolute_import, with_statement + +import os +import struct + +from .helper import debug + +from . import exceptions as ex + +def _get_bytes (file, length, expect_list = False): + s = file.read(length) + + if len(s) != length: + raise EndOfFileException, file.name + + if length == 1 and not expect_list: + return ord(s) # is faster than unpack and we have a scalar + else: + return struct.unpack("%sB" % length, s) + +def number (file): + n = _get_bytes(file, 1) + + if n < 0xFF: + value = n + else: + count = 0 + + while (n == 0xFF): + count += 1 + n = _get_bytes(file, 1) + + if n == 0: + n = 0xFF # 0xFF is encoded as 0xFF 0x00 + count -= 1 + + value = n << (count*8) + + if count > 0: + rest = _get_bytes(file, count, expect_list = True) + + for i, r in enumerate(rest): + value += r << ((count - i - 1)*8) + + return value + +def vector (file, get_type, skip = False): + nelems = number(file) + + if skip: + for i in range(nelems): + get_type(file, skip = True) + else: + return (get_type(file) for i in range(nelems)) + +def string (file, skip = False): + nelems = number(file) + + if skip: + file.seek(nelems, os.SEEK_CUR) + else: + s = file.read(nelems) + + if len(s) != nelems: + raise EndOfFileException, file.name + + return s + +def overlay (file, skip = False): + if skip: + string(file, skip = True) # path + string(file, skip = True) # label + else: + return (string(file), string(file)) -- cgit v1.2.3 From d44ce453d88624c8444f0733a56197d5291f52f6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20=27Necoro=27=20Neumann?= Date: Fri, 14 Aug 2009 15:18:10 +0200 Subject: Rename DictDatabase to HashDatabase --- portato/db/__init__.py | 6 +- portato/db/dict.py | 153 ------------------------------------------------- portato/db/hash.py | 153 +++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 156 insertions(+), 156 deletions(-) delete mode 100644 portato/db/dict.py create mode 100644 portato/db/hash.py (limited to 'portato') diff --git a/portato/db/__init__.py b/portato/db/__init__.py index da8a81e..e6f9759 100644 --- a/portato/db/__init__.py +++ b/portato/db/__init__.py @@ -43,9 +43,9 @@ def Database(type): return SQLDatabase(SectionDict(_SESSION, "SQL")) elif type == "dict": - debug("Using DictDatabase") - from .dict import DictDatabase - return DictDatabase(SectionDict(_SESSION, "dict")) + debug("Using HashDatabase") + from .hash import HashDatabase + return HashDatabase(SectionDict(_SESSION, "dict")) else: error(_("Unknown database type: %s"), type) diff --git a/portato/db/dict.py b/portato/db/dict.py deleted file mode 100644 index 279ab97..0000000 --- a/portato/db/dict.py +++ /dev/null @@ -1,153 +0,0 @@ -# -*- coding: utf-8 -*- -# -# File: portato/db/dict.py -# This file is part of the Portato-Project, a graphical portage-frontend. -# -# Copyright (C) 2006-2009 René 'Necoro' Neumann -# This is free software. You may redistribute copies of it under the terms of -# the GNU General Public License version 2. -# There is NO WARRANTY, to the extent permitted by law. -# -# Written by René 'Necoro' Neumann - -from __future__ import absolute_import, with_statement - -import re -from collections import defaultdict - -from ..helper import info -from ..backend import system -from .database import Database, PkgData - -class DictDatabase (Database): - """An internal database which holds a simple dictionary cat -> [package_list].""" - - lock = Database.lock - - def __init__ (self, session): - """Constructor.""" - Database.__init__(self) - self.session = session - - self.__initialize() - self.populate() - - def __initialize (self): - self._db = defaultdict(list) - self.inst_cats = set([self.ALL]) - self._restrict = None - - def __sort_key (self, x): - return x.pkg.lower() - - @lock - def populate (self, category = None): - - # get the lists - packages = system.find_packages(category, with_version = False) - installed = system.find_packages(category, system.SET_INSTALLED, with_version = False) - - # cycle through packages - for p in packages: - cat, pkg = p.split("/") - inst = p in installed - t = PkgData(cat, pkg, inst, False) - self._db[cat].append(t) - self._db[self.ALL].append(t) - - if inst: - self.inst_cats.add(cat) - - for key in self._db: # sort alphabetically - self._db[key].sort(key = self.__sort_key) - - @lock - def get_cat (self, cat = None, byName = True, showDisabled = False): - if not cat: - cat = self.ALL - - def get_pkgs(): - if byName: - for pkg in self._db[cat]: - if showDisabled or not pkg.disabled: - yield pkg - else: - ninst = [] - for pkg in self._db[cat]: - if not showDisabled and pkg.disabled: continue - - if pkg.inst: - yield pkg - else: - ninst.append(pkg) - - for pkg in ninst: - yield pkg - - try: - if self.restrict: - return (pkg for pkg in get_pkgs() if self.restrict.search(pkg.cat+"/"+pkg.pkg)) - else: - return get_pkgs() - - except KeyError: # cat is in category list - but not in portage - info(_("Catched KeyError => %s seems not to be an available category. Have you played with rsync-excludes?"), cat) - - @lock - def get_categories (self, installed = False): - if not self.restrict: - if installed: - cats = self.inst_cats - else: - cats = self._db.iterkeys() - - else: - if installed: - cats = set((pkg.cat for pkg in self.get_cat(self.ALL) if pkg.inst)) - else: - cats = set((pkg.cat for pkg in self.get_cat(self.ALL))) - - if len(cats)>1: - cats.add(self.ALL) - - return (cat for cat in cats) - - @lock - def reload (self, cat = None): - if cat: - del self._db[cat] - try: - self.inst_cats.remove(cat) - except KeyError: # not in inst_cats - can be ignored - pass - - self._db[self.ALL] = filter(lambda x: x.cat != cat, self._db[self.ALL]) - self.populate(cat+"/*") - else: - self.__initialize() - self.populate() - - @lock - def disable (self, cpv): - cat, pkg = cpv.split("/") - - c = self._db[cat] - p = c[c.index(PkgData(cat, pkg))] - p.disabled = True - - def get_restrict (self): - return self._restrict - - @lock - def set_restrict (self, restrict): - if not restrict: - self._restrict = None - else: - try: - regex = re.compile(restrict, re.I) - except re.error, e: - info(_("Error while compiling search expression: '%s'."), str(e)) - else: # only set self._restrict if no error occurred - self._restrict = regex - - restrict = property(get_restrict, set_restrict) diff --git a/portato/db/hash.py b/portato/db/hash.py new file mode 100644 index 0000000..8cea6f2 --- /dev/null +++ b/portato/db/hash.py @@ -0,0 +1,153 @@ +# -*- coding: utf-8 -*- +# +# File: portato/db/hash.py +# This file is part of the Portato-Project, a graphical portage-frontend. +# +# Copyright (C) 2006-2009 René 'Necoro' Neumann +# This is free software. You may redistribute copies of it under the terms of +# the GNU General Public License version 2. +# There is NO WARRANTY, to the extent permitted by law. +# +# Written by René 'Necoro' Neumann + +from __future__ import absolute_import, with_statement + +import re +from collections import defaultdict + +from ..helper import info +from ..backend import system +from .database import Database, PkgData + +class HashDatabase (Database): + """An internal database which holds a simple dictionary cat -> [package_list].""" + + lock = Database.lock + + def __init__ (self, session): + """Constructor.""" + Database.__init__(self) + self.session = session + + self.__initialize() + self.populate() + + def __initialize (self): + self._db = defaultdict(list) + self.inst_cats = set([self.ALL]) + self._restrict = None + + def __sort_key (self, x): + return x.pkg.lower() + + @lock + def populate (self, category = None): + + # get the lists + packages = system.find_packages(category, with_version = False) + installed = system.find_packages(category, system.SET_INSTALLED, with_version = False) + + # cycle through packages + for p in packages: + cat, pkg = p.split("/") + inst = p in installed + t = PkgData(cat, pkg, inst, False) + self._db[cat].append(t) + self._db[self.ALL].append(t) + + if inst: + self.inst_cats.add(cat) + + for key in self._db: # sort alphabetically + self._db[key].sort(key = self.__sort_key) + + @lock + def get_cat (self, cat = None, byName = True, showDisabled = False): + if not cat: + cat = self.ALL + + def get_pkgs(): + if byName: + for pkg in self._db[cat]: + if showDisabled or not pkg.disabled: + yield pkg + else: + ninst = [] + for pkg in self._db[cat]: + if not showDisabled and pkg.disabled: continue + + if pkg.inst: + yield pkg + else: + ninst.append(pkg) + + for pkg in ninst: + yield pkg + + try: + if self.restrict: + return (pkg for pkg in get_pkgs() if self.restrict.search(pkg.cat+"/"+pkg.pkg)) + else: + return get_pkgs() + + except KeyError: # cat is in category list - but not in portage + info(_("Catched KeyError => %s seems not to be an available category. Have you played with rsync-excludes?"), cat) + + @lock + def get_categories (self, installed = False): + if not self.restrict: + if installed: + cats = self.inst_cats + else: + cats = self._db.iterkeys() + + else: + if installed: + cats = set((pkg.cat for pkg in self.get_cat(self.ALL) if pkg.inst)) + else: + cats = set((pkg.cat for pkg in self.get_cat(self.ALL))) + + if len(cats)>1: + cats.add(self.ALL) + + return (cat for cat in cats) + + @lock + def reload (self, cat = None): + if cat: + del self._db[cat] + try: + self.inst_cats.remove(cat) + except KeyError: # not in inst_cats - can be ignored + pass + + self._db[self.ALL] = filter(lambda x: x.cat != cat, self._db[self.ALL]) + self.populate(cat+"/*") + else: + self.__initialize() + self.populate() + + @lock + def disable (self, cpv): + cat, pkg = cpv.split("/") + + c = self._db[cat] + p = c[c.index(PkgData(cat, pkg))] + p.disabled = True + + def get_restrict (self): + return self._restrict + + @lock + def set_restrict (self, restrict): + if not restrict: + self._restrict = None + else: + try: + regex = re.compile(restrict, re.I) + except re.error, e: + info(_("Error while compiling search expression: '%s'."), str(e)) + else: # only set self._restrict if no error occurred + self._restrict = regex + + restrict = property(get_restrict, set_restrict) -- cgit v1.2.3 From 1e88fe3bee51cd04928499f8ec29e00cf129ad33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20=27Necoro=27=20Neumann?= Date: Fri, 14 Aug 2009 15:27:49 +0200 Subject: Add LazyElement --- portato/eix/parser.py | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) (limited to 'portato') diff --git a/portato/eix/parser.py b/portato/eix/parser.py index e89bffe..676cdd5 100644 --- a/portato/eix/parser.py +++ b/portato/eix/parser.py @@ -63,7 +63,7 @@ def vector (file, get_type, skip = False): for i in range(nelems): get_type(file, skip = True) else: - return (get_type(file) for i in range(nelems)) + return [get_type(file) for i in range(nelems)] def string (file, skip = False): nelems = number(file) @@ -84,3 +84,25 @@ def overlay (file, skip = False): string(file, skip = True) # label else: return (string(file), string(file)) + +class LazyElement (object): + def __init__ (self, get_type, file): + self.file = file + self.get_type = get_type + self._value = None + + self.pos = file.tell() + get_type(skip=True) # skip it for the moment + + @property + def value (self): + if self._value is None: + old_pos = self.file.tell() + self.file.seek(self.pos) + self._value = self.get_type(skip = False) + self.file.seek(old_pos) + + return self._value + + def __call__ (self): + return self.value -- cgit v1.2.3 From 0e6702e069aeabcd995b8968d0ebbc1593d59bf6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20=27Necoro=27=20Neumann?= Date: Fri, 14 Aug 2009 15:54:22 +0200 Subject: Add header-object --- portato/eix/__init__.py | 7 ++++++- portato/eix/parser.py | 44 +++++++++++++++++++++++++++++++++++--------- 2 files changed, 41 insertions(+), 10 deletions(-) (limited to 'portato') diff --git a/portato/eix/__init__.py b/portato/eix/__init__.py index 3c1958e..6768ca7 100644 --- a/portato/eix/__init__.py +++ b/portato/eix/__init__.py @@ -18,7 +18,7 @@ from . import exceptions as ex from ..helper import debug class EixReader(object): - supported_version = (28,) + supported_versions = (28,) def __init__ (self, filename): self.filename = filename @@ -31,10 +31,15 @@ class EixReader(object): raise ex.UnsupportedVersionError(self.version) debug("Started EixReader for version %s.", self.version) + + self.file.seek(0) except: self.close() raise + def header (self): + return parser.header(self.file) + def close (self): self.file.close() debug("EixReader closed.") diff --git a/portato/eix/parser.py b/portato/eix/parser.py index 676cdd5..90c569b 100644 --- a/portato/eix/parser.py +++ b/portato/eix/parser.py @@ -15,7 +15,8 @@ from __future__ import absolute_import, with_statement import os import struct -from .helper import debug +from ..helper import debug +from functools import partial from . import exceptions as ex @@ -30,7 +31,7 @@ def _get_bytes (file, length, expect_list = False): else: return struct.unpack("%sB" % length, s) -def number (file): +def number (file, skip = False): n = _get_bytes(file, 1) if n < 0xFF: @@ -49,10 +50,16 @@ def number (file): value = n << (count*8) if count > 0: - rest = _get_bytes(file, count, expect_list = True) - for i, r in enumerate(rest): - value += r << ((count - i - 1)*8) + if skip: + file.seek(count, os.SEEK_CUR) + return + + else: + rest = _get_bytes(file, count, expect_list = True) + + for i, r in enumerate(rest): + value += r << ((count - i - 1)*8) return value @@ -65,11 +72,15 @@ def vector (file, get_type, skip = False): else: return [get_type(file) for i in range(nelems)] +def typed_vector(type): + return partial(vector, get_type = type) + def string (file, skip = False): nelems = number(file) if skip: file.seek(nelems, os.SEEK_CUR) + return else: s = file.read(nelems) @@ -92,17 +103,32 @@ class LazyElement (object): self._value = None self.pos = file.tell() - get_type(skip=True) # skip it for the moment + get_type(file, skip=True) # skip it for the moment @property def value (self): if self._value is None: old_pos = self.file.tell() - self.file.seek(self.pos) - self._value = self.get_type(skip = False) - self.file.seek(old_pos) + self.file.seek(self.pos, os.SEEK_SET) + self._value = self.get_type(self.file, skip = False) + self.file.seek(old_pos, os.SEEK_SET) return self._value def __call__ (self): return self.value + +class header (object): + def __init__ (self, file): + def LE (t): + return LazyElement(t, file) + + self.version = LE(number) + self.ncats = LE(number) + self.overlays = LE(typed_vector(overlay)) + self.provide = LE(typed_vector(string)) + self.licenses = LE(typed_vector(string)) + self.keywords = LE(typed_vector(string)) + self.useflags = LE(typed_vector(string)) + self.slots = LE(typed_vector(string)) + self.sets = LE(typed_vector(string)) -- cgit v1.2.3 From a16cc9501a2a9b7910124caf11b77568e801433b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20=27Necoro=27=20Neumann?= Date: Fri, 14 Aug 2009 16:14:48 +0200 Subject: Add 'skip' parameter to header ... this is ignored though --- portato/eix/parser.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'portato') diff --git a/portato/eix/parser.py b/portato/eix/parser.py index 90c569b..ebf2566 100644 --- a/portato/eix/parser.py +++ b/portato/eix/parser.py @@ -119,7 +119,7 @@ class LazyElement (object): return self.value class header (object): - def __init__ (self, file): + def __init__ (self, file, skip = False): def LE (t): return LazyElement(t, file) -- cgit v1.2.3 From b2dadbfb614395d2c2827b43c91cc2f798f990b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20=27Necoro=27=20Neumann?= Date: Fri, 14 Aug 2009 16:16:49 +0200 Subject: Allow to pass the number of elements in a vector --- portato/eix/parser.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) (limited to 'portato') diff --git a/portato/eix/parser.py b/portato/eix/parser.py index ebf2566..ecfb74c 100644 --- a/portato/eix/parser.py +++ b/portato/eix/parser.py @@ -63,8 +63,9 @@ def number (file, skip = False): return value -def vector (file, get_type, skip = False): - nelems = number(file) +def vector (file, get_type, skip = False, nelems = None): + if nelems is None: + nelems = number(file) if skip: for i in range(nelems): @@ -72,8 +73,11 @@ def vector (file, get_type, skip = False): else: return [get_type(file) for i in range(nelems)] -def typed_vector(type): - return partial(vector, get_type = type) +def typed_vector(type, nelems = None): + if nelems is None: + return partial(vector, get_type = type) + else: + return partial(vector, get_type = type, nelems = nelems) def string (file, skip = False): nelems = number(file) -- cgit v1.2.3 From 02e754313d35a11882aaaf252e474081c18570dd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20=27Necoro=27=20Neumann?= Date: Fri, 14 Aug 2009 16:20:13 +0200 Subject: Make LazyElement use __slots__ - to decrease memory consumption --- portato/eix/parser.py | 1 + 1 file changed, 1 insertion(+) (limited to 'portato') diff --git a/portato/eix/parser.py b/portato/eix/parser.py index ecfb74c..44c4214 100644 --- a/portato/eix/parser.py +++ b/portato/eix/parser.py @@ -101,6 +101,7 @@ def overlay (file, skip = False): return (string(file), string(file)) class LazyElement (object): + __slots__ = ("file", "get_type", "_value", "pos") def __init__ (self, get_type, file): self.file = file self.get_type = get_type -- cgit v1.2.3 From e3aa37a05b150604de50bd9ec6e004a688d698f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20=27Necoro=27=20Neumann?= Date: Fri, 14 Aug 2009 16:45:31 +0200 Subject: Add function to add _ to namespace --- portato/__init__.py | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'portato') diff --git a/portato/__init__.py b/portato/__init__.py index 653329c..4252a0a 100644 --- a/portato/__init__.py +++ b/portato/__init__.py @@ -54,6 +54,11 @@ def get_parser (use_ = False): return parser +def _sub_start (): + # set gettext stuff + locale.setlocale(locale.LC_ALL, '') + gettext.install(APP, LOCALE_DIR, unicode = True) + def start(): # set gettext stuff -- cgit v1.2.3 From d041a06226aec18308b846bd90ef99a744367f3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20=27Necoro=27=20Neumann?= Date: Fri, 14 Aug 2009 17:13:19 +0200 Subject: Add package and category objects --- portato/eix/parser.py | 32 +++++++++++++++++++++++++++++--- 1 file changed, 29 insertions(+), 3 deletions(-) (limited to 'portato') diff --git a/portato/eix/parser.py b/portato/eix/parser.py index 44c4214..ac49789 100644 --- a/portato/eix/parser.py +++ b/portato/eix/parser.py @@ -14,11 +14,10 @@ from __future__ import absolute_import, with_statement import os import struct - -from ..helper import debug from functools import partial -from . import exceptions as ex +from ..helper import debug +from .exceptions import EndOfFileException def _get_bytes (file, length, expect_list = False): s = file.read(length) @@ -137,3 +136,30 @@ class header (object): self.useflags = LE(typed_vector(string)) self.slots = LE(typed_vector(string)) self.sets = LE(typed_vector(string)) + +class package (object): + def __init__ (self, file, skip = False): + def LE (t): + return LazyElement(t, file) + + self.offset = number(file) + + after_offset = file.tell() + + self.name = LE(string) + self.description = LE(string) + self.provide = LE(typed_vector(number)) + self.homepage = LE(string) + self.license = LE(number) + self.useflags = LE(typed_vector(number)) + + # self.versions = LE(typed_vector(version)) + # for the moment just skip the versions + file.seek(self.offset - (file.tell() - after_offset), os.SEEK_CUR) + +class category (object): + __slots__ = ("name", "packages") + + def __init__ (self, file, skip = False): + self.name = LazyElement(string, file) + self.packages = LazyElement(typed_vector(package), file) -- cgit v1.2.3 From 2da9ef4570c72d4b67121890447ac37cc5cc3749 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20=27Necoro=27=20Neumann?= Date: Fri, 14 Aug 2009 17:15:27 +0200 Subject: Add __slots__ --- portato/eix/parser.py | 7 +++++++ 1 file changed, 7 insertions(+) (limited to 'portato') diff --git a/portato/eix/parser.py b/portato/eix/parser.py index ac49789..20e1e06 100644 --- a/portato/eix/parser.py +++ b/portato/eix/parser.py @@ -101,6 +101,7 @@ def overlay (file, skip = False): class LazyElement (object): __slots__ = ("file", "get_type", "_value", "pos") + def __init__ (self, get_type, file): self.file = file self.get_type = get_type @@ -123,6 +124,9 @@ class LazyElement (object): return self.value class header (object): + __slots__ = ("version", "ncats", "overlays", "provide", + "licenses", "keywords", "useflags", "slots", "sets") + def __init__ (self, file, skip = False): def LE (t): return LazyElement(t, file) @@ -138,6 +142,9 @@ class header (object): self.sets = LE(typed_vector(string)) class package (object): + __slots__ = ("offset","name", "description", + "provide", "homepage", "license", "useflags") + def __init__ (self, file, skip = False): def LE (t): return LazyElement(t, file) -- cgit v1.2.3 From 1b6c4b715293c97dcd7256469aa2287ecfc2e656 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20=27Necoro=27=20Neumann?= Date: Fri, 14 Aug 2009 17:17:47 +0200 Subject: Make overlay a class too --- portato/eix/parser.py | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'portato') diff --git a/portato/eix/parser.py b/portato/eix/parser.py index 20e1e06..d5265fa 100644 --- a/portato/eix/parser.py +++ b/portato/eix/parser.py @@ -92,13 +92,6 @@ def string (file, skip = False): return s -def overlay (file, skip = False): - if skip: - string(file, skip = True) # path - string(file, skip = True) # label - else: - return (string(file), string(file)) - class LazyElement (object): __slots__ = ("file", "get_type", "_value", "pos") @@ -123,6 +116,13 @@ class LazyElement (object): def __call__ (self): return self.value +class overlay (object): + __slots__ = ("path", "label") + + def __init__ (file, skip = False): + self.path = LazyElement(string, file) + self.label = LazyElement(string, label) + class header (object): __slots__ = ("version", "ncats", "overlays", "provide", "licenses", "keywords", "useflags", "slots", "sets") -- cgit v1.2.3 From 308766600eadee21d122091fbe24950e89417280 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20=27Necoro=27=20Neumann?= Date: Fri, 14 Aug 2009 17:30:07 +0200 Subject: Fix overlay class --- portato/eix/parser.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'portato') diff --git a/portato/eix/parser.py b/portato/eix/parser.py index d5265fa..6e12973 100644 --- a/portato/eix/parser.py +++ b/portato/eix/parser.py @@ -119,9 +119,9 @@ class LazyElement (object): class overlay (object): __slots__ = ("path", "label") - def __init__ (file, skip = False): + def __init__ (self, file, skip = False): self.path = LazyElement(string, file) - self.label = LazyElement(string, label) + self.label = LazyElement(string, file) class header (object): __slots__ = ("version", "ncats", "overlays", "provide", -- cgit v1.2.3 From d2afc4afe4d16b3ecd475d62c1baa86d385667a9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20=27Necoro=27=20Neumann?= Date: Fri, 14 Aug 2009 17:30:43 +0200 Subject: Finish EixReader --- portato/eix/__init__.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'portato') diff --git a/portato/eix/__init__.py b/portato/eix/__init__.py index 6768ca7..977c9e2 100644 --- a/portato/eix/__init__.py +++ b/portato/eix/__init__.py @@ -13,7 +13,7 @@ from __future__ import absolute_import, with_statement from . import parser -from . import exceptions as ex +from .exceptions import UnsupportedVersionError from ..helper import debug @@ -25,21 +25,21 @@ class EixReader(object): self.file = open(filename, "r") try: - self.version = parser.number(self.file) + version = parser.number(self.file) - if self.version not in self.supported_versions: - raise ex.UnsupportedVersionError(self.version) + if version not in self.supported_versions: + raise UnsupportedVersionError(self.version) - debug("Started EixReader for version %s.", self.version) + debug("Started EixReader for version %s.", version) self.file.seek(0) + + self.header = parser.header(self.file) + self.categories = parser.vector(self.file, parser.category, nelems = self.header.ncats()) except: self.close() raise - def header (self): - return parser.header(self.file) - def close (self): self.file.close() debug("EixReader closed.") -- cgit v1.2.3 From 82f6e37bdd112d223580abdbe788442a6bb22666 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ren=C3=A9=20=27Necoro=27=20Neumann?= Date: Fri, 14 Aug 2009 18:53:17 +0200 Subject: Add documentation --- portato/eix/__init__.py | 37 ++++++- portato/eix/exceptions.py | 16 +++ portato/eix/parser.py | 252 +++++++++++++++++++++++++++++++++++++++++++++- 3 files changed, 300 insertions(+), 5 deletions(-) (limited to 'portato') diff --git a/portato/eix/__init__.py b/portato/eix/__init__.py index 977c9e2..aed5855 100644 --- a/portato/eix/__init__.py +++ b/portato/eix/__init__.py @@ -10,7 +10,12 @@ # # Written by René 'Necoro' Neumann +""" +A module to parse the eix-cache files. +""" + from __future__ import absolute_import, with_statement +__docformat__ = "restructuredtext" from . import parser from .exceptions import UnsupportedVersionError @@ -18,10 +23,37 @@ from .exceptions import UnsupportedVersionError from ..helper import debug class EixReader(object): + """ + The main class to use to have access to the eix-cache. + + Note that the file used internally stays open during the whole operation. + So please call `close()` when you are finished. + + :CVariables: + + supported_versions : int[] + The list of versions of the eix-cache, which are supported by this reader. + + :IVariables: + + file : file + The eix cache file. + + header : `parser.header` + The header of the eix cache. + + categories : `parser.category` [] + The list of categories. + """ + supported_versions = (28,) def __init__ (self, filename): - self.filename = filename + """ + :param filename: Path to the cache file + :type filename: string + """ + self.file = open(filename, "r") try: @@ -41,5 +73,8 @@ class EixReader(object): raise def close (self): + """ + Closes the cache file. + """ self.file.close() debug("EixReader closed.") diff --git a/portato/eix/exceptions.py b/portato/eix/exceptions.py index fd72dcf..8145af4 100644 --- a/portato/eix/exceptions.py +++ b/portato/eix/exceptions.py @@ -10,20 +10,36 @@ # # Written by René 'Necoro' Neumann +""" +Different exceptions used in the eix module. +""" + from __future__ import absolute_import, with_statement +__docformat__ = "restructuredtext" class EixError (Exception): + """ + The base class for all exceptions of this module. + + :ivar message: The error message + """ message = _("Unknown error.") def __str__ (self): return self.message class EndOfFileException (EixError): + """ + Denotes the unexpected EOF. + """ def __init__ (self, filename): self.message = _("End of file reached though it was not expected: '%s'") % filename class UnsupportedVersionError (EixError): + """ + The version of the cache file found is not supported. + """ def __init__ (self, version): self.message = _("Version '%s' is not supported.") % version 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 +""" +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 (