Usuari:TronaBot/Python/common.py

#!/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")