public inbox for gentoo-commits@lists.gentoo.org
 help / color / mirror / Atom feed
From: "André Erdmann" <dywi@mailerd.de>
To: gentoo-commits@lists.gentoo.org
Subject: [gentoo-commits] proj/R_overlay:master commit in: roverlay/
Date: Fri,  1 Jun 2012 15:46:35 +0000 (UTC)	[thread overview]
Message-ID: <1338562569.ac0b226fd7d16dad7a4b8d30b4293fe401406c1c.dywi@gentoo> (raw)

commit:     ac0b226fd7d16dad7a4b8d30b4293fe401406c1c
Author:     André Erdmann <dywi <AT> mailerd <DOT> de>
AuthorDate: Fri Jun  1 14:56:09 2012 +0000
Commit:     André Erdmann <dywi <AT> mailerd <DOT> de>
CommitDate: Fri Jun  1 14:56:09 2012 +0000
URL:        http://git.overlays.gentoo.org/gitweb/?p=proj/R_overlay.git;a=commit;h=ac0b226f

2012-01-06:
	* description field definition is now configurable

	modified:   roverlay/__init__.py
	modified:   roverlay/config.py
	new file:   roverlay/const.py
	modified:   roverlay/descriptionfields.py
	renamed:    roverlay/fileio.py -> roverlay/descriptionreader.py
	modified:   roverlay/ebuild.py
	modified:   roverlay/ebuildjob.py
	deleted:    roverlay/tmpconst.py

---
 roverlay/__init__.py                         |    5 +
 roverlay/config.py                           |  164 ++++++++++++-
 roverlay/const.py                            |   51 ++++
 roverlay/descriptionfields.py                |  343 +++++++++++++++++++++++---
 roverlay/{fileio.py => descriptionreader.py} |  248 ++++++-------------
 roverlay/ebuild.py                           |    5 +-
 roverlay/ebuildjob.py                        |   15 +-
 roverlay/tmpconst.py                         |  108 --------
 8 files changed, 615 insertions(+), 324 deletions(-)

diff --git a/roverlay/__init__.py b/roverlay/__init__.py
index b1557ef..f50cec9 100644
--- a/roverlay/__init__.py
+++ b/roverlay/__init__.py
@@ -4,6 +4,11 @@
 
 import logging
 
+
+from roverlay import config
+
+config.access().load_field_definition ( 'description_fields.conf' )
+
 logging.basicConfig (
 	level=logging.DEBUG,
 	filename='roverlay.log',

diff --git a/roverlay/config.py b/roverlay/config.py
index 248fc22..b0bf7a3 100644
--- a/roverlay/config.py
+++ b/roverlay/config.py
@@ -3,20 +3,53 @@
 # Distributed under the terms of the GNU General Public License v2
 
 import sys
-
-from roverlay import descriptionfields
+import shlex
 
 try:
 	import configparser
-except ImportError:
+except ImportError as running_python2:
+	# configparser is named ConfigParser in python2
 	import ConfigParser as configparser
 
+
+
+
+from roverlay import descriptionfields
+from roverlay import const
+
+
+
+
 def access():
+	"""Returns the ConfigTree."""
 	return ConfigTree() if ConfigTree.instance is None else ConfigTree.instance
+# --- end of access (...) ---
+
+
+def get ( key, fallback_value=None ):
+	"""Searches for key in the ConfigTree and returns its value if possible,
+	else fallback_value.
+	'key' is a config path [<section>[.<subsection>*]]<option name>.
+
+	arguments:
+	* key --
+	* fallback_value --
+	"""
+	if fallback_value:
+		return access().get ( key, fallback_value )
+	else:
+		return access().get ( key )
+# --- end of get (...) ---
+
 
 class InitialLogger:
 
 	def __init__ ( self ):
+		"""Initializes an InitialLogger.
+		It implements the debug/info/warning/error/critical/exception methods
+		known from the logging module and its output goes directly to sys.stderr.
+		This can be used until the real logging has been configured.
+		"""
 		self.debug     = lambda x : sys.stderr.write ( "DBG  " + str ( x ) + "\n" )
 		self.info      = lambda x : sys.stderr.write ( "INFO " + str ( x ) + "\n" )
 		self.warning   = lambda x : sys.stderr.write ( "WARN " + str ( x ) + "\n" )
@@ -24,11 +57,23 @@ class InitialLogger:
 		self.critical  = lambda x : sys.stderr.write ( "CRIT " + str ( x ) + "\n" )
 		self.exception = lambda x : sys.stderr.write ( "EXC! " + str ( x ) + "\n" )
 
+	# --- end of __init__ (...) ---
+
 class ConfigTree:
 	# static access to the first created ConfigTree
 	instance = None
 
-	def __init__ ( self ):
+	def __init__ ( self, import_const=True ):
+		"""Initializes an ConfigTree, which is a container for options/config values.
+		values can be stored directly (such as the field_definitions) or in a
+		tree-like { section -> subsection[s] -> option = value } structure.
+		Config keys cannot contain dots because they're used as config path
+		separator.
+
+		arguments:
+		* import_const -- whether to deepcopy constants into the config tree or
+		                  not. Copying allows faster lookups.
+		"""
 		if ConfigTree.instance is None:
 			ConfigTree.instance = self
 
@@ -36,8 +81,52 @@ class ConfigTree:
 
 		self.parser = dict()
 
+		self._config = const.clone() if import_const else None
+		self._const_imported = import_const
+		self._field_definitions = None
+
+	# --- end of __init__ (...) ---
+
+
+	def get ( self, key, fallback_value=None ):
+		"""Searches for key in the ConfigTree and returns its value.
+		Searches in const if ConfigTree does not contain the requested key and
+		returns the fallback_value if key not found.
+
+		arguments:
+		* key --
+		* fallback_value --
+		"""
+		if self._config:
+			config_path = key.split ( '.' )
+			config_path.reverse ()
+
+			config_position = self._config
+			while len ( config_path ) and config_position:
+				next_key = config_path.pop ()
+				if next_key in config_position:
+					config_position = config_position [next_key]
+				else:
+					config_position = None
+
+			if config_position:
+				return config_position
+
+		if self._const_imported:
+			return fallback_value
+		else:
+			return const.lookup ( key, fallback_value )
+
+	# --- end of get (...) ---
 
 	def load_field_definition ( self, def_file, lenient=False ):
+		"""Loads a field definition file. Please see the example file for format
+		details.
+
+		arguments:
+		* def_file -- file (str) to read, this can be a list of str if lenient is True
+		* lenient  -- if True: do not fail if a file cannot be read; defaults to False
+		"""
 		if not 'field_def' in self.parser:
 			self.parser ['field_def'] = configparser.SafeConfigParser ( allow_no_value=True )
 
@@ -57,4 +146,71 @@ class ConfigTree:
 			self.logger.exception ( mshe )
 			raise
 
+	# --- end of load_field_definition (...) ---
+
+
+	def get_field_definition ( self, force_update=False ):
+		"""Gets the field definition stored in this ConfigTree.
+
+		arguments:
+		* force_update -- enforces recreation of the field definition data.
+		"""
+		if force_update or not self._field_definitions:
+			self._field_definitions = self._make_field_definition ()
+
+		return self._field_definitions
+
+	# --- end of get_field_definition (...) ---
+
+
+	def _make_field_definition ( self ):
+		"""Creates and returns field definition data. Please see the example
+		field definition config file for details.
+		"""
+
+		def get_list ( value_str ):
+			if value_str is None:
+				return []
+			else:
+				l = value_str.split ( ', ' )
+				return [ e for e in l if e.strip() ]
+
+		if not 'field_def' in self.parser:
+			return None
+
+		fdef = descriptionfields.DescriptionFields ()
+
+		for field_name in self.parser ['field_def'].sections():
+			field = descriptionfields.DescriptionField ( field_name )
+			for option, value in self.parser ['field_def'].items ( field_name, 1 ):
+
+				if option == 'alias' or option == 'alias_withcase':
+					for alias in get_list ( value ):
+						field.add_simple_alias ( alias, True )
+
+				elif option == 'alias_nocase':
+					for alias in get_list ( value ):
+						field.add_simple_alias ( alias, False )
+
+				elif option == 'default_value':
+					field.set_default_value ( value )
+
+				elif option == 'allowed_value':
+					field.add_allowed_value ( value )
+
+				elif option == 'allowed_values':
+					for item in get_list ( value ):
+						field.add_allowed_value ( item )
+
+				elif option == 'flags':
+					for flag in get_list ( value ):
+						field.add_flag ( flag )
+				else:
+					# treat option as flag
+					field.add_flag ( option )
+
+			fdef.add ( field )
+
+		return fdef
 
+	# --- end of _make_field_definition (...) ---

diff --git a/roverlay/const.py b/roverlay/const.py
new file mode 100644
index 0000000..32630cf
--- /dev/null
+++ b/roverlay/const.py
@@ -0,0 +1,51 @@
+# R Overlay -- constants
+# Copyright 2006-2012 Gentoo Foundation
+# Distributed under the terms of the GNU General Public License v2
+
+import copy
+import time
+
+_CONSTANTS = dict (
+	DESCRIPTION = dict (
+		field_separator  = ':',
+		comment_char     = '#',
+		list_split_regex = '\s*[,;]{1}\s*',
+		file_name        = 'DESCRIPTION',
+	),
+	R_PACKAGE = dict (
+		suffix_regex       = '[.](tgz|tbz2|tar|(tar[.](gz|bz2)))',
+		name_ver_separator = '_',
+	),
+	EBUILD = dict (
+		indent         = '\t',
+		default_header = [	'# Copyright 1999-' + str ( time.gmtime() [0] ) + ' Gentoo Foundation',
+									'# Distributed under the terms of the GNU General Public License v2',
+									'# $Header: $',
+									'',
+									'EAPI=4',
+									'',
+									'inherit R-packages'
+								],
+	)
+)
+
+def lookup ( key, fallback_value=None ):
+	path = key.split ( '.' )
+	path.reverse ()
+
+	const_position = _CONSTANTS
+
+	while len ( path ) and const_position:
+		next_key = path.pop ()
+		if next_key in const_position:
+			const_position = const_position [next_key]
+		else:
+			const_position = None
+
+	if const_position:
+		return const_position
+	else:
+		return fallback_value
+
+def clone ( ):
+	return copy.deepcopy ( _CONSTANTS )

diff --git a/roverlay/descriptionfields.py b/roverlay/descriptionfields.py
index 9c028c2..72175cb 100644
--- a/roverlay/descriptionfields.py
+++ b/roverlay/descriptionfields.py
@@ -2,39 +2,95 @@
 # Copyright 2006-2012 Gentoo Foundation
 # Distributed under the terms of the GNU General Public License v2
 
-
-# split from tmpconst / fileio to make configuration possible, but TODO
-
 class DescriptionField:
+	"""Configuration for a field in the R package description file."""
 
 	def __init__ ( self, name ):
+		"""Initializes a DescriptionField with a valid(!) name.
+
+		arguments:
+		* name -- name of the field, has to be True (neither empty nor None)
+
+		raises: Exception if name not valid
+		"""
+
 		if not name:
 			raise Exception ( "description field name is empty." )
 
 		self.name = name
 
+	# --- end of __init__ (...) ---
 
 
 	def get_name ( self ):
+		"""Returns the name of this DescriptionField."""
 		return self.name
 
-	def add_flag ( self, flag, lowercase=True ):
-		if not hasattr ( self, flags ):
+	# --- end of get_name (...) ---
+
+
+	def add_flag ( self, flag ):
+		"""Adds a flag to this DescriptionField. Flags are always stored in
+		their lowercase form.
+
+		arguments:
+		* flag -- name of the flag
+		"""
+		if not hasattr ( self, 'flags' ):
 			self.flags = set ()
 
-		self.flags.add ( flag, flag.lower() if lowercase else flag )
+		self.flags.add ( flag.lower() )
 
 		return None
 
+	# --- end of add_flag (...) ---
+
+
+	def add_allowed_value ( self, value ):
+		"""Adds an allowed value to this DescriptionField, which creates a
+		value whitelist for it. You can later check if a value is allowed using
+		value_allowed (<value> [, <case insensitive?>]).
+
+		arguments:
+		* value -- allowed value
+		"""
+
+		if not hasattr ( self, 'allowed_values' ):
+			self.allowed_values = set ()
+
+		self.allowed_values.add ( value )
+
+		return None
+
+	# --- end of add_allowed_value (...) ---
+
 
 	def del_flag ( self, flag ):
-		if hasattr ( self, flags ):
-			self.flags.discard ( flag )
+		"""Removes a flag from this DescriptionField. Does nothing if the flag
+		does not exist.
+		"""
+		if hasattr ( self, 'flags' ):
+			self.flags.discard ( flag.lower() )
 		return None
 
+	# --- end of del_flag (...) ---
+
 
 	def add_alias ( self, alias, alias_type='withcase' ):
-		if not hasattr ( self, aliases ):
+		"""Adds an alias for this DescriptionField's name. This can also be used
+		to combine different fields ('Description' and 'Title') or to fix
+		typos ('Depend' -> 'Depends').
+
+		arguments:
+		* alias -- alias name
+		* alias_type -- type of the alias; currently this is limited to
+		                 'withcase' : alias is case sensitive,
+		                 'nocase'   : alias is case insensitive
+		                any other type leads to an error
+
+		raises: KeyError if alias_type unknown.
+		"""
+		if not hasattr ( self, 'aliases' ):
 			self.aliases = dict ()
 
 		to_add = dict (
@@ -50,37 +106,83 @@ class DescriptionField:
 
 		return None
 
+	# --- end of add_alias (...) ---
 
 
 	def add_simple_alias ( self, alias, withcase=True ):
-		if withcase:
-			return self.add_alias ( alias, alias_type='withcase' )
-		else:
-			return self.add_alias ( alias, alias_type='nocase' )
+		"""Adds an alias to this DescriptionField. Its type is either withcase
+		or nocase. See add_alias (...) for details.
+
+		arguments:
+		alias --
+		withcase -- if True (the default): alias_type is withcase, else nocase
+
+		raises: KeyError (passed from add_alias (...))
+		"""
+		return self.add_alias ( alias, ( 'withcase' if withcase else 'nocase' ) )
 
+	# --- end of add_simple_alias (...) ---
 
 
 	def get_default_value ( self ):
-		if hasattr ( self, 'default_value' ):
-			return self.default_value
-		else:
-			return None
+		"""Returns the default value for this DescriptionField if it exists,
+		else None.
+		"""
+		return self.default_value if hasattr ( self, 'default_value' ) else None
 
+	# --- end of get_default_value (...) ---
+
+
+	def set_default_value ( self, value ):
+		"""Sets the default value for this this DescriptionField.
+
+		arguments:
+		* value -- new default value
+		"""
+		self.default_value = value
+
+	# --- end of set_default_value (...) ---
+
+
+	def get_flags ( self ):
+		"""Returns the flags of this DescriptionField or an empty list (=no flags)."""
+		return self.flags if hasattr ( self, 'flags' ) else []
+
+	# --- end of get_flags (...) ---
+
+
+	def get_allowed_values ( self ):
+		"""Returns the allowed values of this DescriptionField or an empty list,
+		which should be interpreted as 'no value restriction'.
+		"""
+		return self.allowed_values if hasattr ( self, 'allowed_values' ) else []
+
+	# --- end of get_allowed_values (...) ---
 
-	def get ( self, key, fallback_value=None ):
-		if hasattr ( self, key ):
-			return self.key
-		else:
-			return fallback_value
 
 	def matches ( self, field_identifier ):
+		"""Returns whether field_identifier equals the name of this DescriptionField.
+
+		arguments:
+		* field_identifier --
+		"""
 		return bool ( self.name == field_identifier ) if field_identifier else False
 
+	# --- end of matches (...) ---
+
+
 	def matches_alias ( self, field_identifier ):
+		"""Returns whether field_identifier equals any alias of this DescriptionField.
+
+		arguments:
+		* field_identifier --
+		"""
 
 		if not field_identifier:
+			# bad identifier
 			return False
-		if not hasattr ( self, aliases ):
+		elif not hasattr ( self, aliases ):
+			# no aliases
 			return False
 
 		if 'withcase' in self.aliases:
@@ -92,30 +194,211 @@ class DescriptionField:
 			if field_id_lower in self.aliases ['nocase']:
 				return True
 
-	def has_flag ( self, flag, lowercase=True ):
-		if not hasattr ( self, flags ):
+	# --- end of matches_alias (...) ---
+
+
+	def has_flag ( self, flag  ):
+		"""Returns whether this DescriptionField has the given flag.
+
+		arguments:
+		* flag --
+		"""
+		if not hasattr ( self, 'flags' ):
 			return False
 
-		return bool ( (flag.lower() if lowercase else flag) in self.flags )
+		return bool ( flag.lower() in self.flags )
+
+	def value_allowed ( self, value, nocase=True ):
+		"""Returns whether value is allowed for this DescriptionField.
+
+		arguments:
+		* value -- value to check
+		* nocase -- if True (the default): be case insensitive
+		"""
+		allowed_values = self.get_allowed_values ()
+
+		if not allowed_values:
+			return True
+		elif nocase:
+			lowval = value.lower()
+			for allowed in allowed_values:
+				if allowed.lower() == lowval:
+					return True
+
+		else:
+			return bool ( value in allowed_values )
+
+		return False
+
+	# --- end of has_flag (...) ---
+
+# --- end of DescriptionField ---
+
 
 class DescriptionFields:
+	"""DescriptionFields stores several instances of DescriptionField and provides
+	'search in all' methods such as get_fields_with_flag (<flag>).
+	"""
 
 	def __init__ ( self ):
-		fields = dict ()
+		"""Initializes an DescriptionFields object."""
+		self.fields = dict ()
+		# result 'caches'
+		## flag -> [<fields>]
+		self._fields_by_flag   = None
+		## option -> [<fields>]
+		self._fields_by_option = None
+
+	# --- end of __init__ (...) ---
+
 
 	def add ( self, desc_field ):
+		"""Adds an DescriptionField. Returns 1 desc_field was a DescriptionField
+		and has been added as obj ref, 2 if a new DescriptionField with
+		name=desc_field has been created and added and 0 if this was not
+		possible.
+
+		arguments:
+		* desc_field -- this can either be a DescriptionField or a name.
+		"""
 		if desc_field:
 			if isinstance ( desc_field, DescriptionField ):
-				fields [desc_field.get_name()] = desc_field
+				self.fields [desc_field.get_name()] = desc_field
 				return 1
 			elif isinstance ( desc_field, str ):
-				fields [desc_field] = DescriptionField ( desc_field )
+				self.fields [desc_field] = DescriptionField ( desc_field )
 				return 2
 
 		return 0
 
+	# --- end of add (...) ---
+
+
 	def get ( self, field_name ):
+		"""Returns the DescriptionField to which field_name belongs to.
+		This method does, unlike others in DescriptionFields, return a
+		reference to the matching DescriptionField object, not the field name!
+		Returns None if field_name not found.
+
+		arguments:
+		* field_name --
+		"""
+
 		return self.fields [field_name] if field_name in self.fields else None
 
-	# ... TODO
+	# --- end of get (...) ---
+
+
+	def find_field ( self, field_name ):
+		"""Determines the name of the DescriptionField to which field_name belongs
+		to. Returns the name of the matching field or None.
+
+		arguments:
+		* field_name --
+		"""
+
+		field = get ( field_name )
+		if field is None:
+			for field in self.fields:
+				if field.matches_alias ( field_name ):
+					return field.get_name ()
+		else:
+			return field.get_name ()
+
+	# --- end of find_field (...) ---
+
+
+	def _field_search ( self ):
+		"""Scans all stored DescriptionField(s) and creates fast-accessible
+		data to be used in get_fields_with_<sth> (...).
+		"""
+		flagmap   = dict ()
+		optionmap = dict (
+			defaults       = dict (),
+			allowed_values = set ()
+		)
+
+		for field_name in self.fields.keys():
+
+			d = self.fields [field_name].get_default_value()
+			if not d is None:
+				optionmap ['defaults'] [field_name] = d
+
+			if self.fields [field_name].get_allowed_values():
+				optionmap ['allowed_values'].add ( field_name )
+
+			for flag in self.fields [field_name].get_flags():
+				if not flag in flagmap:
+					flagmap [flag] = set ()
+				flagmap [flag].add ( field_name )
+
+		self._fields_by_flag   = flagmap
+		self._fields_by_option = optionmap
+		return None
+
+	# --- end of _field_search (...) ---
+
+
+	def get_fields_with_flag ( self, flag, force_update=False ):
+		"""Returns the names of the fields that have the given flag.
+
+		arguments:
+		* flag --
+		* force_update -- force recreation of data
+		"""
+		if force_update or self._fields_by_flag is None:
+			self._field_search ()
+
+		flag = flag.lower()
+
+		if flag in self._fields_by_flag:
+			return self._fields_by_flag [flag]
+		else:
+			return []
+
+	# --- end of get_fields_with_flag (...) ---
+
+
+	def get_fields_with_option ( self, option, force_update=False ):
+		"""Returns a struct with fields that have the given option. The actual
+		data type depends on the requested option.
+
+		arguments:
+		* option --
+		* force_update -- force recreation of data
+		"""
+		if force_update or self._fields_by_option is None:
+			self._field_search ()
+
+		if option in self._fields_by_option:
+			return self._fields_by_option [option]
+		else:
+			return []
+
+	# --- end of get_field_with_option (...) ---
+
+
+	def get_fields_with_default_value ( self, force_update=False ):
+		"""Returns a dict { '<field name>' -> '<default value>' } for all
+		fields that have a default value.
+
+		arguments:
+		* force_update -- force recreation of data
+		"""
+		return self.get_fields_with_option ( 'defaults', force_update )
+
+	# --- end of get_fields_with_default_value (...) ---
+
+
+	def get_fields_with_allowed_values ( self, force_update=False ):
+		"""Returns a set { <field name> } for all fields that allow only
+		certain values.
+
+		arguments:
+		* force_update -- force recreation of data
+		"""
+		return self.get_fields_with_option ( 'allowed_values', force_update )
+
+	# --- end of get_fields_with_allowed_values (...) ---
 
+# --- end of DescriptionFields ---

diff --git a/roverlay/fileio.py b/roverlay/descriptionreader.py
similarity index 54%
rename from roverlay/fileio.py
rename to roverlay/descriptionreader.py
index 5c01527..2af4372 100644
--- a/roverlay/fileio.py
+++ b/roverlay/descriptionreader.py
@@ -1,4 +1,4 @@
-# R Overlay -- file in/out
+# R Overlay -- description reader
 # Copyright 2006-2012 Gentoo Foundation
 # Distributed under the terms of the GNU General Public License v2
 
@@ -7,21 +7,25 @@ import tarfile
 import logging
 import os.path
 
-
-# temporary import until config and real constants are implemented
-from roverlay import tmpconst as const
+from roverlay import config
+from roverlay import descriptionfields
 
 class DescriptionReader:
 	"""Description Reader"""
 
 	LOGGER = logging.getLogger ( 'DescriptionReader' )
 
+
 	def __init__ ( self, package_file, read_now=False ):
 		"""Initializes a DESCRIPTION file reader."""
 
-		self.fileinfo  = self.make_fileinfo ( package_file )
-		self.logger    = DescriptionReader.LOGGER.getChild ( self.get_log_name() )
-		self.desc_data = None
+		if not config.access().get_field_definition():
+			raise Exception ( "Field definition is missing, cannot initialize DescriptionReader." )
+
+		self.field_definition = config.access().get_field_definition()
+		self.fileinfo         = self.make_fileinfo ( package_file )
+		self.logger           = DescriptionReader.LOGGER.getChild ( self.get_log_name() )
+		self.desc_data        = None
 
 
 		if read_now:
@@ -30,10 +34,13 @@ class DescriptionReader:
 	# --- end of __init__ (...) ---
 
 	def get_log_name ( self ):
+		"""Returns a logging name that can be used in other modules."""
 		try:
 			return self.fileinfo ['filename']
 		except Exception as any_exception:
 			return '__undef__'
+	# --- end of get_log_name (...) ---
+
 
 	def get_desc ( self, run_if_unset=True ):
 		if self.desc_data is None:
@@ -58,10 +65,11 @@ class DescriptionReader:
 
 		package_file = os.path.basename ( filepath )
 
-		filename = re.sub ( const.RPACKAGE_SUFFIX_REGEX + '$', '', package_file )
+		filename = re.sub ( config.get ( 'R_PACKAGE.suffix_regex' ) + '$', '', package_file )
 
-		# todo move that separator to const
-		package_name, sepa, package_version = filename.partition ( '_' )
+		package_name, sepa, package_version = filename.partition (
+			config.get ( 'R_PACKAGE.name_ver_separator', '_' )
+		)
 
 		if not sepa:
 			# file name unexpected, tarball extraction will (probably) fail
@@ -78,7 +86,6 @@ class DescriptionReader:
 
 	# --- end of make_fileinfo (...) ---
 
-
 	def _parse_read_data ( self, read_data ):
 		"""Verifies and parses/fixes read data.
 
@@ -86,80 +93,42 @@ class DescriptionReader:
 		* read_data -- data from file, will be modified
 		"""
 
-		def get_fields_with_flag ( flag, foce_update=False ):
-
-			matching_fields = []
-
-			field = None
-			for field in const.DESCRIPTION_FIELD_MAP.keys():
-				if flag is None:
-					matching_fields.append ( field )
-
-				elif 'flags' in const.DESCRIPTION_FIELD_MAP [field]:
-					if flag in const.DESCRIPTION_FIELD_MAP [field] ['flags']:
-						matching_fields.append ( field )
-
-			del field
-			return matching_fields
-
-		# --- end of get_fields_with_flag (...) ---
-
-		def value_in_strlist ( _val, _list, case_insensitive=True ):
-			"""Returns true if value is in the given list."""
-			el = None
-			if case_insensitive:
-				lowval = _val.lower()
-				for el in _list:
-					if el.lower() == lowval:
-						return True
-				del lowval
-			else:
-				for el in _list:
-					if el == _val:
-						return True
-
-			del el
-			return False
-		# --- end of value_in_strlist (...) ---
-
-		field = None
 
 		# insert default values
-		for field in const.DESCRIPTION_FIELD_MAP.keys():
-			if not field in read_data and 'default_value' in const.DESCRIPTION_FIELD_MAP [field]:
-				read_data [field] = const.DESCRIPTION_FIELD_MAP [field] ['default_value']
+		default_values = self.field_definition.get_fields_with_default_value()
+		for field_name in default_values.keys():
+			if not field_name in read_data:
+				read_data [field_name] = default_values [field_name]
+
 
 		# join values to a single string
-		for field in get_fields_with_flag ( 'joinValues' ):
-			if field in read_data.keys():
-				read_data [field] = ' ' . join ( read_data [field] )
+		for field_name in self.field_definition.get_fields_with_flag ( 'joinValues' ):
+			print ( "?, ".join ( [ field_name, 'join', str ( read_data ) ] ) )
+			if field_name in read_data:
+				read_data [field_name] = ' ' . join ( read_data [field_name] )
 
 		# ensure that all mandatory fields are set
-		missing_fields = list()
+		missing_fields = set ()
 
-		for field in get_fields_with_flag ( 'mandatory' ):
-			if field in read_data:
-				if not len (read_data [field]):
-					missing_fields.append ( field )
+		for field_name in self.field_definition.get_fields_with_flag ( 'mandatory' ):
+			if field_name in read_data:
+				if read_data [field_name] is None or len ( read_data [field_name] ) < 1:
+					missing_fields.add ( field_name )
+				#else: ok
 			else:
-				missing_fields.append ( field )
-
-
+				missing_fields.add ( field_name )
 
 
 		# check for fields that allow only certain values
-		unsuitable_fields = dict()
+		unsuitable_fields = set()
 
-		for field in read_data.keys():
-			if 'allowed_values' in const.DESCRIPTION_FIELD_MAP [field]:
-				if not value_in_strlist (
-							read_data [field],
-							const.DESCRIPTION_FIELD_MAP [field] ['allowed_values']
-						):
-					unsuitable_fields.append [field] = read_data [field]
-
-		del field
+		restricted_fields = self.field_definition.get_fields_with_allowed_values()
+		for field_name in restricted_fields:
+			if field_name in read_data:
+				if not self.field_definition.get ( field_name ).value_allowed ( read_data [field_name] ):
+					unsuitable_fields.add ( field_name )
 
+		# summarize results
 		valid = not bool ( len ( missing_fields ) or len ( unsuitable_fields ) )
 		if not valid:
 			self.logger.info ( "Cannot use R package" ) # name?
@@ -200,27 +169,6 @@ class DescriptionReader:
 			multiple values arranged in a list (dep0, dep1 [, depK]*).
 			"""
 
-			def check_fieldflag ( field, flag_to_check=None ):
-				"""Checks if the given field has the specified flag and returns a bool.
-
-				arguments:
-				* field -- name of the field that should be checked
-				* flag_to_check -- name of the flag to check; optional, defaults to None
-
-				This method acts as 'field has any flags?' if flag_to_check is None (its default value).
-				"""
-
-				if field in const.DESCRIPTION_FIELD_MAP:
-					if 'flags' in const.DESCRIPTION_FIELD_MAP [field]:
-						if flag_to_check in const.DESCRIPTION_FIELD_MAP [field] ['flags']:
-							return True
-						elif flag_to_check is None:
-							# 'flags' exist, return true
-							return True
-
-				return False
-			# --- end of check_fieldflag (...) ---
-
 			svalue_str = value_str.strip()
 
 			if not svalue_str:
@@ -231,16 +179,17 @@ class DescriptionReader:
 				# default return if no context given
 				return [ svalue_str ]
 
-			elif check_fieldflag ( field_context ):
-				# value str is not empty and have flags for field_context, check these
-
-				if check_fieldflag ( field_context, 'isList' ):
-						# split up this list (that is separated by commata and/or semicolons)
-						return re.split (const.DESCRIPTION_LIST_SPLIT_REGEX, svalue_str, 0)
+			elif field_context in self.field_definition.get_fields_with_flag ( 'isList' ):
+					# split up this list (that is separated by commata and/or semicolons)
+					return re.split (
+						config.get ( 'DESCRIPTION.list_split_regex' ),
+						svalue_str,
+						0
+					)
 
-				elif check_fieldflag ( field_context, 'isWhitespaceList' ):
-						# split up this list (that is separated whitespace)
-						return re.split ( '\s+', svalue_str, 0 )
+			elif field_context in self.field_definition.get_fields_with_flag ( 'isWhitespaceList' ):
+					# split up this list (that is separated whitespace)
+					return re.split ( '\s+', svalue_str, 0 )
 
 
 			# default return
@@ -275,9 +224,12 @@ class DescriptionReader:
 				# filepath is a tarball, open tar handle + file handle
 				th = tarfile.open ( filepath, 'r' )
 				if pkg_name:
-					fh = th.extractfile ( os.path.join ( pkg_name, const.DESCRIPTION_FILE_NAME ) )
+					fh = th.extractfile ( os.path.join (
+						pkg_name,
+						config.get ( 'DESCRIPTION.file_name' )
+						) )
 				else:
-					fh = th.extractfile ( const.DESCRIPTION_FILE_NAME )
+					fh = th.extractfile ( config.get ( 'DESCRIPTION.file_name' ) )
 
 				# have to decode the lines
 				read = lambda lines : [ line.decode().rstrip() for line in lines ]
@@ -298,60 +250,9 @@ class DescriptionReader:
 
 		# --- end of get_desc_from_file (...) ---
 
-		def find_field ( field_identifier ):
-			"""Determines the real name of a field.
-
-			arguments:
-			* field_identifier -- name of the field as it appears in the DESCRIPTION file
-
-			At first, it is checked whether field_identifier matches the name of
-			a field listed in DESCRIPTION_FIELD_MAP (any match results in immediate return).
-			Then, a new iteration over the field map compares field_identifier
-			with all aliases until the first case-(in)sensitive match (-> immediate return).
-			None will be returned if none of the above searches succeed.
-
-			In other words: this method decides whether a field_identifier will be used and if so,
-			with which name.
-			"""
-
-			# save some time by prevent searching if field_id is empty
-			if not field_identifier:
-				return None
-
-			# search for real field names first
-			for field in const.DESCRIPTION_FIELD_MAP.keys():
-				if field_identifier == field:
-					return field
-
-			field_id_lower = field_identifier.lower()
-
-			for field in const.DESCRIPTION_FIELD_MAP.keys():
-
-				# does extra information (-> alias(es)) for this field exist?
-				if 'alias' in const.DESCRIPTION_FIELD_MAP [field]:
-
-					if 'withcase' in const.DESCRIPTION_FIELD_MAP [field] ['alias']:
-						for alias in const.DESCRIPTION_FIELD_MAP [field] ['alias'] ['withcase']:
-							if field_identifier == alias:
-								return field
-
-					if 'nocase' in const.DESCRIPTION_FIELD_MAP [field] ['alias']:
-						for alias in const.DESCRIPTION_FIELD_MAP [field] ['alias'] ['nocase']:
-							if field_id_lower == alias.lower():
-								return field
-
-					#if 'other_alias_type' in const.DESCRIPTION_FIELD_MAP [field] ['alias']:
-
-			# returning None if no valid field identifier matches
-			return None
-
-		# --- end of find_field (...) ---
-
-
 		self.desc_data = None
 		read_data = dict ()
 
-
 		try:
 			desc_lines = get_desc_from_file (
 				self.fileinfo ['filepath'],
@@ -362,14 +263,15 @@ class DescriptionReader:
 			self.logger.exception ( err )
 			return self.desc_data
 
+		field_context = None
 
-		field_context = val = line = sline = None
 		for line in desc_lines:
+			field_context_ref = None
 
 			# using s(tripped)line whenever whitespace doesn't matter
 			sline = line.lstrip()
 
-			if (not sline) or (line [0] == const.DESCRIPTION_COMMENT_CHAR):
+			if (not sline) or (line [0] == config.get ( 'DESCRIPTION.comment_char' ) ):
 				# empty line or comment
 				pass
 
@@ -389,14 +291,26 @@ class DescriptionReader:
 				# line introduces a new field context, forget last one
 				field_context = None
 
-				line_components = sline.partition ( const.DESCRIPTION_FIELD_SEPARATOR )
+				line_components = sline.partition ( config.get ( 'DESCRIPTION.field_separator' ) )
 
 				if line_components [1]:
 					# line contains a field separator, set field context
-					field_context = find_field ( line_components [0] )
+					field_context_ref = self.field_definition.get ( line_components [0] )
+
+					if field_context_ref is None:
+						# useless line, skip
+						self.logger.info ( "Skipped a description field: '%s'.", line_components [0] )
+					elif field_context_ref.has_flag ( 'ignore' ):
+						# field ignored
+						self.logger.debug ( "Ignored field '%s'.", field_context )
+
+					else:
+						field_context = field_context_ref.get_name()
+
+						if not field_context:
+							raise Exception ( "Field name is not valid! This should've already been catched in DescriptionField..." )
 
-					if field_context:
-						# create a new empty list for field_context
+						# create a new empty list for this field_context
 						read_data [field_context] = []
 
 						# add values to read_data
@@ -404,9 +318,7 @@ class DescriptionReader:
 						for val in make_values ( line_components [2], field_context ):
 							read_data [field_context] . append ( val )
 
-					else:
-						# useless line, skip
-						self.logger.info ( "Skipped a description field: '%s'.", line_components [0] )
+
 
 				else:
 					# reaching this branch means that
@@ -415,11 +327,7 @@ class DescriptionReader:
 					# this should not occur in description files (bad syntax?)
 					self.logger.warning ( "Unexpected line in description file: '%s'.", line_components [0] )
 
-
-				del line_components
-
-		del sline, line, val, field_context
-
+		# -- end for --
 
 		if self._parse_read_data ( read_data ):
 			self.logger.debug ( "Successfully read file '%s' with data = %s.",
@@ -430,4 +338,4 @@ class DescriptionReader:
 		# get_desc() is preferred, but this method returns the desc data, too
 		return self.desc_data
 
-	# --- end of readfile (...) ---
+	# --- end of run (...) ---

diff --git a/roverlay/ebuild.py b/roverlay/ebuild.py
index 1634ef3..4493bb7 100644
--- a/roverlay/ebuild.py
+++ b/roverlay/ebuild.py
@@ -2,9 +2,10 @@
 # Copyright 2006-2012 Gentoo Foundation
 # Distributed under the terms of the GNU General Public License v2
 
+import roverlay.config
+
 class Ebuild:
-	# could move this to const
-	EBUILD_INDENT = "\t"
+	EBUILD_INDENT = roverlay.config.get ( 'EBUILD.indent', '\t' )
 
 	ADD_REMAP = {
 		# pkg vs package

diff --git a/roverlay/ebuildjob.py b/roverlay/ebuildjob.py
index 0357e77..b6c9456 100644
--- a/roverlay/ebuildjob.py
+++ b/roverlay/ebuildjob.py
@@ -2,16 +2,18 @@
 # Copyright 2006-2012 Gentoo Foundation
 # Distributed under the terms of the GNU General Public License v2
 
-import time
 import logging
 import re
 
-from roverlay.fileio import DescriptionReader
+from roverlay.descriptionreader import DescriptionReader
 from roverlay.ebuild import Ebuild
+from roverlay import config
 
 class EbuildJob:
 	LOGGER = logging.getLogger ( 'EbuildJob' )
 
+	DEFAULT_EBUILD_HEADER = config.get ( 'EBUILD.default_header' )
+
 	# move this to const / config
 	DEPENDENCY_FIELDS = {
 		'R_SUGGESTS' : [ 'Suggests' ],
@@ -142,14 +144,7 @@ class EbuildJob:
 
 			## default ebuild header, could use some const here (eclass name,..)
 			ebuild.add ( 'ebuild_header',
-								[ 	'# Copyright 1999-' + str ( time.gmtime() [0] ) + ' Gentoo Foundation',
-									'# Distributed under the terms of the GNU General Public License v2',
-									'# $Header: $',
-									'',
-									'EAPI=4',
-									'',
-									'inherit R-packages'
-								],
+								EbuildJob.DEFAULT_EBUILD_HEADER,
 								False
 							)
 

diff --git a/roverlay/tmpconst.py b/roverlay/tmpconst.py
deleted file mode 100644
index 8519aad..0000000
--- a/roverlay/tmpconst.py
+++ /dev/null
@@ -1,108 +0,0 @@
-# R overlay -- constants (temporary file)
-# Copyright 2006-2012 Gentoo Foundation
-# Distributed under the terms of the GNU General Public License v2
-
-# matches .tgz .tbz2 .tar .tar.gz .tar.bz2
-RPACKAGE_SUFFIX_REGEX = '[.](tgz|tbz2|tar|(tar[.](gz|bz2)))'
-
-PACKAGE_CATEGORY = 'sci-R'
-
-DESCRIPTION_FIELD_SEPARATOR = ':'
-
-DESCRIPTION_COMMENT_CHAR = '#'
-
-DESCRIPTION_LIST_SPLIT_REGEX = '\s*[,;]{1}\s*'
-
-DESCRIPTION_FILE_NAME = 'DESCRIPTION'
-
-# moved to <field> -> 'allowed_values'
-##DESCRIPTION_VALID_OS_TYPES = [ "unix" ]
-
-
-# note for 2012-05-25: make this struct more organized, assign real values
-"""The map of used fields in the DESCRIPTION file
-
-	stores the real field name as well as field flags and aliases
-	that can be case-sensitive (withcase) or not (nocase)
-
-	access to these values is
-	* for aliases
-		DESCRIPTION_FIELD_MAP [<field name>] [alias] [case sensitive ? withcase : nocase] [<index>]
-
-	* for flags
-		DESCRIPTION_FIELD_MAP [<field name>] [flags] [<index>]
-
-	* default values
-		DESCRIPTION_FIELD_MAP [<field name>] [default_value]
-
-	notable flags:
-	* isList : indicates that this field has several values that are
-	           separated by commata/semicolons =:<DESCRIPTION_LIST_SPLIT_REGEX>
-	   this disables isWhitespaceList
-
-	* isWhitespaceList : indicates that this field has several values separated
-	                     by whitespace
-
-	* joinValues : indicates that the values of this field should be concatenated
-	               after reading them (with a ' ' as separator)
-	   (this implies that the read values are one string)
-
-	* mandatory : cannot proceed if a file does not contain this field (implies ignoring default values)
-
-"""
-
-DESCRIPTION_FIELD_MAP = {
-	'Description' : {
-		'flags' : [ 'joinValues' ],
-	},
-	'Title' : {
-		'flags' : [ 'joinValues' ],
-	},
-	'Package' : {
-		'flags' : [ 'joinValues' ],
-	},
-	'License' : {
-		'flags' : [ 'isList' ],
-	},
-	'Version' : {
-		'flags' : [ 'mandatory', 'joinValues' ]
-	},
-	'Suggests' : {
-		'alias' : {
-			'nocase' : [ 'Suggests', 'Suggest',
-							'%Suggests', 'Suggets', 'Recommends' ]
-		},
-	},
-	'Depends' : {
-		'alias' : {
-			'nocase' : [ 'Depends', 'Dependencies', 'Dependes',
-							'%Depends', 'Depents', 'Require', 'Requires' ],
-		},
-		'flags' : [ 'isList' ],
-		'default_value' : '',
-	},
-	'Imports' : {
-		'alias' : {
-			'nocase' : [ 'Imports', 'Import' ]
-		},
-	},
-	'LinkingTo' : {
-		'alias' : {
-			'nocase' : [ 'LinkingTo', 'LinkingdTo' ]
-		},
-	},
-	'SystemRequirements' : {
-		'alias' : {
-			'nocase' : [ 'SystemRequirements', 'SystemRequirement' ]
-		},
-	},
-	'OS_Type' : {
-		'alias' : {
-			'nocase' : [ 'OS_TYPE' ]
-		},
-		'allowed_values' : [ 'unix' ],
-	},
-	'test-default' : {
-		'default_value' : 'some default value'
-	}
-}



             reply	other threads:[~2012-06-01 15:47 UTC|newest]

Thread overview: 159+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-06-01 15:46 André Erdmann [this message]
  -- strict thread matches above, loose matches on Subject: below --
2015-01-26 17:41 [gentoo-commits] proj/R_overlay:master commit in: roverlay/ André Erdmann
2015-01-26 17:41 André Erdmann
2014-07-18 16:20 André Erdmann
2014-07-18  2:50 [gentoo-commits] proj/R_overlay:wip/addition_control " André Erdmann
2014-07-18 16:20 ` [gentoo-commits] proj/R_overlay:master " André Erdmann
2014-07-18  2:28 [gentoo-commits] proj/R_overlay:wip/addition_control " André Erdmann
2014-07-18 16:20 ` [gentoo-commits] proj/R_overlay:master " André Erdmann
2014-07-16 15:14 André Erdmann
2014-06-05 22:09 André Erdmann
2014-04-01 16:38 André Erdmann
2014-02-16 16:30 André Erdmann
2014-02-15 19:49 André Erdmann
2014-02-15 19:49 André Erdmann
2014-01-25 18:14 André Erdmann
2013-12-11 18:40 André Erdmann
2013-12-11 18:40 André Erdmann
2013-09-23 15:30 André Erdmann
2013-09-20 15:57 André Erdmann
2013-09-19 15:00 André Erdmann
2013-09-17 16:40 André Erdmann
2013-09-17 16:40 André Erdmann
2013-09-17 16:40 André Erdmann
2013-09-17 16:40 André Erdmann
2013-09-16 13:43 André Erdmann
2013-09-13 15:10 André Erdmann
2013-09-12 16:36 André Erdmann
2013-09-12 16:36 André Erdmann
2013-09-12 16:36 André Erdmann
2013-09-11 11:14 André Erdmann
2013-09-11 10:19 André Erdmann
2013-09-10 14:40 André Erdmann
2013-09-10 14:40 André Erdmann
2013-09-10 14:40 André Erdmann
2013-09-10 14:40 André Erdmann
2013-09-06 17:27 André Erdmann
2013-09-06 17:27 André Erdmann
2013-09-03 15:50 André Erdmann
2013-09-02 12:27 André Erdmann
2013-09-02  8:44 André Erdmann
2013-08-30 14:49 André Erdmann
2013-08-30 14:49 André Erdmann
2013-08-29 12:36 André Erdmann
2013-08-29 12:36 André Erdmann
2013-08-28 15:54 André Erdmann
2013-08-27 15:39 André Erdmann
2013-08-23 13:52 André Erdmann
2013-08-23 13:52 André Erdmann
2013-08-23 13:52 André Erdmann
2013-08-19 15:42 André Erdmann
2013-08-16 14:05 André Erdmann
2013-08-16 11:02 André Erdmann
2013-08-16 10:43 André Erdmann
2013-08-16 10:43 André Erdmann
2013-08-14 14:56 André Erdmann
2013-08-14 14:56 André Erdmann
2013-08-13  8:56 André Erdmann
2013-08-13  8:56 André Erdmann
2013-08-13  8:56 André Erdmann
2013-08-12  8:28 André Erdmann
2013-08-12  8:18 André Erdmann
2013-08-07 16:10 André Erdmann
2013-08-02 14:30 André Erdmann
2013-08-02 10:34 André Erdmann
2013-08-02 10:34 André Erdmann
2013-08-01 12:44 André Erdmann
2013-08-01 12:44 André Erdmann
2013-07-29 14:56 André Erdmann
2013-07-29  8:55 André Erdmann
2013-07-26 13:02 André Erdmann
2013-07-23  7:51 André Erdmann
2013-07-23  7:51 André Erdmann
2013-07-19 18:00 [gentoo-commits] proj/R_overlay:gsoc13/next " André Erdmann
2013-07-23  7:51 ` [gentoo-commits] proj/R_overlay:master " André Erdmann
2013-07-17 18:05 [gentoo-commits] proj/R_overlay:gsoc13/next " André Erdmann
2013-07-17 18:05 ` [gentoo-commits] proj/R_overlay:master " André Erdmann
2013-07-15 22:31 [gentoo-commits] proj/R_overlay:gsoc13/next " André Erdmann
2013-07-16 16:36 ` [gentoo-commits] proj/R_overlay:master " André Erdmann
2013-07-12 13:57 André Erdmann
2013-06-22 15:24 André Erdmann
2013-06-22 15:24 André Erdmann
2013-06-22 15:24 André Erdmann
2013-06-22 15:24 André Erdmann
2013-06-19 18:58 [gentoo-commits] proj/R_overlay:gsoc13/next " André Erdmann
2013-06-22 15:24 ` [gentoo-commits] proj/R_overlay:master " André Erdmann
2013-06-19 18:58 [gentoo-commits] proj/R_overlay:gsoc13/next " André Erdmann
2013-06-19 18:59 ` [gentoo-commits] proj/R_overlay:master " André Erdmann
2013-06-13 16:34 André Erdmann
2013-06-05 18:08 [gentoo-commits] proj/R_overlay:gsoc13/next " André Erdmann
2013-06-13 16:34 ` [gentoo-commits] proj/R_overlay:master " André Erdmann
2013-06-05 18:08 [gentoo-commits] proj/R_overlay:gsoc13/next " André Erdmann
2013-06-13 16:34 ` [gentoo-commits] proj/R_overlay:master " André Erdmann
2013-06-04 21:06 André Erdmann
2013-04-25 16:44 André Erdmann
2013-04-25 16:44 André Erdmann
2013-03-05 11:27 André Erdmann
2013-02-09 20:45 André Erdmann
2013-02-05 17:48 André Erdmann
2013-02-05 17:48 André Erdmann
2013-01-30 20:16 André Erdmann
2013-01-30 20:16 André Erdmann
2013-01-28 23:54 André Erdmann
2013-01-28 23:54 André Erdmann
2013-01-28 23:54 André Erdmann
2012-10-02 10:04 André Erdmann
2012-08-20 11:16 André Erdmann
2012-08-13 18:07 André Erdmann
2012-08-09  9:26 André Erdmann
2012-08-08 23:46 André Erdmann
2012-08-08 23:46 André Erdmann
2012-08-07  8:50 André Erdmann
2012-08-02 15:14 André Erdmann
2012-08-01  7:25 André Erdmann
2012-07-31 17:51 André Erdmann
2012-07-30  8:52 André Erdmann
2012-07-30  8:52 André Erdmann
2012-07-24 16:59 [gentoo-commits] proj/R_overlay:overlay_wip " André Erdmann
2012-07-30  8:52 ` [gentoo-commits] proj/R_overlay:master " André Erdmann
2012-07-18 16:49 [gentoo-commits] proj/R_overlay:overlay_wip " André Erdmann
2012-07-30  8:52 ` [gentoo-commits] proj/R_overlay:master " André Erdmann
2012-07-16 16:15 André Erdmann
2012-07-16 16:15 André Erdmann
2012-07-16 16:15 [gentoo-commits] proj/R_overlay:depres_wip " André Erdmann
2012-07-16 16:15 ` [gentoo-commits] proj/R_overlay:master " André Erdmann
2012-07-10 17:43 André Erdmann
2012-07-09 17:19 André Erdmann
2012-07-04 18:21 André Erdmann
2012-07-04 18:21 André Erdmann
2012-07-03 17:48 André Erdmann
2012-06-28 13:29 André Erdmann
2012-06-26 15:42 André Erdmann
2012-06-25 18:19 André Erdmann
2012-06-21 16:55 André Erdmann
2012-06-20 19:03 André Erdmann
2012-06-20 19:03 André Erdmann
2012-06-18 16:27 André Erdmann
2012-06-15 20:34 André Erdmann
2012-06-15 20:34 André Erdmann
2012-06-15 20:34 André Erdmann
2012-06-15 20:34 André Erdmann
2012-06-15 20:34 André Erdmann
2012-06-15 20:34 André Erdmann
2012-06-12 17:17 André Erdmann
2012-06-06 19:52 André Erdmann
2012-06-06 19:52 André Erdmann
2012-06-06 19:52 André Erdmann
2012-06-05 17:30 André Erdmann
2012-06-04 19:07 André Erdmann
2012-06-04 19:07 André Erdmann
2012-06-04 15:43 André Erdmann
2012-06-01 16:19 André Erdmann
2012-06-01 16:19 André Erdmann
2012-05-31 18:24 André Erdmann
2012-05-30 20:15 André Erdmann
2012-05-30 19:36 André Erdmann
2012-05-30 19:36 André Erdmann
2012-05-30 16:09 André Erdmann
2012-05-30 16:09 André Erdmann
2012-05-30 16:09 André Erdmann
2012-05-30 16:09 André Erdmann
2012-05-30 10:58 André Erdmann
2012-05-30 10:58 André Erdmann
2012-05-30 10:58 André Erdmann
2012-05-30 10:58 André Erdmann
2012-05-29 17:09 André Erdmann
2012-05-29 17:09 André Erdmann
2012-05-29 17:09 André Erdmann
2012-05-29 17:09 André Erdmann
2012-05-29 17:09 André Erdmann
2012-05-26 13:14 André Erdmann
2012-05-26 13:14 André Erdmann

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1338562569.ac0b226fd7d16dad7a4b8d30b4293fe401406c1c.dywi@gentoo \
    --to=dywi@mailerd.de \
    --cc=gentoo-commits@lists.gentoo.org \
    --cc=gentoo-dev@lists.gentoo.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox