Permalink
Browse files

Improve indentation in HTML postprocessor.

Improve text postprocessor based on HTML one.
  • Loading branch information...
1 parent a30c1a0 commit 8bc6768d2ac73a6709712e14d8ed0c7223d4e0a9 @peter17 committed Apr 26, 2012
Showing with 692 additions and 109 deletions.
  1. +11 −0 README.rst
  2. +1 −1 apostrophes.py
  3. +25 −25 article.htm
  4. +107 −0 article.txt
  5. +21 −17 html.py
  6. +31 −2 parser.py
  7. +40 −40 tests/test_html_postprocessor.py
  8. +173 −0 tests/test_text_postprocessor.py
  9. +281 −23 text.py
  10. +2 −1 wikitext.txt
View
11 README.rst
@@ -108,6 +108,17 @@ If you just want to replace the templates in a given wikitext, you can just call
The `output` string will contain the rendered wikitext.
Put the templates names and content in the `templates` dict (e.g.: `{'my template': 'my template content'}`)
+Postprocessors
+--------------
+
+The parser produces an AST. In order to provide human readable output, three postprocessors are provided:
+ * html.py, for HTML output
+ * text.py, for text output
+ * raw.py, for raw output
+
+For now, we mainly focused on HTML postprocessor. The text output might not be as cleaned as expected.
+
+You can adapt them according to your needs.
Known bugs
==========
View
2 apostrophes.py
@@ -28,7 +28,7 @@
def parse_one_line(text, tags=default_tags):
- arr = _quotePat.split(text.strip())
+ arr = _quotePat.split(text)
if len(arr) == 1:
return text
View
50 article.htm
@@ -1,7 +1,7 @@
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN" "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="fr">
<head><title>Test!</title></head><body>
-<p>Contenu soumis à la licence CC-BY-SA 3.0 (<a href="http://creativecommons.org/licenses/by-sa/3.0/deed.fr)">http://creativecommons.org/licenses/by-sa/3.0/deed.fr)</a> Source : Article Berceuse de Wikipédia en français (<a href="http://fr.wikipedia.org/wiki/Berceuse).">http://fr.wikipedia.org/wiki/Berceuse).</a> </p>
+<p>Contenu soumis à la licence CC-BY-SA 3.0 (<a href="http://creativecommons.org/licenses/by-sa/3.0/deed.fr">[1]</a>)<br />Source : Article Berceuse de Wikipédia en français (<a href="http://fr.wikipedia.org/wiki/Berceuse">[2]</a>).</p>
<p><a href="Template:Voir homonymes">Template:Voir homonymes</a><a href="Template:ébauche">Template:ébauche</a><div class="thumbnail"><img src="William-Adolphe Bouguereau (1825-1905) - Lullaby (1875).jpg" style="float:right;width:250px;" alt="" /><p><em>La Berceuse</em> (œuvre de <a href="William Bouguereau">William Bouguereau</a> - <a href="Template:XIXe siècle">Template:XIXe siècle</a>)</p></div>
Une <strong>berceuse</strong> est une <a href="chanson">chanson</a> douce destinée à aider les <a href="enfant">enfant</a>s à <a href="Sommeil">s'endormir</a>. Il existe des berceuses chantées dans toutes les langues du monde.</p>
<h2>Berceuses françaises</h2>
@@ -20,79 +20,79 @@
</table>
<ul>
<li> <a href="Jean-Sébastien Bach">Bach</a><ul>
- <li> Menuet pour clavier Nº1 en sol majeur (Carnets intimes d'Anna Magdalena II/1), BWV Anh. 114</li>
- <li> « <a href="Jésus que ma joie demeure">Jésus, que ma joie demeure</a> », (<a href="Liste des cantates de Johann Sebastian Bach">Cantate Nº147</a>), BWV 147</li>
- <li> Suite pour orchestre n°3 en re majeur, BWV 1068</li>
+ <li> Menuet pour clavier Nº1 en sol majeur (Carnets intimes d'Anna Magdalena II/1), BWV Anh. 114</li>
+ <li> « <a href="Jésus que ma joie demeure">Jésus, que ma joie demeure</a> », (<a href="Liste des cantates de Johann Sebastian Bach">Cantate Nº147</a>), BWV 147</li>
+ <li> Suite pour orchestre n°3 en re majeur, BWV 1068</li>
</ul>
</li>
<li> <a href="Ludwig van Beethoven">Beethoven</a><ul>
- <li> « <a href="Sonate pour piano nº 14 de Beethoven">Sonate au clair de lune</a> » (Sonate pour piano n° 14 en do dièse mineur), op. 27/2</li>
- <li> « <a href="La Lettre à Élise">Lettre à Élise</a> » (La Bagatelle en la mineur), WoO 59</li>
- <li> « <a href="Sonate pour piano nº 8 de Beethoven">Sonate Pathétique</a> » (Sonate pour piano nº 8 de Beethoven), op. 13/2, Adagio cantabile</li>
+ <li> « <a href="Sonate pour piano nº 14 de Beethoven">Sonate au clair de lune</a> » (Sonate pour piano n° 14 en do dièse mineur), op. 27/2</li>
+ <li> « <a href="La Lettre à Élise">Lettre à Élise</a> » (La Bagatelle en la mineur), WoO 59</li>
+ <li> « <a href="Sonate pour piano nº 8 de Beethoven">Sonate Pathétique</a> » (Sonate pour piano nº 8 de Beethoven), op. 13/2, Adagio cantabile</li>
</ul>
</li>
<li> <a href="Johannes Brahms">Brahms</a><ul>
- <li> « Bonsoir et bonne nuit », berceuse ("Guten Abend, gut Nacht", Wiegenlied), op. 49/4</li>
+ <li> « Bonsoir et bonne nuit », berceuse ("Guten Abend, gut Nacht", Wiegenlied), op. 49/4</li>
</ul>
</li>
<li> <a href="Frédéric Chopin">Chopin</a><ul>
- <li> « <a href="Berceuse (Chopin)">Berceuse en ré bémol majeur</a> », op.57</li>
+ <li> « <a href="Berceuse (Chopin)">Berceuse en ré bémol majeur</a> », op.57</li>
</ul>
</li>
<li> <a href="Claude Debussy">Debussy</a><ul>
- <li> « <a href="Suite Bergamasque">Clair de lune</a> » ("Suite Bergamasque" pour piano), L. 75/3</li>
+ <li> « <a href="Suite Bergamasque">Clair de lune</a> » ("Suite Bergamasque" pour piano), L. 75/3</li>
</ul>
</li>
<li> <a href="Antonín Dvořák">Dvořák</a><ul>
- <li> « <a href="Symphonie nº 9 de Dvořák">Sonate du Nouveau Monde</a> » (Symphonie n° 9 en mi mineur, B. 178), op. 95</li>
+ <li> « <a href="Symphonie nº 9 de Dvořák">Sonate du Nouveau Monde</a> » (Symphonie n° 9 en mi mineur, B. 178), op. 95</li>
</ul>
</li>
<li> <a href="Gabriel Fauré">Fauré</a><ul>
- <li> « <a href="Dolly (Fauré)">Berceuse</a> » (première pièce de la suite Dolly, pour piano à quatre mains), op. 56</li>
+ <li> « <a href="Dolly (Fauré)">Berceuse</a> » (première pièce de la suite Dolly, pour piano à quatre mains), op. 56</li>
</ul>
</li>
<li> <a href="Felix Mendelssohn Bartholdy">Mendelssohn</a><ul>
- <li> « <a href="Romances sans paroles (Mendelssohn)">Chant du printemps</a> » (Romances sans paroles pour piano), op. 62/6</li>
+ <li> « <a href="Romances sans paroles (Mendelssohn)">Chant du printemps</a> » (Romances sans paroles pour piano), op. 62/6</li>
</ul>
</li>
<li> <a href="Wolfgang Amadeus Mozart">Mozart</a><ul>
- <li> « <a href="Ah ! vous dirai-je, maman">Ah ! vous dirai-je, maman</a> » (Douze variations), K. 265</li>
- <li> « <a href="Sonate pour piano n° 16 de Mozart">Sonate facile</a> » (Sonate pour piano nº 16 en do majeur), K. 545</li>
- <li> « La Chasse » (Sonate pour piano n° 18 en ré majeur), K. 576, 3ème mouvement</li>
+ <li> « <a href="Ah ! vous dirai-je, maman">Ah ! vous dirai-je, maman</a> » (Douze variations), K. 265</li>
+ <li> « <a href="Sonate pour piano n° 16 de Mozart">Sonate facile</a> » (Sonate pour piano nº 16 en do majeur), K. 545</li>
+ <li> « La Chasse » (Sonate pour piano n° 18 en ré majeur), K. 576, 3ème mouvement</li>
</ul>
</li>
<li> <a href="Johann Pachelbel">Pachelbel</a><ul>
- <li> « <a href="Canon de Pachelbel">Canon en ré majeur sur une basse obstinée</a> »</li>
+ <li> « <a href="Canon de Pachelbel">Canon en ré majeur sur une basse obstinée</a> »</li>
</ul>
</li>
<li> <a href="Robert Schumann">Schumann</a><ul>
- <li> « <a href="Scènes d'enfants (Schumann)">Gens et pays étrangers</a> » (Les Scènes d'enfants, "Kinderszenen"), op. 15</li>
+ <li> « <a href="Scènes d'enfants (Schumann)">Gens et pays étrangers</a> » (Les Scènes d'enfants, "Kinderszenen"), op. 15</li>
</ul>
</li>
<li> <a href="Bedřich Smetana">Smetana</a><ul>
- <li> « <a href="Má Vlast">La Moldau</a> » (Second poèmes symphoniques de "Ma Patrie")</li>
+ <li> « <a href="Má Vlast">La Moldau</a> » (Second poèmes symphoniques de "Ma Patrie")</li>
</ul>
</li>
<li> <a href="Antonio Vivaldi">Vivaldi</a><ul>
- <li> « <a href="Les Quatre Saisons">L'Hiver</a> » (Les Quatre Saisons, Opus 8/4)</li>
+ <li> « <a href="Les Quatre Saisons">L'Hiver</a> » (Les Quatre Saisons, Opus 8/4)</li>
</ul>
</li>
</ul>
<p>Les plus connues sont la berceuse « <em>Bonsoir et bonne nuit</em> » de Brahms et la « <em>Berceuse en ré bémol majeur</em> » de <a href="Chopin">Chopin</a>. Des compositeurs comme <a href="Franz Liszt">Franz Liszt</a>, <a href="Gabriel Fauré">Gabriel Fauré</a>, <a href="Maurice Ravel">Maurice Ravel</a>, <a href="Mili Balakirev">Mili Balakirev</a>, <a href="Igor Stravinski">Igor Stravinski</a> (<em><a href="Berceuses du chat">Berceuses du chat</a></em>) et <a href="George Gershwin">George Gershwin</a> ont aussi écrit des berceuses.</p>
<h2>Dans la musique populaire</h2>
<h3>Dans la chanson française</h3>
<ul>
- <li><a href="Bénabar">Bénabar</a> : « La berceuse », de l'album <em><a href="Reprise des négociations">Reprise des négociations</a></em>, 2005</li>
- <li><a href="Henri Salvador">Henri Salvador</a> : « Une chanson douce » (Le Loup, la Biche et le Chevalier), de l'album <em>Rigolo</em>, 1968</li>
+ <li> <a href="Bénabar">Bénabar</a> : « La berceuse », de l'album <em><a href="Reprise des négociations">Reprise des négociations</a></em>, 2005</li>
+ <li> <a href="Henri Salvador">Henri Salvador</a> : « Une chanson douce » (Le Loup, la Biche et le Chevalier), de l'album <em>Rigolo</em>, 1968</li>
</ul>
<h3>Dans la musique pop</h3>
<ul>
- <li><a href="Beatles">Beatles</a> : « <a href="Good Night (chanson)">Good Night</a> », de l'<em><a href="The Beatles (album)">album blanc</a></em>, 1968</li>
+ <li> <a href="Beatles">Beatles</a> : « <a href="Good Night (chanson)">Good Night</a> », de l'<em><a href="The Beatles (album)">album blanc</a></em>, 1968</li>
</ul>
<h3>Dans le jazz</h3>
<ul>
- <li><a href="Baden Powell de Aquino">Baden Powell</a> : « Berceuse a Jussara », de l'album <em>Le Monde Musical</em>, 1964</li>
- <li><a href="Henri Salvador">Henri Salvador</a> : « Une chanson douce » (Le Loup, la Biche et le Chevalier), de l'album <em>Rigolo</em>, 1968</li>
+ <li> <a href="Baden Powell de Aquino">Baden Powell</a> : « Berceuse a Jussara », de l'album <em>Le Monde Musical</em>, 1964</li>
+ <li> <a href="Henri Salvador">Henri Salvador</a> : « Une chanson douce » (Le Loup, la Biche et le Chevalier), de l'album <em>Rigolo</em>, 1968</li>
</ul>
<h2> Voir aussi </h2>
<ul>
View
107 article.txt
@@ -0,0 +1,107 @@
+Contenu soumis à la licence CC-BY-SA 3.0 ([1])
+Source : Article Berceuse de Wikipédia en français ([2]).
+Template:Voir homonymes (link: Template:Voir homonymes)Template:ébauche (link: Template:ébauche)Image: William-Adolphe Bouguereau (1825-1905) - Lullaby (1875).jpg, style: float:right;width:250px;, legend:_La Berceuse_ (œuvre de William Bouguereau (link: William Bouguereau) - Template:XIXe siècle (link: Template:XIXe siècle))
+Une *berceuse* est une chanson (link: chanson) douce destinée à aider les enfant (link: enfant)s à s'endormir (link: Sommeil). Il existe des berceuses chantées dans toutes les langues du monde.
+Berceuses françaises
+Les berceuses françaises traditionnelles les plus connues sont « Dodo, l'enfant do / l'enfant dormira peut-être » et « Fais dodo, Colas mon p'tit frère ». Une berceuse du Nord, « P'tit Quinquin (link: P'tit Quinquin (chanson)) » est également devenue célèbre.
+Dans la musique classique
+Les berceuses en musique classique obéissent souvent à une forme nommée aussi "berceuse" dans les langues étrangères. Elles sont généralement à trois temps (link: Temps (solfège)), à tonalité (link: tonalité) simple, alternant des harmonies dominante (link: dominante) et tonique (link: Tonique (musique)).
+*Berceuses célèbres classées par compositeurs :*
+----------
+ Fichier audio
+<span style="height:20px; width:100%; padding:4pt; padding-left:0.3em; line-height:2em;" cellspacing="0">*_Wiegenlied_ (Berceuse, op 49/4) (link: Media:Brahms_-_Schumann-Heink_-_Wiegenlied_(Berceuse)_(1915).ogg‎)* _(info (link: Fichier:Brahms_-_Schumann-Heink_-_Wiegenlied_(Berceuse)_(1915).ogg‎))_
+<small>_Wiegenlied_ interprété par Ernestine Schumann-Heink (link: Ernestine Schumann-Heink)<center>Image: Brahms_-_Schumann-Heink_-_Wiegenlied_(Berceuse)_(1915).ogg‎, style:
+<span style="height:20px; width:100%; padding-left:0.3em;" cellspacing="0"><span title="Des difficultés pour écouter le fichier ?">Image: Circle question mark.png, style: width:14px; _Des problèmes pour écouter le fichier ? (link: Aide:Écouter des sons ogg)_
+
+----------
+
+ * Bach (link: Jean-Sébastien Bach)
+ * Menuet pour clavier Nº1 en sol majeur (Carnets intimes d'Anna Magdalena II/1), BWV Anh. 114
+ * « Jésus, que ma joie demeure (link: Jésus que ma joie demeure) », (Cantate Nº147 (link: Liste des cantates de Johann Sebastian Bach)), BWV 147
+ * Suite pour orchestre n°3 en re majeur, BWV 1068
+
+ * Beethoven (link: Ludwig van Beethoven)
+ * « Sonate au clair de lune (link: Sonate pour piano nº 14 de Beethoven) » (Sonate pour piano n° 14 en do dièse mineur), op. 27/2
+ * « Lettre à Élise (link: La Lettre à Élise) » (La Bagatelle en la mineur), WoO 59
+ * « Sonate Pathétique (link: Sonate pour piano nº 8 de Beethoven) » (Sonate pour piano nº 8 de Beethoven), op. 13/2, Adagio cantabile
+
+ * Brahms (link: Johannes Brahms)
+ * « Bonsoir et bonne nuit », berceuse ("Guten Abend, gut Nacht", Wiegenlied), op. 49/4
+
+ * Chopin (link: Frédéric Chopin)
+ * « Berceuse en ré bémol majeur (link: Berceuse (Chopin)) », op.57
+
+ * Debussy (link: Claude Debussy)
+ * « Clair de lune (link: Suite Bergamasque) » ("Suite Bergamasque" pour piano), L. 75/3
+
+ * Dvořák (link: Antonín Dvořák)
+ * « Sonate du Nouveau Monde (link: Symphonie nº 9 de Dvořák) » (Symphonie n° 9 en mi mineur, B. 178), op. 95
+
+ * Fauré (link: Gabriel Fauré)
+ * « Berceuse (link: Dolly (Fauré)) » (première pièce de la suite Dolly, pour piano à quatre mains), op. 56
+
+ * Mendelssohn (link: Felix Mendelssohn Bartholdy)
+ * « Chant du printemps (link: Romances sans paroles (Mendelssohn)) » (Romances sans paroles pour piano), op. 62/6
+
+ * Mozart (link: Wolfgang Amadeus Mozart)
+ * « Ah ! vous dirai-je, maman (link: Ah ! vous dirai-je, maman) » (Douze variations), K. 265
+ * « Sonate facile (link: Sonate pour piano n° 16 de Mozart) » (Sonate pour piano nº 16 en do majeur), K. 545
+ * « La Chasse » (Sonate pour piano n° 18 en ré majeur), K. 576, 3ème mouvement
+
+ * Pachelbel (link: Johann Pachelbel)
+ * « Canon en ré majeur sur une basse obstinée (link: Canon de Pachelbel) »
+
+ * Schumann (link: Robert Schumann)
+ * « Gens et pays étrangers (link: Scènes d'enfants (Schumann)) » (Les Scènes d'enfants, "Kinderszenen"), op. 15
+
+ * Smetana (link: Bedřich Smetana)
+ * « La Moldau (link: Má Vlast) » (Second poèmes symphoniques de "Ma Patrie")
+
+ * Vivaldi (link: Antonio Vivaldi)
+ * « L'Hiver (link: Les Quatre Saisons) » (Les Quatre Saisons, Opus 8/4)
+
+Les plus connues sont la berceuse « _Bonsoir et bonne nuit_ » de Brahms et la « _Berceuse en ré bémol majeur_ » de Chopin (link: Chopin). Des compositeurs comme Franz Liszt (link: Franz Liszt), Gabriel Fauré (link: Gabriel Fauré), Maurice Ravel (link: Maurice Ravel), Mili Balakirev (link: Mili Balakirev), Igor Stravinski (link: Igor Stravinski) (_Berceuses du chat (link: Berceuses du chat)_) et George Gershwin (link: George Gershwin) ont aussi écrit des berceuses.
+Dans la musique populaire
+Dans la chanson française
+
+ * Bénabar (link: Bénabar) : « La berceuse », de l'album _Reprise des négociations (link: Reprise des négociations)_, 2005
+ * Henri Salvador (link: Henri Salvador) : « Une chanson douce » (Le Loup, la Biche et le Chevalier), de l'album _Rigolo_, 1968
+Dans la musique pop
+
+ * Beatles (link: Beatles) : « Good Night (link: Good Night (chanson)) », de l'_album blanc (link: The Beatles (album))_, 1968
+Dans le jazz
+
+ * Baden Powell (link: Baden Powell de Aquino) : « Berceuse a Jussara », de l'album _Le Monde Musical_, 1964
+ * Henri Salvador (link: Henri Salvador) : « Une chanson douce » (Le Loup, la Biche et le Chevalier), de l'album _Rigolo_, 1968
+ Voir aussi
+
+ * Lullaby (link: Lullaby) dans la section anglaise et italienne où il y a une description d'une bonne partie des berceuses européennes.
+Template:Wiktionnaire (link: Template:Wiktionnaire)
+Bibliographie
+
+ * Claudine Antoine, _La Berceuse populaire dans le contexte de la petite enfance_, Université Strasbourg 2, 1988, 2 vol., 358 + 333 p. (thèse de Musiocologie)
+ * Marina Altmann de Litvan (dir.), _La berceuse : jeux d'amour et de magie_, Erès, Ramonville Saint-Agne, 2008, 151 p. Template:ISBN (link: Template:ISBN)
+Template:portail musique (link: Template:portail musique)
+
+
+
+Links:
+[1] http://creativecommons.org/licenses/by-sa/3.0/deed.fr
+[2] http://fr.wikipedia.org/wiki/Berceuse
+
+Categories:
+* Berceuse
+* Musique par genre
+* Musique traditionnelle
+
+Interwiki:
+* http://ar.wikipedia.org/wiki/تهويدة
+* http://az.wikipedia.org/wiki/Layla
+* http://br.wikipedia.org/wiki/Luskellerez
+* http://ca.wikipedia.org/wiki/Cançó de bressol
+* http://cs.wikipedia.org/wiki/Ukolébavka
+* http://da.wikipedia.org/wiki/Berceuse
+* http://de.wikipedia.org/wiki/Wiegenlied
+* http://en.wikipedia.org/wiki/Lullaby
+* http://eo.wikipedia.org/wiki/Lulkanto
+* http://es.wikipedia.org/wiki/Nana (canción de cuna)
View
38 html.py
@@ -242,31 +242,35 @@ def render_preformatted(node):
def render_hr(node):
node.value = '<hr />\n'
- def render_ul(list):
+ def render_ul(list, level):
+ indent = level * '\t'
result = '<ul>\n'
for i in range(len(list)):
- result += '\t<li>' + content(list[i]) + '</li>\n'
+ result += indent + '<li>' + content(list[i]) + '</li>\n'
result += '</ul>\n'
return result
- def render_ol(list):
+ def render_ol(list, level):
+ indent = level * '\t'
result = '<ol>\n'
for i in range(len(list)):
- result += '\t<li>' + content(list[i]) + '</li>\n'
+ result += indent + '<li>' + content(list[i]) + '</li>\n'
result += '</ol>\n'
return result
- def render_dd(list):
+ def render_dd(list, level):
+ indent = level * '\t'
result = '<dl>\n'
for i in range(len(list)):
- result += '\t<dd>' + content(list[i]) + '</dd>\n'
+ result += indent + '<dd>' + content(list[i]) + '</dd>\n'
result += '</dl>\n'
return result
- def render_dt(list):
+ def render_dt(list, level):
+ indent = level * '\t'
result = '<dl>\n'
for i in range(len(list)):
- result += '\t<dt>' + content(list[i]) + '</dt>\n'
+ result += indent + '<dt>' + content(list[i]) + '</dt>\n'
result += '</dl>\n'
return result
@@ -285,35 +289,35 @@ def collapse_list(list):
if isinstance(list[i].value, Nodes):
collapse_list(list[i].value)
- def select_items(nodes, i, value):
+ def select_items(nodes, i, value, level):
list_tags = ['bullet_list_leaf', 'number_list_leaf', 'colon_list_leaf', 'semi_colon_list_leaf']
list_tags.remove(value)
if isinstance(nodes[i].value, Nodes):
- render_lists(nodes[i].value)
+ render_lists(nodes[i].value, level + 1)
items = [nodes[i]]
while i + 1 < len(nodes) and nodes[i+1].tag not in list_tags:
if isinstance(nodes[i+1].value, Nodes):
- render_lists(nodes[i+1].value)
+ render_lists(nodes[i+1].value, level + 1)
items.append(nodes.pop(i+1))
return items
- def render_lists(list):
+ def render_lists(list, level):
i = 0
while i < len(list):
if list[i].tag == 'bullet_list_leaf' or list[i].tag == '@bullet_sub_list@':
- list[i].value = render_ul(select_items(list, i, 'bullet_list_leaf'))
+ list[i].value = render_ul(select_items(list, i, 'bullet_list_leaf', level), level)
elif list[i].tag == 'number_list_leaf' or list[i].tag == '@number_sub_list@':
- list[i].value = render_ol(select_items(list, i, 'number_list_leaf'))
+ list[i].value = render_ol(select_items(list, i, 'number_list_leaf', level), level)
elif list[i].tag == 'colon_list_leaf' or list[i].tag == '@colon_sub_list@':
- list[i].value = render_dd(select_items(list, i, 'colon_list_leaf'))
+ list[i].value = render_dd(select_items(list, i, 'colon_list_leaf', level), level)
elif list[i].tag == 'semi_colon_list_leaf' or list[i].tag == '@semi_colon_sub_list@':
- list[i].value = render_dt(select_items(list, i, 'semi_colon_list_leaf'))
+ list[i].value = render_dt(select_items(list, i, 'semi_colon_list_leaf', level), level)
i += 1
def render_list(node):
assert isinstance(node.value, Nodes), "Bad AST shape!"
collapse_list(node.value)
- render_lists(node.value)
+ render_lists(node.value, 1)
def render_url(node):
node.value = '<a href="%s">%s</a>' % (node.leaf(), node.leaf())
View
33 parser.py
@@ -1,6 +1,10 @@
# -*- coding: utf8 -*-
import time
+import codecs
+
+print "*** Parsing to HTML ***"
+
start_time = time.time()
# get the parser
@@ -48,14 +52,13 @@
parser = make_parser(allowed_tags, allowed_autoclose_tags, allowed_parameters, interwiki, namespaces)
# import the source in a utf-8 string
-import codecs
fileObj = codecs.open("wikitext.txt", "r", "utf-8")
source = fileObj.read()
# The last line of the file will not be parsed correctly if
# there is no newline at the end of file, so, we add one.
if source[-1] != '\n':
- source += '\n'
+ source += '\n'
preprocessed_text = preprocessor.parse(source)
tree = parser.parse(preprocessed_text.leaves())
@@ -67,4 +70,30 @@
file("article.htm", "w").write(output.encode('UTF-8'))
end_time = time.time()
+print "Parsed and rendered in", end_time - start_time, "s."
+
+print "*** Parsing to text ***"
+
+start_time = time.time()
+
+from text import make_parser
+parser = make_parser(interwiki, namespaces)
+
+# import the source in a utf-8 string
+fileObj = codecs.open("wikitext.txt", "r", "utf-8")
+source = fileObj.read()
+
+# The last line of the file will not be parsed correctly if
+# there is no newline at the end of file, so, we add one.
+if source[-1] != '\n':
+ source += '\n'
+
+preprocessed_text = preprocessor.parse(source)
+tree = parser.parse(preprocessed_text.leaves())
+
+output = tree.leaves()
+
+file("article.txt", "w").write(output.encode('UTF-8'))
+
+end_time = time.time()
print "Parsed and rendered in", end_time - start_time, "s."
View
80 tests/test_html_postprocessor.py
@@ -145,8 +145,8 @@ def test_wikitext_in_table(self):
<hr />
<dl>
\t<dd><dl>
-\t<dd><dl>
-\t<dd> lists</dd>
+\t\t<dd><dl>
+\t\t\t<dd> lists</dd>
</dl>
</dd>
</dl>
@@ -355,11 +355,11 @@ def test_simple_bullet_list(self):
"""
result = """<ul>
\t<li> item 1<ul>
-\t<li> item 2<ul>
-\t<li> item 3</li>
+\t\t<li> item 2<ul>
+\t\t\t<li> item 3</li>
</ul>
</li>
-\t<li> item 2</li>
+\t\t<li> item 2</li>
</ul>
</li>
</ul>
@@ -374,23 +374,23 @@ def test_simple_numbered_list(self):
"""
result = """<ol>
\t<li><ol>
-\t<li> item 2</li>
+\t\t<li> item 2</li>
</ol>
</li>
\t<li><ol>
-\t<li><ol>
-\t<li> item 3</li>
+\t\t<li><ol>
+\t\t\t<li> item 3</li>
</ol>
</li>
</ol>
</li>
\t<li><ol>
-\t<li> item 2</li>
+\t\t<li> item 2</li>
</ol>
</li>
\t<li><ol>
-\t<li><ol>
-\t<li> item 3</li>
+\t\t<li><ol>
+\t\t\t<li> item 3</li>
</ol>
</li>
</ol>
@@ -409,14 +409,14 @@ def test_simple_semicolon_list(self):
"""
result = """<dl>
\t<dt> item 1<dl>
-\t<dt> item 2</dt>
-\t<dt> item 2</dt>
+\t\t<dt> item 2</dt>
+\t\t<dt> item 2</dt>
</dl>
</dt>
\t<dt> item 1</dt>
\t<dt> item 1<dl>
-\t<dt><dl>
-\t<dt> item 3</dt>
+\t\t<dt><dl>
+\t\t\t<dt> item 3</dt>
</dl>
</dt>
</dl>
@@ -435,16 +435,16 @@ def test_simple_colon_list(self):
"""
result = """<dl>
\t<dd> item 1<dl>
-\t<dd><dl>
-\t<dd> item 3</dd>
+\t\t<dd><dl>
+\t\t\t<dd> item 3</dd>
</dl>
</dd>
-\t<dd> item 2</dd>
+\t\t<dd> item 2</dd>
</dl>
</dd>
\t<dd> item 1<dl>
-\t<dd> item 2</dd>
-\t<dd> item 2</dd>
+\t\t<dd> item 2</dd>
+\t\t<dd> item 2</dd>
</dl>
</dd>
</dl>
@@ -462,10 +462,10 @@ def test_formatted_mixed_list(self):
\t<dd> item 1</dd>
</dl>
<dl>
-\t<dt>this is <em>italic</em></dt>
+\t<dt> this is <em>italic</em></dt>
</dl>
<ul>
-\t<li>and <strong>bold</strong> here</li>
+\t<li> and <strong>bold</strong> here</li>
</ul>
<ol>
\t<li> a <a href="link">link</a></li>
@@ -495,57 +495,57 @@ def test_complex_mixed_list(self):
result = """<ul>
\t<li>level 1</li>
\t<li>level 1<ul>
-\t<li>level 2<ol>
-\t<li>level 3</li>
+\t\t<li>level 2<ol>
+\t\t\t<li>level 3</li>
</ol>
</li>
-\t<li>level 2</li>
+\t\t<li>level 2</li>
</ul>
</li>
\t<li><dl>
-\t<dd> level 2</dd>
+\t\t<dd> level 2</dd>
</dl>
</li>
</ul>
<dl>
\t<dt> level 1</dt>
\t<dt><ol>
-\t<li>level 2</li>
+\t\t<li>level 2</li>
</ol>
</dt>
\t<dt><ol>
-\t<li><dl>
-\t<dt>level 3</dt>
+\t\t<li><dl>
+\t\t\t<dt>level 3</dt>
</dl>
</li>
</ol>
</dt>
\t<dt><ol>
-\t<li><ol>
-\t<li><ol>
-\t<li>level 4</li>
+\t\t<li><ol>
+\t\t\t<li><ol>
+\t\t\t\t<li>level 4</li>
</ol>
</li>
</ol>
</li>
</ol>
</dt>
\t<dt><ul>
-\t<li><ul>
-\t<li><ol>
-\t<li>level 4</li>
+\t\t<li><ul>
+\t\t\t<li><ol>
+\t\t\t\t<li>level 4</li>
</ol>
</li>
</ul>
</li>
</ul>
</dt>
\t<dt><ul>
-\t<li><dl>
-\t<dt><ol>
-\t<li><ul>
-\t<li><dl>
-\t<dd> weird syntax</dd>
+\t\t<li><dl>
+\t\t\t<dt><ol>
+\t\t\t\t<li><ul>
+\t\t\t\t\t<li><dl>
+\t\t\t\t\t\t<dd> weird syntax</dd>
</dl>
</li>
</ul>
View
173 tests/test_text_postprocessor.py
@@ -169,3 +169,176 @@ def test_styles_in_template(self):
result = "Here, we have *text* and _more text_ and _*still more text*_.\n"
templates = {'template': "'''text''' and ''more text'' and '''''still more text'''''"}
self.parsed_equal_string(source, result, None, templates, 'text')
+
+ def test_simple_bullet_list(self):
+ source = """* item 1
+** item 2
+*** item 3
+** item 2
+"""
+ result = """
+\t* item 1
+\t\t* item 2
+\t\t\t* item 3
+
+\t\t* item 2
+
+"""
+ self.parsed_equal_string(source, result, 'wikitext', {}, 'text')
+
+ def test_simple_numbered_list(self):
+ source = """## item 2
+### item 3
+## item 2
+### item 3
+"""
+ result = """
+\t1.
+\t\t1. item 2
+
+\t2.
+\t\t1.
+\t\t\t1. item 3
+
+
+\t3.
+\t\t1. item 2
+
+\t4.
+\t\t1.
+\t\t\t1. item 3
+
+
+"""
+ self.parsed_equal_string(source, result, 'wikitext', {}, 'text')
+
+ def test_simple_semicolon_list(self):
+ source = """; item 1
+;; item 2
+;; item 2
+; item 1
+; item 1
+;;; item 3
+"""
+ result = """
+\t* item 1
+\t\t* item 2
+\t\t* item 2
+
+\t* item 1
+\t* item 1
+\t\t*
+\t\t\t* item 3
+
+
+"""
+ self.parsed_equal_string(source, result, 'wikitext', {}, 'text')
+
+ def test_simple_colon_list(self):
+ source = """: item 1
+::: item 3
+:: item 2
+: item 1
+:: item 2
+:: item 2
+"""
+ result = """
+\t* item 1
+\t\t*
+\t\t\t* item 3
+
+\t\t* item 2
+
+\t* item 1
+\t\t* item 2
+\t\t* item 2
+
+"""
+ self.parsed_equal_string(source, result, 'wikitext', {}, 'text')
+
+ def test_formatted_mixed_list(self):
+ source = """: item 1
+; this is ''italic''
+* and '''bold''' here
+# a [[link]]
+: a {{template}}
+"""
+ result = """
+\t* item 1
+
+\t* this is _italic_
+
+\t* and *bold* here
+
+\t1. a link (link: link)
+
+\t* a template!
+"""
+ templates = {'template': 'template!'}
+ self.parsed_equal_string(source, result, 'wikitext', templates, 'text')
+
+ def test_complex_mixed_list(self):
+ source = """*level 1
+*level 1
+**level 2
+**#level 3
+**level 2
+:: level 2
+; level 1
+##level 2
+##;level 3
+####level 4
+#**#level 4
+:*;#*: weird syntax
+* end
+"""
+ result = """
+\t* level 1
+\t* level 1
+\t\t* level 2
+\t\t\t1. level 3
+
+\t\t* level 2
+
+\t*
+\t\t* level 2
+
+
+\t* level 1
+\t*
+\t\t1. level 2
+
+\t*
+\t\t1.
+\t\t\t* level 3
+
+
+\t*
+\t\t1.
+\t\t\t1.
+\t\t\t\t1. level 4
+
+
+
+\t*
+\t\t*
+\t\t\t*
+\t\t\t\t1. level 4
+
+
+
+\t*
+\t\t*
+\t\t\t*
+\t\t\t\t1.
+\t\t\t\t\t*
+\t\t\t\t\t\t* weird syntax
+
+
+
+
+
+
+\t* end
+"""
+ self.parsed_equal_string(source, result, 'wikitext', {}, 'text')
View
304 text.py
@@ -1,8 +1,51 @@
from constants import html_entities
+from pijnu.library.node import Nil, Nodes, Node
from mediawiki_parser import wikitextParser
import apostrophes
-def toolset():
+def toolset(interwiki, namespaces):
+ tags_stack = []
+
+ external_autonumber = []
+ """ This is for the autonumbering of external links.
+ e.g.: "[http://www.mozilla.org] [http://fr.wikipedia.org] text"
+ is rendered as: "[1] [2] text
+ Links:
+ [1] http://www.mozilla.org
+ [2] http://fr.wikipedia.org
+ """
+
+ external_links = []
+ """ This will contain the external links of the article. """
+ category_links = []
+ """ This will contain the links to the categories of the article. """
+ interwiki_links = []
+ """ This will contain the links to the foreign versions of the article. """
+
+ style_tags = {'bold': '*', 'bold_close': '*', 'italic': '_', 'italic_close': '_'}
+
+ for namespace, value in namespaces.iteritems():
+ assert value in range(16), "Incorrect value for namespaces"
+ """
+ Predefined namespaces; source: includes/Defines.php of MediaWiki-1.17.0
+ 'NS_MAIN', 0
+ 'NS_TALK', 1
+ 'NS_USER', 2
+ 'NS_USER_TALK', 3
+ 'NS_PROJECT', 4
+ 'NS_PROJECT_TALK', 5
+ 'NS_FILE', 6
+ 'NS_FILE_TALK', 7
+ 'NS_MEDIAWIKI', 8
+ 'NS_MEDIAWIKI_TALK', 9
+ 'NS_TEMPLATE', 10
+ 'NS_TEMPLATE_TALK', 11
+ 'NS_HELP', 12
+ 'NS_HELP_TALK', 13
+ 'NS_CATEGORY', 14
+ 'NS_CATEGORY_TALK', 15
+ """
+
def render_tag_p(attributes):
return '\n'
@@ -12,6 +55,9 @@ def render_tag_br(attributes):
allowed_tags = {'p': render_tag_p,
'br': render_tag_br}
+ def content(node):
+ return apostrophes.parse('%s' % node.leaf(), style_tags)
+
def render_title1(node):
node.value = '%s\n' % node.leaf()
@@ -40,21 +86,27 @@ def render_wikitext(node):
pass
def render_body(node):
- tags = {'bold': '*', 'bold_close': '*', 'italic': '_', 'italic_close': '_'}
- node.value = apostrophes.parse('%s' % node.leaves(), tags)
+ metadata = ''
+ if external_links != []:
+ metadata += '\nLinks:\n' + '\n'.join(external_links) + '\n'
+ if category_links != []:
+ metadata += '\nCategories:\n' + '\n'.join(category_links) + '\n'
+ if interwiki_links != []:
+ metadata += '\nInterwiki:\n' + '\n'.join(interwiki_links) + '\n'
+ node.value = apostrophes.parse('%s' % node.leaves(), style_tags) + metadata
def render_entity(node):
value = '%s' % node.leaf()
if value in html_entities:
node.value = '%s' % unichr(html_entities[value])
else:
- node.value = '&%s;' % value
+ node.value = '&amp;%s;' % value
def render_lt(node):
- pass
+ node.value = '<'
def render_gt(node):
- pass
+ node.value = '>'
def process_attribute(node, allowed_tag):
assert len(node.value) == 2, "Bad AST shape!"
@@ -103,45 +155,251 @@ def render_tag_autoclose(node):
node.value = '<%s%s />' % (tag_name, attributes)
def render_table(node):
- pass
-
- def render_table_line_break(node):
- node.value = '\n'
+ table_content = ''
+ if isinstance(node.value, Nodes) and node.value[0].tag == 'table_begin':
+ contents = node.value[1].value
+ for item in contents:
+ table_content += content(item)
+ else:
+ table_content = content(node)
+ node.value = '----------\n%s\n----------\n' % table_content
+
+ def render_cell_content(node):
+ if isinstance(node.value, Nil):
+ return None
+ cell_content = ''
+ if len(node.value) > 1:
+ values = node.value[0].value
+ for value in values:
+ if isinstance(value, Node):
+ if not (value.tag == 'HTML_attribute' and value.value != ''):
+ cell_content += value.leaf()
+ else:
+ cell_content += value
+ cell_content += content(node.value[1])
+ else:
+ cell_content = content(node)
+ return cell_content
def render_table_header_cell(node):
- pass
+ result = ''
+ if isinstance(node.value, Nodes):
+ for i in range(len(node.value)):
+ content = render_cell_content(node.value[i])
+ result += '%s\t' % content
+ else:
+ content = render_cell_content(node)
+ result = '%s\t' % content
+ if result != '':
+ node.value = result
def render_table_normal_cell(node):
- pass
+ result = ''
+ if isinstance(node.value, Nodes):
+ for i in range(len(node.value)):
+ content = render_cell_content(node.value[i])
+ result += '%s\t' % content
+ else:
+ content = render_cell_content(node)
+ result = '%s\t' % content
+ if result != '':
+ node.value = result
def render_table_empty_cell(node):
- pass
+ node.value = '\t'
def render_table_caption(node):
- pass
+ content = render_cell_content(node)
+ if content is not None:
+ node.value = '\t%s\n' % content
+
+ def render_table_line_break(node):
+ node.value = '\n'
def render_preformatted(node):
- pass
+ node.value = content(node) + '\n'
def render_hr(node):
node.value = '------'
- def render_li(node):
- pass
+ def render_ul(list, level):
+ indent = level * '\t'
+ result = '\n'
+ for i in range(len(list)):
+ result += indent + '* ' + content(list[i]) + '\n'
+ return result
+
+ def render_ol(list, level):
+ indent = level * '\t'
+ result = '\n'
+ for i in range(len(list)):
+ result += indent + '%i. %s\n' % (i + 1, content(list[i]))
+ return result
+
+ def render_dd(list, level):
+ indent = level * '\t'
+ result = '\n'
+ for i in range(len(list)):
+ result += indent + '* ' + content(list[i]) + '\n'
+ return result
+
+ def render_dt(list, level):
+ indent = level * '\t'
+ result = '\n'
+ for i in range(len(list)):
+ result += indent + '* ' + content(list[i]) + '\n'
+ return result
+
+ def collapse_list(list):
+ i = 0
+ while i+1 < len(list):
+ if list[i].tag == 'bullet_list_leaf' and list[i+1].tag == '@bullet_sub_list@' or \
+ list[i].tag == 'number_list_leaf' and list[i+1].tag == '@number_sub_list@' or \
+ list[i].tag == 'colon_list_leaf' and list[i+1].tag == '@colon_sub_list@' or \
+ list[i].tag == 'semi_colon_list_leaf' and list[i+1].tag == '@semi_colon_sub_list@':
+ list[i].value.append(list[i+1].value[0])
+ list.pop(i+1)
+ else:
+ i += 1
+ for i in range(len(list)):
+ if isinstance(list[i].value, Nodes):
+ collapse_list(list[i].value)
+
+ def select_items(nodes, i, value, level):
+ list_tags = ['bullet_list_leaf', 'number_list_leaf', 'colon_list_leaf', 'semi_colon_list_leaf']
+ list_tags.remove(value)
+ if isinstance(nodes[i].value, Nodes):
+ render_lists(nodes[i].value, level + 1)
+ items = [nodes[i]]
+ while i + 1 < len(nodes) and nodes[i+1].tag not in list_tags:
+ if isinstance(nodes[i+1].value, Nodes):
+ render_lists(nodes[i+1].value, level + 1)
+ items.append(nodes.pop(i+1))
+ return items
+
+ def render_lists(list, level):
+ i = 0
+ while i < len(list):
+ if list[i].tag == 'bullet_list_leaf' or list[i].tag == '@bullet_sub_list@':
+ list[i].value = render_ul(select_items(list, i, 'bullet_list_leaf', level), level)
+ elif list[i].tag == 'number_list_leaf' or list[i].tag == '@number_sub_list@':
+ list[i].value = render_ol(select_items(list, i, 'number_list_leaf', level), level)
+ elif list[i].tag == 'colon_list_leaf' or list[i].tag == '@colon_sub_list@':
+ list[i].value = render_dd(select_items(list, i, 'colon_list_leaf', level), level)
+ elif list[i].tag == 'semi_colon_list_leaf' or list[i].tag == '@semi_colon_sub_list@':
+ list[i].value = render_dt(select_items(list, i, 'semi_colon_list_leaf', level), level)
+ i += 1
def render_list(node):
- pass
+ assert isinstance(node.value, Nodes), "Bad AST shape!"
+ collapse_list(node.value)
+ render_lists(node.value, 1)
def render_url(node):
- pass
+ node.value = '<a href="%s">%s</a>' % (node.leaf(), node.leaf())
def render_external_link(node):
- pass
+ if len(node.value) == 1:
+ external_autonumber.append(node.leaf())
+ url = node.leaf()
+ text = len(external_autonumber)
+ else:
+ url = node.value[0].leaf()
+ text = node.value[1].leaf()
+ node.value = '[%s]' % (text)
+ external_links.append('[%s] %s' % (text, url))
+
+ def render_interwiki(prefix, page):
+ link = '* %s' % (interwiki[prefix] + page)
+ if link not in interwiki_links:
+ interwiki_links.append(link)
+
+ def render_category(category_name):
+ link = '* %s' % (category_name)
+ if link not in category_links:
+ category_links.append(link)
+
+ def render_file(file_name, arguments):
+ """ This implements a basic handling of images.
+ MediaWiki supports much more parameters (see includes/Parser.php).
+ """
+ style = ''
+ thumbnail = False
+ legend = ''
+ if arguments != []:
+ parameters = arguments[0].value
+ for parameter in parameters:
+ parameter = '%s' % parameter.leaf()
+ if parameter[-2:] == 'px':
+ size = parameter[0:-2]
+ if 'x' in size:
+ size_x, size_y = size.split('x', 1)
+ try:
+ size_x = int(size_x)
+ size_y = int(size_y)
+ style += 'width:%spx;height:%spx' % (size_x, size_y)
+ except:
+ legend = parameter
+ else:
+ try:
+ size_x = int(size)
+ style += 'width:%spx;' % size_x
+ except:
+ legend = parameter
+ elif parameter in ['left', 'right', 'center']:
+ style += 'float:%s;' % parameter
+ elif parameter in ['thumb', 'thumbnail']:
+ thumbnail = True
+ elif parameter == 'border':
+ style += 'border:1px solid grey'
+ else:
+ legend = parameter
+ result = 'Image: %s, style: %s' % (file_name, style)
+ if thumbnail:
+ result = '%s, legend:%s\n' % (result, legend)
+ return result
def render_internal_link(node):
- pass
+ force_link = False
+ url = ''
+ page_name = node.value.pop(0).value
+ if page_name[0] == ':':
+ force_link = True
+ page_name = page_name[1:]
+ if ':' in page_name:
+ namespace, page_name = page_name.split(':', 1)
+ if namespace in interwiki and not force_link:
+ render_interwiki(namespace, page_name)
+ node.value = ''
+ return
+ elif namespace in interwiki:
+ url = interwiki[namespace]
+ namespace = ''
+ if namespace in namespaces:
+ if namespaces[namespace] == 6 and not force_link: # File
+ node.value = render_file(page_name, node.value)
+ return
+ elif namespaces[namespace] == 14 and not force_link: # Category
+ render_category(page_name)
+ node.value = ''
+ return
+ if namespace:
+ page_name = namespace + ':' + page_name
+ if len(node.value) == 0:
+ text = page_name
+ else:
+ text = '|'.join('%s' % item.leaf() for item in node.value[0])
+ node.value = '%s (link: %s%s)' % (text, url, page_name)
return locals()
-def make_parser():
- return wikitextParser.make_parser(toolset())
+def make_parser(interwiki={}, namespaces={}):
+ """Constructs the parser for the text backend.
+
+ :arg interwiki: List of the allowed interwiki prefixes (en, fr, es, commons, etc.)
+ :arg namespaces: List of the namespaces of the wiki (File, Category, Template, etc.),
+ including the localized version of those strings (Modele, Categorie, etc.),
+ associated to the corresponding namespace code.
+ """
+ tools = toolset(interwiki, namespaces)
+ return wikitextParser.make_parser(tools)
View
3 wikitext.txt
@@ -1,4 +1,5 @@
-Contenu soumis à la licence CC-BY-SA 3.0 (http://creativecommons.org/licenses/by-sa/3.0/deed.fr) Source : Article Berceuse de Wikipédia en français (http://fr.wikipedia.org/wiki/Berceuse).
+Contenu soumis à la licence CC-BY-SA 3.0 ([http://creativecommons.org/licenses/by-sa/3.0/deed.fr])<br />
+Source : Article Berceuse de Wikipédia en français ([http://fr.wikipedia.org/wiki/Berceuse]).
{{Voir homonymes|Berceuse (homonymie)}}
{{ébauche|musique}}

0 comments on commit 8bc6768

Please sign in to comment.