aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorThomas Touhey <thomas@touhey.fr>2019-06-25 14:49:22 +0200
committerThomas Touhey <thomas@touhey.fr>2019-06-25 14:49:22 +0200
commitf1f1be481a4022f394aadb0d9f0935b4c07d8ef3 (patch)
treed1f55753db85b380ae31a8a8087d450d08ac1552
parent0df1699c2748020cfd486aee22dc7d89002c9ef9 (diff)
Latest changes (and mirror testing)
-rw-r--r--LICENSE.txt2
-rwxr-xr-xMakefile2
-rw-r--r--Pipfile3
-rw-r--r--Pipfile.lock66
-rw-r--r--README.rst33
-rw-r--r--TODO.rst15
-rw-r--r--docs/conf.py38
-rw-r--r--setup.cfg6
-rwxr-xr-xtest/html.py232
-rwxr-xr-xtest/htmli.py55
-rwxr-xr-xtest/ls.py47
-rwxr-xr-xtests/__init__.py (renamed from test/__init__.py)0
-rwxr-xr-xtests/test_html.py230
-rwxr-xr-xtests/test_htmli.py30
-rwxr-xr-xtests/test_ls.py21
-rwxr-xr-xtextoutpc/_stream.py2
16 files changed, 409 insertions, 373 deletions
diff --git a/LICENSE.txt b/LICENSE.txt
index 45ccebe..d1defab 100644
--- a/LICENSE.txt
+++ b/LICENSE.txt
@@ -1,6 +1,6 @@
The MIT License (MIT)
-Copyright (C) 2018 Thomas Touhey <thomas@touhey.fr>
+Copyright (C) 2018-2019 Thomas Touhey <thomas@touhey.fr>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the “Software”), to deal
diff --git a/Makefile b/Makefile
index 598e30d..3d840bc 100755
--- a/Makefile
+++ b/Makefile
@@ -4,7 +4,7 @@
DNAME := dist/$(shell $(ST) --name)-$(shell $(ST) --version).tar.gz
test tests:
- @$(ST) test
+ @$(PE) pytest -s -q
prepare:
@pipenv install --dev
diff --git a/Pipfile b/Pipfile
index bbe8e01..686c863 100644
--- a/Pipfile
+++ b/Pipfile
@@ -8,9 +8,10 @@ python_version = '3.7'
[packages]
regex = '*'
-thcolor = { git = 'https://forge.touhey.org/thcolor.git' }
+thcolor = '*'
[dev-packages]
sphinx = '*'
"collective.checkdocs" = '*'
pudb = '*'
+pytest = '*'
diff --git a/Pipfile.lock b/Pipfile.lock
index 31cb1f4..3a3f5e8 100644
--- a/Pipfile.lock
+++ b/Pipfile.lock
@@ -1,7 +1,7 @@
{
"_meta": {
"hash": {
- "sha256": "6505cb754ffede68e416ce5cf702dddf8f956a3181a4d58924aff57847b29ee9"
+ "sha256": "f2cea3a09ae48de290ab5aa3af45cedaf158f9586a8f84c50c1825de25732522"
},
"pipfile-spec": 6,
"requires": {
@@ -32,8 +32,11 @@
"version": "==2019.4.14"
},
"thcolor": {
- "git": "https://forge.touhey.org/thcolor.git",
- "ref": "be32e9875ea503dd80bc00abaff12727fdb6c645"
+ "hashes": [
+ "sha256:a93a535f7f81b5e38460531b5731f0fa8c4b30b2a63d1fe763c2ac15e37df8b7"
+ ],
+ "index": "pypi",
+ "version": "==0.3.1"
}
},
"develop": {
@@ -44,6 +47,20 @@
],
"version": "==0.7.12"
},
+ "atomicwrites": {
+ "hashes": [
+ "sha256:03472c30eb2c5d1ba9227e4c2ca66ab8287fbfbbda3888aa93dc2e28fc6811b4",
+ "sha256:75a9445bac02d8d058d5e1fe689654ba5a6556a1dfd8ce6ec55a0ed79866cfa6"
+ ],
+ "version": "==1.3.0"
+ },
+ "attrs": {
+ "hashes": [
+ "sha256:69c0dbf2ed392de1cb5ec704444b08a5ef81680a61cb899dc08127123af36a79",
+ "sha256:f0b870f674851ecbfbbbd364d6b5cbdff9dcedbc7f3f5e18a6891057f21fe399"
+ ],
+ "version": "==19.1.0"
+ },
"babel": {
"hashes": [
"sha256:6778d85147d5d85345c14a26aada5e478ab04e39b078b0745ee6870c2b5cf669",
@@ -134,6 +151,14 @@
],
"version": "==1.1.1"
},
+ "more-itertools": {
+ "hashes": [
+ "sha256:2112d2ca570bb7c3e53ea1a35cd5df42bb0fd10c45f0fb97178679c3c03d64c7",
+ "sha256:c3e4748ba1aad8dba30a4886b0b1a2004f9a863837b8654e7059eebf727afa5a"
+ ],
+ "markers": "python_version > '2.7'",
+ "version": "==7.0.0"
+ },
"packaging": {
"hashes": [
"sha256:0c98a5d0be38ed775798ece1b9727178c4469d9c3b4ada66e8e6b7849f8732af",
@@ -141,6 +166,13 @@
],
"version": "==19.0"
},
+ "pluggy": {
+ "hashes": [
+ "sha256:25a1bc1d148c9a640211872b4ff859878d422bccb59c9965e04eed468a0aa180",
+ "sha256:964cedd2b27c492fbf0b7f58b3284a09cf7f99b0f715941fb24a439b3af1bd1a"
+ ],
+ "version": "==0.11.0"
+ },
"pudb": {
"hashes": [
"sha256:ac30cfc64580958ab7265decb4cabb9141f08781ff072e9a336d5a7942ce35a6"
@@ -148,12 +180,19 @@
"index": "pypi",
"version": "==2019.1"
},
+ "py": {
+ "hashes": [
+ "sha256:64f65755aee5b381cea27766a3a147c3f15b9b6b9ac88676de66ba2ae36793fa",
+ "sha256:dc639b046a6e2cff5bbe40194ad65936d6ba360b52b3c3fe1d08a82dd50b5e53"
+ ],
+ "version": "==1.8.0"
+ },
"pygments": {
"hashes": [
- "sha256:5ffada19f6203563680669ee7f53b64dabbeb100eb51b61996085e99c03b284a",
- "sha256:e8218dd399a61674745138520d0d4cf2621d7e032439341bc3f647bff125818d"
+ "sha256:31cba6ffb739f099a85e243eff8cb717089fdd3c7300767d9fc34cb8e1b065f5",
+ "sha256:5ad302949b3c98dd73f8d9fcdc7e9cb592f120e32a18e23efd7f3dc51194472b"
],
- "version": "==2.3.1"
+ "version": "==2.4.0"
},
"pyparsing": {
"hashes": [
@@ -162,6 +201,14 @@
],
"version": "==2.4.0"
},
+ "pytest": {
+ "hashes": [
+ "sha256:1a8aa4fa958f8f451ac5441f3ac130d9fc86ea38780dd2715e6d5c5882700b24",
+ "sha256:b8bf138592384bd4e87338cb0f256bf5f615398a649d4bd83915f0e4047a5ca6"
+ ],
+ "index": "pypi",
+ "version": "==4.5.0"
+ },
"pytz": {
"hashes": [
"sha256:303879e36b721603cc54604edcac9d20401bdbe31e1e4fdee5b9f98d5d31dfda",
@@ -252,6 +299,13 @@
"sha256:644d3e3900867161a2fc9287a9762753d66bd194754679adb26aede559bcccbc"
],
"version": "==2.0.1"
+ },
+ "wcwidth": {
+ "hashes": [
+ "sha256:3df37372226d6e63e1b1e1eda15c594bca98a22d33a23832a90998faa96bc65e",
+ "sha256:f4ebe71925af7b40a864553f761ed559b43544f8f71746c2d756c7fe788ade7c"
+ ],
+ "version": "==0.1.7"
}
}
}
diff --git a/README.rst b/README.rst
index c3fa6fd..8c3e159 100644
--- a/README.rst
+++ b/README.rst
@@ -7,27 +7,28 @@ documentation accessible on `the official website`_.
.. warning::
- If you are accessing this repository from `Planète Casio's forge`_,
+ If you are accessing this repository from another forge (such as
+ `Planète Casio's forge <https://gitea.planet-casio.com/>`_),
keep in mind that it is only a mirror and that the real repository
is located `in my forge <Thomas' forge_>`_ for now.
-What is left to do
-------------------
+For example, if we want to translate some basic multi-block text to HTML:
-- Add an ``[imgurl]`` tag?
-- Manage lightscript (or even markdown?) as output languages;
-- Check where the errors are to display them to the user:
+.. code-block:: python
- * Count character offset, line number and column number in the lexer;
- * Produce readable exceptions;
- * Make a clean interface to transmit them;
-- Check why exceptions on raw tags effectively escape the content, as it
- shouldn't…?
-- Look for security flaws (we really don't want stored XSS flaws!).
-- Implement match names (such as ``\[\*+\]`` for lists).
-- Manage keywords with tags such as ``[tag key=value other="something else"]``.
+ from textoutpc import tohtml
+
+ text = """[img=center]https://www.planet-casio.com/skins/bootstrap/img/default/logo.png[/img]
+
+ Hello [color=R10]world[/color]!
+ [ul]
+ [*]This module is made by [url=https://thomas.touhey.fr/]Thomas Touhey[/url]!
+ [*]Use `.tohtml()` to translate magically to HTML!
+ [/]
+ """
+
+ print(tohtml(text))
.. _Planète Casio: https://www.planet-casio.com/
-.. _Planète Casio's forge: https://gitea.planet-casio.com/
.. _Thomas' forge: https://forge.touhey.fr/pc/textout.git
-.. _the official website: https://textout.touhey.fr/
+.. _the official website: https://textout.touhey.pro/
diff --git a/TODO.rst b/TODO.rst
new file mode 100644
index 0000000..6e9a6fa
--- /dev/null
+++ b/TODO.rst
@@ -0,0 +1,15 @@
+What is left to do
+==================
+
+- Add an ``[imgurl]`` tag?
+- Manage lightscript (or even markdown?) as output languages;
+- Check where the errors are to display them to the user:
+
+ * Count character offset, line number and column number in the lexer;
+ * Produce readable exceptions;
+ * Make a clean interface to transmit them;
+- Check why exceptions on raw tags effectively escape the content, as it
+ shouldn't…?
+- Look for security flaws (we really don't want stored XSS flaws!).
+- Implement match names (such as ``\[\*+\]`` for lists).
+- Manage keywords with tags such as ``[tag key=value other="something else"]``.
diff --git a/docs/conf.py b/docs/conf.py
index 5483889..2b15153 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -11,22 +11,33 @@
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
-#
-# import os
-# import sys
-# sys.path.insert(0, os.path.abspath('.'))
+
+def _add_paths():
+ import os
+ import sys
+
+ sys.path.insert(0, os.path.abspath('..'))
+
+_add_paths()
# -- Project information -----------------------------------------------------
project = 'textoutpc'
-copyright = '2018, Thomas Touhey'
+copyright = '2019, Thomas Touhey'
author = 'Thomas Touhey'
-# The short X.Y version
-version = ''
# The full version, including alpha/beta/rc tags
-release = ''
+
+def _get_release():
+ from os.path import dirname, join
+ from pkg_resources import find_distributions as find_dist
+
+ module_path = join(dirname(__file__), '..')
+ dist = next(find_dist(module_path, True))
+ return dist.version
+
+release = _get_release()
# -- General configuration ---------------------------------------------------
@@ -39,10 +50,11 @@ release = ''
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
+ 'sphinx.ext.autodoc'
]
# Add any paths that contain templates here, relative to this directory.
-templates_path = ['_templates']
+templates_path = []
# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
@@ -63,7 +75,7 @@ language = None
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path .
-exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
+exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store', '.goutput*']
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
@@ -74,7 +86,7 @@ pygments_style = 'sphinx'
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
-html_theme = 'alabaster'
+html_theme = 'sphinx_rtd_theme'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
@@ -85,7 +97,7 @@ html_theme = 'alabaster'
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
-html_static_path = ['_static']
+html_static_path = []
# Custom sidebar templates, must be a dictionary that maps document names
# to template names.
@@ -152,4 +164,4 @@ texinfo_documents = [
(master_doc, 'textoutpc', 'textoutpc Documentation',
author, 'textoutpc', 'One line description of project.',
'Miscellaneous'),
-] \ No newline at end of file
+]
diff --git a/setup.cfg b/setup.cfg
index ba87393..e3a14dd 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -2,6 +2,8 @@
name = textoutpc
version = attr: textoutpc.version
url = https://textout.touhey.pro/
+project_urls =
+ Documentation = https://textout.touhey.pro/docs/
author = Thomas Touhey
author_email = thomas@touhey.fr
description = textout() equivalent from Planète Casio
@@ -42,3 +44,7 @@ universal = True
[flake8]
ignore = F401, F403, E128, E131, E241, E261, E265, E271, W191
exclude = .git, __pycache__, build, dist, docs/conf.py, test.py, test
+
+[tool:pytest]
+python_files = tests.py test_*.py *_tests.py
+testpaths = tests
diff --git a/test/html.py b/test/html.py
deleted file mode 100755
index 33f98b4..0000000
--- a/test/html.py
+++ /dev/null
@@ -1,232 +0,0 @@
-#!/usr/bin/env python3
-#******************************************************************************
-# Copyright (C) 2018 Thomas "Cakeisalie5" Touhey <thomas@touhey.fr>
-# This file is part of the textoutpc project, which is MIT-licensed.
-#******************************************************************************
-""" Unit tests for the Python version of textout.
- Uses the builtin `unittest` module.
-"""
-
-import unittest as _unittest
-
-__all__ = ["TextoutHTMLTest"]
-
-# Define the tests.
-
-__test_cases = {
- # Basic text.
- '': '',
- 'lol': '<p>lol</p>',
- '<script>alert(1);</script>': \
- '<p>&lt;script&gt;alert(1);&lt;/script&gt;</p>',
-
- # Other tests. (?)
- '[a][c][/a]': '<p>[a][c][/a]</p>',
- '[a][a]': '<p>[a][a]</p>',
- "[<>]><[/<>]": "<p>[&lt;&gt;]&gt;&lt;[/&lt;&gt;]</p>",
-
- # Autolinking.
- '(http://www.example.org/some-[damn-url]-(youknow))': \
- '<p>(<a href="http://www.example.org/some-[damn-url]-(youknow)">' \
- 'http://www.example.org/some-[damn-url]-(youknow)</a>)</p>',
- 'https://thomas.touhey.fr/, tu vois ?': \
- '<p><a href="https://thomas.touhey.fr/">https://thomas.touhey.fr/' \
- '</a>, tu vois ?</p>',
-
- # Basic text styling.
- '[u][b][a][i][/b]': "<p><u><b>[a]</b></u></p>",
- '[u][b]a[/]mdr': '<p><u><b>a</b>mdr</u></p>',
-
- # Blocks, alignment.
- '[left]': '',
- '[left]lol[/]hi': '<div class="align-left"><p>lol</p></div><p>hi</p>',
- 'a[justify]b': '<p>a</p><div class="align-justify"><p>b</p></div>',
- 'a[i]': '<p>a</p>',
- 'a[i][justify]b': '<p>a</p>' \
- '<div class="align-justify"><p><i>b</i></p></div>',
- 'a[i]k[center]b': '<p>a<i>k</i></p>' \
- '<div class="align-center"><p><i>b</i></p></div>',
- 'a[i]k[center][b]b[justify]c[/center]d[/]wouhou': \
- '<p>a<i>k</i></p>' \
- '<div class="align-center"><p><i><b>b</b></i></p>' \
- '<div class="align-justify"><p><i><b>c</b></i></p></div></div>' \
- '<p><i>d</i>wouhou</p>',
-
- # Show tag for super preprocessing blocks.
- '[show]lol': '<p><span class="inline-code">lol</span></p>',
- '[quote][show][justify]hehe': \
- '<div class="citation"><p><span class="inline-code">' \
- '&lt;div class=&quot;align-justify&quot;&gt;' \
- '&lt;p&gt;hehe&lt;/p&gt;&lt;/div&gt;' \
- '</span></p></div>',
-
- # Titles.
- 'lolk[title]smth': '<p>lolk</p>' '<h1 class="title">smth</h1>',
- '[subtitle]<>': '<h2 class="subtitle">&lt;&gt;</h2>',
-
- # Fonts.
- '[arial]test': '<p><span style="font-family: arial">test</span></p>',
- '[font=mono]stereo': \
- '<p><span style="font-family: monospace">stereo</span></p>',
- '[haettenschweiler]': '',
- '[font=hello]yea': '<p>[font=hello]yea</p>',
-
- # Color.
- 'yea[color=blue]dabadee': \
- '<p>yea<span style="color: #0000FF">dabadee</span></p>',
- '[color=#12345F]a': '<p><span style="color: #12345F">a</span></p>',
- '[color=#123]a': '<p><span style="color: #112233">a</span></p>',
- '[color=123]a': '<p><span style="color: #010203">a</span></p>',
- '[color=chucknorris]a': '<p><span style="color: #C00000">a</span></p>',
- '[color=rgb(1, 22,242)]a': '<p><span style="color: #0116F2">a</span></p>',
- '[color= rgb (1,22, 242 , 50.0% )]a': '<p><span style="color: #0116F2; ' \
- 'color: rgba(1, 22, 242, 0.5)">a</span></p>',
- '[color=rgba(1,22,242,0.500)]a': '<p><span style="color: #0116F2; ' \
- 'color: rgba(1, 22, 242, 0.5)">a</span></p>',
- '[color=rbga(5, 7)]b': '<p><span style="color: #050007">b</span></p>',
- '[color=hsl(0, 1,50.0%)]r': '<p><span style="color: #FF0000">r</span></p>',
- # TODO: hls, hwb
-
- # Links.
- '[url]': '<p>[url]</p>',
- '[url=https://thomas.touhey.fr/]mon profil est le meilleur[/url]':
- '<p><a href="https://thomas.touhey.fr/">mon profil est le meilleur' \
- '</a></p>',
- '[url=https://thomas.touhey.fr/]': \
- '<p><a href="https://thomas.touhey.fr/">https://thomas.touhey.fr/' \
- '</a></p>',
- '[url=http://hey.org/lol[]>"a]': '<p><a href="http://hey.org/lol[]&gt;' \
- '&quot;a">' \
- 'http://hey.org/lol[]&gt;&quot;a</a></p>',
- '[url]javascript:alert(1)[/url]': '<p>[url]javascript:alert(1)[/url]</p>',
- '[url]<script>alert(1);</script>[/url]': \
- '<p>[url]&lt;script&gt;alert(1);&lt;/script&gt;[/url]</p>',
-
- '[profil]cake[/profil]': \
- '<p><a href="https://www.planet-casio.com/Fr/compte/voir_profil.php' \
- '?membre=cake">cake</a></p>',
- '[profile]ekac': \
- '<p><a href="https://www.planet-casio.com/Fr/compte/voir_profil.php' \
- '?membre=ekac">ekac</a></p>',
-
- # Quotes.
- '[quote]': '',
- '[quote]a': \
- '<div class="citation"><p>a</p></div>',
- '[quote=Test 1 :)]lel[/quote]': \
- '<div class="citation"><p><b>Test 1 ' \
- '<img src="/images/smileys/smile.gif"> a écrit :</b></p><p>' \
- 'lel</p></div>',
-
- # Spoilers.
- '[spoiler]': '',
- '[spoiler=Hello|world> :D]Close this, quick![/spoiler]': \
- '<div class="spoiler"><div class="title on" ' \
- 'onclick="toggleSpoiler(this.parentNode, ' "'open'" ');"><p>Hello' \
- '</p></div><div class="title off" ' \
- 'onclick="toggleSpoiler(this.parentNode, ' "'close'" ');"><p>world' \
- '&gt; <img src="/images/smileys/grin.gif"></p></div>' \
- '<div class="off"><p>Close this, quick!</p></div></div>',
-
- # Code.
- '[code]': '',
- "`[code]`": '<p><span class="inline-code">[code]</span></p>',
-
- '[inlinecode]': '',
- "[inlinecode]`[/inlinecode]": '<p><span class="inline-code">`</span></p>',
-
- "[b]a[noeval]b[/b]c[/noeval]d": "<p><b>ab[/b]cd</b></p>",
- "a[noeval]b[noeval]c[/noeval]d[/noeval]e": "<p>ab[noeval]c[/noeval]de</p>",
- "[noeval]``[/noeval]": "<p>``</p>",
- '[noeval]<>[/noeval]': '<p>&lt;&gt;</p>',
-
- # Pictures.
- '[img]': '<p>[img]</p>',
- '[img]"incroyable<>"[/img]': \
- '<p>[img]&quot;incroyable&lt;&gt;&quot;[/img]</p>',
- '[img=right|float|12x345]https://example.org/image.png': \
- '<img src="https://example.org/image.png" class="img-float-right" ' \
- 'style="width: 12px; height: 345px" />',
-
- # Videos.
- '[video]"><script>alert(1)</script>[/video]': \
- '<p>[video]&quot;&gt;&lt;script&gt;alert(1)&lt;/script&gt;' \
- '[/video]</p>',
- '[video]<script>alert(document.cookie)</script>[/video]': \
- '<p>[video]&lt;script&gt;alert(document.cookie)&lt;/script&gt;' \
- '[/video]</p>',
- '[video]https://www.youtube.com/watch?v=6odDOOyUawY[/video]': \
- '<div class="video-wrapper" style="padding-bottom: 56.25%"><iframe ' \
- 'src="https://www.youtube.com/embed/6odDOOyUawY" ' \
- 'frameborder="0" allowfullscreen></iframe></div>',
- '[video]https://www.youtube.com/watch?v=<script>alert(1)</script>': \
- '<p><a href="https://www.youtube.com/watch?v=&lt;script&gt;alert(1)' \
- '&lt;/script&gt;">' \
- 'https://www.youtube.com/watch?v=&lt;script&gt;alert(1)' \
- '&lt;/script&gt;</a></p>',
- '[video=left|float|4:3]https://www.youtube.com/watch?v=XEjLoHdbVeE': \
- '<div class="video-wrapper img-float-left" ' \
- 'style="padding-bottom: 75%"><iframe ' \
- 'src="https://www.youtube.com/embed/XEjLoHdbVeE" frameborder="0" ' \
- 'allowfullscreen></iframe></div>',
- 'lol[youtube]h4WLX8hfpJw': '<p>lol</p><div class="video-wrapper" ' \
- 'style="padding-bottom: 56.25%"><iframe ' \
- 'src="https://www.youtube.com/embed/h4WLX8hfpJw" frameborder="0" ' \
- 'allowfullscreen></iframe></div>',
-
- '[color=blue][youtube]h4WLX8hfpJw': \
- '<div class="video-wrapper" style="padding-bottom: 56.25%">' \
- '<iframe src="https://www.youtube.com/embed/h4WLX8hfpJw" ' \
- 'frameborder="0" allowfullscreen></iframe></div>',
- '[color=blue]oh[youtube]h4WLX8hfpJw': \
- '<p><span style="color: #0000FF">oh</span></p>' \
- '<div class="video-wrapper" style="padding-bottom: 56.25%"><iframe ' \
- 'src="https://www.youtube.com/embed/h4WLX8hfpJw" frameborder="0" ' \
- 'allowfullscreen></iframe></div>',
-
- # Progress bars.
- '[progress=lol]mdr[/progress]': '<p>[progress=lol]mdr[/progress]</p>',
-
- # Text rotation obfuscation.
- '[rot13]obawbhe[/rot13]': '<p>bonjour</p>',
-
- # Lists.
- '[list]haha[b][*]wow[*]incredible[/b][/*]wow[*]yuy[/list]': \
- '<ul><li><p>wow</p></li><li><p>incredible[/b]</p></li>' \
- '<li><p>yuy</p></li></ul>',
- '[list]\n[*]bonjour': '<ul><li><p>bonjour</p></li></ul>',
-
- # Smileys.
- ':)': '<p><img src="/images/smileys/smile.gif"></p>',
- ':):)': '<p>:):)</p>',
- ':) :D': '<p><img src="/images/smileys/smile.gif"> ' \
- '<img src="/images/smileys/grin.gif"></p>',
-}
-
-# Define the tests wrapper, and define the classes.
-
-_cnt = 0
-_len = len(str(len(__test_cases)))
-_templ = """\
- def test_html{n:0>{l}}(self):
- import textoutpc as _textoutpc
- self.assertEqual(_textoutpc.tohtml({i}), {r}, \\
- "for the following text: " + {i})
-"""
-
-def _wrap_test(inp, res):
- global _cnt
-
- _cnt += 1
- return _templ.format(n = _cnt, l = _len, i = repr(inp), r = repr(res))
-
-exec("class TextoutHTMLTest(_unittest.TestCase):\n maxDiff = None\n" + \
- '\n'.join(map(lambda args: _wrap_test(*args), __test_cases.items())),
- globals())
-
-# If run as main script, run the test function.
-
-if __name__ == '__main__':
- _unittest.main()
-
-# End of file.
diff --git a/test/htmli.py b/test/htmli.py
deleted file mode 100755
index f308f01..0000000
--- a/test/htmli.py
+++ /dev/null
@@ -1,55 +0,0 @@
-#!/usr/bin/env python3
-#******************************************************************************
-# Copyright (C) 2018 Thomas "Cakeisalie5" Touhey <thomas@touhey.fr>
-# This file is part of the textoutpc project, which is MIT-licensed.
-#******************************************************************************
-""" Unit tests for the Python version of textout.
- Uses the builtin `unittest` module.
-"""
-
-import unittest as _unittest
-
-__all__ = ["TextoutHTMLiTest"]
-
-# Define the tests.
-
-__test_cases = {
- # Basic text.
- '': '',
- 'lol': 'lol',
-
- # Basic text styling.
- '[u][b]a[/]mdr': '<u><b>a</b>mdr</u>',
-
- # Links.
- '[url=https://thomas.touhey.fr/]': '<a href="https://thomas.touhey.fr/">' \
- 'https://thomas.touhey.fr/</a>',
-}
-
-# Define the tests wrapper, and define the classes.
-
-_cnt = 0
-_len = len(str(len(__test_cases)))
-_templ = """\
- def test_htmli{n:0>{l}}(self):
- import textoutpc as _textoutpc
- self.assertEqual(_textoutpc.tohtml({i}, inline = True), {r}, \\
- "for the following text: " + {i})
-"""
-
-def _wrap_test(inp, res):
- global _cnt
-
- _cnt += 1
- return _templ.format(n = _cnt, l = _len, i = repr(inp), r = repr(res))
-
-exec("class _TextoutHTMLiTest(_unittest.TestCase):\n maxDiff = None\n" + \
- '\n'.join(map(lambda args: _wrap_test(*args), __test_cases.items())),
- globals())
-
-# If run as main script, run the test function.
-
-if __name__ == '__main__':
- _unittest.main()
-
-# End of file.
diff --git a/test/ls.py b/test/ls.py
deleted file mode 100755
index e4186de..0000000
--- a/test/ls.py
+++ /dev/null
@@ -1,47 +0,0 @@
-#!/usr/bin/env python3
-#******************************************************************************
-# Copyright (C) 2018 Thomas "Cakeisalie5" Touhey <thomas@touhey.fr>
-# This file is part of the textoutpc project, which is MIT-licensed.
-#******************************************************************************
-""" Unit tests for the Python version of textout, lightscript-related funcs.
- Uses the builtin `unittest` module.
-"""
-
-import unittest as _unittest
-
-__all__ = ["TextoutLSTest"]
-
-# Define the tests.
-
-__test_cases = {
- # Basic text.
-
- '': '',
-}
-
-# Define the tests wrapper, and define the classes.
-
-_cnt = 0
-_len = len(str(len(__test_cases)))
-_templ = """\
- def test_lgsp{n:0>{l}}(self):
- import textoutpc as _textoutpc
- self.assertEqual(_textoutpc.tolightscript({i}), {r}, {i})
-"""
-
-def _wrap_test(inp, res):
- global _cnt
-
- _cnt += 1
- return _templ.format(n = _cnt, l = _len, i = repr(inp), r = repr(res))
-
-exec("class TextoutLSTest(_unittest.TestCase):\n maxDiff = None\n" + \
- '\n'.join(map(lambda args: _wrap_test(*args), __test_cases.items())),
- globals())
-
-# If run as main script, run the test function.
-
-if __name__ == '__main__':
- _unittest.main()
-
-# End of file.
diff --git a/test/__init__.py b/tests/__init__.py
index e969492..e969492 100755
--- a/test/__init__.py
+++ b/tests/__init__.py
diff --git a/tests/test_html.py b/tests/test_html.py
new file mode 100755
index 0000000..51618c9
--- /dev/null
+++ b/tests/test_html.py
@@ -0,0 +1,230 @@
+#!/usr/bin/env python3
+#******************************************************************************
+# Copyright (C) 2018 Thomas "Cakeisalie5" Touhey <thomas@touhey.fr>
+# This file is part of the textoutpc project, which is MIT-licensed.
+#******************************************************************************
+""" Unit tests for the Python version of textout. """
+
+import pytest
+
+from textoutpc import tohtml as _tohtml
+
+# Define the tests.
+
+@pytest.mark.parametrize('test_input,expected', (
+ # Basic text.
+
+ ('', ''),
+ ('lol', '<p>lol</p>'),
+ ('<script>alert(1);</script>',
+ '<p>&lt;script&gt;alert(1);&lt;/script&gt;</p>'),
+
+ # Other tests. (?)
+
+ ('[a][c][/a]', '<p>[a][c][/a]</p>'),
+ ('[a][a]', '<p>[a][a]</p>'),
+ ("[<>]><[/<>]", "<p>[&lt;&gt;]&gt;&lt;[/&lt;&gt;]</p>"),
+
+ # Autolinking.
+
+ ('(http://www.example.org/some-[damn-url]-(youknow))',
+ '<p>(<a href="http://www.example.org/some-[damn-url]-(youknow)">' \
+ 'http://www.example.org/some-[damn-url]-(youknow)</a>)</p>'),
+ ('https://thomas.touhey.fr/, tu vois ?',
+ '<p><a href="https://thomas.touhey.fr/">https://thomas.touhey.fr/' \
+ '</a>, tu vois ?</p>'),
+
+ # Basic text styling.
+
+ ('[u][b][a][i][/b]', "<p><u><b>[a]</b></u></p>"),
+ ('[u][b]a[/]mdr', '<p><u><b>a</b>mdr</u></p>'),
+
+ # Blocks, alignment.
+
+ ('[left]', ''),
+ ('[left]lol[/]hi', '<div class="align-left"><p>lol</p></div><p>hi</p>'),
+ ('a[justify]b', '<p>a</p><div class="align-justify"><p>b</p></div>'),
+ ('a[i]', '<p>a</p>'),
+ ('a[i][justify]b', '<p>a</p>' \
+ '<div class="align-justify"><p><i>b</i></p></div>'),
+ ('a[i]k[center]b', '<p>a<i>k</i></p>' \
+ '<div class="align-center"><p><i>b</i></p></div>'),
+ ('a[i]k[center][b]b[justify]c[/center]d[/]wouhou',
+ '<p>a<i>k</i></p>' \
+ '<div class="align-center"><p><i><b>b</b></i></p>' \
+ '<div class="align-justify"><p><i><b>c</b></i></p></div></div>' \
+ '<p><i>d</i>wouhou</p>'),
+
+ # Show tag for super preprocessing blocks.
+
+ ('[show]lol', '<p><span class="inline-code">lol</span></p>'),
+ ('[quote][show][justify]hehe',
+ '<div class="citation"><p><span class="inline-code">' \
+ '&lt;div class=&quot;align-justify&quot;&gt;' \
+ '&lt;p&gt;hehe&lt;/p&gt;&lt;/div&gt;' \
+ '</span></p></div>'),
+
+ # Titles.
+
+ ('lolk[title]smth', '<p>lolk</p>' '<h1 class="title">smth</h1>'),
+ ('[subtitle]<>', '<h2 class="subtitle">&lt;&gt;</h2>'),
+
+ # Fonts.
+
+ ('[arial]test', '<p><span style="font-family: arial">test</span></p>'),
+ ('[font=mono]stereo',
+ '<p><span style="font-family: monospace">stereo</span></p>'),
+ ('[haettenschweiler]', ''),
+ ('[font=hello]yea', '<p>[font=hello]yea</p>'),
+
+ # Color.
+
+ ('yea[color=blue]dabadee', \
+ '<p>yea<span style="color: #0000FF">dabadee</span></p>'),
+ ('[color=#12345F]a', '<p><span style="color: #12345F">a</span></p>'),
+ ('[color=#123]a', '<p><span style="color: #112233">a</span></p>'),
+ ('[color=123]a', '<p><span style="color: #010203">a</span></p>'),
+ ('[color=chucknorris]a', '<p><span style="color: #C00000">a</span></p>'),
+ ('[color=rgb(1, 22,242)]a',
+ '<p><span style="color: #0116F2">a</span></p>'),
+ ('[color= rgb (1,22, 242 , 50.0% )]a',
+ '<p><span style="color: #0116F2; ' \
+ 'color: rgba(1, 22, 242, 0.5)">a</span></p>'),
+ ('[color=rgba(1,22,242,0.500)]a', '<p><span style="color: #0116F2; ' \
+ 'color: rgba(1, 22, 242, 0.5)">a</span></p>'),
+ ('[color=rbga(5, 7)]b', '<p><span style="color: #050007">b</span></p>'),
+ ('[color=hsl(0, 1,50.0%)]r',
+ '<p><span style="color: #FF0000">r</span></p>'),
+ # TODO: hls, hwb
+
+ # Links.
+
+ ('[url]', '<p>[url]</p>'),
+ ('[url=https://thomas.touhey.fr/]mon profil est le meilleur[/url]',
+ '<p><a href="https://thomas.touhey.fr/">mon profil est le meilleur' \
+ '</a></p>'),
+ ('[url=https://thomas.touhey.fr/]',
+ '<p><a href="https://thomas.touhey.fr/">https://thomas.touhey.fr/' \
+ '</a></p>'),
+ ('[url=http://hey.org/lol[]>"a]', '<p><a href="http://hey.org/lol[]&gt;' \
+ '&quot;a">' 'http://hey.org/lol[]&gt;&quot;a</a></p>'),
+ ('[url]javascript:alert(1)[/url]',
+ '<p>[url]javascript:alert(1)[/url]</p>'),
+ ('[url]<script>alert(1);</script>[/url]',
+ '<p>[url]&lt;script&gt;alert(1);&lt;/script&gt;[/url]</p>'),
+
+ ('[profil]cake[/profil]',
+ '<p><a href="https://www.planet-casio.com/Fr/compte/voir_profil.php' \
+ '?membre=cake">cake</a></p>'),
+ ('[profile]ekac',
+ '<p><a href="https://www.planet-casio.com/Fr/compte/voir_profil.php' \
+ '?membre=ekac">ekac</a></p>'),
+
+ # Quotes.
+
+ ('[quote]', ''),
+ ('[quote]a',
+ '<div class="citation"><p>a</p></div>'),
+ ('[quote=Test 1 :)]lel[/quote]',
+ '<div class="citation"><p><b>Test 1 ' \
+ '<img src="/images/smileys/smile.gif"> a écrit :</b></p><p>' \
+ 'lel</p></div>'),
+
+ # Spoilers.
+
+ ('[spoiler]', ''),
+ ('[spoiler=Hello|world> :D]Close this, quick![/spoiler]',
+ '<div class="spoiler"><div class="title on" ' \
+ 'onclick="toggleSpoiler(this.parentNode, ' "'open'" ');"><p>Hello' \
+ '</p></div><div class="title off" ' \
+ 'onclick="toggleSpoiler(this.parentNode, ' "'close'" ');"><p>world' \
+ '&gt; <img src="/images/smileys/grin.gif"></p></div>' \
+ '<div class="off"><p>Close this, quick!</p></div></div>'),
+
+ # Code.
+
+ ('[code]', ''),
+ ("`[code]`", '<p><span class="inline-code">[code]</span></p>'),
+
+ ('[inlinecode]', ''),
+ ("[inlinecode]`[/inlinecode]",
+ '<p><span class="inline-code">`</span></p>'),
+
+ ("[b]a[noeval]b[/b]c[/noeval]d", "<p><b>ab[/b]cd</b></p>"),
+ ("a[noeval]b[noeval]c[/noeval]d[/noeval]e",
+ "<p>ab[noeval]c[/noeval]de</p>"),
+ ("[noeval]``[/noeval]", "<p>``</p>"),
+ ('[noeval]<>[/noeval]', '<p>&lt;&gt;</p>'),
+
+ # Pictures.
+
+ ('[img]', '<p>[img]</p>'),
+ ('[img]"incroyable<>"[/img]',
+ '<p>[img]&quot;incroyable&lt;&gt;&quot;[/img]</p>'),
+ ('[img=right|float|12x345]https://example.org/image.png',
+ '<img src="https://example.org/image.png" class="img-float-right" ' \
+ 'style="width: 12px; height: 345px" />'),
+
+ # Videos.
+
+ ('[video]"><script>alert(1)</script>[/video]',
+ '<p>[video]&quot;&gt;&lt;script&gt;alert(1)&lt;/script&gt;' \
+ '[/video]</p>'),
+ ('[video]<script>alert(document.cookie)</script>[/video]',
+ '<p>[video]&lt;script&gt;alert(document.cookie)&lt;/script&gt;' \
+ '[/video]</p>'),
+ ('[video]https://www.youtube.com/watch?v=6odDOOyUawY[/video]',
+ '<div class="video-wrapper" style="padding-bottom: 56.25%"><iframe ' \
+ 'src="https://www.youtube.com/embed/6odDOOyUawY" ' \
+ 'frameborder="0" allowfullscreen></iframe></div>'),
+ ('[video]https://www.youtube.com/watch?v=<script>alert(1)</script>',
+ '<p><a href="https://www.youtube.com/watch?v=&lt;script&gt;alert(1)' \
+ '&lt;/script&gt;">' \
+ 'https://www.youtube.com/watch?v=&lt;script&gt;alert(1)' \
+ '&lt;/script&gt;</a></p>'),
+ ('[video=left|float|4:3]https://www.youtube.com/watch?v=XEjLoHdbVeE',
+ '<div class="video-wrapper img-float-left" ' \
+ 'style="padding-bottom: 75%"><iframe ' \
+ 'src="https://www.youtube.com/embed/XEjLoHdbVeE" frameborder="0" ' \
+ 'allowfullscreen></iframe></div>'),
+ ('lol[youtube]h4WLX8hfpJw', '<p>lol</p><div class="video-wrapper" ' \
+ 'style="padding-bottom: 56.25%"><iframe ' \
+ 'src="https://www.youtube.com/embed/h4WLX8hfpJw" frameborder="0" ' \
+ 'allowfullscreen></iframe></div>'),
+
+ ('[color=blue][youtube]h4WLX8hfpJw',
+ '<div class="video-wrapper" style="padding-bottom: 56.25%">' \
+ '<iframe src="https://www.youtube.com/embed/h4WLX8hfpJw" ' \
+ 'frameborder="0" allowfullscreen></iframe></div>'),
+ ('[color=blue]oh[youtube]h4WLX8hfpJw',
+ '<p><span style="color: #0000FF">oh</span></p>' \
+ '<div class="video-wrapper" style="padding-bottom: 56.25%"><iframe ' \
+ 'src="https://www.youtube.com/embed/h4WLX8hfpJw" frameborder="0" ' \
+ 'allowfullscreen></iframe></div>'),
+
+ # Progress bars.
+
+ ('[progress=lol]mdr[/progress]', '<p>[progress=lol]mdr[/progress]</p>'),
+
+ # Text rotation obfuscation.
+
+ ('[rot13]obawbhe[/rot13]', '<p>bonjour</p>'),
+
+ # Lists.
+
+ ('[list]haha[b][*]wow[*]incredible[/b][/*]wow[*]yuy[/list]',
+ '<ul><li><p>wow</p></li><li><p>incredible[/b]</p></li>' \
+ '<li><p>yuy</p></li></ul>'),
+ ('[list]\n[*]bonjour', '<ul><li><p>bonjour</p></li></ul>'),
+
+ # Smileys.
+
+ (':)', '<p><img src="/images/smileys/smile.gif"></p>'),
+ (':):)', '<p>:):)</p>'),
+ (':) :D', '<p><img src="/images/smileys/smile.gif"> ' \
+ '<img src="/images/smileys/grin.gif"></p>'),
+))
+def test_html(test_input, expected):
+ assert _tohtml(test_input) == expected
+
+# End of file.
diff --git a/tests/test_htmli.py b/tests/test_htmli.py
new file mode 100755
index 0000000..9c13fea
--- /dev/null
+++ b/tests/test_htmli.py
@@ -0,0 +1,30 @@
+#!/usr/bin/env python3
+#******************************************************************************
+# Copyright (C) 2018 Thomas "Cakeisalie5" Touhey <thomas@touhey.fr>
+# This file is part of the textoutpc project, which is MIT-licensed.
+#******************************************************************************
+""" Unit tests for the Python version of textout. """
+
+import pytest
+
+from textoutpc import tohtml as _tohtml
+
+@pytest.mark.parametrize('test_input,expected', (
+ # Basic text.
+
+ ('', ''),
+ ('lol', 'lol'),
+
+ # Basic text styling.
+
+ ('[u][b]a[/]mdr', '<u><b>a</b>mdr</u>'),
+
+ # Links.
+
+ ('[url=https://thomas.touhey.fr/]',
+ '<a href="https://thomas.touhey.fr/">https://thomas.touhey.fr/</a>'),
+))
+def test_htmli(test_input, expected):
+ assert _tohtml(test_input, inline = True) == expected
+
+# End of file.
diff --git a/tests/test_ls.py b/tests/test_ls.py
new file mode 100755
index 0000000..5c9e9d8
--- /dev/null
+++ b/tests/test_ls.py
@@ -0,0 +1,21 @@
+#!/usr/bin/env python3
+#******************************************************************************
+# Copyright (C) 2018 Thomas "Cakeisalie5" Touhey <thomas@touhey.fr>
+# This file is part of the textoutpc project, which is MIT-licensed.
+#******************************************************************************
+""" Unit tests for the Python version of textout, lightscript-related
+ functions. """
+
+import pytest
+
+from textoutpc import tolightscript as _tolightscript
+
+@pytest.mark.parametrize('test_input,expected', (
+ # Basic text.
+
+ ('', ''),
+))
+def test_lightscript(test_input, expected):
+ assert _tolightscript(test_input) == expected
+
+# End of file.
diff --git a/textoutpc/_stream.py b/textoutpc/_stream.py
index ee51979..ab6e5a5 100755
--- a/textoutpc/_stream.py
+++ b/textoutpc/_stream.py
@@ -101,7 +101,7 @@ class TextoutStream:
#
# FIXME: check the sizes? it seems that it stopped working…
- _Tag = _re.compile("""\
+ _Tag = _re.compile(r"""
\[\s?
(?P<bname>
(?P<bname_e>[^\/\[\]\=][^\[\]\=]* (\[(?&bname_e)\]?)*)*