Utilisateur:Botomatik/Scripts/de-tab-cas.py

Définition, traduction, prononciation, anagramme et synonyme sur le dictionnaire libre Wiktionnaire.
Sauter à la navigation Sauter à la recherche

Ce script réalise les actions suivantes :

Si ce remplacement a eu lieu :
  • Remplacement de {{de-nom}} par un de ses dérivés
  • Placement du tableau de flexions avant la ligne de forme s’il est placé après
  • Retrait du pluriel entre parenthèses dans la ligne de forme s’il est déjà présent dans la boite de flexion (si une prononciation est présente sur la ligne de forme, le bot laisse tel quel)
  • Remplacement de // par {{pron}} sur la ligne de forme
  • Ajout du modèle {{pron}} à la ligne de forme lorsqu’absent (pour les lignes de forme simples du type '''xxx''' {{genre}})
  1 # -*- coding: utf-8 -*-
  2 '''
  3 Ce script remplace le modèle {{de-tab-cas}} par
  4 {{de-nom}} ou ses dérivés
  5 '''
  6 import re
  7 import codecs
  8 from wikipedia import *
  9 import pagegenerators
 10 import webbrowser
 11 from pywikibot import i18n
 12 
 13 Test = False # Pour tester le script
 14 edit_fileerror = True # Pour mettre à jour le fichier d'erreur qd une page n'est pas modifiée
 15 editcounter = 0 # Nombres de pages traitées
 16 pages_traitees = [] # Liste des pages traitées (modifiées ou listées comme erreur)
 17 pages_modifiees = [] # Liste des pages modifiées
 18 # Fréquence de mise à jour des listes de pages à traiter et traitées
 19 frqMajListes = 100
 20 
 21 # Fichier contenant la liste des pages à modifier
 22 fichier_entree = u'_Entrées\de-tab-cas.txt'
 23 # Pour lister les pages non modifiées à cause d'une erreur
 24 fichier_erreurs = u'_Sorties\Erreurs de-tab-cas.txt'
 25 # Pour lister les pages modifiées
 26 fichier_sortie = u'_Sorties\Modifiées de-tab-cas.txt'
 27 
 28 def main(page):
 29 	'''
 30 	Fonction éditant une page avec de-tab-cas pour le remplacer par de-nom
 31 	'''
 32 	summary = u'Bot: Remplacement : {{de-tab-cas}} → {{de-nom}}'
 33 	titre = page.title()
 34 	if Test:
 35 		output('\n' + titre)
 36 	try:
 37 		contenu = page.get()
 38 	except wikipedia.NoPage:
 39 		non_modifiee(titre, msg=u'Pas de telle page')
 40 		return
 41 	except wikipedia.LockedPage:
 42 		non_modifiee(titre, msg=u'Page bloquée en écriture')
 43 		return
 44 	except wikipedia.IsRedirectPage:
 45 		non_modifiee(titre, msg=u'Page de redirection')
 46 		return
 47 	except wikipedia.ServerError:
 48 		non_modifiee(titre, msg=u'ServerError pour .get()')
 49 		return
 50 	except wikipedia.BadTitle:
 51 		non_modifiee(titre, msg=u'BadTitle pour .get()')
 52 		return
 53 	except wikipedia.EditConflict:
 54 		non_modifiee(titre, msg=u'EditConflict pour .get()')
 55 		return
 56 
 57 	# Fonction de remplacement de {{de-nom}} en un modèle plus spécifique
 58 	def deNomRepl(match):
 59 		# Nom du modèle de remplacement
 60 		modele = ''
 61 
 62 		genre = match.group(1)
 63 		cas = {
 64 			# strip() pour éradiquer d'éventuelles tabulations avant le terme
 65 			'ns': match.group(2).strip(),
 66 			'np': match.group(3).strip(),
 67 			'as': match.group(4).strip(),
 68 			'ap': match.group(5).strip(),
 69 			'gs': match.group(6).strip(),
 70 			'gp': match.group(7).strip(),
 71 			'ds': match.group(8).strip(),
 72 			'dp': match.group(9).strip()
 73 		}
 74 
 75 		def plurEndWithSuffix(suffix):
 76 			'''
 77 			Est-ce que toutes les formes du pluriel ont la même graphie que le
 78 			nominatif singulier suffixé par "suffix" ?
 79 			'''
 80 			if cas['ap'] == cas['np'] and cas['np'] == cas['dp'] and cas['dp'] == cas['gp'] and cas['gp'] == cas['ns'] + suffix:
 81 				return True
 82 			else:
 83 				return False
 84 
 85 		def singEndWithSuffix(suffix):
 86 			''' Idem, mais pour les 3 singuliers (excepté 'ns') '''
 87 			if cas['as'] == cas['ds'] and cas['ds'] == cas['gs'] and cas['gs'] == cas['ns'] + suffix:
 88 				return True
 89 			else:
 90 				return False
 91 
 92 		def endWithSuffix(list, suffix):
 93 			'''
 94 			Est-ce que toutes les formes du tableau liste ont la même graphie que le
 95 			nominatif singulier suffixé par "suffix" ?
 96 
 97 			@type list: Liste de chaines de caractères ('ns', 'ap',...)
 98 			'''
 99 			for elt in list:
100 				if cas[elt] == cas['ns'] + suffix:
101 					continue
102 				else:
103 					return False
104 			return True
105 
106 		def equalToNs(list):
107 			'''
108 			Toutes les formes de la liste donnée sont-elles identique au nominatif singulier ?
109 			'''
110 			for elt in list:
111 				if cas[elt] == cas['ns']:
112 					continue
113 				else:
114 					return False
115 			return True
116 
117 		if genre == 'f':
118 			if plurEndWithSuffix('nen') and singEndWithSuffix('') and cas['ns'][-2:] == 'in':
119 				modele = 'de-nom-f-in'
120 			elif plurEndWithSuffix('en') and singEndWithSuffix(''):
121 				modele = 'de-nom-f-en'
122 			elif plurEndWithSuffix('n') and singEndWithSuffix(''):
123 				modele = 'de-nom-f-n'
124 		if genre == 'm':
125 			if plurEndWithSuffix('en') and singEndWithSuffix('en'):
126 				modele = 'de-nom-m-en'
127 			elif plurEndWithSuffix('n') and singEndWithSuffix('n'):
128 				modele = 'de-nom-m-n'
129 			elif equalToNs(['as', 'ds', 'np', 'ap', 'gp']) and cas['gs'] == cas['ns'] + 's' and \
130 				cas['dp'] == cas['ns'] + 'n' and cas['ns'][-2:] == 'er':
131 				modele = 'de-nom-m-er'
132 			elif equalToNs(['as', 'ds']) and cas['gs'] == cas['ns'] + 's' and \
133 				endWithSuffix(['np', 'ap', 'gp'], 'e') and cas['dp'] == cas['ns'] + 'en':
134 				modele = 'de-nom-m-s-e'
135 			elif plurEndWithSuffix('en') and equalToNs(['as', 'ds']) and cas['gs'] == cas['ns'] + 's':
136 				modele = 'de-nom-m-s-en'
137 			elif plurEndWithSuffix('s') and equalToNs(['as', 'ds']) and cas['gs'] == cas['ns'] + 's':
138 				modele = 'de-nom-m-s-s'
139 		if genre == 'n':
140 			if equalToNs(['as', 'ds']) and cas['gs'] == cas['ns'] + 's' and \
141 				endWithSuffix(['np', 'ap', 'gp'], 'e') and cas['dp'] == cas['ns'] + 'en':
142 				modele = 'de-nom-n-s-e'
143 			elif equalToNs(['ns', 'ds']) and cas['gs'] == cas['ns'] + 's' and plurEndWithSuffix('') and cas['ns'][-4:] == 'chen':
144 				modele = 'de-nom-n-chen'
145 			elif equalToNs(['as', 'ds']) and cas['gs'] == cas['ns'] + 'ses' and \
146 				endWithSuffix(['np', 'ap', 'gp'], 'se') and cas['dp'] == cas['ns'] + 'sen' and cas['ns'][-2:] == 'is':
147 				modele = 'de-nom-n-is'
148 			elif equalToNs(['as', 'ds']) and cas['gs'] == cas['ns'] + 's' and plurEndWithSuffix('') and cas['ns'][-2:] == 'en':
149 				modele = 'de-nom-n-en'
150 			elif equalToNs(['as', 'ds']) and cas['gs'] == cas['ns'] + 's' and plurEndWithSuffix('en'):
151 				modele = 'de-nom-n-s-en'
152 
153 		if modele != '':
154 			if cas['ns'] == titre:
155 				return '{{' + modele + '}}'
156 			else:
157 				return '{{' + modele + '|ns=' + cas['ns'] + '}}'
158 		else:
159 			return match.group(0)
160 			
161 	# Si un paramètre contient deux formes séparées par une virgule, on ne traite pas
162 	if re.search(ur'\{\{de-tab-cas\s*\|[^}]+=[^=|}]*?,[^}]+?\}\}', contenu):
163 		non_modifiee(titre, msg=u'Deux formes dans un paramètre')
164 		return
165 			
166 	if Test:
167 		ldf = re.search(ur'(\'\'\'.*?\'\'\'.*?)\n\{\{de-tab-cas', contenu)
168 		if ldf:
169 			output('Ligne de forme : %s' % ldf.group(1))
170 			
171 	# Le pluriel existe-t-il ? Si non, alors on pourra supprimer le pluriel de la ligne
172 	# de forme lorsuqe redondant avec le tableau de flexions
173 	# Cas 1 : absent car égal à -
174 	no_plur = re.search(ur'\{\{de-tab-cas\s*\|[^}]+?\|\s*np *=\s*(?:\[\[[-–—]\]\]|[-–—]+)\s*\|', contenu)
175 	# Cas 2 : absent car vide et (nominatif) singulier présent
176 	if re.search(ur'\{\{de-tab-cas\s*\|\s*ns *=\s*(?:das|der|die) ', contenu) and \
177 		re.search(ur'\{\{de-tab-cas\s*\|[^}]+?\|\s*np *=\s*\|', contenu):
178 		no_plur = True
179 	
180 	# de-tab-cas → de-nom
181 	# contenu = re.sub(ur'\{\{de-tab-cas\s*\|', u'{{de-nom|', contenu)
182 	contenu = re.sub(ur'\{\{de-tab-cas\s*\|\s*?ns *= *das (?:\'\'\'|\[\[)?([^\n|}]+?)(?:\'\'\'|\]\])?\n?\|np= *(?:die)? *(?:\[\[[-–—]?\]\]|\'\'\'|\[\[)?([^][]*?)(?:\'\'\'|\]\])?\n?\|as= *das (?:\[\[|\'\'\')?(.*?)(?:\'\'\'|\]\])?\n?\|ap= *(?:die )?(?:\[\[[-–—]?\]\]|\'\'\'|\[\[)?([^][]*?)(?:\'\'\'|\]\])?\s*\|gs= *des (?:\[\[|\'\'\')?(.*?)(?:\'\'\'|\]\])?\n?\|gp= *(?:der)? *(?:\[\[[-–—]?\]\]|\'\'\'|\[\[)?([^][]*?)(?:\'\'\'|\]\])?\s*\|ds= *dem (?:\[\[|\'\'\')?(.*?)(?:\'\'\'|\]\])?\n?\|dp= *(?:den)? *(?:\[\[[-–—]?\]\]|\'\'\'|\[\[|)?([^][]*?)(?:\'\'\'|\]\])?\}\}',
183 	ur'{{de-nom|genre=n\n|ns= \1 |np= \2\n|as= \3 |ap= \4\n|gs= \5 |gp= \6\n|ds= \7 |dp= \8}}', contenu)
184 	contenu = re.sub(ur'\{\{de-tab-cas\s*\|\s*?ns= *die (?:\'\'\'|\[\[)?([^\n|}]*?)(?:\'\'\'|\]\])?\n?\|np= *(?:die)? *(?:\[\[[-–—]?\]\]|\'\'\'|\[\[)?([^][]*?)(?:\'\'\'|\]\])?\s*\|as= *die (?:\[\[|\'\'\')?(.*?)(?:\'\'\'|\]\])?\n?\|ap= *(?:die)? *(?:\[\[[-–—]?\]\]|\'\'\'|\[\[)?([^][]*?)(?:\'\'\'|\]\])?\s*\|gs= *der (?:\[\[|\'\'\')?(.*?)(?:\'\'\'|\]\])?\n?\|gp= *(?:der)? *(?:\[\[[-–—]?\]\]|\'\'\'|\[\[)?([^][]*?)(?:\'\'\'|\]\])?\s*\|ds= *der (?:\[\[|\'\'\')?(.*?)(?:\'\'\'|\]\])?\n?\|dp= *(?:den)? *(?:\[\[[-–—]?\]\]|\'\'\'|\[\[)?([^][]*?)(?:\'\'\'|\]\])?\}\}',
185 	ur'{{de-nom|genre=f\n|ns= \1 |np= \2\n|as= \3 |ap= \4\n|gs= \5 |gp= \6\n|ds= \7 |dp= \8}}', contenu)
186 	contenu = re.sub(ur'\{\{de-tab-cas\s*\|\s*?ns= *der (?:\'\'\'|\[\[)?([^\n|}]+?)(?:\'\'\'|\]\])?\n?\|np= *(?:die)? *(?:\[\[[-–—]?\]\]|\'\'\'|\[\[)?([^][]*?)(?:\'\'\'|\]\])?\s*\|as= *den (?:\[\[|\'\'\')?(.*?)(?:\'\'\'|\]\])?\n?\|ap= *(?:die)? *(?:\[\[[-–—]?\]\]|\'\'\'|\[\[)?([^][]*?)(?:\'\'\'|\]\])?\s*\|gs= *des (?:\[\[|\'\'\')?(.*?)(?:\'\'\'|\]\])?\n?\|gp= *(?:der)? *(?:\[\[[-–—]?\]\]|\'\'\'|\[\[)?([^][]*?)(?:\'\'\'|\]\])?\s*\|ds= *dem (?:\[\[|\'\'\')?(.*?)(?:\'\'\'|\]\])?\n?\|dp= *(?:den)? *(?:\[\[[-–—]?\]\]|\'\'\'|\[\[)?([^][]*?)(?:\'\'\'|\]\])?\s*\}\}',
187 	ur'{{de-nom|genre=m\n|ns= \1 |np= \2\n|as= \3 |ap= \4\n|gs= \5 |gp= \6\n|ds= \7 |dp= \8}}', contenu)
188 	
189 	# Idem, mais lorsque les paramètres pour le singulier sont tous placés au début
190 	contenu = re.sub(ur'\{\{de-tab-cas\s*\|\s*?ns *= *das (?:\'\'\'|\[\[)?([^\n|}]+?)(?:\'\'\'|\]\])?\n?\|as= *das (?:\[\[|\'\'\')?(.*?)(?:\'\'\'|\]\])?\n?\|gs= *des (?:\[\[|\'\'\')?(.*?)(?:\'\'\'|\]\])?\n?\|ds= *dem (?:\[\[|\'\'\')?(.*?)(?:\'\'\'|\]\])?\n?\|np= *(?:die)? *(?:\[\[[-–—]?\]\]|\'\'\'|\[\[)?([^][]*?)(?:\'\'\'|\]\])?\n?\|ap= *(?:die )?(?:\[\[[-–—]?\]\]|\'\'\'|\[\[)?([^][]*?)(?:\'\'\'|\]\])?\n?\|gp= *(?:der)? *(?:\[\[[-–—]?\]\]|\'\'\'|\[\[)?([^][]*?)(?:\'\'\'|\]\])?\n?\|dp= *(?:den)? *(?:\[\[[-–—]?\]\]|\'\'\'|\[\[|)?([^][]*?)(?:\'\'\'|\]\])?\}\}',
191 	ur'{{de-nom|genre=n\n|ns= \1 |np= \5\n|as= \2 |ap= \6\n|gs= \3 |gp= \7\n|ds= \4 |dp= \8}}', contenu)
192 	contenu = re.sub(ur'\{\{de-tab-cas\s*\|\s*?ns *= *die (?:\'\'\'|\[\[)?([^\n|}]+?)(?:\'\'\'|\]\])?\n?\|as= *die (?:\[\[|\'\'\')?(.*?)(?:\'\'\'|\]\])?\n?\|gs= *der (?:\[\[|\'\'\')?(.*?)(?:\'\'\'|\]\])?\n?\|ds= *der (?:\[\[|\'\'\')?(.*?)(?:\'\'\'|\]\])?\n?\|np= *(?:die)? *(?:\[\[[-–—]?\]\]|\'\'\'|\[\[)?([^][]*?)(?:\'\'\'|\]\])?\n?\|ap= *(?:die )?(?:\[\[[-–—]?\]\]|\'\'\'|\[\[)?([^][]*?)(?:\'\'\'|\]\])?\n?\|gp= *(?:der)? *(?:\[\[[-–—]?\]\]|\'\'\'|\[\[)?([^][]*?)(?:\'\'\'|\]\])?\n?\|dp= *(?:den)? *(?:\[\[[-–—]?\]\]|\'\'\'|\[\[|)?([^][]*?)(?:\'\'\'|\]\])?\}\}',
193 	ur'{{de-nom|genre=f\n|ns= \1 |np= \5\n|as= \2 |ap= \6\n|gs= \3 |gp= \7\n|ds= \4 |dp= \8}}', contenu)
194 	contenu = re.sub(ur'\{\{de-tab-cas\s*\|\s*?ns *= *der (?:\'\'\'|\[\[)?([^\n|}]+?)(?:\'\'\'|\]\])?\n?\|as= *den (?:\[\[|\'\'\')?(.*?)(?:\'\'\'|\]\])?\n?\|gs= *des (?:\[\[|\'\'\')?(.*?)(?:\'\'\'|\]\])?\n?\|ds= *dem (?:\[\[|\'\'\')?(.*?)(?:\'\'\'|\]\])?\n?\|np= *(?:die)? *(?:\[\[[-–—]?\]\]|\'\'\'|\[\[)?([^][]*?)(?:\'\'\'|\]\])?\n?\|ap= *(?:die )?(?:\[\[[-–—]?\]\]|\'\'\'|\[\[)?([^][]*?)(?:\'\'\'|\]\])?\n?\|gp= *(?:der)? *(?:\[\[[-–—]?\]\]|\'\'\'|\[\[)?([^][]*?)(?:\'\'\'|\]\])?\n?\|dp= *(?:den)? *(?:\[\[[-–—]?\]\]|\'\'\'|\[\[|)?([^][]*?)(?:\'\'\'|\]\])?\}\}',
195 	ur'{{de-nom|genre=m\n|ns= \1 |np= \5\n|as= \2 |ap= \6\n|gs= \3 |gp= \7\n|ds= \4 |dp= \8}}', contenu)
196 
197 	# On enlève les tirets signalant l'absence de paramètre
198 	R = re.compile(ur'(\{\{de-nom\|[^}]*?(?:np|ap|dp|gp|ns|as|ds|gs)) *= *[-–—]+(\}|\n|\|)')
199 	oldContenu = contenu
200 	contenu = R.sub(ur'\1=\2', contenu)
201 	while oldContenu != contenu:
202 		oldContenu = contenu
203 		contenu = R.sub(ur'\1=\2', contenu)
204 	contenu = re.sub(ur'\|(ns|as|gs|ds|np|ap|gp|dp)=\|', ur'|\1= |', contenu)
205 	
206 	if re.search('\{\{de-nom\|[^}]*?(?:der|den|die|das) ', contenu):
207 		non_modifiee(titre, msg=u'Article défini incorrect dans le tableau de flexions')
208 		return
209 
210 	# de-nom → de-nom-xx
211 	R = re.compile(ur'\{\{de-nom\|genre=(m|f|n)\n\|ns= ([^|}]*?) \|np= ([^|}]*?)\n\|as= ([^|}]*?) \|ap= ([^|}]*?)\n\|gs= ([^|}]*?) \|gp= ([^|}]*?)\n\|ds= ([^|}]*?) \|dp= ([^|}]*?)\}\}')
212 	m = R.search(contenu)
213 	# On extrait le nominatif pluriel dont on aura besoin plus base
214 	nom_p = None
215 	if m:
216 		nom_p = m.group(3)
217 	contenu = R.sub(deNomRepl, contenu)
218 	
219 	# Placement du modèle avant la ligne du forme
220 	contenu = re.sub(ur'\n(\'\'\'.*?\'\'\'.*?)\n(\{\{de-nom[^}]+?\}\})\n', ur'\n\2\n\1\n', contenu)
221 
222 	# Retrait du pluriel entre parenthèses sur la ligne de forme
223 	# lorsque la pron est absente (ex : {{After]] : ' ({{p}}: '''die After''' /…/)')
224 	if nom_p:
225 		contenu = re.sub(ur'(\n\{\{de-nom[^}]+?\}\})\n(\'\'\'.*?\'\'\'.*?) *\(\{\{p\}\} *:? \'\'\'(?:die )?(?:\[\[)?%s(?:\]\])?\'\'\'(?: /[ …]?/| \{\{pron\|\|de\}\})? *\)\n' % nom_p,
226 						ur'\1\n\2\n', contenu)
227 						
228 	if no_plur:
229 		contenu = re.sub(ur'(\n\{\{de-nom[^}]+?\}\})\n(\'\'\'.*?\'\'\'.*?) *\(\{\{p\}\} *:? \'\'\'[-–—]+\'\'\'(?: /[ …]?/| \{\{pron\|\|de\}\})? *\)\n',
230 						ur'\1\n\2\n', contenu)
231 	
232 	# Remplacement de // par {{pron||de}}
233 	contenu = re.sub(ur'(\n\{\{de-nom[^}]+?\}\}\n\'\'\'[^\']*?\'\'\') /[ …]?/', ur'\1 {{pron||de}}', contenu)
234 	
235 	# Ajout de {{pron}} sur la ligne de forme lorsque manquant
236 	contenu = re.sub(ur'(\n\{\{de-nom[^}]+?\}\})\n(\'\'\'[^\']*?\'\'\') (\{\{[mfn]{1,2}\}\})\n', ur'\1\n\2 {{pron||de}} \3\n', contenu)
237 	
238 	sauvegarde(page, contenu, summary)
239 
240 def non_modifiee(titre, fichier=fichier_erreurs, msg=None):
241 	'''
242 	Ajout une page à un fichier d'erreurs avec un éventuel message d'erreur
243 	'''
244 	global editcounter
245 	editcounter += 1
246 	output(u'\n%s : %s\r\n' % (titre, msg))
247 	pages_traitees.append(titre)
248 	if edit_fileerror:
249 		with codecs.open(fichier, 'a', config.textfile_encoding) as f:
250 			if msg is None:
251 				f.write(u'# [[%s]]\r\n' % titre)
252 			else:
253 				f.write(u'# [[%s]] : %s\r\n' % (titre, msg))
254 
255 # adapté d'un script de JackPotte
256 def sauvegarde(PageCourante, Contenu, summary, log=True):
257 	global pages_traitees
258 	global pages_modifiees
259 	global editcounter
260 	titre = PageCourante.title()
261 	modif = "o"
262 	chgmt = Contenu != PageCourante.get() # Y a-t-il eu des changements ?
263 	
264 	if not chgmt:
265 		non_modifiee(titre, msg=u'Aucun changement')
266 		return
267 		
268 	while 1:
269 		if Test:
270 			showDiff(PageCourante.get(), Contenu)
271 			modif = inputChoice(u"Sauvegarder ?",
272 								["oui", "non", "ouvrir dans le navigateur", "quitter"],
273 								["o", "n", "d", "q"], default="n")
274 		if modif == "o":
275 			if not Test:
276 				ArretDUrgence()
277 			try:
278 				PageCourante.put(Contenu, summary)
279 			except wikipedia.NoPage:
280 				non_modifiee(titre, msg=u"NoPage en sauvegarde",)
281 				return
282 			except wikipedia.IsRedirectPage:
283 				non_modifiee(titre, msg=u"IsRedirectPage en sauvegarde")
284 				return
285 			except wikipedia.LockedPage:
286 				non_modifiee(titre, msg=u"LockedPage en sauvegarde")
287 				return
288 			except pywikibot.EditConflict:
289 				non_modifiee(titre, msg=u"EditConflict en sauvegarde")
290 				return
291 			except wikipedia.ServerError:
292 				non_modifiee(titre, msg=u"ServerError en sauvegarde")
293 				return
294 			except wikipedia.BadTitle:
295 				non_modifiee(titre, msg=u"BadTitle en sauvegarde")
296 				return
297 			except AttributeError:
298 				non_modifiee(titre, msg=u"AttributeError en sauvegarde")
299 				return
300 			else:
301 				editcounter += 1
302 				if log:
303 					# Enregistrement de la liste des pages modifiées dans un fichier texte dédié
304 					pages_traitees.append(titre)
305 					pages_modifiees.append(titre)
306 					if editcounter % frqMajListes == 0:
307 						output(u'\03{lightgreen}%s pages modifiée(s)\03{default}' % editcounter)
308 						with codecs.open(fichier_sortie, 'a', config.textfile_encoding) as f:
309 							for page in pages_modifiees:
310 								f.write(u"%s\r\n" % page)
311 						retireFromList(pages_traitees)
312 						pages_traitees = []
313 						pages_modifiees = []
314 				return
315 			non_modifiee(titre, msg=u"Exception levée lors de la sauvegarde")
316 			return
317 		elif modif == 'd':
318 			webbrowser.open("http://%s%s" % (
319 				PageCourante.site.hostname(),
320 				PageCourante.site.nice_get_address(titre)
321 			))
322 			i18n.input('pywikibot-enter-finished-browser')
323 			continue
324 		elif modif == 'q':
325 			if len(pages_traitees) > 0:
326 				with codecs.open(fichier_sortie, 'a', config.textfile_encoding) as f:
327 					for page in pages_traitees:
328 						f.write(u"%s\r\n" % page)
329 				retireFromList(pages_traitees)
330 			sys.exit()
331 		else:
332 			output(u"Non modifié (clic de l'utilisateur)")
333 			return
334 
335 # adapté d'un script de JackPotte
336 def ArretDUrgence():
337 		page = Page(getSite(), u"User talk:Botomatik")
338 		if page.exists():
339 			try:
340 				lastContributor = page.userName()
341 			except wikipedia.NoPage: return
342 			except wikipedia.IsRedirectPage: return
343 			except wikipedia.LockedPage: return
344 			except wikipedia.ServerError: return
345 			except wikipedia.BadTitle: return
346 			except wikipedia.EditConflict: return
347 			if lastContributor != u'Automatik':
348 				output(u"\n*** \03{lightyellow}Arrêt d'urgence demandé\03{default} ***")
349 				sys.exit(0)
350 		else:
351 			output(u'La page de discussion du robot n\'existe pas')
352 
353 def TraiteFichier(fichier=fichier_entree, fonction=main, *args, **kwargs):
354 	"""
355 	Applique une fonction à toutes les pages d'un fichier,
356 	en lui passant la page pour premier paramètre
357 
358 	"""
359 	gen = pagegenerators.TextfilePageGenerator(fichier)
360 	for page in gen:
361 		fonction(page, *args, **kwargs)
362 
363 def retireFromList(pages):
364 	"""Retire des pages du fichier d'entrée
365 	Les pages doivent être sous la forme # [[page]]
366 
367 	@param pages: liste de pages
368 	"""
369 	with codecs.open(fichier_entree, 'r', config.textfile_encoding) as f:
370 		contenu = f.read()
371 		for page in pages:
372 			contenu = re.sub(ur"# \[\[%s\]\]\r\n" % page, u"", contenu)
373 	with codecs.open(fichier_entree, 'w', config.textfile_encoding) as f:
374 		f.write(contenu)
375 
376 if __name__ == '__main__':
377 	try:
378 		# main(Page(getSite(), 'Adamsapfel'))
379 		TraiteFichier()
380 	finally:
381 		stopme()