Usuari:TronaBot/Python/common.py

    De la Viquipèdia, l'enciclopèdia lliure
    #!/usr/bin/python2.7
    #-*- coding:utf8 -*-
    #
    # Copyrleft (!C) 2013 Coet@cawiki
    # This program is free software: you can redistribute it and/or modify
    # it under the terms of the GNU General Public License as published by
    # the Free Software Foundation, either version 3 of the License, or
    # (at your option) any later version.
    #
    # This program is distributed in the hope that it will be useful,
    # but WITHOUT ANY WARRANTY; without even the implied warranty of
    # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    # GNU General Public License for more details.
    #
    # You should have received a copy of the GNU General Public License
    # along with this program.  If not, see <http://www.gnu.org/licenses/>.
    
    u"""
    	Funcions comunes per als scripts pywikimedia en català.
    	Empre pywikimedia i pywikilib en lloc de pywikipedia per considerar
    	injusta la tercera ja que exclou els altres projectes germans.
    	--Coet 2013-03-22_21:47:17
    """
    import argparse, bz2, codecs as cs, difflib, json, locale, os, pickle
    import re, shlex, sys, time
    from platform import system as platfsys
    from datetime import datetime, timedelta
    from dateutil.relativedelta import relativedelta as rdelta, SU
    from math import floor
    on_win = platfsys().lower() == "windows"
    
    #l'arquitectura que he creat per als meus scripts és ficar una carpeta
    #al mateix nivell que la carpeta del pywikimedia, així que he de
    #carregar els mòduls amb les següents línies.
    home = on_win and r"E:\\iShare\SugarSync\My Python scripts" \
    	   or "/home/pasqual/public_html/"
    for folder in ("pywikilib", "pyuserlib"):
    	sys.path.append(os.path.join(home, folder))
    #from pywikipedia
    import query as api, wikipedia as pywikilib, pagegenerators as pg, config as cfg
    
    class File(object):
    	def __init__(self, fname, pref=False, timestamp=False, path=None, sep="-", ext=None):
    		if pref is True: pref = "llista1000_llista{sep}".format(sep=sep)
    		elif pref is False: pref=""
    		else: pref = "{pref}{sep}".format(pref=pref, sep=sep)
    		self._filename = "{pref}{name}{suff}{ext}".format(
    			pref=pref,
    			name= fname,
    			suff= timestamp and time.strftime("_%y%m%d_%H%M%S") or "",
    			ext= ext and ".%s" % ext or ".log"
    		)
    		self._path = path or os.path.join(home, "pywikilab", "logs")
    		self._fullname = os.path.join(self._path, self._filename)
    
    	#file info
    	def exists(self):
    		return os.path.exists(self._fullname)
    
    	def size(self):
    		return os.path.getsize(self._fullname)
    
    	def mtime(self):
    		return datetime.fromtimestamp(os.path.getmtime(self._fullname)).strftime("%Y-%m-%d %H:%M:%S") #last modified
    
    	def atime(self):
    		return datetime.fromtimestamp(os.path.getatime(self._fullname)).strftime("%Y-%m-%d %H:%M:%S") #last access
    
    	def ctime(self):
    		return datetime.fromtimestamp(os.path.getctime(self._fullname)).strftime("%Y-%m-%d %H:%M:%S") #last change
    
    	#data methods
    	def backup(self, data):
    		"""
    		use this method to save data as an object and recover
    		this with load() method"""
    		f = cs.open(self._fullname, "w", "utf8")
    		pywikilib.query.json.dump(data, f, indent=4, encoding="utf8")
    		f.close()
    
    	def load(self):
    		"""data recover"""
    		f = cs.open(self._fullname, "r", "utf8")
    		data = pywikilib.query.json.load(f, encoding="utf8")
    		f.close()
    		return data
    
    	#binary files
    	def quick_read(self):
    		stream = bz2.BZ2File(self._fullname, 'r')
    		obj = pickle.load(stream)
    		stream.close()
    		return obj
    
    	def quick_write(self, obj):
    		stream = bz2.BZ2File(self._fullname, 'w')
    		pickle.dump(obj, stream, protocol=pickle.HIGHEST_PROTOCOL)
    		stream.close()
    
    	#flash instances
    	def read(self):
    		"""read a text file and return its content"""
    		if not self.exists():
    			return ""
    		f = cs.open(self._fullname, "r", "utf8")
    		data = f.read()
    		f.close()
    		return data
    
    	def read_lines(self):
    		"""read a text file and return its content splitted by new line characters"""
    		#f.readlines() keeps the new line char,
    		#I prefer data.splitlines() after data=f.read().
    		data = self.read()
    		return data.splitlines()
    
    	def save(self, data):
    		"""overwrite a text file"""
    		f = cs.open(self._fullname, "w", "utf8")
    		data = f.write(data)
    		f.close()
    
    	def prepend(self, data):
    		"""prepend text before the content of a text file"""
    		old_data = self.read()
    		new_data = "%s\n%s" % (new_data, old_data)
    		self.save(new_data)
    
    	def append(self, new_data):
    		"""append text after the content of a text file"""
    		if not new_data.startswith("\n"):new_data = "\n%s" % new_data
    		old_data = self.read()
    		self.save("%s%s" % (old_data, new_data))
    
    	def open(self):
    		if not on_win: return
    		#Only available on windows.
    		os.startfile(self._fullname)
    
    	#streaming methods
    	def prepare(self):
    		self._stream = cs.open(self._fullname, "w", "utf8")
    
    	def flush(self):
    		self._stream.flush()
    
    	def write(self, string):
    		self._stream.write(string)
    
    	def write_line(self, line="", newlineb4=False):
    		if newlineb4: line = "\n%s" % line
    		if not newlineb4 and not line.endswith("\n"): line = "%s\n" % line
    		self._stream.write(line)
    
    	def close(self):
    		self._stream.close()
    
    class Chrono(object):
    	def __init__(self):
    		self.start_time = datetime.now()
    		self.lapses=[]
    
    	def to_string(self, et):
    		eltime = et.years, et.months, et.days, et.hours, et.minutes, et.seconds, et.microseconds
    		units = ('year', 'months', 'day', 'hour', 'minute', "second", "microsecond")
    		parts = ['%d %s%s' % (quant, unit, ('', 's')[quant > 1]) for quant, unit in zip(eltime, units) if quant]
    		return ' and '.join(c for c in [', '.join(parts[:-1]), parts[-1]] if c)
    
    	def pause(self):
    		self.lapses.append(datetime.now())
    		return len(self.lapses)-1
    
    	def elapsed(self, lapseid=None):
    		lapseid = lapseid and self.lapses[lapseid]
    		elapsed = time_diff(lapseid or datetime.now(), self.start_time)
    		print "elapsed time:", self.to_string(elapsed)
    
    	def running_time(self):
    		td = rdelta(datetime.now(), self.start_time)
    		self.years = td.years
    		self.months = td.months
    		self.days = td.days
    		self.hours = td.hours
    		self.minutes = td.minutes
    		self.seconds = td.seconds
    		self.microseconds = td.microseconds
    
    	def stop(self):
    		stop_time = datetime.now()
    		elapsed = rdelta(stop_time, self.start_time)
    		return (
    			u"began at: %s\nended at:%s\nelapsed time: %s" % (
    				yellow(self.start_time.strftime("%H:%M:%S")),
    				purple(stop_time.strftime("%H:%M:%S")),
    				self.to_string(elapsed)
    			)
    		)
    
    class DateFormatError:
    	"""Error unknow format for the given date."""
    
    class Date(object):
    	"""
    	This class pretends to convert any expression to a valid datetime class.
    
    	There are several formats and types to realize it:
    		* A string in different formats:
    			- 2013-12-03 21:34
    			- 2013-12-03 21:34:56
    			- 2013-12-03T21:34:57Z
    			- 2013/12/03 21:34
    			- 2013/12/03 21:34:56
    			- ...
    		* A time.struct_time object
    		* A tuple or list
    			- 3-tuple/list as a datetime/date object
    			- between 7 and 3 tuple/list as a datetime object
    		* A datetime object
    		* A float will be treated as a timestamp
    		* An integer will be treated as a ordinal
    	"""
    	def __init__(self, obj=None):
    		locale.setlocale(locale.LC_TIME, on_win and "Catalan_Spain.1252" or "ca_ES")
    		self._obj = None
    		if not obj:
    			self._obj = datetime.now()
    		if isinstance(obj, basestring):
    			obj = obj.replace("/","-")
    			date_fmt = {
    				#2013-03-23 20:24
    				"\d{4}[/-][0-3]?\d?[/-]\d\d? (?:2[0-3]|[01]?\d):[0-5]?\d": "%Y-%m-%d %H:%M",
    				#2013-03-23 20:24:45
    				"\d{4}[/-][0-3]?\d?[/-]\d\d? (?:2[0-3]|[01]?\d):[0-5]?\d:[0-5]?\d": "%Y-%m-%d %H:%M:%S",
    				#2013-03-23T20:24:45Z
    				"\d{4}[/-][0-3]\d[/-]\d\dT(?:2[0-3]|[01]\d):[0-5]?\d:[0-5]?\dZ": "%Y-%m-%dT%H:%M:%SZ",
    				#11:58, 27 març 2010
    				ur"(?:2[0-3]|[01]?\d):[0-5]?\d, [1-3]?\d \w{2,4} \d{4}": u"%H:%M, %d %b %Y",
    				#27 març 2010
    				ur"[1-3]?\d \w{3,4} \d{4}": u"%d %b %Y",
    				#2013-03-23
    				"\d{4}-[0-3]?\d-\d?\d": "%Y-%m-%d",
    				#2013-03-23 20:24
    				"\d{4}-\d?\d-[0-3]?\d? (?:2[0-3]|[01]?\d):[0-5]?\d": "%Y-%m-%d %H:%M",
    			}
    			for key in date_fmt:
    				if re.match(key, obj, re.U):
    					self._obj = datetime.strptime(obj.encode("cp1252"), date_fmt[key])
    					break
    		elif isinstance(obj, time.struct_time):
    			self._obj = datetime.fromtimestamp(time.mktime(obj))
    		elif isinstance(obj, (list, tuple)):
    			if 7>=len(tpl)>=3:
    				self._obj = datetime(*obj)
    			elif len(tpl)==9:
    				self._obj = datetime.fromtimestamp(time.mktime(obj))
    		elif isinstance(obj, datetime):
    			self._obj = obj
    		elif isinstance(obj, float):
    				self._obj = datetime.fromtimestamp(obj)
    		elif isinstance(obj, int):
    				self._obj = datetime.fromordinal(obj)
    		if not self._obj: raise DateFormatError
    
    		t = self._obj.timetuple()
    
    		self.year = t.tm_year
    		self.month = t.tm_mon
    		self.day = t.tm_mday
    		self.hour = t.tm_hour
    		self.min = t.tm_min
    		self.sec = t.tm_sec
    		self.weekday = t.tm_wday
    		self.yearday = t.tm_yday
    		self.isdst = t.tm_isdst
    		self.weeknum = int(self._obj.strftime("%W"))
    
    	def __sub__(self, other):
    		if isinstance(self._obj, datetime) and isinstance(other, (timedelta, rdelta)):
    			if isinstance(other, timedelta):
    				return self._obj - other
    			elif isinstance(other, rdelta):
    				return self._obj - other
    		dates = sorted([self._obj, other._obj])
    		return dates[1] - dates[0]
    
    	def __add__(self, other):
    		if isinstance(self._obj, datetime) and isinstance(other, (timedelta, rdelta)):
    			if isinstance(other, timedelta):
    				return self._obj + other
    			elif isinstance(other, rdelta):
    				return self._obj + other
    		return self._obj + other._obj
    
    	def __call__(self):
    		return self._obj
    
    	def time_diff(self, other=None):
    		if not other:
    			other = Date()
    		elif not isinstance(other, datetime):
    			other = Date()
    		dates = sorted([self._obj, other._obj])
    		return rdelta(dates[1], dates[0])
    
    	def time_delta(self):
    		return time_diff(self._obj)
    
    	def local_month(self):
    		return self._obj.strftime("%B")
    
    	def short_local_month(self):
    		return self._obj.strftime("%b")
    
    	def local_weekday(self):
    		return self._obj.strftime("%A")
    
    	def short_local_weekday(self):
    		return self._obj.strftime("%a")
    
    	def local_time(self):
    		return self._obj.strftime("%X")
    
    	def local_date(self):
    		return self._obj.strftime("%x")
    
    	def weeknum(self):
    		#Week number of the year
    		return self._obj.strftime("%W")
    
    	def to_datetime(self):
    		return self._obj
    
    	def to_tuple(self):
    		t = self._obj.timetuple()
    		return t.tm_year, t.tm_mon, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec, t.tm_wday, t.tm_yday, t.tm_isdst
    
    	def to_time_struct(self):
    		"""time.struct_time(tm_year, tm_mon, tm_mday, tm_hour, tm_min, tm_sec, tm_wday, tm_yday, tm_isdst)"""
    		return self._obj.timetuple()
    
    	def to_timestamp(self):
    		"""Unix timestamp"""
    		return time.mktime(self._obj.timetuple())
    
    	def to_ordinal(self):
    		return self._obj.toordinal()
    
    	def to_cest(self):
    		begin = datetime.now()+rdelta(weekday=SU(-1), day=31, month=3,  hour=2, minute=0, second=0)
    		end   = datetime.now()+rdelta(weekday=SU(-1), day=31, month=10, hour=3, minute=0, second=0)
    		if self._obj > begin and self._obj < end: return self._obj+rdelta(hours=2)
    		else: return self._obj+rdelta(hours=1)
    
    	def to_api(self):
    		return self._obj.strftime("%Y-%m-%dT%H:%M:%SZ")
    
    	def to_long_string(self):
    		day = int(self._obj.strftime("%d"))
    		art = "l'" if day in (1,11) else "el "
    		prep = "d'" if self.local_month()[0] in "ao" else "de "
    		return self._obj.strftime("{}%A {} {}%B del %Y a les %H:%M:%S".format(art, day, prep))
    
    	def to_short_string(self):
    		return self._obj.strftime("%Y-%m-%d %H:%M")
    
    	def substract(self, **kwargs):
    		return self._obj - timedelta(**kwargs)
    
    	def add(self, **kwargs):
    		return self._obj + timedelta(**kwargs)
    
    	def replace(self, **kwargs):
    		return self._obj.replace(**kwargs)
    
    def time_diff(td):
    	#get the  object and returns also hours, minutes and seconds
    	#by accessing to .seconds atribute.
    	td = Date(time.time())-Date(td)
    	hours, remainder = divmod(td.seconds, 3600)
    	minutes, seconds = divmod(remainder, 60)
    	return td.days, hours, minutes, seconds, td.microseconds
    
    class List(object):
    	def __init__(self, _list):
    		self._list  = _list
    		self._sorting_pairs = {
    			u"àáâäãăǎąåā": "a", u'æǣ': "ae",
    			u'ḃɓ': "b",
    			u'çćčćĉċ': "c",
    			u'đḍďḋ': "d", u"ð": "dz",
    			u'èéêëẽēę': "e",
    			u'ḟƒ': "f",
    			u'ĝġģğ': "g",
    			u'ĥħ': "h",
    			u'ìíîïīį': "i", u'ij': "ij",
    			u'ĵ': "j",
    			u'ķ': "k",
    			u'ŀļḷḹľł': "l",
    			u'ñńň': "n",
    			u'òóôöõøōǫ': "o", u'œ': "oe",
    			u'ṗ': "p",
    			u'ŗřṛṝ': "r",
    			u'şṡšŝ': "s", u'ß': "sz",
    			u'ţṫṭ': "t", u'Þ': "tz",
    			u'ùúûüŭūų': "u",
    			u'ẁŵẅƿ': "w",
    			u'ýỳŷÿȳỹ': "y",
    			u'źžż': "z"
    		}
    
    		self._nakedletter_pairs = {
    			u"àáâäãăǎąåā": "a", u'æǣ': "ae",
    			u'ḃɓ': "b",
    			u'çćčćĉċ': "c",
    			u'đḍďḋð': "d",
    			u'èéêëẽēę': "e",
    			u'ḟƒ': "f",
    			u'ĝġģğ': "g",
    			u'ĥħ': "h",
    			u'ìíîïīį': "i", u'ij': "ij",
    			u'ĵ': "j",
    			u'ķ': "k",
    			u'ŀļḷḹľł': "l",
    			u'ñńň': "n",
    			u'òóôöõøōǫ': "o", u'œ': "oe",
    			u'ṗ': "p",
    			u'ŗřṛṝ': "r",
    			u'şṡšŝ': "s", u'ß': "sz",
    			u'ţṫṭ': "t", u'Þ': "th",
    			u'ùúûüŭūų': "u",
    			u'ẁŵẅƿ': "w",
    			u'ýỳŷÿȳỹ': "y",
    			u'źžż': "z"
    		}
    
    	def simplify_chars(self, word, sorting=True):
    		#simplifiquem els diacrítics per a l'ordre alfabètic
    		pairs = self._sorting_pairs if sorting else self._nakedletter_pairs
    		diacritics = "".join(pairs.keys())
    		word = word.lower()
    		for ch in word:
    			if ch in diacritics:
    				for keys in pairs:
    					if ch in keys:
    						word=word.replace(ch, pairs[keys])
    						break
    		word=word.replace(u"l·l","ll")
    		word = re.sub("\W","!", word)
    		return word
    
    	def sort_list(self):
    		#ordena una llista
    		simplifiedlist={}
    		for word in self._list:
    			simplifiedlist[self.simplify_chars(word)]=word
    		new_list=[]
    		for word in sorted(simplifiedlist.keys()):
    			new_list.append(simplifiedlist[word])
    		return new_list
    #compat with old scripts
    sort_list = lambda l: List(l).sort_list()
    
    def get_diffs(new, old):
    	added=[]; removed=[]; kept=[]
    	for word in difflib.ndiff(old.split(), new.split()):
    		sign, word = word[0], word[2:]
    		if sign == "?": continue
    		elif sign == '+': added.append(word)
    		elif sign == '-': removed.append(word)
    		else: kept.append(word)
    	return added, removed, kept
    
    def get_line_diffs(new, old):
    	"""returns added, removed, kept as tuple"""
    	added=[]; removed=[]; kept=[]
    	for line in difflib.ndiff(old.splitlines(), new.splitlines()):
    		sign, line = line[0], line[2:]
    		if sign == "?": continue
    		elif sign == "+": added.append(line)
    		elif sign == "-": removed.append(line)
    		else: kept.append(line)
    	return added, removed, kept
    
    def printf(string, *args, **kwargs):
    	collect_args = re.findall("\$(\d+)(?:\[\d+\])??", string)
    	collect_kwargs = re.findall("\$(\D+)(?:\[\d+\])??", string)
    	string = re.sub(r"\$([^[.!: ]+(?:\[\d+\]|\.\w+)?(?:![sr])?(?::[\d,.><^=]+[bcdeEfFgGnosxX%])?)", r"{\1}", string)
    	print collect_args, collect_kwargs
    	args = list(args)
    	if args: args.insert(0,None)
    	if args and kwargs:
    		string = string.format(*args, **kwargs)
    	elif args:
    		string = string.format(*args)
    	elif kwargs:
    		string = string.format(**kwargs)
    	print string
    
    #http://stackoverflow.com/a/10639990
    def find_fixed_repeat(string, n):
        l = len(string)
        i = 0
        while  ((i + n -1) < l):
            ss = string[i:i+n]
            bb = string[i+n:]
            try:
                ff = bb.index(ss)
            except:
                ff = -1
    
            if ff >= 0:
                return ss;
            i = i+1
        return 0
    
    def has_repeated_string(string):
    	rpt=False
    	for word in string.split():
    		if string.count(word)>1:
    			rpt=True
    			break
    	if not rpt:return False
    	n = len(string)
    	#find the maximum length of repeated string first
    	m = int(floor(n/2))
    	#start with maximum length
    	for i in range(m, 1, -1):
    		substr = find_fixed_repeat(string, i)
    		if substr:
    			return substr
    	return False
    
    #01.05.2013
    def decode(string):
    	try:
    		string = unicode(string, pywikilib.config.editor_encoding)
    	except:
    		pass
    	return string
    
    format_color = lambda c, s: u"\3{light%s}%s\3{default}" % (c, s)
    blue = lambda s: format_color("blue", s)
    green = lambda s: format_color("green", s)
    purple = lambda s: format_color("purple", s)
    red = lambda s: format_color("red", s)
    yellow = lambda s: format_color("yellow", s)
    def format_string(string, *args, **kwargs):
    	"""
    	arguments:
    		arg number: $1 (for first arg)
    		arg name: $name
    	colors:
    		&b := lightblue
    		&g := lightgreen
    		&p := lightpurple
    		&r := lightred
    		&y := lightyellow
    		one word: &yword
    		more than one: &y(:word1 word2:)
    	Return a unicode object.
    	Complex formatting must use {}.
    	"""
    	#decoding to unicode each part of the string
    	string = decode(string)
    	args = [decode(arg) for arg in args]
    	kwargs = dict([(kwarg, decode(kwargs[kwarg])) for kwarg in kwargs])
    
    	#replacing template
    	if (args or kwargs) and re.search(r"\$[\d\w]+", string):
    		parsed_args = re.findall(r"\$([\d]+)", string)
    		parsed_kwargs = re.findall(r"\$([a-z]\w*)", string)
    		for arg in parsed_args:
    			index = int(arg)-1
    			if index > len(args) or index == -1:
    				continue
    			string = re.sub(r"\$(%s)" % arg, r"{%i}" % index, string)
    
    		for arg in parsed_kwargs:
    			if kwargs.has_key(arg):
    				string = re.sub(r"\$(%s)" % arg, r"{\1}", string)
    
    	string = string.format(*args, **kwargs)
    
    	#converting coloured string s for wikipedia.output() strings.
    	if re.search("&[bgpry]", string):
    		clr_str = re.finditer(r"(?P<replace>&(?P<color>[bgpry])(?:\(:(?P<string>[^:]+?):\)|(?P<word>[^\b]+?(?:\b|$))))", string)
    		for item in clr_str:
    			expr = item.group("string") or item.group("word")
    			if item.group("color") == "b":
    				string = string.replace(item.group("replace"), blue(expr))
    			elif item.group("color") == "g":
    				string = string.replace(item.group("replace"), green(expr))
    			elif item.group("color") == "p":
    				string = string.replace(item.group("replace"), purple(expr))
    			elif item.group("color") == "r":
    				string = string.replace(item.group("replace"), red(expr))
    			elif item.group("color") == "y":
    				string = string.replace(item.group("replace"), yellow(expr))
    	return string
    
    class ArgumentHandler:
    	def __init__(self):
    		"""This class pretends to be a little string parser with many particularities.
    			programm.py string1 string2 -arg1 -arg2:string3 -arg3:3 -arg4:$4 -arg5:$$5 -arg6:A -arg6:B "string 4" -!arg7
    			the line above gives the following results:
    			args = ArgumentHandler()
    			args.parse_arguments()
    			args.positionals = ["string1", "string2", "string 4"]
    			args.arg1 -> True
    			args.arg2 -> "string3"
    			args.arg3 -> 3
    			args.arg4 -> "4"
    			args.arg5 -> "$5"
    			args.arg6 -> ["A", "B"]
    			args.arg7 -> False
    			args.arg8 -> False (not wrote in the command line)
    		"""
    		#01.05.2013
    		self.positional = []
    		self.optional = {}
    		self.raw = None
    
    		self._sep = ","
    		self._sh_delim = "-"
    		self._lng_delim = "--"
    		self._string_sign = "$"
    		self._digit_sign = "#"
    		self._param_sign = "+"
    
    	def __getattr__(self, attr, value=False):
    		if hasattr(self, attr):
    			return getattr(self, attr, value)
    		else:
    			setattr(self, attr, value)
    			return getattr(self, attr, value)
    
    	def __repr__(self):
    		return "<%s.%s>\n\tpositional: %r\n\toptional: %r" % (
    			self.__module__, self.__class__.__name__, self.positional, self.optional
    		)
    
    	def __str__(self):
    		return "<%s.%s>\n\tpositional: %r\n\toptional: %r" % (
    			self.__module__, self.__class__.__name__, self.positional, self.optional
    		)
    
    	def set_parameter(self, *aliases, **keywords):
    		param={}
    		aliases=list(aliases)
    		name =""
    		for alias in aliases:
    			if alias.startswith("--"):
    				name = alias[2:]
    				aliases.remove(alias)
    				break
    		if not name and len(aliases)>0:
    			name = aliases[0]
    			if name.startswith("-"):name=name.split("-",1)[1]
    		for alias in list(aliases):
    			if alias.startswith("-"):
    				aliases.remove(alias)
    				aliases.append(alias[1:])
    		dico = {
    			"name": name,
    			"aliases": aliases,
    			"choice": [],
    			"type": type,
    			"nargs": 0,
    			"default": True,
    			"required": False,
    		}
    		for keyword, value in keywords.items():
    			if dico.has_key(keyword):
    				if isinstance(value, basestring) and value.isdigit():value=int(value)
    				dico[keyword]=value
    		setattr(self, name, dico)
    
    	def parse_as_unix(self, cmd_line=None):
    		arguments = [] #unnamed parameters /GNU positional arg
    		options = {} #named parameters /GNU optional arg
    		action=""
    		stack=[]
    		last_option=""
    
    		if not cmd_line:
    			cmd_line = sys.argv[1:]
    		elif isinstance(cmd_line, basestring):
    			cmd_line = shlex.split(cmd_line)
    
    		keys=[]
    		print "****** OK *******"
    		print vars(self).items()
    		for k, v in vars(self).items() :
    			print k, v
    			if isinstance(v, dict):
    				print v
    				keys.append((k, v['aliases']))
    		keys = [(k,v['aliases']) ]
    		for arg in cmd_line:
    			if arg.startswith("--"):
    				if arg[2]=="!":
    					arg = arg[3:]
    					options[arg] = False
    				else:
    					arg = arg[2:]
    					options[arg] = True
    				last_option=arg
    			elif arg.startswith("-"):
    				arg = arg[1:]
    				value = False if arg.startswith("!") else True
    				arg = arg[1:] if value == False else arg
    				for a in arg:
    					options[a]=value
    				last_option = a if len(arg)==1 else ""
    			else:
    				stack.append((last_option, arg))
    
    		for key, value in list(stack):
    			if isinstance(getattr(self, key), dict):
    				#option was defined with .set_parameter()
    				if getattr(self, key)["nargs"]>0:
    					limit = getattr(self, key)["nargs"]
    					if options.has_key(key):
    						if isinstance(options[key], bool):
    							options[key]=[value]
    						elif len(options[key])<limit:
    							options[key].append(value)
    						else:
    							arguments.append(value)
    						stack.remove((key,value))
    			elif options.has_key(key):
    				if isinstance(options[key], bool):
    					options[key]=[value]
    				else:
    					arguments.append(value)
    				stack.remove((key,value))
    
    			else:
    				arguments.append(value)
    				stack.remove((key, value))
    
    		for keyword in options:
    			setattr(self, keyword, options[keyword])
    
    		for k,v in vars(self).items():
    			if isinstance(v, dict) and v.has_key("name"):
    				if isinstance(v['nargs'], int):
    					if isinstance(v['default'], (tuple, list)) and len (v['default']) != v['nargs']:
    						v['default']= (","*v['nargs']).split(",")
    				setattr(self, k, v['default'])
    		self._optional = options
    		self._stack = stack
    		self._action = action
    		self.positional = arguments
    		return [(k,v) for k,v in vars(self).items() if not k.startswith("_")]
    
    	def parse_arguments(self, line=None):
    		if not line:
    			args = self.raw = pywikilib.handleArgs()
    		else:
    			args = self.raw = pywikilib.handleArgs(shlex.split(line))
    		for arg in args:
    			option="";idx=0
    			if arg.startswith("-"):
    				option = arg.split("-",1)[1].split(":",1)[0]
    				value = arg[len(option)+2:]
    				if value:
    					if not self.optional.has_key(option):
    						self.optional[option]=[]
    					self.optional[option].append(value)
    				else:
    					self.optional[option]=True
    			else:
    				self.positional.append(arg)
    
    		for option in self.optional.copy():
    			if isinstance(self.optional[option], bool) and option.startswith("!"):
    				# -!opt -> args.opt == False
    				self.optional.pop(option)
    				option = option[1:]
    				self.optional[option]=False
    			if isinstance(self.optional[option], list) and len(self.optional[option])==1:
    				#convert a 1-element-list to a simple basestring
    				self.optional[option]=decode(self.optional[option][0])
    			elif isinstance(self.optional[option], list) and len(self.optional[option])==0:
    				#convert an empty list to None
    				self.optional[option]=None
    			if isinstance(self.optional[option], basestring) and self.optional[option].isdigit():
    				#convert a string to a integer
    				self.optional[option]=int(self.optional[option])
    			elif isinstance(self.optional[option], basestring) and self.optional[option].replace(".","").isdigit():
    				#convert a string to a float
    				self.optional[option]=float(self.optional[option])
    			elif isinstance(self.optional[option], basestring) and self.optional[option].startswith("$"):
    				#a number preceded by $ gives a number into a string.
    				self.optional[option]=self.optional[option].replace("$","",1)
    			setattr(self, option, self.optional[option])
    
    if __name__ == '__main__':
    	locale.setlocale(locale.LC_TIME, on_win and "Catalan_Spain.1252" or "ca_ES")
    	date1=Date(datetime(2009,3,4))
    	date2=Date("23 abr 2009")
    	print date1-date2
    	print time_diff(date1.to_datetime())
    	printf("$3 $2 $1 $f", 1, 2, 3, f="4 5")
    	printf("$f", f="4 5")
    	fnc  = lambda : x
    	fnc.t= "mmm"
    	coord=("leslie","luant","minut", .876)
    	printf("$1[0] $1[1] $1[2] $1[3]:0.2f $2.t", coord, fnc)
    	print Date().substract(hours=5)
    	print Date(Date().replace(hour=5,year=2001)).to_api()
    	print ", ".join([datetime.strftime(datetime.now().replace(month=i), "%b") for i in range(1,13)]).decode("cp1252")