From 0f400c195666ebeeabcb656c1260e55790bf7856 Mon Sep 17 00:00:00 2001 From: Dennis Burke Date: Sun, 28 Nov 2021 12:52:43 -0500 Subject: [PATCH 001/228] first stab at using github actions instead of travis-ci --- .github/workflows/lint_and_test.yml | 28 ++++++++++++++++++++++++++++ .travis.yml | 23 ----------------------- 2 files changed, 28 insertions(+), 23 deletions(-) create mode 100644 .github/workflows/lint_and_test.yml delete mode 100644 .travis.yml diff --git a/.github/workflows/lint_and_test.yml b/.github/workflows/lint_and_test.yml new file mode 100644 index 0000000..f806d24 --- /dev/null +++ b/.github/workflows/lint_and_test.yml @@ -0,0 +1,28 @@ +--- +name: python-textile + +on: [push] + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: [3.5, 3.6, 3.7, 3.8, 3.9] + image-size: ['true', 'false'] + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + image-size: ${{ matrix.image-size }} + - name: Install dependencies + run: | + imagesize='' + pip install -U pytest pytest-cov coverage codecov + if [[ ${{ image-size }} == true ]] ; then imagesize='[imagesize]' ; fi + pip install -e ".${imagesize}" + - name: run tests + run: | + pytest diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 3f7d77c..0000000 --- a/.travis.yml +++ /dev/null @@ -1,23 +0,0 @@ -dist: xenial # required for Python >= 3.7 -language: python -env: - - IMAGESIZE=true - - IMAGESIZE=false -python: - - "3.5" - - "3.6" - - "3.7" - - "3.8" - - "3.9" - # PyPy versions - - "pypy3" -# command to install dependencies -install: - - imagesize='' - - pip install -U pytest pytest-cov coverage codecov - - if [[ $IMAGESIZE == true ]] ; then imagesize='[imagesize]' ; fi - - pip install -e ".${imagesize}" -# command to run tests -script: py.test -after_success: - - codecov From 8338fcbef6ae7998cd864459b069714d55b4291b Mon Sep 17 00:00:00 2001 From: Dennis Burke Date: Sun, 28 Nov 2021 12:55:08 -0500 Subject: [PATCH 002/228] maybe this fixes the job ??? --- .github/workflows/lint_and_test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint_and_test.yml b/.github/workflows/lint_and_test.yml index f806d24..e03c18a 100644 --- a/.github/workflows/lint_and_test.yml +++ b/.github/workflows/lint_and_test.yml @@ -21,7 +21,7 @@ jobs: run: | imagesize='' pip install -U pytest pytest-cov coverage codecov - if [[ ${{ image-size }} == true ]] ; then imagesize='[imagesize]' ; fi + if [[ ${{ matrix.image-size }} == true ]] ; then imagesize='[imagesize]' ; fi pip install -e ".${imagesize}" - name: run tests run: | From c2c42cd9ecd324725f55ceaa568930ce20bf5819 Mon Sep 17 00:00:00 2001 From: Dennis Burke Date: Sun, 28 Nov 2021 13:35:49 -0500 Subject: [PATCH 003/228] add codecov action to send data to codecov --- .github/workflows/lint_and_test.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/lint_and_test.yml b/.github/workflows/lint_and_test.yml index e03c18a..0e10375 100644 --- a/.github/workflows/lint_and_test.yml +++ b/.github/workflows/lint_and_test.yml @@ -26,3 +26,5 @@ jobs: - name: run tests run: | pytest + - name: Codecov + uses: codecov/codecov-action@v2.1.0 From 6704c90ccd170c29d2f2a02e759bd14d6b179da4 Mon Sep 17 00:00:00 2001 From: Dennis Burke Date: Sun, 28 Nov 2021 13:36:47 -0500 Subject: [PATCH 004/228] indentation fix --- .github/workflows/lint_and_test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint_and_test.yml b/.github/workflows/lint_and_test.yml index 0e10375..78bac9c 100644 --- a/.github/workflows/lint_and_test.yml +++ b/.github/workflows/lint_and_test.yml @@ -27,4 +27,4 @@ jobs: run: | pytest - name: Codecov - uses: codecov/codecov-action@v2.1.0 + uses: codecov/codecov-action@v2.1.0 From 5fc0780425a78b10e827c0bbdf74c752c4c05e74 Mon Sep 17 00:00:00 2001 From: Dennis Burke Date: Sun, 28 Nov 2021 14:03:29 -0500 Subject: [PATCH 005/228] add flake8 linting --- .github/workflows/lint_and_test.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/lint_and_test.yml b/.github/workflows/lint_and_test.yml index 78bac9c..cec0b42 100644 --- a/.github/workflows/lint_and_test.yml +++ b/.github/workflows/lint_and_test.yml @@ -17,6 +17,8 @@ jobs: with: python-version: ${{ matrix.python-version }} image-size: ${{ matrix.image-size }} + - name: Python flake8 Lint + uses: py-actions/flake8@v1.2.0 - name: Install dependencies run: | imagesize='' From 8102dad382a507ef8bab365b49cb21a7001cd6db Mon Sep 17 00:00:00 2001 From: Dennis Burke Date: Sun, 28 Nov 2021 14:11:03 -0500 Subject: [PATCH 006/228] maybe we're not quite ready for linting --- .github/workflows/lint_and_test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/lint_and_test.yml b/.github/workflows/lint_and_test.yml index cec0b42..ef6393c 100644 --- a/.github/workflows/lint_and_test.yml +++ b/.github/workflows/lint_and_test.yml @@ -17,8 +17,8 @@ jobs: with: python-version: ${{ matrix.python-version }} image-size: ${{ matrix.image-size }} - - name: Python flake8 Lint - uses: py-actions/flake8@v1.2.0 + #- name: Python flake8 Lint + # uses: py-actions/flake8@v1.2.0 - name: Install dependencies run: | imagesize='' From 0e50d699aa8b7e1733f99a46caab53fb3cccb806 Mon Sep 17 00:00:00 2001 From: Dennis Burke Date: Sun, 28 Nov 2021 14:11:35 -0500 Subject: [PATCH 007/228] testing out variables more like how we used to with travis --- .github/workflows/lint_and_test.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/lint_and_test.yml b/.github/workflows/lint_and_test.yml index ef6393c..16f06b2 100644 --- a/.github/workflows/lint_and_test.yml +++ b/.github/workflows/lint_and_test.yml @@ -21,9 +21,10 @@ jobs: # uses: py-actions/flake8@v1.2.0 - name: Install dependencies run: | - imagesize='' + imagesize='' + IMAGESIZE=${{ matrix.image-size }} pip install -U pytest pytest-cov coverage codecov - if [[ ${{ matrix.image-size }} == true ]] ; then imagesize='[imagesize]' ; fi + if [[ $IMAGESIZE == true ]] ; then imagesize='[imagesize]' ; fi pip install -e ".${imagesize}" - name: run tests run: | From 7e84e94f6f07733be13dcebc6db23017f90ed697 Mon Sep 17 00:00:00 2001 From: Dennis Burke Date: Sun, 28 Nov 2021 14:28:00 -0500 Subject: [PATCH 008/228] flake8: cleanup unused imports --- setup.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/setup.py b/setup.py index 118c2fb..80488d2 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,5 @@ from setuptools import setup, find_packages -from setuptools.command.test import test as TestCommand import os -import sys def get_version(): From c796c60a32a2a806629373a863223ce0bd71d160 Mon Sep 17 00:00:00 2001 From: Dennis Burke Date: Sun, 28 Nov 2021 14:30:08 -0500 Subject: [PATCH 009/228] flake8: variable cleanup --- tests/test_block.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_block.py b/tests/test_block.py index 44f3ea2..613bf41 100644 --- a/tests/test_block.py +++ b/tests/test_block.py @@ -21,7 +21,6 @@ def test_block(): assert result == expect b = Block(t, "bq", "", None, "http://google.com", "Hello BlockQuote") - citation = '{0}1:url'.format(t.uid) expect = ('blockquote', OrderedDict([('cite', '{0.uid}{0.refIndex}:url'.format(t))]), 'p', OrderedDict(), 'Hello BlockQuote') From 95c29703eb92c01e97e50f3282ae41077f8e6a86 Mon Sep 17 00:00:00 2001 From: Dennis Burke Date: Sun, 28 Nov 2021 15:30:38 -0500 Subject: [PATCH 010/228] add flake8 config file --- .flake8 | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 .flake8 diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..07c24d1 --- /dev/null +++ b/.flake8 @@ -0,0 +1,9 @@ +[flake8] +ignore = + # line too long + E501 + # temporarily ignore warnings and errors + W + E +exclude = + build/ From 9ef05332d372e7da3607145a7da6825cd660a3e9 Mon Sep 17 00:00:00 2001 From: Dennis Burke Date: Sun, 28 Nov 2021 15:31:19 -0500 Subject: [PATCH 011/228] flake8: inline ignore unused variable --- tests/test_textile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_textile.py b/tests/test_textile.py index 0c37690..cc4ce84 100644 --- a/tests/test_textile.py +++ b/tests/test_textile.py @@ -121,7 +121,7 @@ def test_sanitize(): assert result == expect def test_imagesize(): - PIL = pytest.importorskip('PIL') + PIL = pytest.importorskip('PIL') # noqa: F841 test = "!http://www.google.com/intl/en_ALL/images/srpr/logo1w.png!" result = '\t

' From 3159a32f5bc633a6d42ad81eb359fa4bae2b0450 Mon Sep 17 00:00:00 2001 From: Dennis Burke Date: Sun, 28 Nov 2021 15:35:19 -0500 Subject: [PATCH 012/228] flake8: remove unused import --- tests/test_urls.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_urls.py b/tests/test_urls.py index 7a9798e..39ff163 100644 --- a/tests/test_urls.py +++ b/tests/test_urls.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- from textile import Textile -import re def test_urls(): t = Textile() From 3aa0de7d8e4c2ca35161b78362813f992b0c4513 Mon Sep 17 00:00:00 2001 From: Dennis Burke Date: Sun, 28 Nov 2021 15:41:44 -0500 Subject: [PATCH 013/228] flake8: remove unused import --- tests/test_attributes.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_attributes.py b/tests/test_attributes.py index 70da842..0f5d019 100644 --- a/tests/test_attributes.py +++ b/tests/test_attributes.py @@ -1,5 +1,4 @@ from textile.utils import parse_attributes -import re def test_parse_attributes(): assert parse_attributes('\\1', element='td') == {'colspan': '1'} From 2283c8f34e3d54fffbeab884e4f168a58de4fbc3 Mon Sep 17 00:00:00 2001 From: Dennis Burke Date: Sun, 28 Nov 2021 15:44:42 -0500 Subject: [PATCH 014/228] flake8: inline ignore unused import --- tests/test_imagesize.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_imagesize.py b/tests/test_imagesize.py index 112989e..0a89b43 100644 --- a/tests/test_imagesize.py +++ b/tests/test_imagesize.py @@ -4,7 +4,7 @@ def test_imagesize(): imgurl = 'http://www.google.com/intl/en_ALL/images/srpr/logo1w.png' result = textile.tools.imagesize.getimagesize(imgurl) try: - import PIL + import PIL # noqa: F401 expect = (275, 95) assert result == expect From 4804417121de191571429f5ca380e924617c6766 Mon Sep 17 00:00:00 2001 From: Dennis Burke Date: Sun, 28 Nov 2021 15:45:45 -0500 Subject: [PATCH 015/228] flake8: remove unused import --- tests/test_footnoteRef.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_footnoteRef.py b/tests/test_footnoteRef.py index b773ad2..c973ee7 100644 --- a/tests/test_footnoteRef.py +++ b/tests/test_footnoteRef.py @@ -1,5 +1,4 @@ from textile import Textile -import re def test_footnoteRef(): t = Textile() From 8e1a9ea088dcb1ceb608630dd65f72fef968fd55 Mon Sep 17 00:00:00 2001 From: Dennis Burke Date: Sun, 28 Nov 2021 15:49:47 -0500 Subject: [PATCH 016/228] flake8: inline ignore unused import --- textile/regex_strings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/textile/regex_strings.py b/textile/regex_strings.py index 470203c..b20fc82 100644 --- a/textile/regex_strings.py +++ b/textile/regex_strings.py @@ -4,7 +4,7 @@ try: # Use regex module for matching uppercase characters if installed, # otherwise fall back to finding all the uppercase chars in a loop. - import regex as re + import regex as re # noqa: F401 upper_re_s = r'\p{Lu}' regex_snippets = { 'acr': r'\p{Lu}\p{Nd}', From 7c0394a269bee84ef13a62bf5eff4121da530c54 Mon Sep 17 00:00:00 2001 From: Dennis Burke Date: Sun, 28 Nov 2021 16:03:16 -0500 Subject: [PATCH 017/228] flake8: remove unused imports --- textile/__init__.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/textile/__init__.py b/textile/__init__.py index bb7829f..e7ea665 100644 --- a/textile/__init__.py +++ b/textile/__init__.py @@ -1,8 +1,5 @@ from __future__ import unicode_literals -import sys -import warnings - from .core import textile, textile_restricted, Textile from .version import VERSION From c1b08ac96436c5adde1b8b02ddd84470c22d0903 Mon Sep 17 00:00:00 2001 From: Dennis Burke Date: Sun, 28 Nov 2021 16:03:34 -0500 Subject: [PATCH 018/228] flake8: inline ignore unused import. It's actually used farther down the line in the module. --- textile/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/textile/__init__.py b/textile/__init__.py index e7ea665..9ca8a65 100644 --- a/textile/__init__.py +++ b/textile/__init__.py @@ -1,6 +1,6 @@ from __future__ import unicode_literals -from .core import textile, textile_restricted, Textile +from .core import textile, textile_restricted, Textile # noqa: F401 from .version import VERSION __all__ = ['textile', 'textile_restricted'] From cfcaa4fbcfb4ccc15ef23b017f00640a3049634b Mon Sep 17 00:00:00 2001 From: Dennis Burke Date: Sun, 28 Nov 2021 16:08:51 -0500 Subject: [PATCH 019/228] flake8: inline ignores because it's not actually a problem --- textile/core.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/textile/core.py b/textile/core.py index 7b66af0..a8d5eb9 100644 --- a/textile/core.py +++ b/textile/core.py @@ -467,10 +467,12 @@ def block(self, text): content = out[-2] if not multiline_para: - content = generate_tag(block.inner_tag, content, - block.inner_atts) - content = generate_tag(block.outer_tag, content, - block.outer_atts) + # block will have been defined in a previous run of the + # loop + content = generate_tag(block.inner_tag, content, # noqa: F821 + block.inner_atts) # noqa: F821 + content = generate_tag(block.outer_tag, content, # noqa: F821 + block.outer_atts) # noqa: F821 out[-2] = content tag, atts, ext, cite, content = match.groups() block = Block(self, **match.groupdict()) From 3e1442706e3a0d96b8259377793f45a370f38882 Mon Sep 17 00:00:00 2001 From: Dennis Burke Date: Sun, 28 Nov 2021 16:12:53 -0500 Subject: [PATCH 020/228] flake8: remove unused variables --- textile/core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/textile/core.py b/textile/core.py index a8d5eb9..936bebb 100644 --- a/textile/core.py +++ b/textile/core.py @@ -1298,7 +1298,7 @@ def fNoteLists(self, match): def makeBackrefLink(self, info, g_links, i): """Given the pieces of a back reference link, create an tag.""" - atts, content, infoid, link = '', '', '', '' + link = '' if 'def' in info: link = info['def']['link'] backlink_type = link or g_links From 7b1ac69f028355beed181da4b18c5a831dfe2fc9 Mon Sep 17 00:00:00 2001 From: Dennis Burke Date: Sun, 28 Nov 2021 16:14:15 -0500 Subject: [PATCH 021/228] flake8: remove unused import --- textile/objects/table.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/textile/objects/table.py b/textile/objects/table.py index 60b6804..ad95e48 100644 --- a/textile/objects/table.py +++ b/textile/objects/table.py @@ -5,7 +5,7 @@ from textile.regex_strings import (align_re_s, cls_re_s, regex_snippets, table_span_re_s, valign_re_s) -from textile.utils import encode_html, generate_tag, parse_attributes +from textile.utils import generate_tag, parse_attributes try: import regex as re From a20b5676bda2c33dfee4b0b6adf581b662541b79 Mon Sep 17 00:00:00 2001 From: Dennis Burke Date: Sun, 28 Nov 2021 16:15:03 -0500 Subject: [PATCH 022/228] flake8: remove unused variable --- textile/objects/table.py | 1 - 1 file changed, 1 deletion(-) diff --git a/textile/objects/table.py b/textile/objects/table.py index ad95e48..882e040 100644 --- a/textile/objects/table.py +++ b/textile/objects/table.py @@ -161,7 +161,6 @@ def process(self): colgroup = ElementTree.Element('colgroup', attrib=group_atts) colgroup.text = '\n\t' if self.cols is not None: - has_newline = "\n" in self.cols match_cols = self.cols.replace('.', '').split('|') # colgroup is the first item in match_cols, the remaining items are # cols. From 70841f67bac5cc118791c5f16874b8b5e448f4d6 Mon Sep 17 00:00:00 2001 From: Dennis Burke Date: Sun, 28 Nov 2021 16:16:25 -0500 Subject: [PATCH 023/228] add flake8 linting back in now that `F` class violations have been resolved --- .github/workflows/lint_and_test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/lint_and_test.yml b/.github/workflows/lint_and_test.yml index 16f06b2..437820d 100644 --- a/.github/workflows/lint_and_test.yml +++ b/.github/workflows/lint_and_test.yml @@ -17,8 +17,8 @@ jobs: with: python-version: ${{ matrix.python-version }} image-size: ${{ matrix.image-size }} - #- name: Python flake8 Lint - # uses: py-actions/flake8@v1.2.0 + - name: Python flake8 Lint + uses: py-actions/flake8@v1.2.0 - name: Install dependencies run: | imagesize='' From acd6cb9aa367da8e3e058b0333773f811e323490 Mon Sep 17 00:00:00 2001 From: Dennis Burke Date: Sun, 28 Nov 2021 17:01:46 -0500 Subject: [PATCH 024/228] flake8: various indentation cleanups --- textile/regex_strings.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/textile/regex_strings.py b/textile/regex_strings.py index b20fc82..68085d9 100644 --- a/textile/regex_strings.py +++ b/textile/regex_strings.py @@ -4,7 +4,7 @@ try: # Use regex module for matching uppercase characters if installed, # otherwise fall back to finding all the uppercase chars in a loop. - import regex as re # noqa: F401 + import regex as re # noqa: F401 upper_re_s = r'\p{Lu}' regex_snippets = { 'acr': r'\p{Lu}\p{Nd}', @@ -15,12 +15,12 @@ 'digit': r'\p{N}', 'space': r'(?:\p{Zs}|\v)', 'char': r'(?:[^\p{Zs}\v])', - } + } except ImportError: from sys import maxunicode upper_re_s = "".join( - [chr(c) for c in range(maxunicode) if chr(c).isupper()] - ) + [chr(c) for c in range(maxunicode) if chr(c).isupper()] + ) regex_snippets = { 'acr': r'{0}0-9'.format(upper_re_s), 'abr': r'{0}'.format(upper_re_s), @@ -30,7 +30,7 @@ 'digit': r'\d', 'space': r'(?:\s|\v)', 'char': r'\S', - } + } halign_re_s = r'(?:\<(?!>)|(?|\<\>|\=|[()]+(?! ))' valign_re_s = r'[\-^~]' @@ -43,10 +43,10 @@ table_span_re_s = r'(?:{0}|{1})*'.format(colspan_re_s, rowspan_re_s) # regex string to match class, style and language attributes cls_re_s = (r'(?:' - r'{c}(?:{l}(?:{s})?|{s}(?:{l})?)?|' - r'{l}(?:{c}(?:{s})?|{s}(?:{c})?)?|' - r'{s}(?:{c}(?:{l})?|{l}(?:{c})?)?' + r'{c}(?:{l}(?:{s})?|{s}(?:{l})?)?|' + r'{l}(?:{c}(?:{s})?|{s}(?:{c})?)?|' + r'{s}(?:{c}(?:{l})?|{l}(?:{c})?)?' r')?' - ).format(c=class_re_s, s=style_re_s, l=language_re_s) + ).format(c=class_re_s, s=style_re_s, l=language_re_s) pnct_re_s = r'[-!"#$%&()*+,/:;<=>?@\'\[\\\]\.^_`{|}~]' syms_re_s = '¤§µ¶†‡•∗∴◊♠♣♥♦' From e3d0c4afaf20d429a5838c4e651b9b6f7644d4e2 Mon Sep 17 00:00:00 2001 From: Dennis Burke Date: Sun, 28 Nov 2021 17:04:00 -0500 Subject: [PATCH 025/228] flake8: spacing cleanup --- textile/utils.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/textile/utils.py b/textile/utils.py index 1b18945..fed1d51 100644 --- a/textile/utils.py +++ b/textile/utils.py @@ -20,10 +20,12 @@ def decode_high(text): text = '&#{0};'.format(text) return html.unescape(text) + def encode_high(text): """Encode the text so that it is an appropriate HTML entity.""" return ord(text) + def encode_html(text, quotes=True): """Return text that's safe for an HTML attribute.""" a = ( @@ -39,6 +41,7 @@ def encode_html(text, quotes=True): text = text.replace(k, v) return text + def generate_tag(tag, content, attributes=None): """Generate a complete html tag using the ElementTree module. tag and content are strings, the attributes argument is a dictionary. As @@ -59,11 +62,12 @@ def generate_tag(tag, content, attributes=None): # non-ascii text being html-entity encoded. Not bad, but not entirely # matching php-textile either. element_tag = ElementTree.tostringlist(element, encoding=enc, - method='html') + method='html') element_tag.insert(len(element_tag) - 1, content) element_text = ''.join(element_tag) return element_text + def has_raw_text(text): """checks whether the text has text not already enclosed by a block tag""" # The php version has orders the below list of tags differently. The @@ -75,17 +79,20 @@ def has_raw_text(text): r = re.compile(r'<(hr|br)[^>]*?/>').sub('', r) return '' != r + def is_rel_url(url): """Identify relative urls.""" (scheme, netloc) = urlparse(url)[0:2] return not scheme and not netloc + def is_valid_url(url): parsed = urlparse(url) if parsed.scheme == '': return True return False + def list_type(list_string): listtypes = { list_string.startswith('*'): 'u', @@ -95,6 +102,7 @@ def list_type(list_string): } return listtypes.get(True, False) + def normalize_newlines(string): out = string.strip() out = re.sub(r'\r\n?', '\n', out) @@ -102,6 +110,7 @@ def normalize_newlines(string): out = re.sub(r'"$', '" ', out) return out + def parse_attributes(block_attributes, element=None, include_id=True, restricted=False): vAlign = {'^': 'top', '-': 'middle', '~': 'bottom'} hAlign = {'<': 'left', '=': 'center', '>': 'right', '<>': 'justify'} @@ -195,6 +204,7 @@ def parse_attributes(block_attributes, element=None, include_id=True, restricted result['width'] = width return result + def pba(block_attributes, element=None, include_id=True, restricted=False): """Parse block attributes.""" attrs = parse_attributes(block_attributes, element, include_id, restricted) From b85969ca6ddfc1f210179031827ba77a65317583 Mon Sep 17 00:00:00 2001 From: Dennis Burke Date: Sun, 28 Nov 2021 17:04:47 -0500 Subject: [PATCH 026/228] flake8: comment spacing cleanup --- textile/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/textile/__init__.py b/textile/__init__.py index 9ca8a65..08a242f 100644 --- a/textile/__init__.py +++ b/textile/__init__.py @@ -1,6 +1,6 @@ from __future__ import unicode_literals -from .core import textile, textile_restricted, Textile # noqa: F401 +from .core import textile, textile_restricted, Textile # noqa: F401 from .version import VERSION __all__ = ['textile', 'textile_restricted'] From 152a570721a31a5eb7fa8aae00721deb15fb2994 Mon Sep 17 00:00:00 2001 From: Dennis Burke Date: Sun, 28 Nov 2021 18:11:13 -0500 Subject: [PATCH 027/228] flake8: indentation cleanup --- textile/core.py | 180 ++++++++++++++++++++++++------------------------ 1 file changed, 91 insertions(+), 89 deletions(-) diff --git a/textile/core.py b/textile/core.py index 936bebb..4c8e8a2 100644 --- a/textile/core.py +++ b/textile/core.py @@ -23,10 +23,10 @@ from textile.tools import sanitizer, imagesize from textile.regex_strings import (align_re_s, cls_re_s, pnct_re_s, - regex_snippets, syms_re_s, table_span_re_s) + regex_snippets, syms_re_s, table_span_re_s) from textile.utils import (decode_high, encode_high, encode_html, generate_tag, - has_raw_text, is_rel_url, is_valid_url, list_type, normalize_newlines, - parse_attributes, pba) + has_raw_text, is_rel_url, is_valid_url, list_type, + normalize_newlines, parse_attributes, pba) from textile.objects import Block, Table try: @@ -37,8 +37,8 @@ class Textile(object): restricted_url_schemes = ('http', 'https', 'ftp', 'mailto') - unrestricted_url_schemes = restricted_url_schemes + ('file', 'tel', - 'callto', 'sftp', 'data') + unrestricted_url_schemes = restricted_url_schemes + ( + 'file', 'tel', 'callto', 'sftp', 'data') btag = ('bq', 'bc', 'notextile', 'pre', 'h[1-6]', r'fn\d+', 'p', '###') btag_lite = ('bq', 'bc', 'p') @@ -71,7 +71,7 @@ class Textile(object): } def __init__(self, restricted=False, lite=False, noimage=False, - get_sizes=False, html_type='xhtml', rel='', block_tags=True): + get_sizes=False, html_type='xhtml', rel='', block_tags=True): """Textile properties that are common to regular textile and textile_restricted""" self.restricted = restricted @@ -96,7 +96,7 @@ def __init__(self, restricted=False, lite=False, noimage=False, cur = r'' if regex_snippets['cur']: # pragma: no branch cur = r'(?:[{0}]{1}*)?'.format(regex_snippets['cur'], - regex_snippets['space']) + regex_snippets['space']) # We'll be searching for characters that need to be HTML-encoded to # produce properly valid html. These are the defaults that work in @@ -105,7 +105,7 @@ def __init__(self, restricted=False, lite=False, noimage=False, self.glyph_search = [ # apostrophe's re.compile(r"(^|{0}|\))'({0})".format(regex_snippets['wrd']), - flags=re.U), + flags=re.U), # back in '88 re.compile(r"({0})'(\d+{1}?)\b(?![.]?[{1}]*?')".format( regex_snippets['space'], regex_snippets['wrd']), @@ -134,17 +134,18 @@ def __init__(self, restricted=False, lite=False, noimage=False, # en dash re.compile(r' - '), # dimension sign - re.compile(r'([0-9]+[\])]?[\'"]? ?)[x]( ?[\[(]?)' + re.compile( + r'([0-9]+[\])]?[\'"]? ?)[x]( ?[\[(]?)' r'(?=[+-]?{0}[0-9]*\.?[0-9]+)'.format(cur), flags=re.I | re.U), # trademark - re.compile(r'(\b ?|{0}|^)[([]TM[])]'.format(regex_snippets['space'] - ), flags=re.I | re.U), + re.compile(r'(\b ?|{0}|^)[([]TM[])]'.format(regex_snippets['space']), + flags=re.I | re.U), # registered - re.compile(r'(\b ?|{0}|^)[([]R[])]'.format(regex_snippets['space'] - ), flags=re.I | re.U), + re.compile(r'(\b ?|{0}|^)[([]R[])]'.format(regex_snippets['space']), + flags=re.I | re.U), # copyright - re.compile(r'(\b ?|{0}|^)[([]C[])]'.format(regex_snippets['space'] - ), flags=re.I | re.U), + re.compile(r'(\b ?|{0}|^)[([]C[])]'.format(regex_snippets['space']), + flags=re.I | re.U), # 1/2 re.compile(r'[([]1\/2[])]'), # 1/4 @@ -160,9 +161,11 @@ def __init__(self, restricted=False, lite=False, noimage=False, regex_snippets['abr'], regex_snippets['acr']), flags=re.U), # 3+ uppercase re.compile(r'({space}|^|[>(;-])([{abr}]{{3,}})([{nab}]*)' - '(?={space}|{pnct}|<|$)(?=[^">]*?(<|$))'.format(**{ 'space': - regex_snippets['space'], 'abr': regex_snippets['abr'], - 'nab': regex_snippets['nab'], 'pnct': pnct_re_s}), re.U), + '(?={space}|{pnct}|<|$)(?=[^">]*?(<|$))'.format( + **{'space': regex_snippets['space'], 'abr': + regex_snippets['abr'], 'nab': + regex_snippets['nab'], 'pnct': pnct_re_s}), + re.U), ] # These are the changes that need to be made for characters that occur # at the beginning of the string. @@ -172,10 +175,10 @@ def __init__(self, restricted=False, lite=False, noimage=False, regex_snippets['wrd']), flags=re.U) # single closing self.glyph_search_initial[3] = re.compile(r"(\S)'(?={0}|{1}|$)".format( - regex_snippets['space'], pnct_re_s), re.U) + regex_snippets['space'], pnct_re_s), re.U) # double closing self.glyph_search_initial[6] = re.compile(r'(\S)"(?={0}|{1}|<|$)'.format( - regex_snippets['space'], pnct_re_s), re.U) + regex_snippets['space'], pnct_re_s), re.U) self.glyph_replace = [x.format(**self.glyph_definitions) for x in ( r'\1{apostrophe}\2', # apostrophe's @@ -201,7 +204,7 @@ def __init__(self, restricted=False, lite=False, noimage=False, r'{plusminus}', # plus/minus r'\1', # 3+ uppercase acronym r'\1{0}:glyph:\2' # 3+ uppercase - r'\3'.format(self.uid), + r'\3'.format(self.uid), )] if self.html_type == 'html5': @@ -241,9 +244,9 @@ def parse(self, text, rel=None, sanitize=False): self.blocktag_whitelist = ['bq', 'p'] text = self.block(text) else: - self.blocktag_whitelist = [ 'bq', 'p', 'bc', 'notextile', - 'pre', 'h[1-6]', - 'fn{0}+'.format(regex_snippets['digit']), '###'] + self.blocktag_whitelist = ['bq', 'p', 'bc', 'notextile', + 'pre', 'h[1-6]', 'fn{0}+'.format( + regex_snippets['digit']), '###'] text = self.block(text) text = self.placeNoteLists(text) else: @@ -280,9 +283,10 @@ def parse(self, text, rel=None, sanitize=False): def table(self, text): text = "{0}\n\n".format(text) pattern = re.compile(r'^(?:table(?P_?{s}{a}{c})\.' - r'(?P.*?)\n)?^(?P{a}{c}\.? ?\|.*\|)' - r'[\s]*\n\n'.format(**{'s': table_span_re_s, 'a': align_re_s, - 'c': cls_re_s}), flags=re.S | re.M | re.U) + r'(?P.*?)\n)?^(?P{a}{c}\.? ?\|.*\|)' + r'[\s]*\n\n'.format( + **{'s': table_span_re_s, 'a': align_re_s, + 'c': cls_re_s}), flags=re.S | re.M | re.U) match = pattern.search(text) if match: table = Table(self, **match.groupdict()) @@ -291,7 +295,7 @@ def table(self, text): def textileLists(self, text): pattern = re.compile(r'^((?:[*;:]+|[*;:#]*#(?:_|\d+)?){0}[ .].*)$' - r'(?![^#*;:])'.format(cls_re_s), re.U | re.M | re.S) + r'(?![^#*;:])'.format(cls_re_s), re.U | re.M | re.S) return pattern.sub(self.fTextileList, text) def fTextileList(self, match): @@ -306,7 +310,7 @@ def fTextileList(self, match): nextline = '' m = re.search(r"^(?P[#*;:]+)(?P_|\d+)?(?P{0})[ .]" - "(?P.*)$".format(cls_re_s), line, re.S) + "(?P.*)$".format(cls_re_s), line, re.S) if m: tl, start, atts, content = m.groups() content = content.strip() @@ -354,7 +358,7 @@ def fTextileList(self, match): self.olstarts[tl] = 1 nm = re.match(r"^(?P[#\*;:]+)(_|[\d]+)?{0}" - r"[ .].*".format(cls_re_s), nextline) + r"[ .].*".format(cls_re_s), nextline) if nm: nl = nm.group('nextlistitem') @@ -374,7 +378,7 @@ def fTextileList(self, match): if tl not in ls: ls[tl] = 1 itemtag = ("\n{0}\t<{1}>{2}".format(tabs, litem, content) if - showitem else '') + showitem else '') line = "<{0}l{1}{2}>{3}".format(ltype, atts, start, itemtag) else: line = ("\t<{0}{1}>{2}".format(litem, atts, content) if @@ -387,18 +391,13 @@ def fTextileList(self, match): for k, v in reversed(list(ls.items())): if len(k) > len(nl): if v != 2: - line = "{0}\n{1}".format(line, tabs, - list_type(k)) + line = "{0}\n{1}".format( + line, tabs, list_type(k)) if len(k) > 1 and v != 2: line = "{0}".format(line, litem) del ls[k] # Remember the current Textile tag: pt = tl - # This else exists in the original php version. I'm not sure how - # to come up with a case where the line would not match. I think - # it may have been necessary due to the way php returns matches. - # else: - #line = "{0}\n".format(line) result.append(line) return self.doTagBr(litem, "\n".join(result)) @@ -414,7 +413,7 @@ def doBr(self, match): content = re.sub(r'(.+)(?:(?)|(?))\n(?![#*;:\s|])', r'\1
', match.group(3)) return '<{0}{1}>{2}{3}'.format(match.group(1), match.group(2), content, - match.group(4)) + match.group(4)) def block(self, text): if not self.lite: @@ -450,8 +449,8 @@ def block(self, text): eat_whitespace = False pattern = (r'^(?P{0})(?P{1}{2})\.(?P\.?)' - r'(?::(?P\S+))? (?P.*)$'.format(tre, - align_re_s, cls_re_s)) + r'(?::(?P\S+))? (?P.*)$'.format( + tre, align_re_s, cls_re_s)) match = re.search(pattern, line, flags=re.S | re.U) # tag specified on this line. if match: @@ -469,15 +468,15 @@ def block(self, text): if not multiline_para: # block will have been defined in a previous run of the # loop - content = generate_tag(block.inner_tag, content, # noqa: F821 - block.inner_atts) # noqa: F821 - content = generate_tag(block.outer_tag, content, # noqa: F821 - block.outer_atts) # noqa: F821 + content = generate_tag(block.inner_tag, content, # noqa: F821 + block.inner_atts) # noqa: F821 + content = generate_tag(block.outer_tag, content, # noqa: F821 + block.outer_atts) # noqa: F821 out[-2] = content tag, atts, ext, cite, content = match.groups() block = Block(self, **match.groupdict()) inner_block = generate_tag(block.inner_tag, block.content, - block.inner_atts) + block.inner_atts) # code tags and raw text won't be indented inside outer_tag. if block.inner_tag != 'code' and not has_raw_text(inner_block): inner_block = "\n\t\t{0}\n\t".format(inner_block) @@ -485,7 +484,7 @@ def block(self, text): line = block.content else: line = generate_tag(block.outer_tag, inner_block, - block.outer_atts) + block.outer_atts) # pre tags and raw text won't be indented. if block.outer_tag != 'pre' and not has_raw_text(line): line = "\t{0}".format(line) @@ -517,7 +516,7 @@ def block(self, text): line = block.content else: line = generate_tag(block.outer_tag, block.content, - block.outer_atts) + block.outer_atts) line = "\t{0}".format(line) else: if block.tag == 'pre' or block.inner_tag == 'code': @@ -571,14 +570,15 @@ def block(self, text): def footnoteRef(self, text): # somehow php-textile gets away with not capturing the space. return re.compile(r'(?<=\S)\[(?P{0}+)(?P!?)\]' - r'(?P{1}?)'.format(regex_snippets['digit'], - regex_snippets['space']), re.U).sub(self.footnoteID, text) + r'(?P{1}?)'.format( + regex_snippets['digit'], regex_snippets['space']), + re.U).sub(self.footnoteID, text) def footnoteID(self, m): fn_att = OrderedDict({'class': 'footnote'}) if m.group('id') not in self.fn: - self.fn[m.group('id')] = '{0}{1}'.format(self.linkPrefix, - self._increment_link_index()) + self.fn[m.group('id')] = '{0}{1}'.format( + self.linkPrefix, self._increment_link_index()) fnid = self.fn[m.group('id')] fn_att['id'] = 'fnrev{0}'.format(fnid) fnid = self.fn[m.group('id')] @@ -612,7 +612,7 @@ def glyphs(self, text): searchlist = self.glyph_search_initial # split the text by any angle-bracketed tags for i, line in enumerate(re.compile(r'(<[\w\/!?].*?>)', re.U).split( - text)): + text)): if not i % 2: for s, r in zip(searchlist, self.glyph_replace): line = s.sub(r, line) @@ -777,7 +777,7 @@ def markStartOfLinks(self, text): # Re-assemble the link starts with a specific marker for the # next regex. o = '{0}{1}linkStartMarker:"{2}'.format(pre_link, self.uid, - link_content) + link_content) output.append(o) # Add the last part back @@ -819,7 +819,7 @@ def fLink(self, m): ) # end of $text (?:\((?P[^)]+?)\))? # $title (if any) $'''.format(cls_re_s, regex_snippets['space']), inner, - flags=re.X | re.U) + flags=re.X | re.U) atts = (m and m.group('atts')) or '' text = (m and m.group('text')) or inner @@ -914,20 +914,20 @@ def _casesdefault(c, pop, popped, url_chars, counts, pre): return pop, popped, url_chars, counts, pre cases = { - '!': _endchar, - '?': _endchar, - ':': _endchar, - ';': _endchar, - '.': _endchar, - ',': _endchar, - '>': _rightanglebracket, - ']': _closingsquarebracket, - ')': _closingparenthesis, - } + '!': _endchar, + '?': _endchar, + ':': _endchar, + ';': _endchar, + '.': _endchar, + ',': _endchar, + '>': _rightanglebracket, + ']': _closingsquarebracket, + ')': _closingparenthesis, + } for c in url_chars[-1::-1]: # pragma: no branch popped = False - pop, popped, url_chars, counts, pre = cases.get(c, - _casesdefault)(c, pop, popped, url_chars, counts, pre) + pop, popped, url_chars, counts, pre = cases.get( + c, _casesdefault)(c, pop, popped, url_chars, counts, pre) first = False if popped is False: break @@ -991,8 +991,7 @@ def encode_url(self, url): """, re.X | re.U) netloc_parsed = netloc_pattern.match(parsed.netloc).groupdict() else: - netloc_parsed = {'user': '', 'password': '', 'host': '', 'port': - ''} + netloc_parsed = {'user': '', 'password': '', 'host': '', 'port': ''} # encode each component scheme = parsed.scheme @@ -1008,7 +1007,7 @@ def encode_url(self, url): # because the quote and unquote functions expects different input # types: unicode strings for PY2 and str for PY3. path_parts = (quote(unquote(pce), b'') for pce in - parsed.path.split('/')) + parsed.path.split('/')) path = '/'.join(path_parts) # put it back together @@ -1041,8 +1040,10 @@ def span(self, text): (?P<end>[{pnct}]*) {tag} (?P<tail>$|[\[\]}}<]|(?=[{pnct}]{{1,2}}[^0-9]|\s|\))) - """.format(**{'tag': tag, 'cls': cls_re_s, 'pnct': pnct, - 'space': regex_snippets['space']}), flags=re.X | re.U) + """.format( + **{'tag': tag, 'cls': cls_re_s, 'pnct': pnct, 'space': + regex_snippets['space']} + ), flags=re.X | re.U) text = pattern.sub(self.fSpan, text) self.span_depth = self.span_depth - 1 return text @@ -1189,7 +1190,7 @@ def redcloth_list(self, text): """Parse the text for definition lists and send them to be formatted.""" pattern = re.compile(r"^([-]+{0}[ .].*:=.*)$(?![^-])".format(cls_re_s), - re.M | re.U | re.S) + re.M | re.U | re.S) return pattern.sub(self.fRCList, text) def fRCList(self, match): @@ -1199,7 +1200,7 @@ def fRCList(self, match): for line in text: # parse the attributes and content m = re.match(r'^[-]+({0})[ .](.*)$'.format(cls_re_s), line, - flags=re.M | re.S) + flags=re.M | re.S) if not m: continue @@ -1233,7 +1234,7 @@ def fRCList(self, match): definition = self.graf(definition) out.extend(['\t<dt{0}>{1}</dt>'.format(atts, term), - '\t<dd>{0}</dd>'.format(definition)]) + '\t<dd>{0}</dd>'.format(definition)]) out.append('</dl>') out = '\n'.join(out) @@ -1256,7 +1257,7 @@ def placeNoteLists(self, text): o = OrderedDict(sorted(o.items(), key=lambda t: t[0])) self.notes = o text_re = re.compile(r'<p>notelist({0})(?:\:([\w|{1}]))?([\^!]?)(\+?)' - r'\.?[\s]*</p>'.format(cls_re_s, syms_re_s), re.U) + r'\.?[\s]*</p>'.format(cls_re_s, syms_re_s), re.U) text = text_re.sub(self.fNoteLists, text) return text @@ -1278,11 +1279,12 @@ def fNoteLists(self, match): atts = info['def']['atts'] content = info['def']['content'] li = ('\t\t<li{0}>{1}<span id="note{2}"> ' - '</span>{3}</li>').format(atts, links, infoid, - content) + '</span>{3}</li>').format(atts, links, infoid, + content) else: - li = ('\t\t<li{0}>{1} Undefined Note [#{2}].<li>' - ).format(atts, links, info['seq']) + li = ( + '\t\t<li{0}>{1} Undefined Note [#{2}].<li>' + ).format(atts, links, info['seq']) o.append(li) if '+' == extras and self.unreferencedNotes: for seq, info in self.unreferencedNotes.items(): @@ -1316,7 +1318,7 @@ def makeBackrefLink(self, info, g_links, i): for refid in info['refids']: i_entity = decode_high(i_) sup = """<sup><a href="#noteref{0}">{1}</a></sup>""".format( - refid, i_entity) + refid, i_entity) if allow_inc: i_ = i_ + 1 result.append(sup) @@ -1332,13 +1334,14 @@ def fParseNoteDefs(self, m): # Assign an id if the note reference parse hasn't found the label yet. if label not in self.notes: - self.notes[label] = {'id': '{0}{1}'.format(self.linkPrefix, - self._increment_link_index())} + self.notes[label] = {'id': '{0}{1}'.format( + self.linkPrefix, self._increment_link_index())} # Ignores subsequent defs using the same label if 'def' not in self.notes[label]: # pragma: no branch - self.notes[label]['def'] = {'atts': pba(att, restricted=self.restricted), 'content': - self.graf(content), 'link': link} + self.notes[label]['def'] = { + 'atts': pba(att, restricted=self.restricted), 'content': + self.graf(content), 'link': link} return '' def noteRef(self, text): @@ -1380,8 +1383,8 @@ def fParseNoteRefs(self, match): # If we are referencing a note that hasn't had the definition parsed # yet, then assign it an ID... if not self.notes[label]['id']: - self.notes[label]['id'] = '{0}{1}'.format(self.linkPrefix, - self._increment_link_index()) + self.notes[label]['id'] = '{0}{1}'.format( + self.linkPrefix, self._increment_link_index()) labelid = self.notes[label]['id'] # Build the link (if any)... @@ -1447,5 +1450,4 @@ def textile_restricted(text, lite=True, noimage=True, html_type='xhtml'): """ return Textile(restricted=True, lite=lite, noimage=noimage, - html_type=html_type, rel='nofollow').parse( - text) + html_type=html_type, rel='nofollow').parse(text) From 411d23d6d218041e86cf47bba1691f2e29585578 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 18:37:03 -0500 Subject: [PATCH 028/228] flake8: mostly whitespace cleanup --- textile/core.py | 94 ++++++++++++++++++++++++------------------------- 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/textile/core.py b/textile/core.py index 4c8e8a2..fad4f5d 100644 --- a/textile/core.py +++ b/textile/core.py @@ -48,26 +48,26 @@ class Textile(object): doctype_whitelist = ['xhtml', 'html5'] glyph_definitions = { - 'quote_single_open': '‘', - 'quote_single_close': '’', - 'quote_double_open': '“', - 'quote_double_close': '”', - 'apostrophe': '’', - 'prime': '′', - 'prime_double': '″', - 'ellipsis': '…', - 'ampersand': '&', - 'emdash': '—', - 'endash': '–', - 'dimension': '×', - 'trademark': '™', - 'registered': '®', - 'copyright': '©', - 'half': '½', - 'quarter': '¼', - 'threequarters': '¾', - 'degrees': '°', - 'plusminus': '±', + 'quote_single_open': '‘', # noqa: E241 + 'quote_single_close': '’', # noqa: E241 + 'quote_double_open': '“', # noqa: E241 + 'quote_double_close': '”', # noqa: E241 + 'apostrophe': '’', # noqa: E241 + 'prime': '′', # noqa: E241 + 'prime_double': '″', # noqa: E241 + 'ellipsis': '…', # noqa: E241 + 'ampersand': '&', # noqa: E241 + 'emdash': '—', # noqa: E241 + 'endash': '–', # noqa: E241 + 'dimension': '×', # noqa: E241 + 'trademark': '™', # noqa: E241 + 'registered': '®', # noqa: E241 + 'copyright': '©', # noqa: E241 + 'half': '½', # noqa: E241 + 'quarter': '¼', # noqa: E241 + 'threequarters': '¾', # noqa: E241 + 'degrees': '°', # noqa: E241 + 'plusminus': '±', # noqa: E241 } def __init__(self, restricted=False, lite=False, noimage=False, @@ -94,7 +94,7 @@ def __init__(self, restricted=False, lite=False, noimage=False, self.block_tags = block_tags cur = r'' - if regex_snippets['cur']: # pragma: no branch + if regex_snippets['cur']: # pragma: no branch cur = r'(?:[{0}]{1}*)?'.format(regex_snippets['cur'], regex_snippets['space']) @@ -721,7 +721,7 @@ def markStartOfLinks(self, text): linkparts = [] i = 0 - while balanced != 0 or i == 0: # pragma: no branch + while balanced != 0 or i == 0: # pragma: no branch # Starting at the end, pop off the previous part of the # slice's fragments. @@ -730,9 +730,9 @@ def markStartOfLinks(self, text): if len(possibility) > 0: # did this part inc or dec the balanced count? - if re.search(r'^\S|=$', possibility, flags=re.U): # pragma: no branch + if re.search(r'^\S|=$', possibility, flags=re.U): # pragma: no branch balanced = balanced - 1 - if re.search(r'\S$', possibility, flags=re.U): # pragma: no branch + if re.search(r'\S$', possibility, flags=re.U): # pragma: no branch balanced = balanced + 1 try: possibility = possible_start_quotes.pop() @@ -752,7 +752,7 @@ def markStartOfLinks(self, text): try: possibility = possible_start_quotes.pop() - except IndexError: # pragma: no cover + except IndexError: # pragma: no cover # If out of possible starting segments we back the # last one from the linkparts array linkparts.pop() @@ -761,7 +761,7 @@ def markStartOfLinks(self, text): # we have a closing ". if (possibility == '' or possibility.endswith(' ')): # force search exit - balanced = 0; + balanced = 0 if balanced <= 0: possible_start_quotes.append(possibility) @@ -826,7 +826,7 @@ def fLink(self, m): title = (m and m.group('title')) or '' pop, tight = '', '' - counts = { '[': None, ']': url.count(']'), '(': None, ')': None } + counts = {'[': None, ']': url.count(']'), '(': None, ')': None} # Look for footnotes or other square-bracket delimited stuff at the end # of the url... @@ -893,13 +893,13 @@ def _closingsquarebracket(c, pop, popped, url_chars, counts, pre): # it popped = True url_chars.pop() - counts[']'] = counts[']'] - 1; - if first: # pragma: no branch + counts[']'] = counts[']'] - 1 + if first: # pragma: no branch pre = '' return pop, popped, url_chars, counts, pre def _closingparenthesis(c, pop, popped, url_chars, counts, pre): - if counts[')'] is None: # pragma: no branch + if counts[')'] is None: # pragma: no branch counts['('] = url.count('(') counts[')'] = url.count(')') @@ -924,7 +924,7 @@ def _casesdefault(c, pop, popped, url_chars, counts, pre): ']': _closingsquarebracket, ')': _closingparenthesis, } - for c in url_chars[-1::-1]: # pragma: no branch + for c in url_chars[-1::-1]: # pragma: no branch popped = False pop, popped, url_chars, counts, pre = cases.get( c, _casesdefault)(c, pop, popped, url_chars, counts, pre) @@ -950,7 +950,7 @@ def _casesdefault(c, pop, popped, url_chars, counts, pre): text = text.strip() title = encode_html(title) - if not self.noimage: # pragma: no branch + if not self.noimage: # pragma: no branch text = self.image(text) text = self.span(text) text = self.glyphs(text) @@ -1052,16 +1052,16 @@ def fSpan(self, match): pre, tag, atts, cite, content, end, tail = match.groups() qtags = { - '*': 'strong', - '**': 'b', - '??': 'cite', - '_': 'em', - '__': 'i', - '-': 'del', - '%': 'span', - '+': 'ins', - '~': 'sub', - '^': 'sup' + '*': 'strong', # noqa: E241 + '**': 'b', # noqa: E241 + '??': 'cite', # noqa: E241 + '_': 'em', # noqa: E241 + '__': 'i', # noqa: E241 + '-': 'del', # noqa: E241 + '%': 'span', # noqa: E241 + '+': 'ins', # noqa: E241 + '~': 'sub', # noqa: E241 + '^': 'sup' # noqa: E241 } tag = qtags[tag] @@ -1164,7 +1164,7 @@ def noTextile(self, text): def fTextile(self, match): before, notextile, after = match.groups() - if after is None: # pragma: no branch + if after is None: # pragma: no branch after = '' return ''.join([before, self.shelve(notextile), after]) @@ -1252,7 +1252,7 @@ def placeNoteLists(self, text): else: self.unreferencedNotes[label] = info - if o: # pragma: no branch + if o: # pragma: no branch # sort o by key o = OrderedDict(sorted(o.items(), key=lambda t: t[0])) self.notes = o @@ -1268,9 +1268,9 @@ def fNoteLists(self, match): index = '{0}{1}{2}'.format(g_links, extras, start_char) result = '' - if index not in self.notelist_cache: # pragma: no branch + if index not in self.notelist_cache: # pragma: no branch o = [] - if self.notes: # pragma: no branch + if self.notes: # pragma: no branch for seq, info in self.notes.items(): links = self.makeBackrefLink(info, g_links, start_char) atts = '' @@ -1338,7 +1338,7 @@ def fParseNoteDefs(self, m): self.linkPrefix, self._increment_link_index())} # Ignores subsequent defs using the same label - if 'def' not in self.notes[label]: # pragma: no branch + if 'def' not in self.notes[label]: # pragma: no branch self.notes[label]['def'] = { 'atts': pba(att, restricted=self.restricted), 'content': self.graf(content), 'link': link} From ba2736d51ea5e6b67edcfe757ceec0ea31e17e76 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 18:47:46 -0500 Subject: [PATCH 029/228] flake8: warning cleanup --- textile/core.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/textile/core.py b/textile/core.py index fad4f5d..6721d83 100644 --- a/textile/core.py +++ b/textile/core.py @@ -996,8 +996,9 @@ def encode_url(self, url): # encode each component scheme = parsed.scheme user = netloc_parsed['user'] and quote(netloc_parsed['user']) - password = (netloc_parsed['password'] and - quote(netloc_parsed['password'])) + password = ( + netloc_parsed['password'] and quote(netloc_parsed['password']) + ) host = netloc_parsed['host'] port = netloc_parsed['port'] and netloc_parsed['port'] # the below splits the path portion of the url by slashes, translates From 2f128a2f1ce65932d772460dc6689c1770f2df7c Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 18:49:08 -0500 Subject: [PATCH 030/228] flake8: comment cleanup --- textile/__main__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/textile/__main__.py b/textile/__main__.py index 1845961..210c147 100644 --- a/textile/__main__.py +++ b/textile/__main__.py @@ -33,5 +33,5 @@ def main(): outfile.write(output) -if __name__ == '__main__': #pragma: no cover +if __name__ == '__main__': # pragma: no cover main() From 97eb73044a3b4dd088ca63dcacda2432b50c7cfa Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 19:01:03 -0500 Subject: [PATCH 031/228] flake8: mostly indentation cleanup --- textile/objects/block.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/textile/objects/block.py b/textile/objects/block.py index de993e8..7aca9dd 100644 --- a/textile/objects/block.py +++ b/textile/objects/block.py @@ -71,12 +71,11 @@ def process(self): else: supp_id = parse_attributes('(#fn{0})'.format(fnid), restricted=self.textile.restricted) - if '^' not in self.atts: sup = generate_tag('sup', fns.group('fnid'), supp_id) else: fnrev = generate_tag('a', fns.group('fnid'), {'href': - '#fnrev{0}'.format(fnid)}) + '#fnrev{0}'.format(fnid)}) sup = generate_tag('sup', fnrev, supp_id) self.content = '{0} {1}'.format(sup, self.content) From 5333f8486a5954db9302ee8f1014259e3b9efce5 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 19:07:22 -0500 Subject: [PATCH 032/228] flake8: indentation cleanup --- textile/objects/block.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/textile/objects/block.py b/textile/objects/block.py index 7aca9dd..8f44ad1 100644 --- a/textile/objects/block.py +++ b/textile/objects/block.py @@ -40,7 +40,7 @@ def process(self): [{space}]+ # whitespace ends def marker (?P<content>.*)$ # content""".format( space=regex_snippets['space'], cls=cls_re_s), - flags=re.X | re.U) + flags=re.X | re.U) notedef = notedef_re.sub(self.textile.fParseNoteDefs, self.content) # It will be empty if the regex matched and ate it. @@ -49,13 +49,13 @@ def process(self): self.eat = True fns = re.search(r'fn(?P<fnid>{0}+)'.format(regex_snippets['digit']), - self.tag, flags=re.U) + self.tag, flags=re.U) if fns: self.tag = 'p' fnid = self.textile.fn.get(fns.group('fnid'), None) if fnid is None: fnid = '{0}{1}'.format(self.textile.linkPrefix, - self.textile._increment_link_index()) + self.textile._increment_link_index()) # If there is an author-specified ID goes on the wrapper & the # auto-id gets pushed to the <sup> From 7ffa65d1bbfb9e129aafca6b91590f63323b0379 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 19:07:44 -0500 Subject: [PATCH 033/228] flake8: indentation and whitespace cleanup --- textile/objects/table.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/textile/objects/table.py b/textile/objects/table.py index 882e040..717d457 100644 --- a/textile/objects/table.py +++ b/textile/objects/table.py @@ -131,8 +131,8 @@ def process(self): if rgrp: groups.append('\n\t{0}'.format(rgrp.process())) - content = '{0}{1}{2}{3}\n\t'.format(self.caption, self.colgroup, - ''.join(groups), ''.join(self.content)) + content = '{0}{1}{2}{3}\n\t'.format( + self.caption, self.colgroup, ''.join(groups), ''.join(self.content)) tbl = generate_tag('table', content, self.attributes) return '\t{0}\n\n'.format(tbl) From de319df092100cd078333a048193abeb454e0dd0 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 19:09:31 -0500 Subject: [PATCH 034/228] flake8: indentation and whitespace cleanup --- textile/objects/table.py | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/textile/objects/table.py b/textile/objects/table.py index 717d457..d20f800 100644 --- a/textile/objects/table.py +++ b/textile/objects/table.py @@ -4,7 +4,7 @@ from xml.etree import ElementTree from textile.regex_strings import (align_re_s, cls_re_s, regex_snippets, - table_span_re_s, valign_re_s) + table_span_re_s, valign_re_s) from textile.utils import generate_tag, parse_attributes try: @@ -27,7 +27,7 @@ def __init__(self, textile, tatts, rows, summary): def process(self): rgrp = None groups = [] - if self.input[-1] == '|': # pragma: no branch + if self.input[-1] == '|': # pragma: no branch self.input = '{0}\n'.format(self.input) split = self.input.split('|\n') for i, row in enumerate([x for x in split if x]): @@ -66,8 +66,9 @@ def process(self): # search the row for a table group - thead, tfoot, or tbody grpmatchpattern = (r"(:?^\|(?P<part>{v})(?P<rgrpatts>{s}{a}{c})" - r"\.\s*$\n)?^(?P<row>.*)").format(**{'v': valign_re_s, 's': - table_span_re_s, 'a': align_re_s, 'c': cls_re_s}) + r"\.\s*$\n)?^(?P<row>.*)").format( + **{'v': valign_re_s, 's': table_span_re_s, + 'a': align_re_s, 'c': cls_re_s}) grpmatch_re = re.compile(grpmatchpattern, re.S | re.M) grpmatch = grpmatch_re.match(row.lstrip()) @@ -97,8 +98,9 @@ def process(self): ctag = 'th' cmtch = re.search(r'^(?P<catts>_?{0}{1}{2}\. )' - '(?P<cell>.*)'.format(table_span_re_s, align_re_s, - cls_re_s), cell, flags=re.S) + '(?P<cell>.*)'.format( + table_span_re_s, align_re_s, cls_re_s), + cell, flags=re.S) if cmtch: catts = cmtch.group('catts') cell_atts = parse_attributes(catts, 'td', restricted=self.textile.restricted) @@ -108,7 +110,7 @@ def process(self): if not self.textile.lite: a_pattern = r'(?P<space>{0}*)(?P<cell>.*)'.format( - regex_snippets['space']) + regex_snippets['space']) a = re.search(a_pattern, cell, flags=re.S) cell = self.textile.redcloth_list(a.group('cell')) cell = self.textile.textileLists(cell) From 67df70c201a7b3d21a224720780811ebe37a9cc5 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 19:09:55 -0500 Subject: [PATCH 035/228] flake8: whitespace cleanup --- setup.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 80488d2..b405f28 100644 --- a/setup.py +++ b/setup.py @@ -10,6 +10,7 @@ def get_version(): return variables.get('VERSION') raise RuntimeError('No version info found.') + setup( name='textile', version=get_version(), @@ -38,7 +39,7 @@ def get_version(): install_requires=[ 'html5lib>=1.0.1', 'regex>1.0; implementation_name != "pypy"', - ], + ], extras_require={ 'develop': ['pytest', 'pytest-cov'], 'imagesize': ['Pillow>=3.0.0'], From 1f26d4cd9b4f6d93d7e1c04b09c81203a213df9a Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 19:16:22 -0500 Subject: [PATCH 036/228] flake8: whitespace cleanup --- tests/test_block.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_block.py b/tests/test_block.py index 613bf41..93d3c17 100644 --- a/tests/test_block.py +++ b/tests/test_block.py @@ -69,6 +69,7 @@ def test_blockcode_comment(): result = t.parse(input) assert result == expect + def test_extended_pre_block_with_many_newlines(): """Extra newlines in an extended pre block should not get cut down to only two.""" From c20f9a575fc5ea7bab3825e825c47e2d0b75d4c3 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 19:17:30 -0500 Subject: [PATCH 037/228] flake8: whitespace cleanup --- tests/test_block.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/tests/test_block.py b/tests/test_block.py index 93d3c17..691d21a 100644 --- a/tests/test_block.py +++ b/tests/test_block.py @@ -8,6 +8,7 @@ except ImportError: from ordereddict import OrderedDict + def test_block(): t = textile.Textile() result = t.block('h1. foobar baby') @@ -16,14 +17,14 @@ def test_block(): b = Block(t, "bq", "", None, "", "Hello BlockQuote") expect = ('blockquote', OrderedDict(), 'p', OrderedDict(), - 'Hello BlockQuote') + 'Hello BlockQuote') result = (b.outer_tag, b.outer_atts, b.inner_tag, b.inner_atts, b.content) assert result == expect b = Block(t, "bq", "", None, "http://google.com", "Hello BlockQuote") expect = ('blockquote', OrderedDict([('cite', - '{0.uid}{0.refIndex}:url'.format(t))]), 'p', OrderedDict(), - 'Hello BlockQuote') + '{0.uid}{0.refIndex}:url'.format(t))]), 'p', OrderedDict(), + 'Hello BlockQuote') result = (b.outer_tag, b.outer_atts, b.inner_tag, b.inner_atts, b.content) assert result == expect @@ -39,6 +40,7 @@ def test_block(): result = (b.outer_tag, b.outer_atts, b.inner_tag, b.inner_atts, b.content) assert result == expect + def test_block_tags_false(): t = textile.Textile(block_tags=False) assert t.block_tags is False @@ -47,6 +49,7 @@ def test_block_tags_false(): expect = 'test' assert result == expect + def test_blockcode_extended(): input = 'bc.. text\nmoretext\n\nevenmoretext\n\nmoremoretext\n\np. test' expect = '<pre><code>text\nmoretext\n\nevenmoretext\n\nmoremoretext</code></pre>\n\n\t<p>test</p>' @@ -54,6 +57,7 @@ def test_blockcode_extended(): result = t.parse(input) assert result == expect + def test_blockcode_in_README(): with open('README.textile') as f: readme = ''.join(f.readlines()) @@ -62,6 +66,7 @@ def test_blockcode_in_README(): expect = ''.join(f.readlines()) assert result == expect + def test_blockcode_comment(): input = '###.. block comment\nanother line\n\np. New line' expect = '\t<p>New line</p>' From 73adb81c5cf614b80aebe4121a94648770e10909 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 19:17:53 -0500 Subject: [PATCH 038/228] flake8: whitespace cleanup --- tests/test_image.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_image.py b/tests/test_image.py index aad39e2..b746292 100644 --- a/tests/test_image.py +++ b/tests/test_image.py @@ -1,5 +1,6 @@ from textile import Textile + def test_image(): t = Textile() result = t.image('!/imgs/myphoto.jpg!:http://jsamsa.com') @@ -17,5 +18,5 @@ def test_image(): t = Textile(rel='nofollow') result = t.image('!/imgs/myphoto.jpg!:http://jsamsa.com') expect = ('<a href="{0}1:url" rel="nofollow"><img alt="" src="{0}2:url" ' - '/></a>'.format(t.uid)) + '/></a>'.format(t.uid)) assert result == expect From ffb01d17084abda9ebe18a0e0d379d289b189ae4 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 19:18:41 -0500 Subject: [PATCH 039/228] flake8: whitespace cleanup --- tests/test_retrieve.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_retrieve.py b/tests/test_retrieve.py index 10bd173..a416524 100644 --- a/tests/test_retrieve.py +++ b/tests/test_retrieve.py @@ -1,5 +1,6 @@ from textile import Textile + def test_retrieve(): t = Textile() id = t.shelve("foobar") From d7f3f2a3c8e9fcd74f2c4a705c92c8f540c83d6c Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 19:22:20 -0500 Subject: [PATCH 040/228] flake8: whitespace cleanup --- tests/test_textile.py | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/tests/test_textile.py b/tests/test_textile.py index cc4ce84..a32fcbd 100644 --- a/tests/test_textile.py +++ b/tests/test_textile.py @@ -4,10 +4,12 @@ import re import textile + def test_FootnoteReference(): html = textile.textile('YACC[1]') assert re.search(r'^\t<p><span class="caps">YACC</span><sup class="footnote" id="fnrev([a-f0-9]{32})-1"><a href="#fn\1-1">1</a></sup></p>', html) is not None + def test_Footnote(): html = textile.textile('This is covered elsewhere[1].\n\nfn1. Down here, in fact.\n\nfn2. Here is another footnote.') assert re.search(r'^\t<p>This is covered elsewhere<sup class="footnote" id="fnrev([a-f0-9]{32})-1"><a href="#fn\1-1">1</a></sup>.</p>\n\n\t<p class="footnote" id="fn\1-1"><sup>1</sup> Down here, in fact.</p>\n\n\t<p class="footnote" id="fn\1-2"><sup>2</sup> Here is another footnote.</p>$', html) is not None @@ -24,6 +26,7 @@ def test_Footnote(): html = textile.textile('''See[4!] for details.\n\nfn4^. Here are the details.''') assert re.search(r'^\t<p>See<sup class="footnote" id="fnrev([a-f0-9]{32})-1">4</sup> for details.</p>\n\n\t<p class="footnote" id="fn\1-1"><sup><a href="#fnrev\1-1">4</a></sup> Here are the details.</p>$', html) is not None + def test_issue_35(): result = textile.textile('"z"') expect = '\t<p>“z” </p>' @@ -33,8 +36,9 @@ def test_issue_35(): expect = '\t<p>“ z” </p>' assert result == expect + def test_restricted(): - #Note that the HTML is escaped, thus rendering the <script> tag harmless. + # Note that the HTML is escaped, thus rendering the <script> tag harmless. test = "Here is some text.\n<script>alert('hello world')</script>" result = textile.textile_restricted(test) expect = "\t<p>Here is some text.<br />\n<script>alert(‘hello world’)</script></p>" @@ -93,10 +97,12 @@ def test_restricted(): assert result == expect + def test_unicode_footnote(): html = textile.textile('текст[1]') assert re.compile(r'^\t<p>текст<sup class="footnote" id="fnrev([a-f0-9]{32})-1"><a href="#fn\1-1">1</a></sup></p>$', re.U).search(html) is not None + def test_autolinking(): test = """some text "test":http://www.google.com http://www.google.com "$":http://www.google.com""" result = """\t<p>some text <a href="http://www.google.com">test</a> http://www.google.com <a href="http://www.google.com">www.google.com</a></p>""" @@ -104,6 +110,7 @@ def test_autolinking(): assert result == expect + def test_sanitize(): test = "a paragraph of benign text" result = "\t<p>a paragraph of benign text</p>" @@ -120,14 +127,16 @@ def test_sanitize(): expect = textile.Textile(html_type='html5').parse(test, sanitize=True) assert result == expect + def test_imagesize(): - PIL = pytest.importorskip('PIL') # noqa: F841 + PIL = pytest.importorskip('PIL') # noqa: F841 test = "!http://www.google.com/intl/en_ALL/images/srpr/logo1w.png!" result = '\t<p><img alt="" height="95" src="http://www.google.com/intl/en_ALL/images/srpr/logo1w.png" width="275" /></p>' expect = textile.Textile(get_sizes=True).parse(test) assert result == expect + def test_endnotes_simple(): test = """Scientists say the moon is slowly shrinking[#my_first_label].\n\nnotelist!.\n\nnote#my_first_label Over the past billion years, about a quarter of the moon's 4.5 billion-year lifespan, it has shrunk about 200 meters (700 feet) in diameter.""" html = textile.textile(test) @@ -135,6 +144,7 @@ def test_endnotes_simple(): result_re = re.compile(result_pattern) assert result_re.search(html) is not None + def test_endnotes_complex(): test = """Tim Berners-Lee is one of the pioneer voices in favour of Net Neutrality[#netneutral] and has expressed the view that ISPs should supply "connectivity with no strings attached"[#netneutral!] [#tbl_quote]\n\nBerners-Lee admitted that the forward slashes ("//") in a web address were actually unnecessary. He told the newspaper that he could easily have designed URLs not to have the forward slashes. "... it seemed like a good idea at the time,"[#slashes]\n\nnote#netneutral. "Web creator rejects net tracking":http://news.bbc.co.uk/2/hi/technology/7613201.stm. BBC. 15 September 2008\n\nnote#tbl_quote. "Web inventor's warning on spy software":http://www.telegraph.co.uk/news/uknews/1581938/Web-inventor%27s-warning-on-spy-software.html. The Daily Telegraph (London). 25 May 2008\n\nnote#slashes. "Berners-Lee 'sorry' for slashes":http://news.bbc.co.uk/1/hi/technology/8306631.stm. BBC. 14 October 2009\n\nnotelist.""" html = textile.textile(test) @@ -142,6 +152,7 @@ def test_endnotes_complex(): result_re = re.compile(result_pattern) assert result_re.search(html) is not None + def test_endnotes_unreferenced_note(): test = """Scientists say[#lavader] the moon is quite small. But I, for one, don't believe them. Others claim it to be made of cheese[#aardman]. If this proves true I suspect we are in for troubled times[#apollo13] as people argue over their "share" of the moon's cheese. In the end, its limited size[#lavader] may prove problematic.\n\nnote#lavader(noteclass). "Proof of the small moon hypothesis":http://antwrp.gsfc.nasa.gov/apod/ap080801.html. Copyright(c) Laurent Laveder\n\nnote#aardman(#noteid). "Proof of a cheese moon":http://www.imdb.com/title/tt0104361\n\nnote#apollo13. After all, things do go "wrong":http://en.wikipedia.org/wiki/Apollo_13#The_oxygen_tank_incident.\n\nnotelist{padding:1em; margin:1em; border-bottom:1px solid gray}.\n\nnotelist{padding:1em; margin:1em; border-bottom:1px solid gray}:§^.\n\nnotelist{padding:1em; margin:1em; border-bottom:1px solid gray}:‡""" html = textile.textile(test) @@ -149,6 +160,7 @@ def test_endnotes_unreferenced_note(): result_re = re.compile(result_pattern, re.U) assert result_re.search(html) is not None + def test_endnotes_malformed(): test = """Scientists say[#lavader] the moon is quite small. But I, for one, don't believe them. Others claim it to be made of cheese[#aardman]. If this proves true I suspect we are in for troubled times[#apollo13!] as people argue over their "share" of the moon's cheese. In the end, its limited size[#lavader] may prove problematic.\n\nnote#unused An unreferenced note.\n\nnote#lavader^ "Proof of the small moon hypothesis":http://antwrp.gsfc.nasa.gov/apod/ap080801.html. Copyright(c) Laurent Laveder\n\nnote#aardman^ "Proof of a cheese moon":http://www.imdb.com/title/tt0104361\n\nnote#apollo13^ After all, things do go "wrong":http://en.wikipedia.org/wiki/Apollo_13#The_oxygen_tank_incident.\n\nnotelist{padding:1em; margin:1em; border-bottom:1px solid gray}:α!+""" html = textile.textile(test) @@ -156,6 +168,7 @@ def test_endnotes_malformed(): result_re = re.compile(result_pattern, re.U) assert result_re.search(html) is not None + def test_endnotes_undefined_note(): test = """Scientists say the moon is slowly shrinking[#my_first_label].\n\nnotelist!.""" html = textile.textile(test) @@ -163,6 +176,7 @@ def test_endnotes_undefined_note(): result_re = re.compile(result_pattern) assert result_re.search(html) is not None + def test_encode_url(): # I tried adding these as doctests, but the unicode tests weren't # returning the correct results. @@ -198,21 +212,25 @@ def test_encode_url(): eurl = t.encode_url(url) assert eurl == result + def test_footnote_crosslink(): html = textile.textile('''See[2] for details, and later, reference it again[2].\n\nfn2^(footy#otherid)[en]. Here are the details.''') searchstring = r'\t<p>See<sup class="footnote" id="fnrev([a-f0-9]{32})-1"><a href="#fn\1-1">2</a></sup> for details, and later, reference it again<sup class="footnote"><a href="#fn\1-1">2</a></sup>.</p>\n\n\t<p class="footy" id="otherid" lang="en"><sup id="fn\1-1"><a href="#fnrev\1-1">2</a></sup> Here are the details.</p>$' assert re.compile(searchstring).search(html) is not None + def test_footnote_without_reflink(): html = textile.textile('''See[3!] for details.\n\nfn3. Here are the details.''') searchstring = r'^\t<p>See<sup class="footnote" id="fnrev([a-f0-9]{32})-1">3</sup> for details.</p>\n\n\t<p class="footnote" id="fn\1-1"><sup>3</sup> Here are the details.</p>$' assert re.compile(searchstring).search(html) is not None + def testSquareBrackets(): html = textile.textile("""1[^st^], 2[^nd^], 3[^rd^]. 2 log[~n~]\n\nA close[!http://textpattern.com/favicon.ico!]image.\nA tight["text":http://textpattern.com/]link.\nA ["footnoted link":http://textpattern.com/][182].""") searchstring = r'^\t<p>1<sup>st</sup>, 2<sup>nd</sup>, 3<sup>rd</sup>. 2 log<sub>n</sub></p>\n\n\t<p>A close<img alt="" src="http://textpattern.com/favicon.ico" />image.<br />\nA tight<a href="http://textpattern.com/">text</a>link.<br />\nA <a href="http://textpattern.com/">footnoted link</a><sup class="footnote" id="fnrev([a-f0-9]{32})-1"><a href="#fn\1-1">182</a></sup>.</p>' assert re.compile(searchstring).search(html) is not None + def test_html5(): """docstring for testHTML5""" @@ -221,6 +239,7 @@ def test_html5(): expect = textile.textile(test, html_type="html5") assert result == expect + def test_relURL(): t = textile.Textile() t.restricted = True From 76fda11365685ee425b82efa96d93cef1b813ed7 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 19:23:30 -0500 Subject: [PATCH 041/228] flake8: whitespace cleanup --- tests/test_urls.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/test_urls.py b/tests/test_urls.py index 39ff163..1cd09f9 100644 --- a/tests/test_urls.py +++ b/tests/test_urls.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- from textile import Textile + def test_urls(): t = Textile() assert t.relURL("http://www.google.com/") == 'http://www.google.com/' @@ -53,12 +54,14 @@ def test_urls(): expect = '\t<p>A link that <a href="/test/">contains a\nnewline</a> raises an exception.</p>' assert result == expect + def test_rel_attribute(): t = Textile(rel='nofollow') result = t.parse('"$":http://domain.tld') expect = '\t<p><a href="http://domain.tld" rel="nofollow">domain.tld</a></p>' assert result == expect + def test_quotes_in_link_text(): """quotes in link text are tricky.""" test = '""this is a quote in link text"":url' From d0b90435eb3f7f4bc17925544f962d14d92b908a Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 19:24:15 -0500 Subject: [PATCH 042/228] flake8: whitespace cleanup --- tests/test_lists.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_lists.py b/tests/test_lists.py index 4e85f4c..06d13c3 100644 --- a/tests/test_lists.py +++ b/tests/test_lists.py @@ -1,5 +1,6 @@ from textile import Textile + def test_lists(): t = Textile() result = t.textileLists("* one\n* two\n* three") From 391586aefe33e7acdb354f40547cc34f834f9b7d Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 19:24:51 -0500 Subject: [PATCH 043/228] flake8: whitespace cleanup --- tests/test_span.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_span.py b/tests/test_span.py index d83530d..aab0574 100644 --- a/tests/test_span.py +++ b/tests/test_span.py @@ -1,10 +1,11 @@ from textile import Textile + def test_span(): t = Textile() result = t.span("hello %(bob)span *strong* and **bold**% goodbye") expect = ('hello <span class="bob">span <strong>strong</strong> and ' - '<b>bold</b></span> goodbye') + '<b>bold</b></span> goodbye') assert result == expect result = t.span('%:http://domain.tld test%') From d1cb4089abbaf07db1a173ba52a00b8ebb61f1e2 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 19:34:54 -0500 Subject: [PATCH 044/228] flake8: whitespace cleanup --- tests/test_github_issues.py | 44 ++++++++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/tests/test_github_issues.py b/tests/test_github_issues.py index 2507e5f..c08d067 100644 --- a/tests/test_github_issues.py +++ b/tests/test_github_issues.py @@ -3,51 +3,60 @@ import textile + def test_github_issue_16(): result = textile.textile('"$":http://google.com "$":https://google.com "$":mailto:blackhole@sun.comet') expect = '\t<p><a href="http://google.com">google.com</a> <a href="https://google.com">google.com</a> <a href="mailto:blackhole%40sun.comet">blackhole@sun.comet</a></p>' assert result == expect + def test_github_issue_17(): result = textile.textile('!http://www.ox.ac.uk/favicon.ico!') expect = '\t<p><img alt="" src="http://www.ox.ac.uk/favicon.ico" /></p>' assert result == expect + def test_github_issue_20(): text = 'This is a link to a ["Wikipedia article about Textile":http://en.wikipedia.org/wiki/Textile_(markup_language)].' result = textile.textile(text) expect = '\t<p>This is a link to a <a href="http://en.wikipedia.org/wiki/Textile_%28markup_language%29">Wikipedia article about Textile</a>.</p>' assert result == expect + def test_github_issue_21(): - text = '''h1. xml example + text = ('''h1. xml example -bc. +bc. ''' + ''' <foo> bar -</foo>''' +</foo>''') result = textile.textile(text) expect = '\t<h1>xml example</h1>\n\n<pre><code>\n<foo>\n bar\n</foo></code></pre>' assert result == expect + def test_github_issue_22(): text = '''_(artist-name)Ty Segall_’s''' result = textile.textile(text) expect = '\t<p><em class="artist-name">Ty Segall</em>’s</p>' assert result == expect + def test_github_issue_26(): text = '' result = textile.textile(text) expect = '' assert result == expect + def test_github_issue_27(): test = """* Folders with ":" in their names are displayed with a forward slash "/" instead. (Filed as "#4581709":/test/link, which was considered "normal behaviour" - quote: "Please note that Finder presents the 'Carbon filesystem' view, regardless of the underlying filesystem.")""" result = textile.textile(test) expect = """\t<ul>\n\t\t<li>Folders with “:” in their names are displayed with a forward slash “/” instead. (Filed as <a href="/test/link">#4581709</a>, which was considered “normal behaviour” – quote: “Please note that Finder presents the ‘Carbon filesystem’ view, regardless of the underlying filesystem.”)</li>\n\t</ul>""" assert result == expect + def test_github_issue_28(): test = """So here I am porting my ancient "newspipe":newspipe "front-end":blog/2006/09/30/0950 to "Snakelets":Snakelets and "Python":Python, and I've just trimmed down over 20 lines of "PHP":PHP down to essentially one line of "BeautifulSoup":BeautifulSoup retrieval: @@ -80,23 +89,26 @@ def parseWapProfile(self, url): \t<p>Of course there’s a lot more error handling to do (and useful data to glean off the <a href="XML"><span class="caps">XML</span></a>), but being able to cut through all the usual parsing crap is immensely gratifying.</p>""") assert result == expect + def test_github_issue_30(): - text ='"Tëxtíle (Tëxtíle)":http://lala.com' + text = '"Tëxtíle (Tëxtíle)":http://lala.com' result = textile.textile(text) expect = '\t<p><a href="http://lala.com" title="Tëxtíle">Tëxtíle</a></p>' assert result == expect - text ='!http://lala.com/lol.gif(♡ imáges)!' + text = '!http://lala.com/lol.gif(♡ imáges)!' result = textile.textile(text) expect = '\t<p><img alt="♡ imáges" src="http://lala.com/lol.gif" title="♡ imáges" /></p>' assert result == expect + def test_github_issue_36(): text = '"Chögyam Trungpa":https://www.google.com/search?q=Chögyam+Trungpa' result = textile.textile(text) expect = '\t<p><a href="https://www.google.com/search?q=Chögyam+Trungpa">Chögyam Trungpa</a></p>' assert result == expect + def test_github_issue_37(): text = '# xxx\n# yyy\n*blah*' result = textile.textile(text) @@ -118,24 +130,28 @@ def test_github_issue_37(): \t</ul>''' assert result == expect + def test_github_issue_40(): text = '\r\n' result = textile.textile(text) expect = '\r\n' assert result == expect + def test_github_issue_42(): text = '!./image.png!' result = textile.textile(text) expect = '\t<p><img alt="" src="./image.png" /></p>' assert result == expect + def test_github_issue_43(): text = 'pre. smart ‘quotes’ are not smart!' result = textile.textile(text) expect = '<pre>smart ‘quotes’ are not smart!</pre>' assert result == expect + def test_github_issue_45(): """Incorrect transform unicode url""" text = '"test":https://myabstractwiki.ru/index.php/%D0%97%D0%B0%D0%B3%D0%BB%D0%B0%D0%B2%D0%BD%D0%B0%D1%8F_%D1%81%D1%82%D1%80%D0%B0%D0%BD%D0%B8%D1%86%D0%B0' @@ -143,6 +159,7 @@ def test_github_issue_45(): expect = '\t<p><a href="https://myabstractwiki.ru/index.php/%D0%97%D0%B0%D0%B3%D0%BB%D0%B0%D0%B2%D0%BD%D0%B0%D1%8F_%D1%81%D1%82%D1%80%D0%B0%D0%BD%D0%B8%D1%86%D0%B0">test</a></p>' assert result == expect + def test_github_issue_46(): """Key error on mal-formed numbered lists. CAUTION: both the input and the ouput are ugly.""" @@ -153,6 +170,7 @@ def test_github_issue_46(): result = textile.textile(text) assert result == expect + def test_github_issue_47(): """Incorrect wrap pre-formatted value""" text = '''pre.. word @@ -172,6 +190,7 @@ def test_github_issue_47(): yet anothe word</pre>''' assert result == expect + def test_github_issue_49(): """Key error on russian hash-route link""" s = '"link":https://ru.vuejs.org/v2/guide/components.html#Входные-параметры' @@ -179,6 +198,7 @@ def test_github_issue_49(): expect = '\t<p><a href="https://ru.vuejs.org/v2/guide/components.html#Входные-параметры">link</a></p>' assert result == expect + def test_github_issue_50(): """Incorrect wrap code with Java generics in pre""" test = ('pre.. public class Tynopet<T extends Framework> {}\n\nfinal ' @@ -189,6 +209,7 @@ def test_github_issue_50(): 'ArrayList<>();</pre>') assert result == expect + def test_github_issue_51(): """Link build with $ sign without "http" prefix broken.""" test = '"$":www.google.com.br' @@ -196,6 +217,7 @@ def test_github_issue_51(): expect = '\t<p><a href="www.google.com.br">www.google.com.br</a></p>' assert result == expect + def test_github_issue_52(): """Table build without space after aligment raise a AttributeError.""" test = '|=.First Header |=. Second Header |' @@ -205,6 +227,7 @@ def test_github_issue_52(): '\n\t\t</tr>\n\t</table>') assert result == expect + def test_github_issue_55(): """Incorrect handling of quote entities in extended pre block""" test = ('pre.. this is the first line\n\nbut "quotes" in an extended pre ' @@ -258,15 +281,17 @@ def test_github_issue_55(): 'return configs;\n}\n}</pre>') assert result == expect + def test_github_issue_56(): """Empty description lists throw error""" result = textile.textile("- :=\n-") expect = '<dl>\n</dl>' assert result == expect + def test_github_pull_61(): """Fixed code block multiline encoding on quotes/span""" - test = '''bc.. This is some TEXT inside a "Code BLOCK" + test = ('''bc.. This is some TEXT inside a "Code BLOCK" { if (JSON) { @@ -275,11 +300,12 @@ def test_github_pull_61(): } } -Back to 10-4 CAPS +Back to 10-4 CAPS ''' + ''' p.. Some multiline Paragragh -Here is some output!!! "Some" CAPS''' +Here is some output!!! "Some" CAPS''') expect = '''<pre><code>This is some TEXT inside a "Code BLOCK" @@ -299,6 +325,7 @@ def test_github_pull_61(): result = t.parse(test) assert result == expect + def test_github_pull_62(): """Fix for paragraph multiline, only last paragraph is rendered correctly""" @@ -341,6 +368,7 @@ def test_github_pull_62(): result = t.parse(test) assert result == expect + def test_github_pull_63(): """Forgot to set multiline_para to False""" test = '''p.. First one 'is' From ed17a4f0fd10acbb51a8a6c8af47efa7fa0fa6d4 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 19:36:35 -0500 Subject: [PATCH 045/228] flake8: whitespace cleanup --- tests/test_cli.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/test_cli.py b/tests/test_cli.py index 5f6e501..57b2d5f 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -3,28 +3,30 @@ import textile + def test_console_script(): command = [sys.executable, '-m', 'textile', 'README.textile'] try: result = subprocess.check_output(command) except AttributeError: command[2] = 'textile.__main__' - result = subprocess.Popen(command, - stdout=subprocess.PIPE).communicate()[0] + result = subprocess.Popen( + command, stdout=subprocess.PIPE).communicate()[0] with open('tests/fixtures/README.txt') as f: expect = ''.join(f.readlines()) if type(result) == bytes: result = result.decode('utf-8') assert result == expect + def test_version_string(): command = [sys.executable, '-m', 'textile', '-v'] try: result = subprocess.check_output(command) except AttributeError: command[2] = 'textile.__main__' - result = subprocess.Popen(command, - stdout=subprocess.PIPE).communicate()[0] + result = subprocess.Popen( + command, stdout=subprocess.PIPE).communicate()[0] if type(result) == bytes: result = result.decode('utf-8') assert result.strip() == textile.__version__ From b010a31482fceeb0fb228c959b18ce48be68aed1 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 19:52:50 -0500 Subject: [PATCH 046/228] flake8: whitespace cleanup --- tests/test_attributes.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_attributes.py b/tests/test_attributes.py index 0f5d019..b07712f 100644 --- a/tests/test_attributes.py +++ b/tests/test_attributes.py @@ -1,5 +1,6 @@ from textile.utils import parse_attributes + def test_parse_attributes(): assert parse_attributes('\\1', element='td') == {'colspan': '1'} assert parse_attributes('/1', element='td') == {'rowspan': '1'} From ba71882313124d17d9d6deb87fd10a8cc250dbee Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 19:53:42 -0500 Subject: [PATCH 047/228] flake8: whitespace cleanup --- tests/test_getimagesize.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_getimagesize.py b/tests/test_getimagesize.py index 43f85e3..3a3c0a9 100644 --- a/tests/test_getimagesize.py +++ b/tests/test_getimagesize.py @@ -3,6 +3,7 @@ PIL = pytest.importorskip('PIL') + def test_imagesize(): assert getimagesize("http://www.google.com/intl/en_ALL/images/logo.gif") == (276, 110) assert getimagesize("http://bad.domain/") == '' From 00ac6423fcecc6d164a2cde1bb547dca8c0c59f8 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 19:55:41 -0500 Subject: [PATCH 048/228] flake8: whitespace cleanup --- tests/test_glyphs.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_glyphs.py b/tests/test_glyphs.py index 56b0d27..ed50ad5 100644 --- a/tests/test_glyphs.py +++ b/tests/test_glyphs.py @@ -1,5 +1,6 @@ from textile import Textile + def test_glyphs(): t = Textile() From 19936d813275f19465fbb695559961f72a983cab Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 19:57:52 -0500 Subject: [PATCH 049/228] flake8: whitespace cleanup --- tests/test_subclassing.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_subclassing.py b/tests/test_subclassing.py index 9235e03..a7db99a 100644 --- a/tests/test_subclassing.py +++ b/tests/test_subclassing.py @@ -1,10 +1,10 @@ import textile + def test_change_glyphs(): class TextilePL(textile.Textile): glyph_definitions = dict(textile.Textile.glyph_definitions, - quote_double_open = '„' - ) + quote_double_open='„') test = 'Test "quotes".' expect = '\t<p>Test „quotes”.</p>' From 5c61671087751271a37c8283c02e84a4f0f505df Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 19:58:40 -0500 Subject: [PATCH 050/228] flake8: whitespace cleanup --- tests/test_imagesize.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_imagesize.py b/tests/test_imagesize.py index 0a89b43..e7d9d88 100644 --- a/tests/test_imagesize.py +++ b/tests/test_imagesize.py @@ -1,10 +1,11 @@ import textile + def test_imagesize(): imgurl = 'http://www.google.com/intl/en_ALL/images/srpr/logo1w.png' result = textile.tools.imagesize.getimagesize(imgurl) try: - import PIL # noqa: F401 + import PIL # noqa: F401 expect = (275, 95) assert result == expect From 8db86d6149017837e1ad7d91d09303b70e2cfb75 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 20:17:17 -0500 Subject: [PATCH 051/228] flake8: whitespace cleanup --- tests/test_textilefactory.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_textilefactory.py b/tests/test_textilefactory.py index 846b927..e9fc027 100644 --- a/tests/test_textilefactory.py +++ b/tests/test_textilefactory.py @@ -1,6 +1,7 @@ from textile import textilefactory import pytest + def test_TextileFactory(): f = textilefactory.TextileFactory() result = f.process("some text here") From db0626b36f14a85b715d00f19b51ef43eece419f Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 20:18:01 -0500 Subject: [PATCH 052/228] flake8: whitespace cleanup --- tests/test_getRefs.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_getRefs.py b/tests/test_getRefs.py index d3cfcd7..8a22d4f 100644 --- a/tests/test_getRefs.py +++ b/tests/test_getRefs.py @@ -1,5 +1,6 @@ from textile import Textile + def test_getRefs(): t = Textile() result = t.getRefs("some text [Google]http://www.google.com") From d6d7f995aa35187215bea5a83ce5b7dab8305f9d Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 20:18:34 -0500 Subject: [PATCH 053/228] flake8: whitespace cleanup --- tests/test_footnoteRef.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_footnoteRef.py b/tests/test_footnoteRef.py index c973ee7..5ac2ea4 100644 --- a/tests/test_footnoteRef.py +++ b/tests/test_footnoteRef.py @@ -1,5 +1,6 @@ from textile import Textile + def test_footnoteRef(): t = Textile() result = t.footnoteRef('foo[1]') From 325014da65763d42dc86c50a99f17512ec143373 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 20:19:14 -0500 Subject: [PATCH 054/228] flake8: whitespace cleanup --- tests/test_table.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_table.py b/tests/test_table.py index 0a3cb0d..1ea34e9 100644 --- a/tests/test_table.py +++ b/tests/test_table.py @@ -1,5 +1,6 @@ from textile import Textile + def test_table(): t = Textile() result = t.table('(rowclass). |one|two|three|\n|a|b|c|') From cbb6bb71cf7c6165aadd1b29c59331542039e233 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 20:21:12 -0500 Subject: [PATCH 055/228] flake8: whitespace cleanup --- tests/test_values.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/test_values.py b/tests/test_values.py index 063ed3e..5b2b7e3 100644 --- a/tests/test_values.py +++ b/tests/test_values.py @@ -309,12 +309,14 @@ ('Hello ["(Mum) & dad"]', '\t<p>Hello [“(Mum) & dad”]</p>'), ) + @pytest.mark.parametrize("input, expected_output", xhtml_known_values) def test_KnownValuesXHTML(input, expected_output): # XHTML output = textile.textile(input, html_type='xhtml') assert output == expected_output + @pytest.mark.parametrize("input, expected_output", html_known_values) def test_KnownValuesHTML(input, expected_output): # HTML5 From 8cb3bf7b95f49af518a4a3231043f35be9788835 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 20:21:42 -0500 Subject: [PATCH 056/228] flake8: whitespace cleanup --- tests/test_values.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/test_values.py b/tests/test_values.py index 5b2b7e3..12dae55 100644 --- a/tests/test_values.py +++ b/tests/test_values.py @@ -35,7 +35,7 @@ ('h3. Header 3', '\t<h3>Header 3</h3>'), ('An old text\n\nbq. A block quotation.\n\nAny old text''', - '\t<p>An old text</p>\n\n\t<blockquote>\n\t\t<p>A block quotation.</p>\n\t</blockquote>\n\n\t<p>Any old text</p>'), + '\t<p>An old text</p>\n\n\t<blockquote>\n\t\t<p>A block quotation.</p>\n\t</blockquote>\n\n\t<p>Any old text</p>'), ('I _believe_ every word.', '\t<p>I <em>believe</em> every word.</p>'), @@ -70,8 +70,8 @@ ('p[fr]. rouge', '\t<p lang="fr">rouge</p>'), ('I seriously *{color:red}blushed*\nwhen I _(big)sprouted_ that\ncorn stalk from my\n%[es]cabeza%.', - '\t<p>I seriously <strong style="color:red;">blushed</strong><br />\nwhen I <em class="big">sprouted</em>' - ' that<br />\ncorn stalk from my<br />\n<span lang="es">cabeza</span>.</p>'), + '\t<p>I seriously <strong style="color:red;">blushed</strong><br />\nwhen I <em class="big">sprouted</em>' + ' that<br />\ncorn stalk from my<br />\n<span lang="es">cabeza</span>.</p>'), ('p<. align left', '\t<p style="text-align:left;">align left</p>'), @@ -226,7 +226,7 @@ ("""table(#dvds){border-collapse:collapse}. Great films on DVD employing Textile summary, caption, thead, tfoot, two tbody elements and colgroups\n|={font-size:140%;margin-bottom:15px}. DVDs with two Textiled tbody elements\n|:\\3. 100 |{background:#ddd}|250||50|300|\n|^(header).\n|_. Title |_. Starring |_. Director |_. Writer |_. Notes |\n|~(footer).\n|\\5=. This is the tfoot, centred |\n|-(toplist){background:#c5f7f6}.\n| _The Usual Suspects_ | Benicio Del Toro, Gabriel Byrne, Stephen Baldwin, Kevin Spacey | Bryan Singer | Chris McQaurrie | One of the finest films ever made |\n| _Se7en_ | Morgan Freeman, Brad Pitt, Kevin Spacey | David Fincher | Andrew Kevin Walker | Great psychological thriller |\n| _Primer_ | David Sullivan, Shane Carruth | Shane Carruth | Shane Carruth | Amazing insight into trust and human psychology <br />rather than science fiction. Terrific! |\n| _District 9_ | Sharlto Copley, Jason Cope | Neill Blomkamp | Neill Blomkamp, Terri Tatchell | Social commentary layered on thick,\nbut boy is it done well |\n|-(medlist){background:#e7e895;}.\n| _Arlington Road_ | Tim Robbins, Jeff Bridges | Mark Pellington | Ehren Kruger | Awesome study in neighbourly relations |\n| _Phone Booth_ | Colin Farrell, Kiefer Sutherland, Forest Whitaker | Joel Schumacher | Larry Cohen | Edge-of-the-seat stuff in this\nshort but brilliantly executed thriller |""", """\t<table id="dvds" style="border-collapse:collapse;" summary="Great films on DVD employing Textile summary, caption, thead, tfoot, two tbody elements and colgroups">\n\t<caption style="font-size:140%; margin-bottom:15px;"><span class="caps">DVD</span>s with two Textiled tbody elements</caption>\n\t<colgroup span="3" width="100">\n\t<col style="background:#ddd;" />\n\t<col width="250" />\n\t<col />\n\t<col width="50" />\n\t<col width="300" />\n\t</colgroup>\n\t<thead class="header">\n\t\t<tr>\n\t\t\t<th>Title </th>\n\t\t\t<th>Starring </th>\n\t\t\t<th>Director </th>\n\t\t\t<th>Writer </th>\n\t\t\t<th>Notes </th>\n\t\t</tr>\n\t</thead>\n\t<tfoot class="footer">\n\t\t<tr>\n\t\t\t<td colspan="5" style="text-align:center;">This is the tfoot, centred </td>\n\t\t</tr>\n\t</tfoot>\n\t<tbody class="toplist" style="background:#c5f7f6;">\n\t\t<tr>\n\t\t\t<td> <em>The Usual Suspects</em> </td>\n\t\t\t<td> Benicio Del Toro, Gabriel Byrne, Stephen Baldwin, Kevin Spacey </td>\n\t\t\t<td> Bryan Singer </td>\n\t\t\t<td> Chris McQaurrie </td>\n\t\t\t<td> One of the finest films ever made </td>\n\t\t</tr>\n\t\t<tr>\n\t\t\t<td> <em>Se7en</em> </td>\n\t\t\t<td> Morgan Freeman, Brad Pitt, Kevin Spacey </td>\n\t\t\t<td> David Fincher </td>\n\t\t\t<td> Andrew Kevin Walker </td>\n\t\t\t<td> Great psychological thriller </td>\n\t\t</tr>\n\t\t<tr>\n\t\t\t<td> <em>Primer</em> </td>\n\t\t\t<td> David Sullivan, Shane Carruth </td>\n\t\t\t<td> Shane Carruth </td>\n\t\t\t<td> Shane Carruth </td>\n\t\t\t<td> Amazing insight into trust and human psychology <br />\nrather than science fiction. Terrific! </td>\n\t\t</tr>\n\t\t<tr>\n\t\t\t<td> <em>District 9</em> </td>\n\t\t\t<td> Sharlto Copley, Jason Cope </td>\n\t\t\t<td> Neill Blomkamp </td>\n\t\t\t<td> Neill Blomkamp, Terri Tatchell </td>\n\t\t\t<td> Social commentary layered on thick,<br />\nbut boy is it done well </td>\n\t\t</tr>\n\t</tbody>\n\t<tbody class="medlist" style="background:#e7e895;">\n\t\t<tr>\n\t\t\t<td> <em>Arlington Road</em> </td>\n\t\t\t<td> Tim Robbins, Jeff Bridges </td>\n\t\t\t<td> Mark Pellington </td>\n\t\t\t<td> Ehren Kruger </td>\n\t\t\t<td> Awesome study in neighbourly relations </td>\n\t\t</tr>\n\t\t<tr>\n\t\t\t<td> <em>Phone Booth</em> </td>\n\t\t\t<td> Colin Farrell, Kiefer Sutherland, Forest Whitaker </td>\n\t\t\t<td> Joel Schumacher </td>\n\t\t\t<td> Larry Cohen </td>\n\t\t\t<td> Edge-of-the-seat stuff in this<br />\nshort but brilliantly executed thriller </td>\n\t\t</tr>\n\t</tbody>\n\t</table>"""), ("""-(hot) *coffee* := Hot _and_ black\n-(hot#tea) tea := Also hot, but a little less black\n-(cold) milk := Nourishing beverage for baby cows.\nCold drink that goes great with cookies. =:\n\n-(hot) coffee := Hot and black\n-(hot#tea) tea := Also hot, but a little less black\n-(cold) milk :=\nNourishing beverage for baby cows.\nCold drink that goes great with cookies. =:""", - """<dl>\n\t<dt class="hot"><strong>coffee</strong></dt>\n\t<dd>Hot <em>and</em> black</dd>\n\t<dt class="hot" id="tea">tea</dt>\n\t<dd>Also hot, but a little less black</dd>\n\t<dt class="cold">milk</dt>\n\t<dd>Nourishing beverage for baby cows.<br />\nCold drink that goes great with cookies.</dd>\n</dl>\n\n<dl>\n\t<dt class="hot">coffee</dt>\n\t<dd>Hot and black</dd>\n\t<dt class="hot" id="tea">tea</dt>\n\t<dd>Also hot, but a little less black</dd>\n\t<dt class="cold">milk</dt>\n\t<dd><p>Nourishing beverage for baby cows.<br />\nCold drink that goes great with cookies.</p></dd>\n</dl>"""), + """<dl>\n\t<dt class="hot"><strong>coffee</strong></dt>\n\t<dd>Hot <em>and</em> black</dd>\n\t<dt class="hot" id="tea">tea</dt>\n\t<dd>Also hot, but a little less black</dd>\n\t<dt class="cold">milk</dt>\n\t<dd>Nourishing beverage for baby cows.<br />\nCold drink that goes great with cookies.</dd>\n</dl>\n\n<dl>\n\t<dt class="hot">coffee</dt>\n\t<dd>Hot and black</dd>\n\t<dt class="hot" id="tea">tea</dt>\n\t<dd>Also hot, but a little less black</dd>\n\t<dt class="cold">milk</dt>\n\t<dd><p>Nourishing beverage for baby cows.<br />\nCold drink that goes great with cookies.</p></dd>\n</dl>"""), (""";(class#id) Term 1\n: Def 1\n: Def 2\n: Def 3""", """\t<dl class="class" id="id">\n\t\t<dt>Term 1</dt>\n\t\t<dd>Def 1</dd>\n\t\t<dd>Def 2</dd>\n\t\t<dd>Def 3</dd>\n\t</dl>"""), ("""*Here is a comment*\n\nHere is *(class)a comment*\n\n*(class)Here is a class* that is a little extended and is\n*followed* by a strong word!\n\nbc. ; Content-type: text/javascript\n; Cache-Control: no-store, no-cache, must-revalidate, pre-check=0, post-check=0, max-age=0\n; Expires: Sat, 24 Jul 2003 05:00:00 GMT\n; Last-Modified: Wed, 1 Jan 2025 05:00:00 GMT\n; Pragma: no-cache\n\n*123 test*\n\n*test 123*\n\n**123 test**\n\n**test 123**""", @@ -258,8 +258,8 @@ ('I __know__.\nI **really** __know__.', '\t<p>I <i>know</i>.<br />\nI <b>really</b> <i>know</i>.</p>'), ("I'm %{color:red}unaware%\nof most soft drinks.", '\t<p>I’m <span style="color:red;">unaware</span><br />\nof most soft drinks.</p>'), ('I seriously *{color:red}blushed*\nwhen I _(big)sprouted_ that\ncorn stalk from my\n%[es]cabeza%.', - '\t<p>I seriously <strong style="color:red;">blushed</strong><br />\nwhen I <em class="big">sprouted</em>' - ' that<br />\ncorn stalk from my<br />\n<span lang="es">cabeza</span>.</p>'), + '\t<p>I seriously <strong style="color:red;">blushed</strong><br />\nwhen I <em class="big">sprouted</em>' + ' that<br />\ncorn stalk from my<br />\n<span lang="es">cabeza</span>.</p>'), ('<pre>\n<code>\na.gsub!( /</, "" )\n</code>\n</pre>', '<pre>\n<code>\na.gsub!( /</, "" )\n</code>\n</pre>'), ('<div style="float:right;">\n\nh3. Sidebar\n\n"Hobix":http://hobix.com/\n"Ruby":http://ruby-lang.org/\n\n</div>\n\n' From 1b65dc74c51493f6167d661338112b5506c11f33 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 20:22:50 -0500 Subject: [PATCH 057/228] flake8: whitespace cleanup --- tests/test_utils.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/test_utils.py b/tests/test_utils.py index 7f386a9..a6e88f8 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -3,21 +3,25 @@ from textile import utils + def test_encode_html(): result = utils.encode_html('''this is a "test" of text that's safe to ''' - 'put in an <html> attribute.') + 'put in an <html> attribute.') expect = ('this is a "test" of text that's safe to put in ' - 'an <html> attribute.') + 'an <html> attribute.') assert result == expect + def test_has_raw_text(): assert utils.has_raw_text('<p>foo bar biz baz</p>') is False assert utils.has_raw_text(' why yes, yes it does') is True + def test_is_rel_url(): assert utils.is_rel_url("http://www.google.com/") is False assert utils.is_rel_url("/foo") is True + def test_generate_tag(): result = utils.generate_tag('span', 'inner text', {'class': 'test'}) expect = '<span class="test">inner text</span>' From 30c4eef40b5a5391802287a49f34e73914a5425e Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 20:25:27 -0500 Subject: [PATCH 058/228] turn flake8 warnings and errors back on --- .flake8 | 3 --- 1 file changed, 3 deletions(-) diff --git a/.flake8 b/.flake8 index 07c24d1..9f8ccc3 100644 --- a/.flake8 +++ b/.flake8 @@ -2,8 +2,5 @@ ignore = # line too long E501 - # temporarily ignore warnings and errors - W - E exclude = build/ From 157ff1edac900c1135ca0b6f2d6f5830ffa60894 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 20:35:36 -0500 Subject: [PATCH 059/228] use environment vars as intended maybe this will fix a warning about unexpected inputs ??? --- .github/workflows/lint_and_test.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/lint_and_test.yml b/.github/workflows/lint_and_test.yml index 437820d..f82f4c2 100644 --- a/.github/workflows/lint_and_test.yml +++ b/.github/workflows/lint_and_test.yml @@ -20,9 +20,10 @@ jobs: - name: Python flake8 Lint uses: py-actions/flake8@v1.2.0 - name: Install dependencies + env: + IMAGESIZE: ${{ matrix.image-size }} run: | imagesize='' - IMAGESIZE=${{ matrix.image-size }} pip install -U pytest pytest-cov coverage codecov if [[ $IMAGESIZE == true ]] ; then imagesize='[imagesize]' ; fi pip install -e ".${imagesize}" From a4b09ab08ec36087ac66c694f185545a76e31ca0 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 20:42:24 -0500 Subject: [PATCH 060/228] further testing of environment variables --- .github/workflows/lint_and_test.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/lint_and_test.yml b/.github/workflows/lint_and_test.yml index f82f4c2..5ba1e86 100644 --- a/.github/workflows/lint_and_test.yml +++ b/.github/workflows/lint_and_test.yml @@ -9,7 +9,7 @@ jobs: strategy: matrix: python-version: [3.5, 3.6, 3.7, 3.8, 3.9] - image-size: ['true', 'false'] + image_size: ['true', 'false'] steps: - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} @@ -20,12 +20,10 @@ jobs: - name: Python flake8 Lint uses: py-actions/flake8@v1.2.0 - name: Install dependencies - env: - IMAGESIZE: ${{ matrix.image-size }} run: | imagesize='' pip install -U pytest pytest-cov coverage codecov - if [[ $IMAGESIZE == true ]] ; then imagesize='[imagesize]' ; fi + if [[ $INPUT_IMAGE_SIZE == true ]] ; then imagesize='[imagesize]' ; fi pip install -e ".${imagesize}" - name: run tests run: | From 674ed26891970f213a0e692cab1e21da502348a3 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 20:44:45 -0500 Subject: [PATCH 061/228] This is probably the source of the `unexpected inputs` warning --- .github/workflows/lint_and_test.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/lint_and_test.yml b/.github/workflows/lint_and_test.yml index 5ba1e86..47ce8cf 100644 --- a/.github/workflows/lint_and_test.yml +++ b/.github/workflows/lint_and_test.yml @@ -16,7 +16,6 @@ jobs: uses: actions/setup-python@v2 with: python-version: ${{ matrix.python-version }} - image-size: ${{ matrix.image-size }} - name: Python flake8 Lint uses: py-actions/flake8@v1.2.0 - name: Install dependencies From 16b70594b23947ccc3c4154369727215e0a7e4b6 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 20:49:10 -0500 Subject: [PATCH 062/228] add pypy3 to github actions --- .github/workflows/lint_and_test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint_and_test.yml b/.github/workflows/lint_and_test.yml index 47ce8cf..0db7ea8 100644 --- a/.github/workflows/lint_and_test.yml +++ b/.github/workflows/lint_and_test.yml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.5, 3.6, 3.7, 3.8, 3.9] + python-version: [3.5, 3.6, 3.7, 3.8, 3.9, pypy3] image_size: ['true', 'false'] steps: - uses: actions/checkout@v2 From cdfa9490bea32b33bde09dac83ff3b219fafd4d8 Mon Sep 17 00:00:00 2001 From: Kirill Mavreshko <kimavr@gmail.com> Date: Wed, 31 Aug 2022 12:56:49 +0500 Subject: [PATCH 063/228] Fix wrong output when "bc.." is the last block #81 --- tests/fixtures/README.txt | 2 +- tests/test_values.py | 8 ++++++++ textile/core.py | 9 ++++----- 3 files changed, 13 insertions(+), 6 deletions(-) diff --git a/tests/fixtures/README.txt b/tests/fixtures/README.txt index 61dc0f0..949db70 100644 --- a/tests/fixtures/README.txt +++ b/tests/fixtures/README.txt @@ -60,4 +60,4 @@ When textile is not installed locally:</code></pre> -<pre>PYTHONPATH=. pytest</pre> \ No newline at end of file +<pre><code>PYTHONPATH=. pytest</code></pre> \ No newline at end of file diff --git a/tests/test_values.py b/tests/test_values.py index 063ed3e..9792881 100644 --- a/tests/test_values.py +++ b/tests/test_values.py @@ -254,6 +254,14 @@ # A few extra cases for HTML4 html_known_values = ( + ("pre.. The beginning\n\nbc.. This code\n\nis the last\n\nblock in the document\n", + "<pre>The beginning</pre>\n\n<pre><code>This code\n\nis the last\n\nblock in the document</code></pre>"), + ("bc.. This code\n\nis not\n\nsurrounded by anything\n", + "<pre><code>This code\n\nis not\n\nsurrounded by anything</code></pre>"), + ("bc.. Paragraph 1\n\nParagraph 2\n\nParagraph 3\n\np.. post-code paragraph", + "<pre><code>Paragraph 1\n\nParagraph 2\n\nParagraph 3</code></pre>\n\n<p>post-code paragraph</p>"), + ("bc.. Paragraph 1\n\nParagraph 2\n\nParagraph 3\n\npre.. post-code non-p block", + "<pre><code>Paragraph 1\n\nParagraph 2\n\nParagraph 3</code></pre>\n\n<pre>post-code non-p block</pre>"), ('I spoke.\nAnd none replied.', '\t<p>I spoke.<br />\nAnd none replied.</p>'), ('I __know__.\nI **really** __know__.', '\t<p>I <i>know</i>.<br />\nI <b>really</b> <i>know</i>.</p>'), ("I'm %{color:red}unaware%\nof most soft drinks.", '\t<p>I’m <span style="color:red;">unaware</span><br />\nof most soft drinks.</p>'), diff --git a/textile/core.py b/textile/core.py index 7b66af0..bf0871d 100644 --- a/textile/core.py +++ b/textile/core.py @@ -559,11 +559,10 @@ def block(self, text): # at this point, we've gone through all the lines. if there's still an # extension in effect, we close it here if ext and out and not block.tag == 'p': - block.content = out.pop() - block.process() - final = generate_tag(block.outer_tag, block.content, - block.outer_atts) - out.append(final) + content = out.pop() + content = generate_tag(block.inner_tag, content, block.inner_atts) + content = generate_tag(block.outer_tag, content, block.outer_atts) + out.append(content) return ''.join(out) def footnoteRef(self, text): From 22d7e7fec2bd9886eceddf7995e1cecb76dc2415 Mon Sep 17 00:00:00 2001 From: Kirill Mavreshko <kimavr@gmail.com> Date: Sun, 4 Sep 2022 22:51:34 +0500 Subject: [PATCH 064/228] Unite glyph regexes and replacements in one structure --- textile/core.py | 241 +++++++++++++++++++++++++----------------------- 1 file changed, 128 insertions(+), 113 deletions(-) diff --git a/textile/core.py b/textile/core.py index bf0871d..5e74b42 100644 --- a/textile/core.py +++ b/textile/core.py @@ -35,6 +35,122 @@ import re +def make_glyph_replacers(html_type, uid, glyph_defs, is_initial): + """ + Generates a list of "replacers" (each is a pair consiting of + a regular expression and a replacing pattern) that, + when applied sequentially, replace some characters of the original + text with their HTML codes to produce valid HTML. + """ + cur = ( + r'(?:[{0}]{1}*)?'.format(regex_snippets['cur'], regex_snippets['space']) + if regex_snippets['cur'] + else r'') + pre_result = [ + # apostrophe's + (re.compile( + (r"(^|{0}|\))'({0})" if not is_initial + else r"({0}|\))'({0})") + .format(regex_snippets['wrd']), + flags=re.U), + r'\1{apostrophe}\2'), + # back in '88 + (re.compile( + r"({0})'(\d+{1}?)\b(?![.]?[{1}]*?')".format( + regex_snippets['space'], regex_snippets['wrd']), + flags=re.U), + r'\1{apostrophe}\2'), + # single opening following an open bracket. + (re.compile(r"([([{])'(?=\S)", flags=re.U), + r'\1{quote_single_open}'), + # single closing + (re.compile( + (r"(^|\S)'(?={0}|{1}|<|$)".format( + regex_snippets['space'], pnct_re_s) + if not is_initial + else r"(\S)'(?={0}|{1}|$)".format( + regex_snippets['space'], pnct_re_s)), + flags=re.U), + r'\1{quote_single_close}'), + # single opening + (re.compile(r"'", re.U), r'{quote_single_open}'), + # double opening following an open bracket. Allows things like + # Hello ["(Mum) & dad"] + (re.compile(r'([([{])"(?=\S)', flags=re.U), + r'\1{quote_double_open}'), + # double closing + (re.compile( + (r'(^|\S)"(?={0}|{1}|<|$)'.format( + regex_snippets['space'], pnct_re_s) + if not is_initial + else r'(\S)"(?={0}|{1}|<|$)'.format( + regex_snippets['space'], pnct_re_s)), + flags=re.U), + r'\1{quote_double_close}'), + # double opening + (re.compile(r'"'), r'{quote_double_open}'), + # ellipsis + (re.compile(r'([^.]?)\.{3}'), r'\1{ellipsis}'), + # ampersand + (re.compile(r'(\s?)&(\s)', re.U), r'\1{ampersand}\2'), + # em dash + (re.compile(r'(\s?)--(\s?)'), r'\1{emdash}\2'), + # en dash + (re.compile(r' - '), r' {endash} '), + # dimension sign + (re.compile( + r'([0-9]+[\])]?[\'"]? ?)[x]( ?[\[(]?)' + r'(?=[+-]?{0}[0-9]*\.?[0-9]+)'.format(cur), + flags=re.I | re.U), + r'\1{dimension}\2'), + # trademark + (re.compile( + r'(\b ?|{0}|^)[([]TM[])]'.format(regex_snippets['space']), + flags=re.I | re.U), + r'\1{trademark}'), + # registered + (re.compile( + r'(\b ?|{0}|^)[([]R[])]'.format(regex_snippets['space']), + flags=re.I | re.U), + r'\1{registered}'), + # copyright + (re.compile( + r'(\b ?|{0}|^)[([]C[])]'.format(regex_snippets['space']), + flags=re.I | re.U), + r'\1{copyright}'), + # 1/2 + (re.compile(r'[([]1\/2[])]'), r'{half}'), + # 1/4 + (re.compile(r'[([]1\/4[])]'), r'{quarter}'), + # 3/4 + (re.compile(r'[([]3\/4[])]'), r'{threequarters}'), + # degrees + (re.compile(r'[([]o[])]'), r'{degrees}'), + # plus/minus + (re.compile(r'[([]\+\/-[])]'), r'{plusminus}'), + # 3+ uppercase acronym + (re.compile( + r'\b([{0}][{1}]{{2,}})\b(?:[(]([^)]*)[)])' + .format(regex_snippets['abr'], regex_snippets['acr']), + flags=re.U), + (r'<abbr title="\2">\1</abbr>' if html_type == 'html5' + else r'<acronym title="\2">\1</acronym>')), + # 3+ uppercase + (re.compile( + r'({space}|^|[>(;-])([{abr}]{{3,}})([{nab}]*)' + '(?={space}|{pnct}|<|$)(?=[^">]*?(<|$))' + .format(space=regex_snippets['space'], + abr=regex_snippets['abr'], + nab=regex_snippets['nab'], + pnct=pnct_re_s), + re.U), + r'\1<span class="caps">{0}:glyph:\2</span>\3'.format(uid)), + ] + return [(regex_obj, replacement.format(**glyph_defs)) + for (regex_obj, replacement) in pre_result] + + + class Textile(object): restricted_url_schemes = ('http', 'https', 'ftp', 'mailto') unrestricted_url_schemes = restricted_url_schemes + ('file', 'tel', @@ -98,114 +214,12 @@ def __init__(self, restricted=False, lite=False, noimage=False, cur = r'(?:[{0}]{1}*)?'.format(regex_snippets['cur'], regex_snippets['space']) - # We'll be searching for characters that need to be HTML-encoded to - # produce properly valid html. These are the defaults that work in - # most cases. Below, we'll copy this and modify the necessary pieces - # to make it work for characters at the beginning of the string. - self.glyph_search = [ - # apostrophe's - re.compile(r"(^|{0}|\))'({0})".format(regex_snippets['wrd']), - flags=re.U), - # back in '88 - re.compile(r"({0})'(\d+{1}?)\b(?![.]?[{1}]*?')".format( - regex_snippets['space'], regex_snippets['wrd']), - flags=re.U), - # single opening following an open bracket. - re.compile(r"([([{])'(?=\S)", flags=re.U), - # single closing - re.compile(r"(^|\S)'(?={0}|{1}|<|$)".format( - regex_snippets['space'], pnct_re_s), flags=re.U), - # single opening - re.compile(r"'", re.U), - # double opening following an open bracket. Allows things like - # Hello ["(Mum) & dad"] - re.compile(r'([([{])"(?=\S)', flags=re.U), - # double closing - re.compile(r'(^|\S)"(?={0}|{1}|<|$)'.format( - regex_snippets['space'], pnct_re_s), re.U), - # double opening - re.compile(r'"'), - # ellipsis - re.compile(r'([^.]?)\.{3}'), - # ampersand - re.compile(r'(\s?)&(\s)', re.U), - # em dash - re.compile(r'(\s?)--(\s?)'), - # en dash - re.compile(r' - '), - # dimension sign - re.compile(r'([0-9]+[\])]?[\'"]? ?)[x]( ?[\[(]?)' - r'(?=[+-]?{0}[0-9]*\.?[0-9]+)'.format(cur), flags=re.I | re.U), - # trademark - re.compile(r'(\b ?|{0}|^)[([]TM[])]'.format(regex_snippets['space'] - ), flags=re.I | re.U), - # registered - re.compile(r'(\b ?|{0}|^)[([]R[])]'.format(regex_snippets['space'] - ), flags=re.I | re.U), - # copyright - re.compile(r'(\b ?|{0}|^)[([]C[])]'.format(regex_snippets['space'] - ), flags=re.I | re.U), - # 1/2 - re.compile(r'[([]1\/2[])]'), - # 1/4 - re.compile(r'[([]1\/4[])]'), - # 3/4 - re.compile(r'[([]3\/4[])]'), - # degrees - re.compile(r'[([]o[])]'), - # plus/minus - re.compile(r'[([]\+\/-[])]'), - # 3+ uppercase acronym - re.compile(r'\b([{0}][{1}]{{2,}})\b(?:[(]([^)]*)[)])'.format( - regex_snippets['abr'], regex_snippets['acr']), flags=re.U), - # 3+ uppercase - re.compile(r'({space}|^|[>(;-])([{abr}]{{3,}})([{nab}]*)' - '(?={space}|{pnct}|<|$)(?=[^">]*?(<|$))'.format(**{ 'space': - regex_snippets['space'], 'abr': regex_snippets['abr'], - 'nab': regex_snippets['nab'], 'pnct': pnct_re_s}), re.U), - ] - # These are the changes that need to be made for characters that occur - # at the beginning of the string. - self.glyph_search_initial = list(self.glyph_search) - # apostrophe's - self.glyph_search_initial[0] = re.compile(r"({0}|\))'({0})".format( - regex_snippets['wrd']), flags=re.U) - # single closing - self.glyph_search_initial[3] = re.compile(r"(\S)'(?={0}|{1}|$)".format( - regex_snippets['space'], pnct_re_s), re.U) - # double closing - self.glyph_search_initial[6] = re.compile(r'(\S)"(?={0}|{1}|<|$)'.format( - regex_snippets['space'], pnct_re_s), re.U) - - self.glyph_replace = [x.format(**self.glyph_definitions) for x in ( - r'\1{apostrophe}\2', # apostrophe's - r'\1{apostrophe}\2', # back in '88 - r'\1{quote_single_open}', # single opening after bracket - r'\1{quote_single_close}', # single closing - r'{quote_single_open}', # single opening - r'\1{quote_double_open}', # double opening after bracket - r'\1{quote_double_close}', # double closing - r'{quote_double_open}', # double opening - r'\1{ellipsis}', # ellipsis - r'\1{ampersand}\2', # ampersand - r'\1{emdash}\2', # em dash - r' {endash} ', # en dash - r'\1{dimension}\2', # dimension sign - r'\1{trademark}', # trademark - r'\1{registered}', # registered - r'\1{copyright}', # copyright - r'{half}', # 1/2 - r'{quarter}', # 1/4 - r'{threequarters}', # 3/4 - r'{degrees}', # degrees - r'{plusminus}', # plus/minus - r'<acronym title="\2">\1</acronym>', # 3+ uppercase acronym - r'\1<span class="caps">{0}:glyph:\2' # 3+ uppercase - r'</span>\3'.format(self.uid), - )] - - if self.html_type == 'html5': - self.glyph_replace[21] = r'<abbr title="\2">\1</abbr>' + self.glyph_replacers = make_glyph_replacers( + html_type, self.uid, self.glyph_definitions, is_initial=False) + # Replacements that need to be made at the beginning + # of the string + self.initial_glyph_replacers = make_glyph_replacers( + html_type, self.uid, self.glyph_definitions, is_initial=True) if self.restricted is True: self.url_schemes = self.restricted_url_schemes @@ -601,21 +615,22 @@ def glyphs(self, text): single quote. If it's the first character of one of those splits, it's an apostrophe or closed single quote, but the regex will bear that out. A similar situation occurs for double quotes as well. - So, for the first pass, we use the glyph_search_initial set of - regexes. For all remaining passes, we use glyph_search + So, for the first pass, we use a set of regexes from + the initial_glyph_replacers. For all remaining passes, + we use glyph_replacers """ text = text.rstrip('\n') result = [] - searchlist = self.glyph_search_initial + replacers = self.initial_glyph_replacers # split the text by any angle-bracketed tags for i, line in enumerate(re.compile(r'(<[\w\/!?].*?>)', re.U).split( text)): if not i % 2: - for s, r in zip(searchlist, self.glyph_replace): + for s, r in replacers: line = s.sub(r, line) result.append(line) if i == 0: - searchlist = self.glyph_search + replacers = self.glyph_replacers return ''.join(result) def getRefs(self, text): From b84a4772b60f85d6ef3a3bfef9455cc494c1c457 Mon Sep 17 00:00:00 2001 From: Kirill Mavreshko <kimavr@gmail.com> Date: Mon, 5 Sep 2022 19:39:30 +0500 Subject: [PATCH 065/228] Fix parsing of dimensions to pass php-textile`s test --- tests/test_values.py | 84 ++++++++++++++++++++++++++++++++++++++++ textile/core.py | 12 +++--- textile/regex_strings.py | 5 ++- 3 files changed, 94 insertions(+), 7 deletions(-) diff --git a/tests/test_values.py b/tests/test_values.py index 9792881..48e8006 100644 --- a/tests/test_values.py +++ b/tests/test_values.py @@ -315,6 +315,90 @@ # cite attribute ('bq.:http://textism.com/ Text...', '\t<blockquote cite="http://textism.com/">\n\t\t<p>Text…</p>\n\t</blockquote>'), ('Hello ["(Mum) & dad"]', '\t<p>Hello [“(Mum) & dad”]</p>'), + # Dimensions + ( + ('[1/2] x [1/4] and (1/2)" x [1/4]" and (1/2)\' x (1/4)\'\n\n' + '(2 x 10) X (3 / 4) x (200 + 64)\n\n' + '1 x 1 = 1\n\n' + '1 x1 = 1\n\n' + '1x 1 = 1\n\n' + '1x1 = 1\n\n' + '1 X 1 = 1\n\n' + '1 X1 = 1\n\n' + '1X 1 = 1\n\n' + '1X1 = 1\n\n' + 'What is 1 x 1?\n\n' + 'What is 1x1?\n\n' + 'What is 1 X 1?\n\n' + 'What is 1X1?\n\n' + '1 x 2 x 3 = 6\n\n' + '1x2x3=6\n\n' + '1x2 x 1x3 = 6\n\n' + '2\' x 2\' = 4 sqft.\n\n' + '2\'x 2\' = 4 sqft.\n\n' + '2\' x2\' = 4 sqft.\n\n' + '2\'x2\' = 4 sqft.\n\n' + '2\' X 2\' = 4 sqft.\n\n' + '2\'X 2\' = 4 sqft.\n\n' + '2\' X2\' = 4 sqft.\n\n' + '2\'X2\' = 4 sqft.\n\n' + '2" x 2" = 4 sqin.\n\n' + '2"x 2" = 4 sqin.\n\n' + '2" x2" = 4 sqin.\n\n' + '2"x2" = 4 sqin.\n\n' + '2" X 2" = 4 sqin.\n\n' + '2"X 2" = 4 sqin.\n\n' + '2" X2" = 4 sqin.\n\n' + '2"X2" = 4in[^2^].\n\n' + 'What is 1.2 x 3.5?\n\n' + 'What is .2 x .5?\n\n' + 'What is 1.2x3.5?\n\n' + 'What is .2x.5?\n\n' + 'What is 1.2\' x3.5\'?\n\n' + 'What is .2"x .5"?\n\n' + '1 x $10.00 x -£ 1.23 x ¥20,000 x -¤120.00 x ฿1,000,000 x -€110,00\n\n'), + + ('\t<p>½ × ¼ and ½” × ¼” and ½’ × ¼’</p>\n\n' + '\t<p>(2 × 10) × (3 / 4) × (200 + 64)</p>\n\n' + '\t<p>1 × 1 = 1</p>\n\n' + '\t<p>1 ×1 = 1</p>\n\n' + '\t<p>1× 1 = 1</p>\n\n' + '\t<p>1×1 = 1</p>\n\n' + '\t<p>1 × 1 = 1</p>\n\n' + '\t<p>1 ×1 = 1</p>\n\n' + '\t<p>1× 1 = 1</p>\n\n' + '\t<p>1×1 = 1</p>\n\n' + '\t<p>What is 1 × 1?</p>\n\n' + '\t<p>What is 1×1?</p>\n\n' + '\t<p>What is 1 × 1?</p>\n\n' + '\t<p>What is 1×1?</p>\n\n' + '\t<p>1 × 2 × 3 = 6</p>\n\n' + '\t<p>1×2×3=6</p>\n\n' + '\t<p>1×2 × 1×3 = 6</p>\n\n' + '\t<p>2’ × 2’ = 4 sqft.</p>\n\n' + '\t<p>2’× 2’ = 4 sqft.</p>\n\n' + '\t<p>2’ ×2’ = 4 sqft.</p>\n\n' + '\t<p>2’×2’ = 4 sqft.</p>\n\n' + '\t<p>2’ × 2’ = 4 sqft.</p>\n\n' + '\t<p>2’× 2’ = 4 sqft.</p>\n\n' + '\t<p>2’ ×2’ = 4 sqft.</p>\n\n' + '\t<p>2’×2’ = 4 sqft.</p>\n\n' + '\t<p>2” × 2” = 4 sqin.</p>\n\n' + '\t<p>2”× 2” = 4 sqin.</p>\n\n' + '\t<p>2” ×2” = 4 sqin.</p>\n\n' + '\t<p>2”×2” = 4 sqin.</p>\n\n' + '\t<p>2” × 2” = 4 sqin.</p>\n\n' + '\t<p>2”× 2” = 4 sqin.</p>\n\n' + '\t<p>2” ×2” = 4 sqin.</p>\n\n' + '\t<p>2”×2” = 4in<sup>2</sup>.</p>\n\n' + '\t<p>What is 1.2 × 3.5?</p>\n\n' + '\t<p>What is .2 × .5?</p>\n\n' + '\t<p>What is 1.2×3.5?</p>\n\n' + '\t<p>What is .2×.5?</p>\n\n' + '\t<p>What is 1.2’ ×3.5’?</p>\n\n' + '\t<p>What is .2”× .5”?</p>\n\n' + '\t<p>1 × $10.00 × -£ 1.23 × ¥20,000 × -¤120.00 × ฿1,000,000 × -€110,00</p>') + ), ) @pytest.mark.parametrize("input, expected_output", xhtml_known_values) diff --git a/textile/core.py b/textile/core.py index 5e74b42..39daa1a 100644 --- a/textile/core.py +++ b/textile/core.py @@ -47,6 +47,12 @@ def make_glyph_replacers(html_type, uid, glyph_defs, is_initial): if regex_snippets['cur'] else r'') pre_result = [ + # dimension sign (before apostrophes/quotes are replaced) + (re.compile( + r'([0-9]+[\])]?[\'"]? ?)[x]( ?[\[(]?)' + r'(?=[+-]?{0}[0-9]*\.?[0-9]+)'.format(cur), + flags=re.I | re.U), + r'\1{dimension}\2'), # apostrophe's (re.compile( (r"(^|{0}|\))'({0})" if not is_initial @@ -97,12 +103,6 @@ def make_glyph_replacers(html_type, uid, glyph_defs, is_initial): (re.compile(r'(\s?)--(\s?)'), r'\1{emdash}\2'), # en dash (re.compile(r' - '), r' {endash} '), - # dimension sign - (re.compile( - r'([0-9]+[\])]?[\'"]? ?)[x]( ?[\[(]?)' - r'(?=[+-]?{0}[0-9]*\.?[0-9]+)'.format(cur), - flags=re.I | re.U), - r'\1{dimension}\2'), # trademark (re.compile( r'(\b ?|{0}|^)[([]TM[])]'.format(regex_snippets['space']), diff --git a/textile/regex_strings.py b/textile/regex_strings.py index 470203c..deb4a4a 100644 --- a/textile/regex_strings.py +++ b/textile/regex_strings.py @@ -26,7 +26,10 @@ 'abr': r'{0}'.format(upper_re_s), 'nab': r'a-z', 'wrd': r'\w', - 'cur': r'', + # All codepoints identified as currency symbols + # by the [mrab-regex library](https://pypi.org/project/regex/) + # and the UNICODE standard. + 'cur': r'$¢-¥֏؋৲৳৻૱௹฿៛\u20a0-\u20cf\ua838﷼﹩$¢£¥₩', 'digit': r'\d', 'space': r'(?:\s|\v)', 'char': r'\S', From 6286b805039880d68ce6442b866ca759c6679496 Mon Sep 17 00:00:00 2001 From: Kirill Mavreshko <kimavr@gmail.com> Date: Tue, 6 Sep 2022 17:47:27 +0500 Subject: [PATCH 066/228] Fix redundant spaces in quote-ending lines --- tests/test_textile.py | 4 ++-- tests/test_values.py | 8 ++++---- textile/utils.py | 1 - 3 files changed, 6 insertions(+), 7 deletions(-) diff --git a/tests/test_textile.py b/tests/test_textile.py index 0c37690..467e31e 100644 --- a/tests/test_textile.py +++ b/tests/test_textile.py @@ -26,11 +26,11 @@ def test_Footnote(): def test_issue_35(): result = textile.textile('"z"') - expect = '\t<p>“z” </p>' + expect = '\t<p>“z”</p>' assert result == expect result = textile.textile('" z"') - expect = '\t<p>“ z” </p>' + expect = '\t<p>“ z”</p>' assert result == expect def test_restricted(): diff --git a/tests/test_values.py b/tests/test_values.py index 48e8006..b1d8ec9 100644 --- a/tests/test_values.py +++ b/tests/test_values.py @@ -14,7 +14,7 @@ ('I spoke.\nAnd none replied.', '\t<p>I spoke.<br />\nAnd none replied.</p>'), - ('"Observe!"', '\t<p>“Observe!” </p>'), + ('"Observe!"', '\t<p>“Observe!”</p>'), ('Observe -- very nice!', '\t<p>Observe — very nice!</p>'), @@ -201,13 +201,13 @@ """\t<p style="text-align:center;"><strong><em>The</em></strong> <em><strong>Prisoner</strong></em></p>"""), ("""p=. "An emphasised _word._" & "*A spanned phrase.*" """, - """\t<p style="text-align:center;">“An emphasised <em>word.</em>” & “<strong>A spanned phrase.</strong>” </p>"""), + """\t<p style="text-align:center;">“An emphasised <em>word.</em>” & “<strong>A spanned phrase.</strong>”</p>"""), ("""p=. "*Here*'s a word!" """, - """\t<p style="text-align:center;">“<strong>Here</strong>’s a word!” </p>"""), + """\t<p style="text-align:center;">“<strong>Here</strong>’s a word!”</p>"""), ("""p=. "Please visit our "Textile Test Page":http://textile.sitemonks.com" """, - """\t<p style="text-align:center;">“Please visit our <a href="http://textile.sitemonks.com">Textile Test Page</a>” </p>"""), + """\t<p style="text-align:center;">“Please visit our <a href="http://textile.sitemonks.com">Textile Test Page</a>”</p>"""), ("""| Foreign EXPÓŅÉNTIAL |""", """\t<table>\n\t\t<tr>\n\t\t\t<td> Foreign <span class="caps">EXPÓŅÉNTIAL</span> </td>\n\t\t</tr>\n\t</table>"""), ("""Piękne ŹDŹBŁO""", diff --git a/textile/utils.py b/textile/utils.py index 1b18945..cd619ed 100644 --- a/textile/utils.py +++ b/textile/utils.py @@ -99,7 +99,6 @@ def normalize_newlines(string): out = string.strip() out = re.sub(r'\r\n?', '\n', out) out = re.compile(r'^[ \t]*\n', flags=re.M).sub('\n', out) - out = re.sub(r'"$', '" ', out) return out def parse_attributes(block_attributes, element=None, include_id=True, restricted=False): From e05ef6dba5c1ebb836c21214e92b4fdf158b0230 Mon Sep 17 00:00:00 2001 From: Kirill Mavreshko <kimavr@gmail.com> Date: Tue, 6 Sep 2022 22:30:32 +0500 Subject: [PATCH 067/228] Fix empty notelist leading to an empty <ol> --- tests/test_values.py | 2 ++ textile/core.py | 5 +++-- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/tests/test_values.py b/tests/test_values.py index b1d8ec9..70f9cee 100644 --- a/tests/test_values.py +++ b/tests/test_values.py @@ -399,6 +399,8 @@ '\t<p>What is .2”× .5”?</p>\n\n' '\t<p>1 × $10.00 × -£ 1.23 × ¥20,000 × -¤120.00 × ฿1,000,000 × -€110,00</p>') ), + # Empty note lists + ('There should be nothing below.\n\nnotelist.', '\t<p>There should be nothing below.</p>\n\n\t'), ) @pytest.mark.parametrize("input, expected_output", xhtml_known_values) diff --git a/textile/core.py b/textile/core.py index 39daa1a..221890d 100644 --- a/textile/core.py +++ b/textile/core.py @@ -1304,8 +1304,9 @@ def fNoteLists(self, match): o.append(li) self.notelist_cache[index] = "\n".join(o) result = self.notelist_cache[index] - list_atts = pba(att, restricted=self.restricted) - result = '<ol{0}>\n{1}\n\t</ol>'.format(list_atts, result) + if result: + list_atts = pba(att, restricted=self.restricted) + result = '<ol{0}>\n{1}\n\t</ol>'.format(list_atts, result) return result def makeBackrefLink(self, info, g_links, i): From 175ac56b4fe09581f6782cb209bd03c0e88fee36 Mon Sep 17 00:00:00 2001 From: Kirill Mavreshko <kimavr@gmail.com> Date: Wed, 7 Sep 2022 10:06:27 +0500 Subject: [PATCH 068/228] Fix HTML escaping and make it match php-textile`s --- tests/test_values.py | 19 +++++++++++++++++++ textile/core.py | 12 ++++++++++-- 2 files changed, 29 insertions(+), 2 deletions(-) diff --git a/tests/test_values.py b/tests/test_values.py index 70f9cee..001c558 100644 --- a/tests/test_values.py +++ b/tests/test_values.py @@ -401,6 +401,25 @@ ), # Empty note lists ('There should be nothing below.\n\nnotelist.', '\t<p>There should be nothing below.</p>\n\n\t'), + # Empty things + (('\'\'\n\n""\n\n%%\n\n^^\n\n&&\n\n**\n\n__\n\n--\n\n++\n\n~~\n\n{}\n\n' + '[]\n\n()\n\n<>\n\n\\\\\n\n//\n\n??\n\n==\n\n@@\n\n##\n\n$$\n\n!!\n\n' + '::\n\n;;\n\n..\n\n,,\n\n||\n\n` `\n\n\' \'\n\n" "\n\n% %\n\n^ ^\n\n' + '& &\n\n* *\n\n_ _\n\n- -\n\n+ +\n\n~ ~\n\n{ }\n\n[ ]\n\n( )\n\n< >\n\n' + '\\ \\\n\n/ /\n\n? ?\n\n= =\n\n@ @\n\n# #\n\n$ $\n\n! !\n\n: :\n\n; ;\n\n' + '. .\n\n, ,'), + ("\t<p>‘’</p>\n\n\t<p>“”</p>\n\n\t<p>%%</p>\n\n\t<p>^^</p>\n\n\t" + "<p>&&</p>\n\n\t<p>**</p>\n\n\t<p>__</p>\n\n\t<p>—</p>\n\n\t<p>++</p>\n\n\t" + "<p>~~</p>\n\n\t<p>{}</p>\n\n\t<p>[]</p>\n\n\t<p>()</p>\n\n\t<p><></p>\n\n\t<p>\\\\</p>\n\n\t" + "<p>//</p>\n\n\t<p>??</p>\n\n\t<p>==</p>\n\n\t<p><code></code></p>\n\n\t<p>##</p>\n\n\t<p>$$</p>\n\n\t" + "<p>!!</p>\n\n\t<p>::</p>\n\n\t<p>;;</p>\n\n\t<p>..</p>\n\n\t<p>,,</p>\n\n\t" + "<table>\n\t\t<tr>\n\t\t\t<td></td>\n\t\t</tr>\n\t</table>\n\n\t<p>` `</p>\n\n\t<p>‘ ‘</p>\n\n\t" + "<p>“ “</p>\n\n\t<p>% %</p>\n\n\t<p>^ ^</p>\n\n\t<p>& &</p>\n\n\t" + "<ul>\n\t\t<li>*</li>\n\t</ul>\n\n\t<p>_ _</p>\n\n\t<p>- -</p>\n\n\t<p>+ +</p>\n\n\t<p>~ ~</p>\n\n\t" + "<p>{ }</p>\n\n\t<p>[ ]</p>\n\n\t<p>( )</p>\n\n\t<p>< ></p>\n\n\t<p>\\ \\</p>\n\n\t" + "<p>/ /</p>\n\n\t<p>? ?</p>\n\n\t<p>= =</p>\n\n\t<p><code> </code></p>\n\n\t<ol>\n\t\t<li>#</li>\n\t</ol>\n\n\t" + "<p>$ $</p>\n\n\t<p>! !</p>\n\n\t<dl>\n\t\t<dd>:</dd>\n\t</dl>\n\n\t<dl>\n\t\t<dt>;</dt>\n\t</dl>\n\n\t" + "<p>. .</p>\n\n\t<p>, ,</p>")) ) @pytest.mark.parametrize("input, expected_output", xhtml_known_values) diff --git a/textile/core.py b/textile/core.py index 221890d..f321809 100644 --- a/textile/core.py +++ b/textile/core.py @@ -97,8 +97,6 @@ def make_glyph_replacers(html_type, uid, glyph_defs, is_initial): (re.compile(r'"'), r'{quote_double_open}'), # ellipsis (re.compile(r'([^.]?)\.{3}'), r'\1{ellipsis}'), - # ampersand - (re.compile(r'(\s?)&(\s)', re.U), r'\1{ampersand}\2'), # em dash (re.compile(r'(\s?)--(\s?)'), r'\1{emdash}\2'), # en dash @@ -622,10 +620,20 @@ def glyphs(self, text): text = text.rstrip('\n') result = [] replacers = self.initial_glyph_replacers + standalone_amp_re = re.compile(r'&(?!#?[a-z0-9]+;)', flags=re.I) + html_amp_symbol = self.glyph_definitions['ampersand'] # split the text by any angle-bracketed tags for i, line in enumerate(re.compile(r'(<[\w\/!?].*?>)', re.U).split( text)): if not i % 2: + if not self.restricted: + # Raw < > & chars have already been encoded + # when in restricted mode + line = ( + standalone_amp_re + .sub(html_amp_symbol, line) + .replace('<', '<') + .replace('>', '>')) for s, r in replacers: line = s.sub(r, line) result.append(line) From b8061b505e9359e90efc82b07d4e234208aefada Mon Sep 17 00:00:00 2001 From: Kirill Mavreshko <kimavr@gmail.com> Date: Fri, 30 Sep 2022 09:38:27 +0500 Subject: [PATCH 069/228] Fix unwanted wrapping of HTML comments into p tags --- tests/test_values.py | 9 +++++++-- textile/utils.py | 24 ++++++++++++++++-------- 2 files changed, 23 insertions(+), 10 deletions(-) diff --git a/tests/test_values.py b/tests/test_values.py index 001c558..c4093c9 100644 --- a/tests/test_values.py +++ b/tests/test_values.py @@ -219,7 +219,7 @@ '\t<p style="font-size:0.8em;"><strong>TxStyle</strong> is a documentation project of Textile 2.4 for <a href="http://texpattern.com">Textpattern <span class="caps">CMS</span></a>.</p>'), (""""Übermensch":http://de.wikipedia.org/wiki/Übermensch""", """\t<p><a href="http://de.wikipedia.org/wiki/%C3%9Cbermensch">Übermensch</a></p>"""), ("""Here is some text with a <!-- Commented out[1] --> block.\n\n<!-- Here is a single <span>line</span> comment block -->\n\n<!-- Here is a whole\nmultiline\n<span>HTML</span>\nComment\n-->\n\nbc. <!-- Here is a comment block in a code block. -->""", - """\t<p>Here is some text with a <!-- Commented out[1] --> block.</p>\n\n\t<p><!-- Here is a single <span>line</span> comment block --></p>\n\n\t<p><!-- Here is a whole\nmultiline\n<span>HTML</span>\nComment\n--></p>\n\n<pre><code><!-- Here is a comment block in a code block. --></code></pre>"""), + """\t<p>Here is some text with a <!-- Commented out[1] --> block.</p>\n\n<!-- Here is a single <span>line</span> comment block -->\n\n<!-- Here is a whole\nmultiline\n<span>HTML</span>\nComment\n-->\n\n<pre><code><!-- Here is a comment block in a code block. --></code></pre>"""), (""""Textile(c)" is a registered(r) 'trademark' of Textpattern(tm) -- or TXP(That's textpattern!) -- at least it was - back in '88 when 2x4 was (+/-)5(o)C ... QED!\n\np{font-size: 200%;}. 2(1/4) 3(1/2) 4(3/4)""", """\t<p>“Textile©” is a registered® ‘trademark’ of Textpattern™ — or <acronym title="That’s textpattern!"><span class="caps">TXP</span></acronym> — at least it was – back in ’88 when 2×4 was ±5°C … <span class="caps">QED</span>!</p>\n\n\t<p style="font-size: 200%;">2¼ 3½ 4¾</p>"""), ("""|=. Testing colgroup and col syntax\n|:\\5. 80\n|a|b|c|d|e|\n\n|=. Testing colgroup and col syntax|\n|:\\5. 80|\n|a|b|c|d|e|""", """\t<table>\n\t<caption>Testing colgroup and col syntax</caption>\n\t<colgroup span="5" width="80">\n\t</colgroup>\n\t\t<tr>\n\t\t\t<td>a</td>\n\t\t\t<td>b</td>\n\t\t\t<td>c</td>\n\t\t\t<td>d</td>\n\t\t\t<td>e</td>\n\t\t</tr>\n\t</table>\n\n\t<table>\n\t<caption>Testing colgroup and col syntax</caption>\n\t<colgroup span="5" width="80">\n\t</colgroup>\n\t\t<tr>\n\t\t\t<td>a</td>\n\t\t\t<td>b</td>\n\t\t\t<td>c</td>\n\t\t\t<td>d</td>\n\t\t\t<td>e</td>\n\t\t</tr>\n\t</table>"""), @@ -419,7 +419,12 @@ "<p>{ }</p>\n\n\t<p>[ ]</p>\n\n\t<p>( )</p>\n\n\t<p>< ></p>\n\n\t<p>\\ \\</p>\n\n\t" "<p>/ /</p>\n\n\t<p>? ?</p>\n\n\t<p>= =</p>\n\n\t<p><code> </code></p>\n\n\t<ol>\n\t\t<li>#</li>\n\t</ol>\n\n\t" "<p>$ $</p>\n\n\t<p>! !</p>\n\n\t<dl>\n\t\t<dd>:</dd>\n\t</dl>\n\n\t<dl>\n\t\t<dt>;</dt>\n\t</dl>\n\n\t" - "<p>. .</p>\n\n\t<p>, ,</p>")) + "<p>. .</p>\n\n\t<p>, ,</p>")), + # A lone standing comment must be preserved as is: + # withouth wrapping it into a paragraph + (('An ordinary block.\n\n' + '<!-- A comment block -->\n'), + '\t<p>An ordinary block.</p>\n\n<!-- A comment block -->'), ) @pytest.mark.parametrize("input, expected_output", xhtml_known_values) diff --git a/textile/utils.py b/textile/utils.py index cd619ed..06a51e7 100644 --- a/textile/utils.py +++ b/textile/utils.py @@ -14,6 +14,18 @@ from textile.regex_strings import valign_re_s, halign_re_s +# Regular expressions for stripping chunks of HTML, +# leaving only content not wrapped in a tag or a comment +RAW_TEXT_REVEALERS = ( + # The php version has orders the below list of tags differently. The + # important thing to note here is that the pre must occur before the p or + # else the regex module doesn't properly match pre-s. It only matches the + # p in pre. + re.compile(r'<(pre|p|blockquote|div|form|table|ul|ol|dl|h[1-6])[^>]*?>.*</\1>', + re.S), + re.compile(r'<(hr|br)[^>]*?/>'), + re.compile(r'<!--.*?-->'), +) def decode_high(text): """Decode encoded HTML entities.""" @@ -66,14 +78,10 @@ def generate_tag(tag, content, attributes=None): def has_raw_text(text): """checks whether the text has text not already enclosed by a block tag""" - # The php version has orders the below list of tags differently. The - # important thing to note here is that the pre must occur before the p or - # else the regex module doesn't properly match pre-s. It only matches the - # p in pre. - r = re.compile(r'<(pre|p|blockquote|div|form|table|ul|ol|dl|h[1-6])[^>]*?>.*</\1>', - re.S).sub('', text.strip()).strip() - r = re.compile(r'<(hr|br)[^>]*?/>').sub('', r) - return '' != r + r = text.strip() + for pattern in RAW_TEXT_REVEALERS: + r = pattern.sub('', r).strip() + return r != '' def is_rel_url(url): """Identify relative urls.""" From b4c02b8c0c6e176c1dfe4a0dfb7b0ae2eaf7dae6 Mon Sep 17 00:00:00 2001 From: Kirill Mavreshko <kimavr@gmail.com> Date: Fri, 30 Sep 2022 20:42:56 +0500 Subject: [PATCH 070/228] Make headers h1-6 <br>eakable, as in PHP-Textile --- tests/test_values.py | 3 +++ textile/core.py | 5 +++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/test_values.py b/tests/test_values.py index c4093c9..6cfaf47 100644 --- a/tests/test_values.py +++ b/tests/test_values.py @@ -425,6 +425,9 @@ (('An ordinary block.\n\n' '<!-- A comment block -->\n'), '\t<p>An ordinary block.</p>\n\n<!-- A comment block -->'), + # Headers must be "breakable", just like paragraphs. + ('h1. Two line with *strong*\nheading\n', + '\t<h1>Two line with <strong>strong</strong><br />\nheading</h1>'), ) @pytest.mark.parametrize("input, expected_output", xhtml_known_values) diff --git a/textile/core.py b/textile/core.py index f321809..2c89eb9 100644 --- a/textile/core.py +++ b/textile/core.py @@ -419,8 +419,9 @@ def doTagBr(self, tag, input): re.S).sub(self.doBr, input) def doPBr(self, in_): - return re.compile(r'<(p)([^>]*?)>(.*)(</\1>)', re.S).sub(self.doBr, - in_) + return (re + .compile(r'<(p|h[1-6])([^>]*?)>(.*)(</\1>)', re.S) + .sub(self.doBr, in_)) def doBr(self, match): content = re.sub(r'(.+)(?:(?<!<br>)|(?<!<br />))\n(?![#*;:\s|])', From 691a0f3acf0d33eeef6cdadcd208a8fff1566875 Mon Sep 17 00:00:00 2001 From: Kirill Mavreshko <kimavr@gmail.com> Date: Fri, 30 Sep 2022 20:55:36 +0500 Subject: [PATCH 071/228] Fix the escaping of standalone ampersands --- tests/test_values.py | 7 +++++++ textile/core.py | 4 +++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/tests/test_values.py b/tests/test_values.py index 6cfaf47..bdc4dcb 100644 --- a/tests/test_values.py +++ b/tests/test_values.py @@ -428,6 +428,13 @@ # Headers must be "breakable", just like paragraphs. ('h1. Two line with *strong*\nheading\n', '\t<h1>Two line with <strong>strong</strong><br />\nheading</h1>'), + # Non-standalone ampersands should not be escaped + (("“<span lang=\"en\">test</span>”\n\n" + "“<span lang=\"en\">test</span>”\n\n" + " <span lang=\"en\">test</span> \n"), + ("\t<p>“<span lang=\"en\">test</span>”</p>\n\n" + "\t<p>“<span lang=\"en\">test</span>”</p>\n\n" + "\t<p> <span lang=\"en\">test</span> </p>")), ) @pytest.mark.parametrize("input, expected_output", xhtml_known_values) diff --git a/textile/core.py b/textile/core.py index 2c89eb9..f8e478e 100644 --- a/textile/core.py +++ b/textile/core.py @@ -621,7 +621,9 @@ def glyphs(self, text): text = text.rstrip('\n') result = [] replacers = self.initial_glyph_replacers - standalone_amp_re = re.compile(r'&(?!#?[a-z0-9]+;)', flags=re.I) + standalone_amp_re = re.compile( + r"&(?!#[0-9]+;|#x[a-f0-9]+;|[a-z][a-z0-9]*;)", + flags=re.I) html_amp_symbol = self.glyph_definitions['ampersand'] # split the text by any angle-bracketed tags for i, line in enumerate(re.compile(r'(<[\w\/!?].*?>)', re.U).split( From a047b3b6ec7cae8ddd706a22f0aa958187b9fd1d Mon Sep 17 00:00:00 2001 From: Kirill Mavreshko <kimavr@gmail.com> Date: Sat, 1 Oct 2022 12:15:19 +0500 Subject: [PATCH 072/228] Fix greedy space trimming, match it with PHP-textile --- tests/test_values.py | 6 +++--- textile/utils.py | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/test_values.py b/tests/test_values.py index bdc4dcb..e2194b1 100644 --- a/tests/test_values.py +++ b/tests/test_values.py @@ -201,13 +201,13 @@ """\t<p style="text-align:center;"><strong><em>The</em></strong> <em><strong>Prisoner</strong></em></p>"""), ("""p=. "An emphasised _word._" & "*A spanned phrase.*" """, - """\t<p style="text-align:center;">“An emphasised <em>word.</em>” & “<strong>A spanned phrase.</strong>”</p>"""), + """\t<p style="text-align:center;">“An emphasised <em>word.</em>” & “<strong>A spanned phrase.</strong>” </p>"""), ("""p=. "*Here*'s a word!" """, - """\t<p style="text-align:center;">“<strong>Here</strong>’s a word!”</p>"""), + """\t<p style="text-align:center;">“<strong>Here</strong>’s a word!” </p>"""), ("""p=. "Please visit our "Textile Test Page":http://textile.sitemonks.com" """, - """\t<p style="text-align:center;">“Please visit our <a href="http://textile.sitemonks.com">Textile Test Page</a>”</p>"""), + """\t<p style="text-align:center;">“Please visit our <a href="http://textile.sitemonks.com">Textile Test Page</a>” </p>"""), ("""| Foreign EXPÓŅÉNTIAL |""", """\t<table>\n\t\t<tr>\n\t\t\t<td> Foreign <span class="caps">EXPÓŅÉNTIAL</span> </td>\n\t\t</tr>\n\t</table>"""), ("""Piękne ŹDŹBŁO""", diff --git a/textile/utils.py b/textile/utils.py index 06a51e7..3b0bfac 100644 --- a/textile/utils.py +++ b/textile/utils.py @@ -104,9 +104,9 @@ def list_type(list_string): return listtypes.get(True, False) def normalize_newlines(string): - out = string.strip() - out = re.sub(r'\r\n?', '\n', out) + out = re.sub(r'\r\n?', '\n', string) out = re.compile(r'^[ \t]*\n', flags=re.M).sub('\n', out) + out = out.strip('\n') return out def parse_attributes(block_attributes, element=None, include_id=True, restricted=False): From adba7014d088126cc854514a78dd20d4bb6fb784 Mon Sep 17 00:00:00 2001 From: Kirill Mavreshko <kimavr@gmail.com> Date: Mon, 3 Oct 2022 18:12:05 +0500 Subject: [PATCH 073/228] Fix invalid parsing of nested lists of mixed types --- tests/test_values.py | 27 +++++++++++++++++++++++++++ textile/utils.py | 6 +++--- 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/tests/test_values.py b/tests/test_values.py index e2194b1..60b3513 100644 --- a/tests/test_values.py +++ b/tests/test_values.py @@ -435,6 +435,33 @@ ("\t<p>“<span lang=\"en\">test</span>”</p>\n\n" "\t<p>“<span lang=\"en\">test</span>”</p>\n\n" "\t<p> <span lang=\"en\">test</span> </p>")), + # Nested and mixed multi-level ordered and unordered lists + (("* bullet\n" + "*# number\n" + "*# number\n" + "*#* bullet\n" + "*# number\n" + "*# number with\n" + "a break\n" + "* bullet\n" + "** okay"), + ("\t<ul>\n" + "\t\t<li>bullet\n" + "\t\t<ol>\n" + "\t\t\t<li>number</li>\n" + "\t\t\t<li>number\n" + "\t\t\t<ul>\n" + "\t\t\t\t<li>bullet</li>\n" + "\t\t\t</ul></li>\n" + "\t\t\t<li>number</li>\n" + "\t\t\t<li>number with<br />\n" + "a break</li>\n" + "\t\t</ol></li>\n" + "\t\t<li>bullet\n" + "\t\t<ul>\n" + "\t\t\t<li>okay</li>\n" + "\t\t</ul></li>\n" + "\t\t</ul>")) ) @pytest.mark.parametrize("input, expected_output", xhtml_known_values) diff --git a/textile/utils.py b/textile/utils.py index 3b0bfac..e971645 100644 --- a/textile/utils.py +++ b/textile/utils.py @@ -96,9 +96,9 @@ def is_valid_url(url): def list_type(list_string): listtypes = { - list_string.startswith('*'): 'u', - list_string.startswith('#'): 'o', - (not list_string.startswith('*') and not list_string.startswith('#')): + list_string.endswith('*'): 'u', + list_string.endswith('#'): 'o', + (not list_string.endswith('*') and not list_string.endswith('#')): 'd' } return listtypes.get(True, False) From b270c0d7175331f840589bb67294f69fe340f29f Mon Sep 17 00:00:00 2001 From: Kirill Mavreshko <kimavr@gmail.com> Date: Tue, 4 Oct 2022 10:16:06 +0500 Subject: [PATCH 074/228] Fix improper inserts of <br> after <dd|dt|li> tags --- tests/test_values.py | 15 +++++++++++++-- textile/core.py | 8 ++++++-- 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/tests/test_values.py b/tests/test_values.py index 60b3513..be75b7b 100644 --- a/tests/test_values.py +++ b/tests/test_values.py @@ -236,7 +236,7 @@ ("""# one\n##3 one.three\n## one.four\n## one.five\n# two\n\ntest\n\n#_(continuation#section2).\n# three\n# four\n##_ four.six\n## four.seven\n# five\n\ntest\n\n#21 twenty-one\n# twenty-two""", """\t<ol>\n\t\t<li>one\n\t\t<ol start="3">\n\t\t\t<li>one.three</li>\n\t\t\t<li>one.four</li>\n\t\t\t<li>one.five</li>\n\t\t</ol></li>\n\t\t<li>two</li>\n\t</ol>\n\n\t<p>test</p>\n\n\t<ol class="continuation" id="section2" start="3">\n\t\t<li>three</li>\n\t\t<li>four\n\t\t<ol start="6">\n\t\t\t<li>four.six</li>\n\t\t\t<li>four.seven</li>\n\t\t</ol></li>\n\t\t<li>five</li>\n\t</ol>\n\n\t<p>test</p>\n\n\t<ol start="21">\n\t\t<li>twenty-one</li>\n\t\t<li>twenty-two</li>\n\t</ol>"""), ("""|* Foo[^2^]\n* _bar_\n* ~baz~ |\n|#4 *Four*\n# __Five__ |\n|-(hot) coffee := Hot and black\n-(hot#tea) tea := Also hot, but a little less black\n-(cold) milk :=\nNourishing beverage for baby cows.\nCold drink that goes great with cookies. =:\n|""", - """\t<table>\n\t\t<tr>\n\t\t\t<td>\t<ul>\n\t\t<li>Foo<sup>2</sup></li>\n\t\t<li><em>bar</em></li>\n\t\t<li><sub>baz</sub></li>\n\t</ul></td>\n\t\t</tr>\n\t\t<tr>\n\t\t\t<td>\t<ol start="4">\n\t\t<li><strong>Four</strong></li>\n\t\t<li><i>Five</i></li>\n\t</ol></td>\n\t\t</tr>\n\t\t<tr>\n\t\t\t<td><dl>\n\t<dt class="hot">coffee</dt>\n\t<dd>Hot and black</dd>\n\t<dt class="hot" id="tea">tea</dt>\n\t<dd>Also hot, but a little less black</dd>\n\t<dt class="cold">milk</dt>\n\t<dd><p>Nourishing beverage for baby cows.<br />\nCold drink that goes great with cookies.</p></dd><br />\n</dl></td>\n\t\t</tr>\n\t</table>"""), + """\t<table>\n\t\t<tr>\n\t\t\t<td>\t<ul>\n\t\t<li>Foo<sup>2</sup></li>\n\t\t<li><em>bar</em></li>\n\t\t<li><sub>baz</sub></li>\n\t</ul></td>\n\t\t</tr>\n\t\t<tr>\n\t\t\t<td>\t<ol start="4">\n\t\t<li><strong>Four</strong></li>\n\t\t<li><i>Five</i></li>\n\t</ol></td>\n\t\t</tr>\n\t\t<tr>\n\t\t\t<td><dl>\n\t<dt class="hot">coffee</dt>\n\t<dd>Hot and black</dd>\n\t<dt class="hot" id="tea">tea</dt>\n\t<dd>Also hot, but a little less black</dd>\n\t<dt class="cold">milk</dt>\n\t<dd><p>Nourishing beverage for baby cows.<br />\nCold drink that goes great with cookies.</p></dd>\n</dl></td>\n\t\t</tr>\n\t</table>"""), ("""h4. A more complicated table\n\ntable(tableclass#tableid){color:blue}.\n|_. table |_. more |_. badass |\n|\\3. Horizontal span of 3|\n(firstrow). |first|HAL(open the pod bay doors)|1|\n|some|{color:green}. styled|content|\n|/2. spans 2 rows|this is|quite a|\n| deep test | don't you think?|\n(lastrow). |fifth|I'm a lumberjack|5|\n|sixth| _*bold italics*_ |6|""", """\t<h4>A more complicated table</h4>\n\n\t<table class="tableclass" id="tableid" style="color:blue;">\n\t\t<tr>\n\t\t\t<th>table </th>\n\t\t\t<th>more </th>\n\t\t\t<th>badass </th>\n\t\t</tr>\n\t\t<tr>\n\t\t\t<td colspan="3">Horizontal span of 3</td>\n\t\t</tr>\n\t\t<tr class="firstrow">\n\t\t\t<td>first</td>\n\t\t\t<td><acronym title="open the pod bay doors"><span class="caps">HAL</span></acronym></td>\n\t\t\t<td>1</td>\n\t\t</tr>\n\t\t<tr>\n\t\t\t<td>some</td>\n\t\t\t<td style="color:green;">styled</td>\n\t\t\t<td>content</td>\n\t\t</tr>\n\t\t<tr>\n\t\t\t<td rowspan="2">spans 2 rows</td>\n\t\t\t<td>this is</td>\n\t\t\t<td>quite a</td>\n\t\t</tr>\n\t\t<tr>\n\t\t\t<td> deep test </td>\n\t\t\t<td> don’t you think?</td>\n\t\t</tr>\n\t\t<tr class="lastrow">\n\t\t\t<td>fifth</td>\n\t\t\t<td>I’m a lumberjack</td>\n\t\t\t<td>5</td>\n\t\t</tr>\n\t\t<tr>\n\t\t\t<td>sixth</td>\n\t\t\t<td> <em><strong>bold italics</strong></em> </td>\n\t\t\t<td>6</td>\n\t\t</tr>\n\t</table>"""), ("""| *strong* |\n\n| _em_ |\n\n| Inter-word -dashes- | ZIP-codes are 5- or 9-digit codes |""", @@ -461,7 +461,18 @@ "\t\t<ul>\n" "\t\t\t<li>okay</li>\n" "\t\t</ul></li>\n" - "\t\t</ul>")) + "\t\t</ul>")), + # Checks proper insertion of <br /> within table cells + (("|-(cold) milk :=\n" + "Nourishing beverage for baby cows. =:\n" + "|"), + ("\t<table>\n" + "\t\t<tr>\n" + "\t\t\t<td><dl>\n" + "\t<dt class=\"cold\">milk</dt>\n" + "\t<dd><p>Nourishing beverage for baby cows.</p></dd>\n" + "</dl></td>\n" + "\t\t</tr>\n\t</table>")), ) @pytest.mark.parametrize("input, expected_output", xhtml_known_values) diff --git a/textile/core.py b/textile/core.py index f8e478e..11c6843 100644 --- a/textile/core.py +++ b/textile/core.py @@ -424,8 +424,12 @@ def doPBr(self, in_): .sub(self.doBr, in_)) def doBr(self, match): - content = re.sub(r'(.+)(?:(?<!<br>)|(?<!<br />))\n(?![#*;:\s|])', - r'\1<br />', match.group(3)) + content = ( + re.compile( + r'(.+)(?!(?<=</dd>|</dt>|</li>|<br/>)' + r'|(?<=<br>)|(?<=<br />))\n(?![#*;:\s|])', + re.I) + .sub(r'\1<br />', match.group(3))) return '<{0}{1}>{2}{3}'.format(match.group(1), match.group(2), content, match.group(4)) From 77cfcf86d512b6b6f12a86740f704745fef148aa Mon Sep 17 00:00:00 2001 From: Kirill Mavreshko <kimavr@gmail.com> Date: Wed, 5 Oct 2022 20:21:27 +0500 Subject: [PATCH 075/228] Fix mishandling of extended notextile.. blocks --- tests/test_values.py | 2 ++ textile/core.py | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/test_values.py b/tests/test_values.py index be75b7b..50e3b75 100644 --- a/tests/test_values.py +++ b/tests/test_values.py @@ -473,6 +473,8 @@ "\t<dd><p>Nourishing beverage for baby cows.</p></dd>\n" "</dl></td>\n" "\t\t</tr>\n\t</table>")), + # Long non-textile blocks + ("notextile.. *a very*\n\n*long*\n\n*block*\n", "*a very*\n\n*long*\n\n*block*") ) @pytest.mark.parametrize("input, expected_output", xhtml_known_values) diff --git a/textile/core.py b/textile/core.py index 11c6843..07fdf74 100644 --- a/textile/core.py +++ b/textile/core.py @@ -535,7 +535,7 @@ def block(self, text): block.outer_atts) line = "\t{0}".format(line) else: - if block.tag == 'pre' or block.inner_tag == 'code': + if block.tag in ('pre', 'notextile') or block.inner_tag == 'code': line = self.shelve(encode_html(line, quotes=True)) else: line = self.graf(line) From d7ed71f625332daa2b7949b09366d34614ba6fa0 Mon Sep 17 00:00:00 2001 From: Kirill Mavreshko <kimavr@gmail.com> Date: Sat, 8 Oct 2022 19:35:39 +0500 Subject: [PATCH 076/228] Fix quotes by porting PHP-textile`s tag shelving --- tests/test_span.py | 6 ++-- tests/test_values.py | 5 ++- textile/core.py | 79 ++++++++++++++++++++++++++++---------------- 3 files changed, 57 insertions(+), 33 deletions(-) diff --git a/tests/test_span.py b/tests/test_span.py index d83530d..bafe01c 100644 --- a/tests/test_span.py +++ b/tests/test_span.py @@ -2,18 +2,18 @@ def test_span(): t = Textile() - result = t.span("hello %(bob)span *strong* and **bold**% goodbye") + result = t.retrieveTags(t.span("hello %(bob)span *strong* and **bold**% goodbye")) expect = ('hello <span class="bob">span <strong>strong</strong> and ' '<b>bold</b></span> goodbye') assert result == expect - result = t.span('%:http://domain.tld test%') + result = t.retrieveTags(t.span('%:http://domain.tld test%')) expect = '<span cite="http://domain.tld">test</span>' assert result == expect t = Textile() # cover the partial branch where we exceed the max_span_depth. t.max_span_depth = 2 - result = t.span('_-*test*-_') + result = t.retrieveTags(t.span('_-*test*-_')) expect = '<em><del>*test*</del></em>' assert result == expect diff --git a/tests/test_values.py b/tests/test_values.py index 50e3b75..c2f38b7 100644 --- a/tests/test_values.py +++ b/tests/test_values.py @@ -474,7 +474,10 @@ "</dl></td>\n" "\t\t</tr>\n\t</table>")), # Long non-textile blocks - ("notextile.. *a very*\n\n*long*\n\n*block*\n", "*a very*\n\n*long*\n\n*block*") + ("notextile.. *a very*\n\n*long*\n\n*block*\n", "*a very*\n\n*long*\n\n*block*"), + # Correct use of ‘ and ’ + ("Here is a %(example)'spanned'% word.", + '\t<p>Here is a <span class="example">‘spanned’</span> word.</p>'), ) @pytest.mark.parametrize("input, expected_output", xhtml_known_values) diff --git a/textile/core.py b/textile/core.py index 07fdf74..8214fc8 100644 --- a/textile/core.py +++ b/textile/core.py @@ -35,7 +35,7 @@ import re -def make_glyph_replacers(html_type, uid, glyph_defs, is_initial): +def make_glyph_replacers(html_type, uid, glyph_defs): """ Generates a list of "replacers" (each is a pair consiting of a regular expression and a replacing pattern) that, @@ -55,8 +55,7 @@ def make_glyph_replacers(html_type, uid, glyph_defs, is_initial): r'\1{dimension}\2'), # apostrophe's (re.compile( - (r"(^|{0}|\))'({0})" if not is_initial - else r"({0}|\))'({0})") + r"({0}|\))'({0})" .format(regex_snippets['wrd']), flags=re.U), r'\1{apostrophe}\2'), @@ -71,11 +70,7 @@ def make_glyph_replacers(html_type, uid, glyph_defs, is_initial): r'\1{quote_single_open}'), # single closing (re.compile( - (r"(^|\S)'(?={0}|{1}|<|$)".format( - regex_snippets['space'], pnct_re_s) - if not is_initial - else r"(\S)'(?={0}|{1}|$)".format( - regex_snippets['space'], pnct_re_s)), + r"(\S)'(?={0}|{1}|<|$)".format(regex_snippets['space'], pnct_re_s), flags=re.U), r'\1{quote_single_close}'), # single opening @@ -86,11 +81,7 @@ def make_glyph_replacers(html_type, uid, glyph_defs, is_initial): r'\1{quote_double_open}'), # double closing (re.compile( - (r'(^|\S)"(?={0}|{1}|<|$)'.format( - regex_snippets['space'], pnct_re_s) - if not is_initial - else r'(\S)"(?={0}|{1}|<|$)'.format( - regex_snippets['space'], pnct_re_s)), + r'(\S)"(?={0}|{1}|<|$)'.format(regex_snippets['space'], pnct_re_s), flags=re.U), r'\1{quote_double_close}'), # double opening @@ -184,6 +175,10 @@ class Textile(object): 'plusminus': '±', } + spanWrappers = ( + ('[', ']'), + ) + def __init__(self, restricted=False, lite=False, noimage=False, get_sizes=False, html_type='xhtml', rel='', block_tags=True): """Textile properties that are common to regular textile and @@ -213,11 +208,7 @@ def __init__(self, restricted=False, lite=False, noimage=False, regex_snippets['space']) self.glyph_replacers = make_glyph_replacers( - html_type, self.uid, self.glyph_definitions, is_initial=False) - # Replacements that need to be made at the beginning - # of the string - self.initial_glyph_replacers = make_glyph_replacers( - html_type, self.uid, self.glyph_definitions, is_initial=True) + html_type, self.uid, self.glyph_definitions) if self.restricted is True: self.url_schemes = self.restricted_url_schemes @@ -279,6 +270,7 @@ def parse(self, text, rel=None, sanitize=False): if sanitize: text = sanitizer.sanitize(text) + text = self.retrieveTags(text) text = self.retrieveURLs(text) # if the text contains a break tag (<br> or <br />) not followed by @@ -624,15 +616,14 @@ def glyphs(self, text): """ text = text.rstrip('\n') result = [] - replacers = self.initial_glyph_replacers standalone_amp_re = re.compile( r"&(?!#[0-9]+;|#x[a-f0-9]+;|[a-z][a-z0-9]*;)", flags=re.I) html_amp_symbol = self.glyph_definitions['ampersand'] # split the text by any angle-bracketed tags - for i, line in enumerate(re.compile(r'(<[\w\/!?].*?>)', re.U).split( - text)): - if not i % 2: + lines = re.compile(r'(<[\w/!?].*?>)', re.U | re.S).split(text) + for i, line in enumerate(lines): + if i % 2 == 0: if not self.restricted: # Raw < > & chars have already been encoded # when in restricted mode @@ -641,11 +632,9 @@ def glyphs(self, text): .sub(html_amp_symbol, line) .replace('<', '<') .replace('>', '>')) - for s, r in replacers: + for s, r in self.glyph_replacers: line = s.sub(r, line) result.append(line) - if i == 0: - replacers = self.glyph_replacers return ''.join(result) def getRefs(self, text): @@ -1074,8 +1063,16 @@ def span(self, text): self.span_depth = self.span_depth - 1 return text + def getSpecialOptions(self, pre, tail): + for before, after in self.spanWrappers: + if pre == before and tail == after: + pre = tail = '' + break + return (pre, tail) + def fSpan(self, match): pre, tag, atts, cite, content, end, tail = match.groups() + pre, tail = self.getSpecialOptions(pre, tail) qtags = { '*': 'strong', @@ -1096,11 +1093,32 @@ def fSpan(self, match): atts = '{0} cite="{1}"'.format(atts, cite.rstrip()) content = self.span(content) + opentag = '<{0}{1}>'.format(tag, atts) + closetag = '</{0}>'.format(tag) + tags = self.storeTags(opentag, closetag) + return pre + tags['open'] + content + end + tags['close'] + tail + + def storeTags(self, opentag, closetag=''): + tags = {} + self.refIndex += 1 + self.refCache[self.refIndex] = opentag + tags['open'] = self.uid + str(self.refIndex) + ':ospan '; + + self.refIndex += 1 + self.refCache[self.refIndex] = closetag + tags['close'] = ' ' + self.uid + str(self.refIndex) + ':cspan'; + return tags + + def retrieveTags(self, text): + text = (re.compile('{0}(?P<token>[0-9]+):ospan '.format(self.uid), re.U) + .sub(self.fRetrieveTags, text)) + text = (re.compile(' {0}(?P<token>[0-9]+):cspan'.format(self.uid), re.U) + .sub(self.fRetrieveTags, text)) + return text + + def fRetrieveTags(self, match): + return self.refCache[int(match.group('token'))] - out = "<{0}{1}>{2}{3}</{4}>".format(tag, atts, content, end, tag) - if pre and not tail or tail and not pre: - out = '{0}{1}{2}'.format(pre, out, tail) - return out def image(self, text): pattern = re.compile(r""" @@ -1166,6 +1184,7 @@ def code(self, text): def fCode(self, match): before, text, after = match.groups() after = after or '' + before, after = self.getSpecialOptions(before, after) # text needs to be escaped text = encode_html(text, quotes=False) return ''.join([before, self.shelve('<code>{0}</code>'.format(text)), after]) @@ -1174,6 +1193,7 @@ def fPre(self, match): before, text, after = match.groups() if after is None: after = '' + before, after = self.getSpecialOptions(before, after) # text needs to be escaped text = encode_html(text) return ''.join([before, '<pre>', self.shelve(text), '</pre>', after]) @@ -1192,6 +1212,7 @@ def fTextile(self, match): before, notextile, after = match.groups() if after is None: # pragma: no branch after = '' + before, after = self.getSpecialOptions(before, after) return ''.join([before, self.shelve(notextile), after]) def getHTMLComments(self, text): From 155e2b32ab955072940f085d591227a75d056bd9 Mon Sep 17 00:00:00 2001 From: Kirill Mavreshko <kimavr@gmail.com> Date: Sun, 9 Oct 2022 12:58:29 +0500 Subject: [PATCH 077/228] Fix multi-term definitions in lists. Port from PHP --- tests/test_values.py | 2 +- textile/core.py | 33 +++++++++++++++++++++++---------- 2 files changed, 24 insertions(+), 11 deletions(-) diff --git a/tests/test_values.py b/tests/test_values.py index c2f38b7..6fbde09 100644 --- a/tests/test_values.py +++ b/tests/test_values.py @@ -246,7 +246,7 @@ ("""h2. A definition list\n\n;(class#id) Term 1\n: Def 1\n: Def 2\n: Def 3\n;; Center\n;; NATO(Why Em Cee Ayy)\n:: Subdef 1\n:: Subdef 2\n;;; SubSub Term\n::: SubSub Def 1\n::: SubSub Def 2\n::: Subsub Def 3\nWith newline\n::: Subsub Def 4\n:: Subdef 3\n: DEF 4\n; Term 2\n: Another def\n: And another\n: One more\n:: A def without a term\n:: More defness\n; Third term for good measure\n: My definition of a boombastic jazz""", """\t<h2>A definition list</h2>\n\n\t<dl class="class" id="id">\n\t\t<dt>Term 1</dt>\n\t\t<dd>Def 1</dd>\n\t\t<dd>Def 2</dd>\n\t\t<dd>Def 3\n\t\t<dl>\n\t\t\t<dt>Center</dt>\n\t\t\t<dt><acronym title="Why Em Cee Ayy"><span class="caps">NATO</span></acronym></dt>\n\t\t\t<dd>Subdef 1</dd>\n\t\t\t<dd>Subdef 2\n\t\t\t<dl>\n\t\t\t\t<dt>SubSub Term</dt>\n\t\t\t\t<dd>SubSub Def 1</dd>\n\t\t\t\t<dd>SubSub Def 2</dd>\n\t\t\t\t<dd>Subsub Def 3<br />\nWith newline</dd>\n\t\t\t\t<dd>Subsub Def 4</dd>\n\t\t\t</dl></dd>\n\t\t\t<dd>Subdef 3</dd>\n\t\t</dl></dd>\n\t\t<dd><span class="caps">DEF</span> 4</dd>\n\t\t<dt>Term 2</dt>\n\t\t<dd>Another def</dd>\n\t\t<dd>And another</dd>\n\t\t<dd>One more\n\t\t<dl>\n\t\t\t<dd>A def without a term</dd>\n\t\t\t<dd>More defness</dd>\n\t\t</dl></dd>\n\t\t<dt>Third term for good measure</dt>\n\t\t<dd>My definition of a boombastic jazz</dd>\n\t</dl>"""), ("""###. Here's a comment.\n\nh3. Hello\n\n###. And\nanother\none.\n\nGoodbye.""", """\t<h3>Hello</h3>\n\n\t<p>Goodbye.</p>"""), - ("""h2. A Definition list which covers the instance where a new definition list is created with a term without a definition\n\n- term :=\n- term2 := def""", """\t<h2>A Definition list which covers the instance where a new definition list is created with a term without a definition</h2>\n\n<dl>\n\t<dt>term2</dt>\n\t<dd>def</dd>\n</dl>"""), + ("""h2. A Definition list which covers the instance where a new definition list is created with a term without a definition\n\n- term :=\n- term2 := def""", """\t<h2>A Definition list which covers the instance where a new definition list is created with a term without a definition</h2>\n\n<dl>\n\t<dt>term</dt>\n\t<dt>term2</dt>\n\t<dd>def</dd>\n</dl>"""), ('!{height:20px;width:20px;}https://1.gravatar.com/avatar/!', '\t<p><img alt="" src="https://1.gravatar.com/avatar/" style="height:20px; width:20px;" /></p>'), ('& test', '\t<p>& test</p>'), diff --git a/textile/core.py b/textile/core.py index 8214fc8..c68f344 100644 --- a/textile/core.py +++ b/textile/core.py @@ -1246,7 +1246,7 @@ def fRCList(self, match): text = re.split(r'\n(?=[-])', match.group(), flags=re.M) for line in text: # parse the attributes and content - m = re.match(r'^[-]+({0})[ .](.*)$'.format(cls_re_s), line, + m = re.match(r'^[-]+({0})\.? (.*)$'.format(cls_re_s), line, flags=re.M | re.S) if not m: continue @@ -1257,9 +1257,15 @@ def fRCList(self, match): atts = pba(atts, restricted=self.restricted) # split the content into the term and definition - xm = re.match(r'^(.*?)[\s]*:=(.*?)[\s]*(=:|:=)?[\s]*$', content, - re.S) - term, definition, ending = xm.groups() + xm = re.match( + r'^(.*?){0}*:=(.*?){0}*(=:|:=)?{0}*$' + .format(regex_snippets['space']), + content, + re.S) + if xm: + term, definition, _ = xm.groups() + else: + term, definition = content, '' # cleanup term = term.strip() definition = definition.strip(' ') @@ -1272,16 +1278,23 @@ def fRCList(self, match): dltag = "<dl>" out.append(dltag) - if definition != '' and term != '': - if definition.startswith('\n'): - definition = '<p>{0}</p>'.format(definition.lstrip()) - definition = definition.replace('\n', '<br />').strip() + if term != '': + is_newline_started_def = definition.startswith('\n') + definition = ( + definition + .strip() + .replace('\n', '<br />')) + + if is_newline_started_def: + definition = '<p>{0}</p>'.format(definition) + term = term.replace('\n', '<br />') term = self.graf(term) definition = self.graf(definition) - out.extend(['\t<dt{0}>{1}</dt>'.format(atts, term), - '\t<dd>{0}</dd>'.format(definition)]) + out.append('\t<dt{0}>{1}</dt>'.format(atts, term)) + if definition: + out.append('\t<dd>{0}</dd>'.format(definition)) out.append('</dl>') out = '\n'.join(out) From 8c9f98802bdc297690c33d16f2aee31532c5ecc7 Mon Sep 17 00:00:00 2001 From: Kirill Mavreshko <kimavr@gmail.com> Date: Fri, 14 Oct 2022 08:26:25 +0500 Subject: [PATCH 078/228] Fix coupling of $-links with link aliases --- tests/test_values.py | 5 +++++ textile/core.py | 20 +++++++++++++++----- 2 files changed, 20 insertions(+), 5 deletions(-) diff --git a/tests/test_values.py b/tests/test_values.py index 6fbde09..07f0399 100644 --- a/tests/test_values.py +++ b/tests/test_values.py @@ -478,6 +478,11 @@ # Correct use of ‘ and ’ ("Here is a %(example)'spanned'% word.", '\t<p>Here is a <span class="example">‘spanned’</span> word.</p>'), + # Using $-links with link aliases + ("\"$\":test\n[test]https://textpattern.com/start\n", + "\t<p><a href=\"https://textpattern.com/start\">textpattern.com/start</a></p>"), + ('Please check on "$":test for any updates.\n[test]https://de.wikipedia.org/wiki/Übermensch', + '\t<p>Please check on <a href="https://de.wikipedia.org/wiki/Übermensch">de.wikipedia.org/wiki/Übermensch</a> for any updates.</p>'), ) @pytest.mark.parametrize("input, expected_output", xhtml_known_values) diff --git a/textile/core.py b/textile/core.py index c68f344..bf0b01b 100644 --- a/textile/core.py +++ b/textile/core.py @@ -139,6 +139,13 @@ def make_glyph_replacers(html_type, uid, glyph_defs): for (regex_obj, replacement) in pre_result] +def human_readable_url(url): + if "://" in url: + url = url.split("://")[1] + elif ":" in url: + url = url.split(":")[1] + return url + class Textile(object): restricted_url_schemes = ('http', 'https', 'ftp', 'mailto') @@ -957,11 +964,14 @@ def _casesdefault(c, pop, popped, url_chars, counts, pre): return in_.replace('{0}linkStartMarker:'.format(self.uid), '') if text == '$': - text = url - if "://" in text: - text = text.split("://")[1] - elif ":" in text: - text = text.split(":")[1] + if valid_scheme: + text = human_readable_url(url) + else: + ref_url = self.urlrefs.get(url) + if ref_url is not None: + text = human_readable_url(ref_url) + else: + text = url text = text.strip() title = encode_html(title) From 05c59a787bb719304c74861cc4450d9cf51636e5 Mon Sep 17 00:00:00 2001 From: Kirill Mavreshko <kimavr@gmail.com> Date: Sun, 16 Oct 2022 10:04:44 +0500 Subject: [PATCH 079/228] Fix incorrect placement of <br> (ported from PHP) --- tests/test_values.py | 3 +++ textile/core.py | 11 ++++++++++- 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/tests/test_values.py b/tests/test_values.py index 07f0399..56a5792 100644 --- a/tests/test_values.py +++ b/tests/test_values.py @@ -483,6 +483,9 @@ "\t<p><a href=\"https://textpattern.com/start\">textpattern.com/start</a></p>"), ('Please check on "$":test for any updates.\n[test]https://de.wikipedia.org/wiki/Übermensch', '\t<p>Please check on <a href="https://de.wikipedia.org/wiki/Übermensch">de.wikipedia.org/wiki/Übermensch</a> for any updates.</p>'), + # Make sure smileys don't get recognised as a definition list. + (":(\n\n:)\n\n:( \n:( \n:( \n:) \n\nPinocchio!\n:^)\n\nBaboon!\n:=)\n\nWink!\n;)\n\n:[ \n:]\n\n;(\nsomething\ndark side\n:) \n\n;(c)[de] Item", + '\t<p>:(</p>\n\n\t<p>:)</p>\n\n\t<p>:( <br />\n:( <br />\n:( <br />\n:) </p>\n\n\t<p>Pinocchio!<br />\n:^)</p>\n\n\t<p>Baboon!<br />\n:=)</p>\n\n\t<p>Wink!<br />\n;)</p>\n\n\t<p>:[ <br />\n:]</p>\n\n\t<p>;(<br />\nsomething<br />\ndark side<br />\n:) </p>\n\n\t<dl class="c" lang="de">\n\t\t<dt>Item</dt>\n\t</dl>'), ) @pytest.mark.parametrize("input, expected_output", xhtml_known_values) diff --git a/textile/core.py b/textile/core.py index bf0b01b..4b5c572 100644 --- a/textile/core.py +++ b/textile/core.py @@ -420,7 +420,16 @@ def doTagBr(self, tag, input): def doPBr(self, in_): return (re .compile(r'<(p|h[1-6])([^>]*?)>(.*)(</\1>)', re.S) - .sub(self.doBr, in_)) + .sub(self.fPBr, in_)) + + def fPBr(self, m): + content = m.group(3) + content = ( + re.compile(r"<br[ ]*/?>{0}*\n(?![{0}|])".format(regex_snippets['space']), + re.I) + .sub("\n", content)) + content = re.compile(r"\n(?![\s|])").sub('<br />',content) + return '<{0}{1}>{2}{3}'.format(m.group(1), m.group(2), content, m.group(4)) def doBr(self, match): content = ( From dd6c6854c66e0d916470c49df64903de6bd3fafe Mon Sep 17 00:00:00 2001 From: Kirill Mavreshko <kimavr@gmail.com> Date: Sun, 16 Oct 2022 11:52:13 +0500 Subject: [PATCH 080/228] Fix forbidding invalid classes and ids in HTML --- tests/test_values.py | 3 +++ textile/utils.py | 26 ++++++++++++++++++++------ 2 files changed, 23 insertions(+), 6 deletions(-) diff --git a/tests/test_values.py b/tests/test_values.py index 56a5792..e14b23d 100644 --- a/tests/test_values.py +++ b/tests/test_values.py @@ -486,6 +486,9 @@ # Make sure smileys don't get recognised as a definition list. (":(\n\n:)\n\n:( \n:( \n:( \n:) \n\nPinocchio!\n:^)\n\nBaboon!\n:=)\n\nWink!\n;)\n\n:[ \n:]\n\n;(\nsomething\ndark side\n:) \n\n;(c)[de] Item", '\t<p>:(</p>\n\n\t<p>:)</p>\n\n\t<p>:( <br />\n:( <br />\n:( <br />\n:) </p>\n\n\t<p>Pinocchio!<br />\n:^)</p>\n\n\t<p>Baboon!<br />\n:=)</p>\n\n\t<p>Wink!<br />\n;)</p>\n\n\t<p>:[ <br />\n:]</p>\n\n\t<p>;(<br />\nsomething<br />\ndark side<br />\n:) </p>\n\n\t<dl class="c" lang="de">\n\t\t<dt>Item</dt>\n\t</dl>'), + # Checking proper parsing of classes and IDs + ("_(class1 class2#id1)text1_ -(foobarbaz#boom bang)text2-\n", + '\t<p><em class="class1 class2" id="id1">text1</em> <del class="foobarbaz">text2</del></p>'), ) @pytest.mark.parametrize("input, expected_output", xhtml_known_values) diff --git a/textile/utils.py b/textile/utils.py index e971645..80e57cc 100644 --- a/textile/utils.py +++ b/textile/utils.py @@ -153,8 +153,27 @@ def parse_attributes(block_attributes, element=None, include_id=True, restricted m = re.search(r'\(([^()]+)\)', matched, re.U) if m: - aclass = m.group(1) matched = matched.replace(m.group(0), '') + # Only allow a restricted subset of the CSS standard characters for classes/ids. + # No encoding markers allowed. + id_class_match = re.compile(r"^([-a-zA-Z 0-9_\/\[\]\.\:\#]+)$", re.U).match(m.group(1)) + if id_class_match: + class_regex = re.compile(r"^([-a-zA-Z 0-9_\.\/\[\]]*)$") + id_class = id_class_match.group(1) + # If a textile class block attribute was found with a '#' in it + # split it into the css class and css id... + hashpos = id_class.find('#') + if hashpos >= 0: + id_match = re.match(r"^#([-a-zA-Z0-9_\.\:]*)$", id_class[hashpos:]) + if id_match: + block_id = id_match.group(1) + + cls_match = class_regex.match(id_class[:hashpos]) + else: + cls_match = class_regex.match(id_class) + + if cls_match: + aclass = cls_match.group(1) m = re.search(r'([(]+)', matched) if m: @@ -170,11 +189,6 @@ def parse_attributes(block_attributes, element=None, include_id=True, restricted if m: style.append("text-align:{0}".format(hAlign[m.group(1)])) - m = re.search(r'^(.*)#(.*)$', aclass) - if m: - block_id = m.group(2) - aclass = m.group(1) - if element == 'col': pattern = r'(?:\\(\d+)\.?)?\s*(\d+)?' csp = re.match(pattern, matched) From a4d70089788465ba6da37ba009f7e9eec48c6e03 Mon Sep 17 00:00:00 2001 From: Kirill Mavreshko <kimavr@gmail.com> Date: Mon, 17 Oct 2022 17:46:08 +0500 Subject: [PATCH 081/228] Fix broken images, colgroups, captions in tables --- tests/test_textile.py | 2 +- tests/test_values.py | 11 +++++ textile/core.py | 33 +++++++------- textile/objects/table.py | 94 +++++++++++++++------------------------- 4 files changed, 65 insertions(+), 75 deletions(-) diff --git a/tests/test_textile.py b/tests/test_textile.py index 467e31e..4a94c19 100644 --- a/tests/test_textile.py +++ b/tests/test_textile.py @@ -72,7 +72,7 @@ def test_restricted(): expect = '''\ \t<table> \t<caption>Your caption goes here</caption> -\t + \t<tfoot> \t\t<tr> \t\t\t<td>A footer </td> diff --git a/tests/test_values.py b/tests/test_values.py index e14b23d..298502f 100644 --- a/tests/test_values.py +++ b/tests/test_values.py @@ -489,6 +489,17 @@ # Checking proper parsing of classes and IDs ("_(class1 class2#id1)text1_ -(foobarbaz#boom bang)text2-\n", '\t<p><em class="class1 class2" id="id1">text1</em> <del class="foobarbaz">text2</del></p>'), + # Tables with nested textile elements + ("|!http://tester.local/logo.png!| !http://tester.local/logo.png! |", + '\t<table>\n\t\t<tr>\n\t\t\t<td><img alt="" src="http://tester.local/logo.png" /></td>\n\t\t\t<td> <img alt="" src="http://tester.local/logo.png" /> </td>\n\t\t</tr>\n\t</table>'), + # Tables with colgroups + (("|=. Testing colgroup and col syntax | \n" + "|:\\5. 80 |\x20\n" + "|a|b|c|d|e|\x20\n"), + ('\t<table>\n\t<caption>Testing colgroup and col syntax</caption>\n' + '\t<colgroup span="5" width="80">\n\t</colgroup>\n' + '\t\t<tr>\n\t\t\t<td>a</td>\n\t\t\t<td>b</td>\n\t\t\t<td>c</td>\n\t\t\t<td>d</td>\n\t\t\t<td>e</td>\n\t\t</tr>\n' + '\t</table>')), ) @pytest.mark.parametrize("input, expected_output", xhtml_known_values) diff --git a/textile/core.py b/textile/core.py index 4b5c572..6d0b811 100644 --- a/textile/core.py +++ b/textile/core.py @@ -290,10 +290,13 @@ def parse(self, text, rel=None, sanitize=False): def table(self, text): text = "{0}\n\n".format(text) - pattern = re.compile(r'^(?:table(?P<tatts>_?{s}{a}{c})\.' - r'(?P<summary>.*?)\n)?^(?P<rows>{a}{c}\.? ?\|.*\|)' - r'[\s]*\n\n'.format(**{'s': table_span_re_s, 'a': align_re_s, - 'c': cls_re_s}), flags=re.S | re.M | re.U) + pattern = re.compile( + (r'^(?:table(?P<tatts>_?{s}{a}{c})\.' + r'(?P<summary>.*?)\n)?^(?P<rows>{a}{c}\.? ?\|.*\|)' + r'{sp}*\n\n') + .format(**{'s': table_span_re_s, 'a': align_re_s, + 'c': cls_re_s, 'sp': regex_snippets['space']}), + flags=re.S | re.M | re.U) match = pattern.search(text) if match: table = Table(self, **match.groupdict()) @@ -1141,17 +1144,17 @@ def fRetrieveTags(self, match): def image(self, text): pattern = re.compile(r""" - (?:[\[{{])? # pre - \! # opening ! - (\<|\=|\>)? # optional alignment atts - ({0}) # optional style,class atts - (?:\.\s)? # optional dot-space - ([^\s(!]+) # presume this is the src - \s? # optional space - (?:\(([^\)]+)\))? # optional title - \! # closing - (?::(\S+))? # optional href - (?:[\]}}]|(?=\s|$)) # lookahead: space or end of string + (?:[\[{{])? # pre + \! # opening ! + (\<|\=|\>)? # optional alignment atts + ({0}) # optional style,class atts + (?:\.\s)? # optional dot-space + ([^\s(!]+) # presume this is the src + \s? # optional space + (?:\(([^\)]+)\))? # optional title + \! # closing + (?::(\S+)(?<![\]).,]))? # optional href sans final punct + (?:[\]}}]|(?=[.,\s)|]|$)) # lookahead: space or end of string """.format(cls_re_s), re.U | re.X) return pattern.sub(self.fImage, text) diff --git a/textile/objects/table.py b/textile/objects/table.py index 60b6804..3296c7c 100644 --- a/textile/objects/table.py +++ b/textile/objects/table.py @@ -5,7 +5,7 @@ from textile.regex_strings import (align_re_s, cls_re_s, regex_snippets, table_span_re_s, valign_re_s) -from textile.utils import encode_html, generate_tag, parse_attributes +from textile.utils import encode_html, generate_tag, parse_attributes, pba try: import regex as re @@ -14,6 +14,16 @@ class Table(object): + caption_re = re.compile( + (r'^\|\=(?P<capts>{s}{a}{c})\. ' + r'(?P<cap>[^\n]*)(?P<row>.*)' + .format(**{'s': table_span_re_s, 'a': align_re_s, 'c': cls_re_s})), + re.S) + colgroup_re = re.compile( + r'^\|:(?P<cols>{s}{a}{c}\. .*)' + .format(**{'s': table_span_re_s, 'a': align_re_s, 'c': cls_re_s}), + re.M) + def __init__(self, textile, tatts, rows, summary): self.textile = textile self.attributes = parse_attributes(tatts, 'table', restricted=self.textile.restricted) @@ -27,41 +37,37 @@ def __init__(self, textile, tatts, rows, summary): def process(self): rgrp = None groups = [] - if self.input[-1] == '|': # pragma: no branch - self.input = '{0}\n'.format(self.input) - split = self.input.split('|\n') + split = ( + re.compile(r'\|{0}*?$'.format(regex_snippets['space']), re.M) + .split(self.input)) for i, row in enumerate([x for x in split if x]): row = row.lstrip() # Caption -- only occurs on row 1, otherwise treat '|=. foo |...' # as a normal center-aligned cell. - if i == 0 and row[:2] == '|=': - captionpattern = (r"^\|\=(?P<capts>{s}{a}{c})\. " - r"(?P<cap>[^\n]*)(?P<row>.*)".format(**{ - 's': table_span_re_s, 'a': align_re_s, - 'c': cls_re_s})) - caption_re = re.compile(captionpattern, re.S) - cmtch = caption_re.match(row) - if cmtch: - caption = Caption(restricted=self.textile.restricted, **cmtch.groupdict()) - self.caption = '\n{0}'.format(caption.caption) - row = cmtch.group('row').lstrip() - if row == '': - continue + cmtch = self.caption_re.match(row) + if i == 0 and cmtch: + caption = Caption(restricted=self.textile.restricted, **cmtch.groupdict()) + self.caption = '\n{0}'.format(caption.caption) + row = cmtch.group('row').lstrip() + if row == '': + continue # Colgroup -- A colgroup row will not necessarily end with a |. # Hence it may include the next row of actual table data. - if row[:2] == '|:': - if '\n' in row: - colgroup_data, row = row[2:].split('\n') + + gmtch = self.colgroup_re.match(row) + if gmtch: + cols = gmtch.group(1).replace('.', "") + for idx, col in enumerate(cols.split("|")): + gatts = pba(col.strip(), 'col', restricted=self.textile.restricted) + self.colgroup += '\t<col{0}\n'.format( + ('group' + gatts + '>') if idx == 0 else (gatts + ' />')) + self.colgroup += '\t</colgroup>' + nl_index = row.find('\n') + if nl_index >= 0: + row = row[nl_index:].lstrip() else: - colgroup_data, row = row[2:], '' - colgroup_atts, cols = colgroup_data, None - if '|' in colgroup_data: - colgroup_atts, cols = colgroup_data.split('|', 1) - colgrp = Colgroup(cols, colgroup_atts, restricted=self.textile.restricted) - self.colgroup = colgrp.process() - if row == '': continue # search the row for a table group - thead, tfoot, or tbody @@ -143,38 +149,8 @@ def __init__(self, capts, cap, row, restricted): self.caption = self.process(cap) def process(self, cap): - tag = generate_tag('caption', cap, self.attributes) - return '\t{0}\n\t'.format(tag) - - -class Colgroup(object): - def __init__(self, cols, atts, restricted): - self.row = '' - self.attributes = atts - self.cols = cols - self.restricted = restricted - - def process(self): - enc = 'unicode' - - group_atts = parse_attributes(self.attributes, 'col', restricted=self.restricted) - colgroup = ElementTree.Element('colgroup', attrib=group_atts) - colgroup.text = '\n\t' - if self.cols is not None: - has_newline = "\n" in self.cols - match_cols = self.cols.replace('.', '').split('|') - # colgroup is the first item in match_cols, the remaining items are - # cols. - for idx, col in enumerate(match_cols): - col_atts = parse_attributes(col.strip(), 'col', restricted=self.restricted) - ElementTree.SubElement(colgroup, 'col', col_atts) - colgrp = ElementTree.tostring(colgroup, encoding=enc) - # cleanup the extra xml declaration if it exists, (python versions - # differ) and then format the resulting string accordingly: newline and - # tab between cols and a newline at the end - xml_declaration = "<?xml version='1.0' encoding='UTF-8'?>\n" - colgrp = colgrp.replace(xml_declaration, '') - return colgrp.replace('><', '>\n\t<') + tag = generate_tag('caption', cap.strip(), self.attributes) + return '\t{0}\n'.format(tag) class Row(object): From c344838402cba37504ceb7af5594fe54248df32b Mon Sep 17 00:00:00 2001 From: Kirill Mavreshko <kimavr@gmail.com> Date: Mon, 17 Oct 2022 18:37:56 +0500 Subject: [PATCH 082/228] Fix confusion between cells with _em_ and heading --- tests/test_values.py | 4 ++++ textile/objects/table.py | 9 ++++++--- 2 files changed, 10 insertions(+), 3 deletions(-) diff --git a/tests/test_values.py b/tests/test_values.py index 298502f..5d15950 100644 --- a/tests/test_values.py +++ b/tests/test_values.py @@ -500,6 +500,10 @@ '\t<colgroup span="5" width="80">\n\t</colgroup>\n' '\t\t<tr>\n\t\t\t<td>a</td>\n\t\t\t<td>b</td>\n\t\t\t<td>c</td>\n\t\t\t<td>d</td>\n\t\t\t<td>e</td>\n\t\t</tr>\n' '\t</table>')), + # Table column with an emphasis should not be confused with a heading + ('|_touch_ this!| _touch_ this! |', + '\t<table>\n\t\t<tr>\n\t\t\t<td><em>touch</em> this!</td>\n\t\t\t<td> <em>touch</em> this! </td>\n\t\t</tr>\n\t</table>'), + ) @pytest.mark.parametrize("input, expected_output", xhtml_known_values) diff --git a/textile/objects/table.py b/textile/objects/table.py index 3296c7c..ea31500 100644 --- a/textile/objects/table.py +++ b/textile/objects/table.py @@ -3,8 +3,9 @@ from xml.etree import ElementTree -from textile.regex_strings import (align_re_s, cls_re_s, regex_snippets, - table_span_re_s, valign_re_s) +from textile.regex_strings import ( + align_re_s, cls_re_s, regex_snippets, table_span_re_s, + valign_re_s, pnct_re_s) from textile.utils import encode_html, generate_tag, parse_attributes, pba try: @@ -23,6 +24,8 @@ class Table(object): r'^\|:(?P<cols>{s}{a}{c}\. .*)' .format(**{'s': table_span_re_s, 'a': align_re_s, 'c': cls_re_s}), re.M) + heading_re = re.compile( + r'^_(?={0}|{1})'.format(regex_snippets['space'], pnct_re_s)) def __init__(self, textile, tatts, rows, summary): self.textile = textile @@ -99,7 +102,7 @@ def process(self): r = Row(row_atts, row) for cellctr, cell in enumerate(row.split('|')[1:]): ctag = 'td' - if cell.startswith('_'): + if self.heading_re.match(cell): ctag = 'th' cmtch = re.search(r'^(?P<catts>_?{0}{1}{2}\. )' From 1225abcf57838e22487e9d8566ebd62e24d08ee6 Mon Sep 17 00:00:00 2001 From: Kirill Mavreshko <kimavr@gmail.com> Date: Mon, 17 Oct 2022 22:56:26 +0500 Subject: [PATCH 083/228] Fix unclosed <li> in undefined notes --- textile/core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/textile/core.py b/textile/core.py index 6d0b811..89ea7c3 100644 --- a/textile/core.py +++ b/textile/core.py @@ -1364,7 +1364,7 @@ def fNoteLists(self, match): '</span>{3}</li>').format(atts, links, infoid, content) else: - li = ('\t\t<li{0}>{1} Undefined Note [#{2}].<li>' + li = ('\t\t<li{0}>{1} Undefined Note [#{2}].</li>' ).format(atts, links, info['seq']) o.append(li) if '+' == extras and self.unreferencedNotes: From 82157c7bdfe7e4432161c1195c5015dadb1fd67b Mon Sep 17 00:00:00 2001 From: Kirill Mavreshko <kimavr@gmail.com> Date: Tue, 18 Oct 2022 08:28:47 +0500 Subject: [PATCH 084/228] Fix incorrect html in one of the tests --- tests/test_textile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_textile.py b/tests/test_textile.py index 4a94c19..e66e2dc 100644 --- a/tests/test_textile.py +++ b/tests/test_textile.py @@ -159,7 +159,7 @@ def test_endnotes_malformed(): def test_endnotes_undefined_note(): test = """Scientists say the moon is slowly shrinking[#my_first_label].\n\nnotelist!.""" html = textile.textile(test) - result_pattern = r"""\t<p>Scientists say the moon is slowly shrinking<sup><a href="#note([a-f0-9]{32})-2"><span id="noteref\1-1">1</span></a></sup>.</p>\n\n\t<ol>\n\t\t<li> Undefined Note \[#my_first_label\].<li>\n\t</ol>$""" + result_pattern = r"""\t<p>Scientists say the moon is slowly shrinking<sup><a href="#note([a-f0-9]{32})-2"><span id="noteref\1-1">1</span></a></sup>.</p>\n\n\t<ol>\n\t\t<li> Undefined Note \[#my_first_label\].</li>\n\t</ol>$""" result_re = re.compile(result_pattern) assert result_re.search(html) is not None From 7715a78def0bcfe56ea90194908950d9d01da5e7 Mon Sep 17 00:00:00 2001 From: Brad Schoening <5796692+bschoening@users.noreply.github.com> Date: Mon, 22 Jul 2024 22:23:50 -0400 Subject: [PATCH 085/228] Update setup.py to support Python 3.8..3.12 --- setup.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index 118c2fb..70953c0 100644 --- a/setup.py +++ b/setup.py @@ -29,11 +29,11 @@ def get_version(): 'Programming Language :: Python', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3 :: Only', - 'Programming Language :: Python :: 3.5', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.10', + 'Programming Language :: Python :: 3.11', + 'Programming Language :: Python :: 3.12', 'Topic :: Software Development :: Libraries :: Python Modules', ], keywords='textile,text,html markup', From 3203e30104390543aa5124438a1a2eac6f853c06 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Mon, 5 Aug 2024 11:18:12 -0400 Subject: [PATCH 086/228] update versions to test against --- .github/workflows/lint_and_test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint_and_test.yml b/.github/workflows/lint_and_test.yml index 0db7ea8..f488521 100644 --- a/.github/workflows/lint_and_test.yml +++ b/.github/workflows/lint_and_test.yml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.5, 3.6, 3.7, 3.8, 3.9, pypy3] + python-version: [3.8, 3.9, 3.10, 3.11, 3.12, pypy3] image_size: ['true', 'false'] steps: - uses: actions/checkout@v2 From 66312bdc92c1c74fc1b785468c3993450370d0fa Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Mon, 5 Aug 2024 11:22:46 -0400 Subject: [PATCH 087/228] make sure versions are interpreted as strings 3.10 was being interpreted as 3.1 with a trailing 0 --- .github/workflows/lint_and_test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint_and_test.yml b/.github/workflows/lint_and_test.yml index f488521..935d688 100644 --- a/.github/workflows/lint_and_test.yml +++ b/.github/workflows/lint_and_test.yml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.8, 3.9, 3.10, 3.11, 3.12, pypy3] + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "pypy3"] image_size: ['true', 'false'] steps: - uses: actions/checkout@v2 From 964b71c42543afa92c15abd0463795f7f360f06d Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Mon, 5 Aug 2024 11:26:09 -0400 Subject: [PATCH 088/228] use isinstance for type comparison fixes a flake8 failure --- tests/test_cli.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_cli.py b/tests/test_cli.py index 57b2d5f..5e6ab79 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -14,7 +14,7 @@ def test_console_script(): command, stdout=subprocess.PIPE).communicate()[0] with open('tests/fixtures/README.txt') as f: expect = ''.join(f.readlines()) - if type(result) == bytes: + if isinstance(result, bytes): result = result.decode('utf-8') assert result == expect @@ -27,6 +27,6 @@ def test_version_string(): command[2] = 'textile.__main__' result = subprocess.Popen( command, stdout=subprocess.PIPE).communicate()[0] - if type(result) == bytes: + if isinstance(result, bytes): result = result.decode('utf-8') assert result.strip() == textile.__version__ From b3c0341656a938e4031e062a3d9a490ab41eae90 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Mon, 5 Aug 2024 11:36:54 -0400 Subject: [PATCH 089/228] try the latest version of the github action --- .github/workflows/lint_and_test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint_and_test.yml b/.github/workflows/lint_and_test.yml index 935d688..96bfd6d 100644 --- a/.github/workflows/lint_and_test.yml +++ b/.github/workflows/lint_and_test.yml @@ -17,7 +17,7 @@ jobs: with: python-version: ${{ matrix.python-version }} - name: Python flake8 Lint - uses: py-actions/flake8@v1.2.0 + uses: py-actions/flake8@v2.3.0 - name: Install dependencies run: | imagesize='' From 9c7da86753dc2852e18f01ea75778d2a513f2fd3 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Mon, 5 Aug 2024 11:41:42 -0400 Subject: [PATCH 090/228] update other actions as well --- .github/workflows/lint_and_test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/lint_and_test.yml b/.github/workflows/lint_and_test.yml index 96bfd6d..c4d4aab 100644 --- a/.github/workflows/lint_and_test.yml +++ b/.github/workflows/lint_and_test.yml @@ -11,9 +11,9 @@ jobs: python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "pypy3"] image_size: ['true', 'false'] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Python flake8 Lint From d58b58b0ba7bc83d0d6abf9aa663420845b8fdbe Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Mon, 5 Aug 2024 11:54:17 -0400 Subject: [PATCH 091/228] fix version spec for pypy --- .github/workflows/lint_and_test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint_and_test.yml b/.github/workflows/lint_and_test.yml index c4d4aab..cc2690b 100644 --- a/.github/workflows/lint_and_test.yml +++ b/.github/workflows/lint_and_test.yml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "pypy3"] + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "pypy3.10"] image_size: ['true', 'false'] steps: - uses: actions/checkout@v4 From e5e2e5c40b1604658c85cf3c1622b490bc42bc42 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Mon, 5 Aug 2024 11:59:57 -0400 Subject: [PATCH 092/228] update to the latest version of the codecov action --- .github/workflows/lint_and_test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint_and_test.yml b/.github/workflows/lint_and_test.yml index cc2690b..8cfec1f 100644 --- a/.github/workflows/lint_and_test.yml +++ b/.github/workflows/lint_and_test.yml @@ -28,4 +28,4 @@ jobs: run: | pytest - name: Codecov - uses: codecov/codecov-action@v2.1.0 + uses: codecov/codecov-action@v4 From 3a99e730198a45bcd2b921e8cbf1d0ded4136676 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Mon, 5 Aug 2024 12:07:53 -0400 Subject: [PATCH 093/228] add codecov token which is stored as a secret --- .github/workflows/lint_and_test.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/lint_and_test.yml b/.github/workflows/lint_and_test.yml index 8cfec1f..44a7c91 100644 --- a/.github/workflows/lint_and_test.yml +++ b/.github/workflows/lint_and_test.yml @@ -29,3 +29,5 @@ jobs: pytest - name: Codecov uses: codecov/codecov-action@v4 + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} From abcaaaa6af49d7cd229bd5e2d9f14c5384d606bc Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Mon, 5 Aug 2024 12:58:55 -0400 Subject: [PATCH 094/228] update badge to github actions --- README.textile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.textile b/README.textile index 98f4fbd..797516c 100644 --- a/README.textile +++ b/README.textile @@ -1,4 +1,4 @@ -!https://travis-ci.org/textile/python-textile.svg!:https://travis-ci.org/textile/python-textile !https://codecov.io/github/textile/python-textile/coverage.svg!:https://codecov.io/github/textile/python-textile !https://img.shields.io/pypi/pyversions/textile! !https://img.shields.io/pypi/wheel/textile! +!https://github.com/textile/python-textile/actions/workflows/lint_and_test.yml/badge.svg(python-textile)!:https://github.com/textile/python-textile/actions/workflows/lint_and_test.yml !https://codecov.io/github/textile/python-textile/coverage.svg!:https://codecov.io/github/textile/python-textile !https://img.shields.io/pypi/pyversions/textile! !https://img.shields.io/pypi/wheel/textile! h1. python-textile From 7112abfcb0dd7fd09f9abb5a3f95b11ea80310f2 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Mon, 5 Aug 2024 13:01:08 -0400 Subject: [PATCH 095/228] update fixture to reflect README changes --- tests/fixtures/README.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/fixtures/README.txt b/tests/fixtures/README.txt index 61dc0f0..466b706 100644 --- a/tests/fixtures/README.txt +++ b/tests/fixtures/README.txt @@ -1,4 +1,4 @@ - <p><a href="https://travis-ci.org/textile/python-textile"><img alt="" src="https://travis-ci.org/textile/python-textile.svg" /></a> <a href="https://codecov.io/github/textile/python-textile"><img alt="" src="https://codecov.io/github/textile/python-textile/coverage.svg" /></a> <img alt="" src="https://img.shields.io/pypi/pyversions/textile" /> <img alt="" src="https://img.shields.io/pypi/wheel/textile" /></p> + <p><a href="https://github.com/textile/python-textile/actions/workflows/lint_and_test.yml"><img alt="python-textile" src="https://github.com/textile/python-textile/actions/workflows/lint_and_test.yml/badge.svg" title="python-textile" /></a> <a href="https://codecov.io/github/textile/python-textile"><img alt="" src="https://codecov.io/github/textile/python-textile/coverage.svg" /></a> <img alt="" src="https://img.shields.io/pypi/pyversions/textile" /> <img alt="" src="https://img.shields.io/pypi/wheel/textile" /></p> <h1>python-textile</h1> From 5cb14853894aca092d089b508775e7364597fb12 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Mon, 5 Aug 2024 13:59:53 -0400 Subject: [PATCH 096/228] update minimum supported python version --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index cbbb774..b14b9a6 100644 --- a/setup.py +++ b/setup.py @@ -1,5 +1,5 @@ -from setuptools import setup, find_packages import os +from setuptools import setup, find_packages def get_version(): @@ -48,5 +48,5 @@ def get_version(): tests_require=['pytest', 'pytest-cov'], include_package_data=True, zip_safe=False, - python_requires='>=3.5', + python_requires='>=3.8', ) From 425afff8e67f396ec1f220c9978e2a2e4b2d18e3 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Mon, 5 Aug 2024 14:15:18 -0400 Subject: [PATCH 097/228] codecov is reporting missing coverage in this file testing to see if moving this import will have any affect --- textile/tools/imagesize.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/textile/tools/imagesize.py b/textile/tools/imagesize.py index 6fba73e..ec724a2 100644 --- a/textile/tools/imagesize.py +++ b/textile/tools/imagesize.py @@ -1,3 +1,5 @@ +from urllib.request import urlopen + def getimagesize(url): """ Attempts to determine an image's width and height, and returns a tuple, @@ -11,8 +13,6 @@ def getimagesize(url): except ImportError: return '' - from urllib.request import urlopen - try: p = ImageFile.Parser() f = urlopen(url) From 766c76733eee9094c86476adf85088f23c971b23 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Mon, 5 Aug 2024 14:17:00 -0400 Subject: [PATCH 098/228] flake8 insists on two new lines --- textile/tools/imagesize.py | 1 + 1 file changed, 1 insertion(+) diff --git a/textile/tools/imagesize.py b/textile/tools/imagesize.py index ec724a2..1d758cc 100644 --- a/textile/tools/imagesize.py +++ b/textile/tools/imagesize.py @@ -1,5 +1,6 @@ from urllib.request import urlopen + def getimagesize(url): """ Attempts to determine an image's width and height, and returns a tuple, From 9951604d2d6c665d613935fe89ed5a98d62f6cf3 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Mon, 5 Aug 2024 14:23:05 -0400 Subject: [PATCH 099/228] setting this back to what it was --- textile/tools/imagesize.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/textile/tools/imagesize.py b/textile/tools/imagesize.py index 1d758cc..6fba73e 100644 --- a/textile/tools/imagesize.py +++ b/textile/tools/imagesize.py @@ -1,6 +1,3 @@ -from urllib.request import urlopen - - def getimagesize(url): """ Attempts to determine an image's width and height, and returns a tuple, @@ -14,6 +11,8 @@ def getimagesize(url): except ImportError: return '' + from urllib.request import urlopen + try: p = ImageFile.Parser() f = urlopen(url) From ba2424904042cb2e02ba597d121937cff70eb1e5 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Mon, 5 Aug 2024 14:23:21 -0400 Subject: [PATCH 100/228] debugging the value of imagesize --- .github/workflows/lint_and_test.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/lint_and_test.yml b/.github/workflows/lint_and_test.yml index 44a7c91..e932372 100644 --- a/.github/workflows/lint_and_test.yml +++ b/.github/workflows/lint_and_test.yml @@ -24,6 +24,8 @@ jobs: pip install -U pytest pytest-cov coverage codecov if [[ $INPUT_IMAGE_SIZE == true ]] ; then imagesize='[imagesize]' ; fi pip install -e ".${imagesize}" + echo "INPUT_IMAGE_SIZE: ${INPUT_IMAGE_SIZE}" + echo "imagesize: ${imagesize}" - name: run tests run: | pytest From 05e3bca4d6b8cf902f7c9ec795d8be7a81748f8e Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Mon, 5 Aug 2024 14:25:23 -0400 Subject: [PATCH 101/228] more debugging --- .github/workflows/lint_and_test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/lint_and_test.yml b/.github/workflows/lint_and_test.yml index e932372..f4795b2 100644 --- a/.github/workflows/lint_and_test.yml +++ b/.github/workflows/lint_and_test.yml @@ -24,6 +24,7 @@ jobs: pip install -U pytest pytest-cov coverage codecov if [[ $INPUT_IMAGE_SIZE == true ]] ; then imagesize='[imagesize]' ; fi pip install -e ".${imagesize}" + echo "image_size: ${image_size}" echo "INPUT_IMAGE_SIZE: ${INPUT_IMAGE_SIZE}" echo "imagesize: ${imagesize}" - name: run tests From 05bb7bf524797a2cd790fcd20ec43470714dccda Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Mon, 5 Aug 2024 14:30:29 -0400 Subject: [PATCH 102/228] maybe this is the right way to reference matrix values??? --- .github/workflows/lint_and_test.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/lint_and_test.yml b/.github/workflows/lint_and_test.yml index f4795b2..047e6d8 100644 --- a/.github/workflows/lint_and_test.yml +++ b/.github/workflows/lint_and_test.yml @@ -24,9 +24,7 @@ jobs: pip install -U pytest pytest-cov coverage codecov if [[ $INPUT_IMAGE_SIZE == true ]] ; then imagesize='[imagesize]' ; fi pip install -e ".${imagesize}" - echo "image_size: ${image_size}" - echo "INPUT_IMAGE_SIZE: ${INPUT_IMAGE_SIZE}" - echo "imagesize: ${imagesize}" + echo "image_size: ${{ matrix.image_size }}" - name: run tests run: | pytest From f93078826204732d10ec806eec3464104812985d Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Mon, 5 Aug 2024 14:32:17 -0400 Subject: [PATCH 103/228] correct way to look up matrix value --- .github/workflows/lint_and_test.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/lint_and_test.yml b/.github/workflows/lint_and_test.yml index 047e6d8..14ced37 100644 --- a/.github/workflows/lint_and_test.yml +++ b/.github/workflows/lint_and_test.yml @@ -22,9 +22,8 @@ jobs: run: | imagesize='' pip install -U pytest pytest-cov coverage codecov - if [[ $INPUT_IMAGE_SIZE == true ]] ; then imagesize='[imagesize]' ; fi + if [[ ${{ matrix.image_size }} == true ]] ; then imagesize='[imagesize]' ; fi pip install -e ".${imagesize}" - echo "image_size: ${{ matrix.image_size }}" - name: run tests run: | pytest From 0dedc3802473c03af7e7734b9f4d4f894b49c83c Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 12:52:43 -0500 Subject: [PATCH 104/228] first stab at using github actions instead of travis-ci --- .github/workflows/lint_and_test.yml | 28 ++++++++++++++++++++++++++++ .travis.yml | 23 ----------------------- 2 files changed, 28 insertions(+), 23 deletions(-) create mode 100644 .github/workflows/lint_and_test.yml delete mode 100644 .travis.yml diff --git a/.github/workflows/lint_and_test.yml b/.github/workflows/lint_and_test.yml new file mode 100644 index 0000000..f806d24 --- /dev/null +++ b/.github/workflows/lint_and_test.yml @@ -0,0 +1,28 @@ +--- +name: python-textile + +on: [push] + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + python-version: [3.5, 3.6, 3.7, 3.8, 3.9] + image-size: ['true', 'false'] + steps: + - uses: actions/checkout@v2 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2 + with: + python-version: ${{ matrix.python-version }} + image-size: ${{ matrix.image-size }} + - name: Install dependencies + run: | + imagesize='' + pip install -U pytest pytest-cov coverage codecov + if [[ ${{ image-size }} == true ]] ; then imagesize='[imagesize]' ; fi + pip install -e ".${imagesize}" + - name: run tests + run: | + pytest diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 3f7d77c..0000000 --- a/.travis.yml +++ /dev/null @@ -1,23 +0,0 @@ -dist: xenial # required for Python >= 3.7 -language: python -env: - - IMAGESIZE=true - - IMAGESIZE=false -python: - - "3.5" - - "3.6" - - "3.7" - - "3.8" - - "3.9" - # PyPy versions - - "pypy3" -# command to install dependencies -install: - - imagesize='' - - pip install -U pytest pytest-cov coverage codecov - - if [[ $IMAGESIZE == true ]] ; then imagesize='[imagesize]' ; fi - - pip install -e ".${imagesize}" -# command to run tests -script: py.test -after_success: - - codecov From d4c26db0b68afb7c85897d8ef4bca57809c93c7e Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 12:55:08 -0500 Subject: [PATCH 105/228] maybe this fixes the job ??? --- .github/workflows/lint_and_test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint_and_test.yml b/.github/workflows/lint_and_test.yml index f806d24..e03c18a 100644 --- a/.github/workflows/lint_and_test.yml +++ b/.github/workflows/lint_and_test.yml @@ -21,7 +21,7 @@ jobs: run: | imagesize='' pip install -U pytest pytest-cov coverage codecov - if [[ ${{ image-size }} == true ]] ; then imagesize='[imagesize]' ; fi + if [[ ${{ matrix.image-size }} == true ]] ; then imagesize='[imagesize]' ; fi pip install -e ".${imagesize}" - name: run tests run: | From 7646274d40973dabab2abe9e885ed47619ead339 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 13:35:49 -0500 Subject: [PATCH 106/228] add codecov action to send data to codecov --- .github/workflows/lint_and_test.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/lint_and_test.yml b/.github/workflows/lint_and_test.yml index e03c18a..0e10375 100644 --- a/.github/workflows/lint_and_test.yml +++ b/.github/workflows/lint_and_test.yml @@ -26,3 +26,5 @@ jobs: - name: run tests run: | pytest + - name: Codecov + uses: codecov/codecov-action@v2.1.0 From d5075075f484e9f7945ee4c03645455f0929bef1 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 13:36:47 -0500 Subject: [PATCH 107/228] indentation fix --- .github/workflows/lint_and_test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint_and_test.yml b/.github/workflows/lint_and_test.yml index 0e10375..78bac9c 100644 --- a/.github/workflows/lint_and_test.yml +++ b/.github/workflows/lint_and_test.yml @@ -27,4 +27,4 @@ jobs: run: | pytest - name: Codecov - uses: codecov/codecov-action@v2.1.0 + uses: codecov/codecov-action@v2.1.0 From 6442fc11374a33dc8d3c7e1263d25b67ad89264c Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 14:03:29 -0500 Subject: [PATCH 108/228] add flake8 linting --- .github/workflows/lint_and_test.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/lint_and_test.yml b/.github/workflows/lint_and_test.yml index 78bac9c..cec0b42 100644 --- a/.github/workflows/lint_and_test.yml +++ b/.github/workflows/lint_and_test.yml @@ -17,6 +17,8 @@ jobs: with: python-version: ${{ matrix.python-version }} image-size: ${{ matrix.image-size }} + - name: Python flake8 Lint + uses: py-actions/flake8@v1.2.0 - name: Install dependencies run: | imagesize='' From 01cf90aa390281b07adde7b4a4cb89a418c2215d Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 14:11:03 -0500 Subject: [PATCH 109/228] maybe we're not quite ready for linting --- .github/workflows/lint_and_test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/lint_and_test.yml b/.github/workflows/lint_and_test.yml index cec0b42..ef6393c 100644 --- a/.github/workflows/lint_and_test.yml +++ b/.github/workflows/lint_and_test.yml @@ -17,8 +17,8 @@ jobs: with: python-version: ${{ matrix.python-version }} image-size: ${{ matrix.image-size }} - - name: Python flake8 Lint - uses: py-actions/flake8@v1.2.0 + #- name: Python flake8 Lint + # uses: py-actions/flake8@v1.2.0 - name: Install dependencies run: | imagesize='' From 6164155a43d86887ef0b845762ed5813db97281e Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 14:11:35 -0500 Subject: [PATCH 110/228] testing out variables more like how we used to with travis --- .github/workflows/lint_and_test.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/lint_and_test.yml b/.github/workflows/lint_and_test.yml index ef6393c..16f06b2 100644 --- a/.github/workflows/lint_and_test.yml +++ b/.github/workflows/lint_and_test.yml @@ -21,9 +21,10 @@ jobs: # uses: py-actions/flake8@v1.2.0 - name: Install dependencies run: | - imagesize='' + imagesize='' + IMAGESIZE=${{ matrix.image-size }} pip install -U pytest pytest-cov coverage codecov - if [[ ${{ matrix.image-size }} == true ]] ; then imagesize='[imagesize]' ; fi + if [[ $IMAGESIZE == true ]] ; then imagesize='[imagesize]' ; fi pip install -e ".${imagesize}" - name: run tests run: | From 5297519f246aa53bf0b3a9aefcd11dabbe0842b4 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 14:28:00 -0500 Subject: [PATCH 111/228] flake8: cleanup unused imports --- setup.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/setup.py b/setup.py index 118c2fb..80488d2 100644 --- a/setup.py +++ b/setup.py @@ -1,7 +1,5 @@ from setuptools import setup, find_packages -from setuptools.command.test import test as TestCommand import os -import sys def get_version(): From c46d5cb11b53d4eff5add6ad26d8c3e817b76c70 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 14:30:08 -0500 Subject: [PATCH 112/228] flake8: variable cleanup --- tests/test_block.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_block.py b/tests/test_block.py index 44f3ea2..613bf41 100644 --- a/tests/test_block.py +++ b/tests/test_block.py @@ -21,7 +21,6 @@ def test_block(): assert result == expect b = Block(t, "bq", "", None, "http://google.com", "Hello BlockQuote") - citation = '{0}1:url'.format(t.uid) expect = ('blockquote', OrderedDict([('cite', '{0.uid}{0.refIndex}:url'.format(t))]), 'p', OrderedDict(), 'Hello BlockQuote') From 49115230858507c6b6d71c37f9fb54c2724dadfd Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 15:30:38 -0500 Subject: [PATCH 113/228] add flake8 config file --- .flake8 | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 .flake8 diff --git a/.flake8 b/.flake8 new file mode 100644 index 0000000..07c24d1 --- /dev/null +++ b/.flake8 @@ -0,0 +1,9 @@ +[flake8] +ignore = + # line too long + E501 + # temporarily ignore warnings and errors + W + E +exclude = + build/ From 6f9b95ba60ce5acd3e68849cf9b6088061d74652 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 15:31:19 -0500 Subject: [PATCH 114/228] flake8: inline ignore unused variable --- tests/test_textile.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_textile.py b/tests/test_textile.py index e66e2dc..7c0ebde 100644 --- a/tests/test_textile.py +++ b/tests/test_textile.py @@ -121,7 +121,7 @@ def test_sanitize(): assert result == expect def test_imagesize(): - PIL = pytest.importorskip('PIL') + PIL = pytest.importorskip('PIL') # noqa: F841 test = "!http://www.google.com/intl/en_ALL/images/srpr/logo1w.png!" result = '\t<p><img alt="" height="95" src="http://www.google.com/intl/en_ALL/images/srpr/logo1w.png" width="275" /></p>' From 35ca09900e60ae1c8cf9668a986b456474e4c420 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 15:35:19 -0500 Subject: [PATCH 115/228] flake8: remove unused import --- tests/test_urls.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_urls.py b/tests/test_urls.py index 7a9798e..39ff163 100644 --- a/tests/test_urls.py +++ b/tests/test_urls.py @@ -1,6 +1,5 @@ # -*- coding: utf-8 -*- from textile import Textile -import re def test_urls(): t = Textile() From 88cba865b8e2b029c8aebf461a800b1d0989abaf Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 15:41:44 -0500 Subject: [PATCH 116/228] flake8: remove unused import --- tests/test_attributes.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_attributes.py b/tests/test_attributes.py index 70da842..0f5d019 100644 --- a/tests/test_attributes.py +++ b/tests/test_attributes.py @@ -1,5 +1,4 @@ from textile.utils import parse_attributes -import re def test_parse_attributes(): assert parse_attributes('\\1', element='td') == {'colspan': '1'} From 28b97477f4ee2e426c5e64b2c090b0f0b86409cf Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 15:44:42 -0500 Subject: [PATCH 117/228] flake8: inline ignore unused import --- tests/test_imagesize.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/test_imagesize.py b/tests/test_imagesize.py index 112989e..0a89b43 100644 --- a/tests/test_imagesize.py +++ b/tests/test_imagesize.py @@ -4,7 +4,7 @@ def test_imagesize(): imgurl = 'http://www.google.com/intl/en_ALL/images/srpr/logo1w.png' result = textile.tools.imagesize.getimagesize(imgurl) try: - import PIL + import PIL # noqa: F401 expect = (275, 95) assert result == expect From e6d82098f720e452b38ce74da57b9604759f0f5a Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 15:45:45 -0500 Subject: [PATCH 118/228] flake8: remove unused import --- tests/test_footnoteRef.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_footnoteRef.py b/tests/test_footnoteRef.py index b773ad2..c973ee7 100644 --- a/tests/test_footnoteRef.py +++ b/tests/test_footnoteRef.py @@ -1,5 +1,4 @@ from textile import Textile -import re def test_footnoteRef(): t = Textile() From ebcd25e04813b44ac1aeb95bb3815c786b327a80 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 15:49:47 -0500 Subject: [PATCH 119/228] flake8: inline ignore unused import --- textile/regex_strings.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/textile/regex_strings.py b/textile/regex_strings.py index deb4a4a..effa7a6 100644 --- a/textile/regex_strings.py +++ b/textile/regex_strings.py @@ -4,7 +4,7 @@ try: # Use regex module for matching uppercase characters if installed, # otherwise fall back to finding all the uppercase chars in a loop. - import regex as re + import regex as re # noqa: F401 upper_re_s = r'\p{Lu}' regex_snippets = { 'acr': r'\p{Lu}\p{Nd}', From a249c9ab6987fb46870ce4abd6beec2826436795 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 16:03:16 -0500 Subject: [PATCH 120/228] flake8: remove unused imports --- textile/__init__.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/textile/__init__.py b/textile/__init__.py index bb7829f..e7ea665 100644 --- a/textile/__init__.py +++ b/textile/__init__.py @@ -1,8 +1,5 @@ from __future__ import unicode_literals -import sys -import warnings - from .core import textile, textile_restricted, Textile from .version import VERSION From 22322df34660801cfd9462af346387bb9bdc0f8c Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 16:03:34 -0500 Subject: [PATCH 121/228] flake8: inline ignore unused import. It's actually used farther down the line in the module. --- textile/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/textile/__init__.py b/textile/__init__.py index e7ea665..9ca8a65 100644 --- a/textile/__init__.py +++ b/textile/__init__.py @@ -1,6 +1,6 @@ from __future__ import unicode_literals -from .core import textile, textile_restricted, Textile +from .core import textile, textile_restricted, Textile # noqa: F401 from .version import VERSION __all__ = ['textile', 'textile_restricted'] From 1e633e7ba80e324ffd0bdd22bc7bef9038314463 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 16:08:51 -0500 Subject: [PATCH 122/228] flake8: inline ignores because it's not actually a problem --- textile/core.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/textile/core.py b/textile/core.py index 89ea7c3..7b7e6d1 100644 --- a/textile/core.py +++ b/textile/core.py @@ -495,10 +495,12 @@ def block(self, text): content = out[-2] if not multiline_para: - content = generate_tag(block.inner_tag, content, - block.inner_atts) - content = generate_tag(block.outer_tag, content, - block.outer_atts) + # block will have been defined in a previous run of the + # loop + content = generate_tag(block.inner_tag, content, # noqa: F821 + block.inner_atts) # noqa: F821 + content = generate_tag(block.outer_tag, content, # noqa: F821 + block.outer_atts) # noqa: F821 out[-2] = content tag, atts, ext, cite, content = match.groups() block = Block(self, **match.groupdict()) From 5faf1abf7a3215b243212a9ff3c69489c5372101 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 16:12:53 -0500 Subject: [PATCH 123/228] flake8: remove unused variables --- textile/core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/textile/core.py b/textile/core.py index 7b7e6d1..d87757d 100644 --- a/textile/core.py +++ b/textile/core.py @@ -1384,7 +1384,7 @@ def fNoteLists(self, match): def makeBackrefLink(self, info, g_links, i): """Given the pieces of a back reference link, create an <a> tag.""" - atts, content, infoid, link = '', '', '', '' + link = '' if 'def' in info: link = info['def']['link'] backlink_type = link or g_links From d3cc65f8ddf3ccf9ca79253cda05b515f46d6b99 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 16:14:15 -0500 Subject: [PATCH 124/228] resolving conflicts part of rebase --- textile/objects/table.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/textile/objects/table.py b/textile/objects/table.py index ea31500..fb26689 100644 --- a/textile/objects/table.py +++ b/textile/objects/table.py @@ -6,7 +6,7 @@ from textile.regex_strings import ( align_re_s, cls_re_s, regex_snippets, table_span_re_s, valign_re_s, pnct_re_s) -from textile.utils import encode_html, generate_tag, parse_attributes, pba +from textile.utils import generate_tag, parse_attributes, pba try: import regex as re From 2ba87837a5e2a7f7826adcef6a58a843681e5950 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 16:15:03 -0500 Subject: [PATCH 125/228] continuing to resolve conflicts while rebasing --- textile/objects/table.py | 31 ++++++++++++++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/textile/objects/table.py b/textile/objects/table.py index fb26689..bb8e299 100644 --- a/textile/objects/table.py +++ b/textile/objects/table.py @@ -153,7 +153,36 @@ def __init__(self, capts, cap, row, restricted): def process(self, cap): tag = generate_tag('caption', cap.strip(), self.attributes) - return '\t{0}\n'.format(tag) + return '\t{0}\n\t'.format(tag) + + +class Colgroup(object): + def __init__(self, cols, atts, restricted): + self.row = '' + self.attributes = atts + self.cols = cols + self.restricted = restricted + + def process(self): + enc = 'unicode' + + group_atts = parse_attributes(self.attributes, 'col', restricted=self.restricted) + colgroup = ElementTree.Element('colgroup', attrib=group_atts) + colgroup.text = '\n\t' + if self.cols is not None: + match_cols = self.cols.replace('.', '').split('|') + # colgroup is the first item in match_cols, the remaining items are + # cols. + for idx, col in enumerate(match_cols): + col_atts = parse_attributes(col.strip(), 'col', restricted=self.restricted) + ElementTree.SubElement(colgroup, 'col', col_atts) + colgrp = ElementTree.tostring(colgroup, encoding=enc) + # cleanup the extra xml declaration if it exists, (python versions + # differ) and then format the resulting string accordingly: newline and + # tab between cols and a newline at the end + xml_declaration = "<?xml version='1.0' encoding='UTF-8'?>\n" + colgrp = colgrp.replace(xml_declaration, '') + return colgrp.replace('><', '>\n\t<') class Row(object): From dad644ba99be0d76e2b02aa321341e72e0b35b9c Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 16:16:25 -0500 Subject: [PATCH 126/228] add flake8 linting back in now that `F` class violations have been resolved --- .github/workflows/lint_and_test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/lint_and_test.yml b/.github/workflows/lint_and_test.yml index 16f06b2..437820d 100644 --- a/.github/workflows/lint_and_test.yml +++ b/.github/workflows/lint_and_test.yml @@ -17,8 +17,8 @@ jobs: with: python-version: ${{ matrix.python-version }} image-size: ${{ matrix.image-size }} - #- name: Python flake8 Lint - # uses: py-actions/flake8@v1.2.0 + - name: Python flake8 Lint + uses: py-actions/flake8@v1.2.0 - name: Install dependencies run: | imagesize='' From 510d346236049a2a2e0da6b257d5c13e90e37d71 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 17:01:46 -0500 Subject: [PATCH 127/228] flake8: various indentation cleanups --- textile/regex_strings.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/textile/regex_strings.py b/textile/regex_strings.py index effa7a6..2e096fb 100644 --- a/textile/regex_strings.py +++ b/textile/regex_strings.py @@ -4,7 +4,7 @@ try: # Use regex module for matching uppercase characters if installed, # otherwise fall back to finding all the uppercase chars in a loop. - import regex as re # noqa: F401 + import regex as re # noqa: F401 upper_re_s = r'\p{Lu}' regex_snippets = { 'acr': r'\p{Lu}\p{Nd}', @@ -15,12 +15,12 @@ 'digit': r'\p{N}', 'space': r'(?:\p{Zs}|\v)', 'char': r'(?:[^\p{Zs}\v])', - } + } except ImportError: from sys import maxunicode upper_re_s = "".join( - [chr(c) for c in range(maxunicode) if chr(c).isupper()] - ) + [chr(c) for c in range(maxunicode) if chr(c).isupper()] + ) regex_snippets = { 'acr': r'{0}0-9'.format(upper_re_s), 'abr': r'{0}'.format(upper_re_s), @@ -33,7 +33,7 @@ 'digit': r'\d', 'space': r'(?:\s|\v)', 'char': r'\S', - } + } halign_re_s = r'(?:\<(?!>)|(?<!<)\>|\<\>|\=|[()]+(?! ))' valign_re_s = r'[\-^~]' @@ -46,10 +46,10 @@ table_span_re_s = r'(?:{0}|{1})*'.format(colspan_re_s, rowspan_re_s) # regex string to match class, style and language attributes cls_re_s = (r'(?:' - r'{c}(?:{l}(?:{s})?|{s}(?:{l})?)?|' - r'{l}(?:{c}(?:{s})?|{s}(?:{c})?)?|' - r'{s}(?:{c}(?:{l})?|{l}(?:{c})?)?' + r'{c}(?:{l}(?:{s})?|{s}(?:{l})?)?|' + r'{l}(?:{c}(?:{s})?|{s}(?:{c})?)?|' + r'{s}(?:{c}(?:{l})?|{l}(?:{c})?)?' r')?' - ).format(c=class_re_s, s=style_re_s, l=language_re_s) + ).format(c=class_re_s, s=style_re_s, l=language_re_s) pnct_re_s = r'[-!"#$%&()*+,/:;<=>?@\'\[\\\]\.^_`{|}~]' syms_re_s = '¤§µ¶†‡•∗∴◊♠♣♥♦' From ff882e0cd1391b3f23c470ae0b2bbc4b70a0d42a Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 17:04:00 -0500 Subject: [PATCH 128/228] flake8: spacing cleanup --- textile/utils.py | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/textile/utils.py b/textile/utils.py index 80e57cc..84dadcf 100644 --- a/textile/utils.py +++ b/textile/utils.py @@ -32,10 +32,12 @@ def decode_high(text): text = '&#{0};'.format(text) return html.unescape(text) + def encode_high(text): """Encode the text so that it is an appropriate HTML entity.""" return ord(text) + def encode_html(text, quotes=True): """Return text that's safe for an HTML attribute.""" a = ( @@ -51,6 +53,7 @@ def encode_html(text, quotes=True): text = text.replace(k, v) return text + def generate_tag(tag, content, attributes=None): """Generate a complete html tag using the ElementTree module. tag and content are strings, the attributes argument is a dictionary. As @@ -71,11 +74,12 @@ def generate_tag(tag, content, attributes=None): # non-ascii text being html-entity encoded. Not bad, but not entirely # matching php-textile either. element_tag = ElementTree.tostringlist(element, encoding=enc, - method='html') + method='html') element_tag.insert(len(element_tag) - 1, content) element_text = ''.join(element_tag) return element_text + def has_raw_text(text): """checks whether the text has text not already enclosed by a block tag""" r = text.strip() @@ -83,17 +87,20 @@ def has_raw_text(text): r = pattern.sub('', r).strip() return r != '' + def is_rel_url(url): """Identify relative urls.""" (scheme, netloc) = urlparse(url)[0:2] return not scheme and not netloc + def is_valid_url(url): parsed = urlparse(url) if parsed.scheme == '': return True return False + def list_type(list_string): listtypes = { list_string.endswith('*'): 'u', @@ -103,12 +110,14 @@ def list_type(list_string): } return listtypes.get(True, False) + def normalize_newlines(string): out = re.sub(r'\r\n?', '\n', string) out = re.compile(r'^[ \t]*\n', flags=re.M).sub('\n', out) out = out.strip('\n') return out + def parse_attributes(block_attributes, element=None, include_id=True, restricted=False): vAlign = {'^': 'top', '-': 'middle', '~': 'bottom'} hAlign = {'<': 'left', '=': 'center', '>': 'right', '<>': 'justify'} @@ -216,6 +225,7 @@ def parse_attributes(block_attributes, element=None, include_id=True, restricted result['width'] = width return result + def pba(block_attributes, element=None, include_id=True, restricted=False): """Parse block attributes.""" attrs = parse_attributes(block_attributes, element, include_id, restricted) From 212f0ad1cacc2f30593cd0f5da1c0d794c54dace Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 17:04:47 -0500 Subject: [PATCH 129/228] flake8: comment spacing cleanup --- textile/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/textile/__init__.py b/textile/__init__.py index 9ca8a65..08a242f 100644 --- a/textile/__init__.py +++ b/textile/__init__.py @@ -1,6 +1,6 @@ from __future__ import unicode_literals -from .core import textile, textile_restricted, Textile # noqa: F401 +from .core import textile, textile_restricted, Textile # noqa: F401 from .version import VERSION __all__ = ['textile', 'textile_restricted'] From 8e66b9b386534a86d2ce3ecb600f9c8955fd2d2d Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 18:11:13 -0500 Subject: [PATCH 130/228] continuing to resolve rebase conflicts --- textile/core.py | 147 +++++++++++++++++++++++------------------------- 1 file changed, 71 insertions(+), 76 deletions(-) diff --git a/textile/core.py b/textile/core.py index d87757d..bd33bd5 100644 --- a/textile/core.py +++ b/textile/core.py @@ -23,10 +23,10 @@ from textile.tools import sanitizer, imagesize from textile.regex_strings import (align_re_s, cls_re_s, pnct_re_s, - regex_snippets, syms_re_s, table_span_re_s) + regex_snippets, syms_re_s, table_span_re_s) from textile.utils import (decode_high, encode_high, encode_html, generate_tag, - has_raw_text, is_rel_url, is_valid_url, list_type, normalize_newlines, - parse_attributes, pba) + has_raw_text, is_rel_url, is_valid_url, list_type, + normalize_newlines, parse_attributes, pba) from textile.objects import Block, Table try: @@ -149,8 +149,8 @@ def human_readable_url(url): class Textile(object): restricted_url_schemes = ('http', 'https', 'ftp', 'mailto') - unrestricted_url_schemes = restricted_url_schemes + ('file', 'tel', - 'callto', 'sftp', 'data') + unrestricted_url_schemes = restricted_url_schemes + ( + 'file', 'tel', 'callto', 'sftp', 'data') btag = ('bq', 'bc', 'notextile', 'pre', 'h[1-6]', r'fn\d+', 'p', '###') btag_lite = ('bq', 'bc', 'p') @@ -187,7 +187,7 @@ class Textile(object): ) def __init__(self, restricted=False, lite=False, noimage=False, - get_sizes=False, html_type='xhtml', rel='', block_tags=True): + get_sizes=False, html_type='xhtml', rel='', block_tags=True): """Textile properties that are common to regular textile and textile_restricted""" self.restricted = restricted @@ -212,7 +212,7 @@ def __init__(self, restricted=False, lite=False, noimage=False, cur = r'' if regex_snippets['cur']: # pragma: no branch cur = r'(?:[{0}]{1}*)?'.format(regex_snippets['cur'], - regex_snippets['space']) + regex_snippets['space']) self.glyph_replacers = make_glyph_replacers( html_type, self.uid, self.glyph_definitions) @@ -251,9 +251,9 @@ def parse(self, text, rel=None, sanitize=False): self.blocktag_whitelist = ['bq', 'p'] text = self.block(text) else: - self.blocktag_whitelist = [ 'bq', 'p', 'bc', 'notextile', - 'pre', 'h[1-6]', - 'fn{0}+'.format(regex_snippets['digit']), '###'] + self.blocktag_whitelist = ['bq', 'p', 'bc', 'notextile', + 'pre', 'h[1-6]', 'fn{0}+'.format( + regex_snippets['digit']), '###'] text = self.block(text) text = self.placeNoteLists(text) else: @@ -290,13 +290,11 @@ def parse(self, text, rel=None, sanitize=False): def table(self, text): text = "{0}\n\n".format(text) - pattern = re.compile( - (r'^(?:table(?P<tatts>_?{s}{a}{c})\.' - r'(?P<summary>.*?)\n)?^(?P<rows>{a}{c}\.? ?\|.*\|)' - r'{sp}*\n\n') - .format(**{'s': table_span_re_s, 'a': align_re_s, - 'c': cls_re_s, 'sp': regex_snippets['space']}), - flags=re.S | re.M | re.U) + pattern = re.compile(r'^(?:table(?P<tatts>_?{s}{a}{c})\.' + r'(?P<summary>.*?)\n)?^(?P<rows>{a}{c}\.? ?\|.*\|)' + r'[\s]*\n\n'.format( + **{'s': table_span_re_s, 'a': align_re_s, + 'c': cls_re_s}), flags=re.S | re.M | re.U) match = pattern.search(text) if match: table = Table(self, **match.groupdict()) @@ -305,7 +303,7 @@ def table(self, text): def textileLists(self, text): pattern = re.compile(r'^((?:[*;:]+|[*;:#]*#(?:_|\d+)?){0}[ .].*)$' - r'(?![^#*;:])'.format(cls_re_s), re.U | re.M | re.S) + r'(?![^#*;:])'.format(cls_re_s), re.U | re.M | re.S) return pattern.sub(self.fTextileList, text) def fTextileList(self, match): @@ -320,7 +318,7 @@ def fTextileList(self, match): nextline = '' m = re.search(r"^(?P<tl>[#*;:]+)(?P<st>_|\d+)?(?P<atts>{0})[ .]" - "(?P<content>.*)$".format(cls_re_s), line, re.S) + "(?P<content>.*)$".format(cls_re_s), line, re.S) if m: tl, start, atts, content = m.groups() content = content.strip() @@ -368,7 +366,7 @@ def fTextileList(self, match): self.olstarts[tl] = 1 nm = re.match(r"^(?P<nextlistitem>[#\*;:]+)(_|[\d]+)?{0}" - r"[ .].*".format(cls_re_s), nextline) + r"[ .].*".format(cls_re_s), nextline) if nm: nl = nm.group('nextlistitem') @@ -388,7 +386,7 @@ def fTextileList(self, match): if tl not in ls: ls[tl] = 1 itemtag = ("\n{0}\t<{1}>{2}".format(tabs, litem, content) if - showitem else '') + showitem else '') line = "<{0}l{1}{2}>{3}".format(ltype, atts, start, itemtag) else: line = ("\t<{0}{1}>{2}".format(litem, atts, content) if @@ -401,18 +399,13 @@ def fTextileList(self, match): for k, v in reversed(list(ls.items())): if len(k) > len(nl): if v != 2: - line = "{0}\n{1}</{2}l>".format(line, tabs, - list_type(k)) + line = "{0}\n{1}</{2}l>".format( + line, tabs, list_type(k)) if len(k) > 1 and v != 2: line = "{0}</{1}>".format(line, litem) del ls[k] # Remember the current Textile tag: pt = tl - # This else exists in the original php version. I'm not sure how - # to come up with a case where the line would not match. I think - # it may have been necessary due to the way php returns matches. - # else: - #line = "{0}\n".format(line) result.append(line) return self.doTagBr(litem, "\n".join(result)) @@ -442,7 +435,7 @@ def doBr(self, match): re.I) .sub(r'\1<br />', match.group(3))) return '<{0}{1}>{2}{3}'.format(match.group(1), match.group(2), content, - match.group(4)) + match.group(4)) def block(self, text): if not self.lite: @@ -478,8 +471,8 @@ def block(self, text): eat_whitespace = False pattern = (r'^(?P<tag>{0})(?P<atts>{1}{2})\.(?P<ext>\.?)' - r'(?::(?P<cite>\S+))? (?P<content>.*)$'.format(tre, - align_re_s, cls_re_s)) + r'(?::(?P<cite>\S+))? (?P<content>.*)$'.format( + tre, align_re_s, cls_re_s)) match = re.search(pattern, line, flags=re.S | re.U) # tag specified on this line. if match: @@ -497,15 +490,15 @@ def block(self, text): if not multiline_para: # block will have been defined in a previous run of the # loop - content = generate_tag(block.inner_tag, content, # noqa: F821 - block.inner_atts) # noqa: F821 - content = generate_tag(block.outer_tag, content, # noqa: F821 - block.outer_atts) # noqa: F821 + content = generate_tag(block.inner_tag, content, # noqa: F821 + block.inner_atts) # noqa: F821 + content = generate_tag(block.outer_tag, content, # noqa: F821 + block.outer_atts) # noqa: F821 out[-2] = content tag, atts, ext, cite, content = match.groups() block = Block(self, **match.groupdict()) inner_block = generate_tag(block.inner_tag, block.content, - block.inner_atts) + block.inner_atts) # code tags and raw text won't be indented inside outer_tag. if block.inner_tag != 'code' and not has_raw_text(inner_block): inner_block = "\n\t\t{0}\n\t".format(inner_block) @@ -513,7 +506,7 @@ def block(self, text): line = block.content else: line = generate_tag(block.outer_tag, inner_block, - block.outer_atts) + block.outer_atts) # pre tags and raw text won't be indented. if block.outer_tag != 'pre' and not has_raw_text(line): line = "\t{0}".format(line) @@ -545,7 +538,7 @@ def block(self, text): line = block.content else: line = generate_tag(block.outer_tag, block.content, - block.outer_atts) + block.outer_atts) line = "\t{0}".format(line) else: if block.tag in ('pre', 'notextile') or block.inner_tag == 'code': @@ -598,14 +591,15 @@ def block(self, text): def footnoteRef(self, text): # somehow php-textile gets away with not capturing the space. return re.compile(r'(?<=\S)\[(?P<id>{0}+)(?P<nolink>!?)\]' - r'(?P<space>{1}?)'.format(regex_snippets['digit'], - regex_snippets['space']), re.U).sub(self.footnoteID, text) + r'(?P<space>{1}?)'.format( + regex_snippets['digit'], regex_snippets['space']), + re.U).sub(self.footnoteID, text) def footnoteID(self, m): fn_att = OrderedDict({'class': 'footnote'}) if m.group('id') not in self.fn: - self.fn[m.group('id')] = '{0}{1}'.format(self.linkPrefix, - self._increment_link_index()) + self.fn[m.group('id')] = '{0}{1}'.format( + self.linkPrefix, self._increment_link_index()) fnid = self.fn[m.group('id')] fn_att['id'] = 'fnrev{0}'.format(fnid) fnid = self.fn[m.group('id')] @@ -814,7 +808,7 @@ def markStartOfLinks(self, text): # Re-assemble the link starts with a specific marker for the # next regex. o = '{0}{1}linkStartMarker:"{2}'.format(pre_link, self.uid, - link_content) + link_content) output.append(o) # Add the last part back @@ -856,7 +850,7 @@ def fLink(self, m): ) # end of $text (?:\((?P<title>[^)]+?)\))? # $title (if any) $'''.format(cls_re_s, regex_snippets['space']), inner, - flags=re.X | re.U) + flags=re.X | re.U) atts = (m and m.group('atts')) or '' text = (m and m.group('text')) or inner @@ -951,20 +945,20 @@ def _casesdefault(c, pop, popped, url_chars, counts, pre): return pop, popped, url_chars, counts, pre cases = { - '!': _endchar, - '?': _endchar, - ':': _endchar, - ';': _endchar, - '.': _endchar, - ',': _endchar, - '>': _rightanglebracket, - ']': _closingsquarebracket, - ')': _closingparenthesis, - } + '!': _endchar, + '?': _endchar, + ':': _endchar, + ';': _endchar, + '.': _endchar, + ',': _endchar, + '>': _rightanglebracket, + ']': _closingsquarebracket, + ')': _closingparenthesis, + } for c in url_chars[-1::-1]: # pragma: no branch popped = False - pop, popped, url_chars, counts, pre = cases.get(c, - _casesdefault)(c, pop, popped, url_chars, counts, pre) + pop, popped, url_chars, counts, pre = cases.get( + c, _casesdefault)(c, pop, popped, url_chars, counts, pre) first = False if popped is False: break @@ -1031,8 +1025,7 @@ def encode_url(self, url): """, re.X | re.U) netloc_parsed = netloc_pattern.match(parsed.netloc).groupdict() else: - netloc_parsed = {'user': '', 'password': '', 'host': '', 'port': - ''} + netloc_parsed = {'user': '', 'password': '', 'host': '', 'port': ''} # encode each component scheme = parsed.scheme @@ -1048,7 +1041,7 @@ def encode_url(self, url): # because the quote and unquote functions expects different input # types: unicode strings for PY2 and str for PY3. path_parts = (quote(unquote(pce), b'') for pce in - parsed.path.split('/')) + parsed.path.split('/')) path = '/'.join(path_parts) # put it back together @@ -1081,8 +1074,10 @@ def span(self, text): (?P<end>[{pnct}]*) {tag} (?P<tail>$|[\[\]}}<]|(?=[{pnct}]{{1,2}}[^0-9]|\s|\))) - """.format(**{'tag': tag, 'cls': cls_re_s, 'pnct': pnct, - 'space': regex_snippets['space']}), flags=re.X | re.U) + """.format( + **{'tag': tag, 'cls': cls_re_s, 'pnct': pnct, 'space': + regex_snippets['space']} + ), flags=re.X | re.U) text = pattern.sub(self.fSpan, text) self.span_depth = self.span_depth - 1 return text @@ -1261,7 +1256,7 @@ def redcloth_list(self, text): """Parse the text for definition lists and send them to be formatted.""" pattern = re.compile(r"^([-]+{0}[ .].*:=.*)$(?![^-])".format(cls_re_s), - re.M | re.U | re.S) + re.M | re.U | re.S) return pattern.sub(self.fRCList, text) def fRCList(self, match): @@ -1270,8 +1265,8 @@ def fRCList(self, match): text = re.split(r'\n(?=[-])', match.group(), flags=re.M) for line in text: # parse the attributes and content - m = re.match(r'^[-]+({0})\.? (.*)$'.format(cls_re_s), line, - flags=re.M | re.S) + m = re.match(r'^[-]+({0})[ .](.*)$'.format(cls_re_s), line, + flags=re.M | re.S) if not m: continue @@ -1341,7 +1336,7 @@ def placeNoteLists(self, text): o = OrderedDict(sorted(o.items(), key=lambda t: t[0])) self.notes = o text_re = re.compile(r'<p>notelist({0})(?:\:([\w|{1}]))?([\^!]?)(\+?)' - r'\.?[\s]*</p>'.format(cls_re_s, syms_re_s), re.U) + r'\.?[\s]*</p>'.format(cls_re_s, syms_re_s), re.U) text = text_re.sub(self.fNoteLists, text) return text @@ -1363,8 +1358,8 @@ def fNoteLists(self, match): atts = info['def']['atts'] content = info['def']['content'] li = ('\t\t<li{0}>{1}<span id="note{2}"> ' - '</span>{3}</li>').format(atts, links, infoid, - content) + '</span>{3}</li>').format(atts, links, infoid, + content) else: li = ('\t\t<li{0}>{1} Undefined Note [#{2}].</li>' ).format(atts, links, info['seq']) @@ -1402,7 +1397,7 @@ def makeBackrefLink(self, info, g_links, i): for refid in info['refids']: i_entity = decode_high(i_) sup = """<sup><a href="#noteref{0}">{1}</a></sup>""".format( - refid, i_entity) + refid, i_entity) if allow_inc: i_ = i_ + 1 result.append(sup) @@ -1418,13 +1413,14 @@ def fParseNoteDefs(self, m): # Assign an id if the note reference parse hasn't found the label yet. if label not in self.notes: - self.notes[label] = {'id': '{0}{1}'.format(self.linkPrefix, - self._increment_link_index())} + self.notes[label] = {'id': '{0}{1}'.format( + self.linkPrefix, self._increment_link_index())} # Ignores subsequent defs using the same label if 'def' not in self.notes[label]: # pragma: no branch - self.notes[label]['def'] = {'atts': pba(att, restricted=self.restricted), 'content': - self.graf(content), 'link': link} + self.notes[label]['def'] = { + 'atts': pba(att, restricted=self.restricted), 'content': + self.graf(content), 'link': link} return '' def noteRef(self, text): @@ -1466,8 +1462,8 @@ def fParseNoteRefs(self, match): # If we are referencing a note that hasn't had the definition parsed # yet, then assign it an ID... if not self.notes[label]['id']: - self.notes[label]['id'] = '{0}{1}'.format(self.linkPrefix, - self._increment_link_index()) + self.notes[label]['id'] = '{0}{1}'.format( + self.linkPrefix, self._increment_link_index()) labelid = self.notes[label]['id'] # Build the link (if any)... @@ -1533,5 +1529,4 @@ def textile_restricted(text, lite=True, noimage=True, html_type='xhtml'): """ return Textile(restricted=True, lite=lite, noimage=noimage, - html_type=html_type, rel='nofollow').parse( - text) + html_type=html_type, rel='nofollow').parse(text) From bf07c80df9e32325f86829083b85bd9777340418 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 18:37:03 -0500 Subject: [PATCH 131/228] flake8: mostly whitespace cleanup --- textile/core.py | 94 ++++++++++++++++++++++++------------------------- 1 file changed, 47 insertions(+), 47 deletions(-) diff --git a/textile/core.py b/textile/core.py index bd33bd5..323533b 100644 --- a/textile/core.py +++ b/textile/core.py @@ -160,26 +160,26 @@ class Textile(object): doctype_whitelist = ['xhtml', 'html5'] glyph_definitions = { - 'quote_single_open': '‘', - 'quote_single_close': '’', - 'quote_double_open': '“', - 'quote_double_close': '”', - 'apostrophe': '’', - 'prime': '′', - 'prime_double': '″', - 'ellipsis': '…', - 'ampersand': '&', - 'emdash': '—', - 'endash': '–', - 'dimension': '×', - 'trademark': '™', - 'registered': '®', - 'copyright': '©', - 'half': '½', - 'quarter': '¼', - 'threequarters': '¾', - 'degrees': '°', - 'plusminus': '±', + 'quote_single_open': '‘', # noqa: E241 + 'quote_single_close': '’', # noqa: E241 + 'quote_double_open': '“', # noqa: E241 + 'quote_double_close': '”', # noqa: E241 + 'apostrophe': '’', # noqa: E241 + 'prime': '′', # noqa: E241 + 'prime_double': '″', # noqa: E241 + 'ellipsis': '…', # noqa: E241 + 'ampersand': '&', # noqa: E241 + 'emdash': '—', # noqa: E241 + 'endash': '–', # noqa: E241 + 'dimension': '×', # noqa: E241 + 'trademark': '™', # noqa: E241 + 'registered': '®', # noqa: E241 + 'copyright': '©', # noqa: E241 + 'half': '½', # noqa: E241 + 'quarter': '¼', # noqa: E241 + 'threequarters': '¾', # noqa: E241 + 'degrees': '°', # noqa: E241 + 'plusminus': '±', # noqa: E241 } spanWrappers = ( @@ -210,7 +210,7 @@ def __init__(self, restricted=False, lite=False, noimage=False, self.block_tags = block_tags cur = r'' - if regex_snippets['cur']: # pragma: no branch + if regex_snippets['cur']: # pragma: no branch cur = r'(?:[{0}]{1}*)?'.format(regex_snippets['cur'], regex_snippets['space']) @@ -752,7 +752,7 @@ def markStartOfLinks(self, text): linkparts = [] i = 0 - while balanced != 0 or i == 0: # pragma: no branch + while balanced != 0 or i == 0: # pragma: no branch # Starting at the end, pop off the previous part of the # slice's fragments. @@ -761,9 +761,9 @@ def markStartOfLinks(self, text): if len(possibility) > 0: # did this part inc or dec the balanced count? - if re.search(r'^\S|=$', possibility, flags=re.U): # pragma: no branch + if re.search(r'^\S|=$', possibility, flags=re.U): # pragma: no branch balanced = balanced - 1 - if re.search(r'\S$', possibility, flags=re.U): # pragma: no branch + if re.search(r'\S$', possibility, flags=re.U): # pragma: no branch balanced = balanced + 1 try: possibility = possible_start_quotes.pop() @@ -783,7 +783,7 @@ def markStartOfLinks(self, text): try: possibility = possible_start_quotes.pop() - except IndexError: # pragma: no cover + except IndexError: # pragma: no cover # If out of possible starting segments we back the # last one from the linkparts array linkparts.pop() @@ -792,7 +792,7 @@ def markStartOfLinks(self, text): # we have a closing ". if (possibility == '' or possibility.endswith(' ')): # force search exit - balanced = 0; + balanced = 0 if balanced <= 0: possible_start_quotes.append(possibility) @@ -857,7 +857,7 @@ def fLink(self, m): title = (m and m.group('title')) or '' pop, tight = '', '' - counts = { '[': None, ']': url.count(']'), '(': None, ')': None } + counts = {'[': None, ']': url.count(']'), '(': None, ')': None} # Look for footnotes or other square-bracket delimited stuff at the end # of the url... @@ -924,13 +924,13 @@ def _closingsquarebracket(c, pop, popped, url_chars, counts, pre): # it popped = True url_chars.pop() - counts[']'] = counts[']'] - 1; - if first: # pragma: no branch + counts[']'] = counts[']'] - 1 + if first: # pragma: no branch pre = '' return pop, popped, url_chars, counts, pre def _closingparenthesis(c, pop, popped, url_chars, counts, pre): - if counts[')'] is None: # pragma: no branch + if counts[')'] is None: # pragma: no branch counts['('] = url.count('(') counts[')'] = url.count(')') @@ -955,7 +955,7 @@ def _casesdefault(c, pop, popped, url_chars, counts, pre): ']': _closingsquarebracket, ')': _closingparenthesis, } - for c in url_chars[-1::-1]: # pragma: no branch + for c in url_chars[-1::-1]: # pragma: no branch popped = False pop, popped, url_chars, counts, pre = cases.get( c, _casesdefault)(c, pop, popped, url_chars, counts, pre) @@ -984,7 +984,7 @@ def _casesdefault(c, pop, popped, url_chars, counts, pre): text = text.strip() title = encode_html(title) - if not self.noimage: # pragma: no branch + if not self.noimage: # pragma: no branch text = self.image(text) text = self.span(text) text = self.glyphs(text) @@ -1094,16 +1094,16 @@ def fSpan(self, match): pre, tail = self.getSpecialOptions(pre, tail) qtags = { - '*': 'strong', - '**': 'b', - '??': 'cite', - '_': 'em', - '__': 'i', - '-': 'del', - '%': 'span', - '+': 'ins', - '~': 'sub', - '^': 'sup' + '*': 'strong', # noqa: E241 + '**': 'b', # noqa: E241 + '??': 'cite', # noqa: E241 + '_': 'em', # noqa: E241 + '__': 'i', # noqa: E241 + '-': 'del', # noqa: E241 + '%': 'span', # noqa: E241 + '+': 'ins', # noqa: E241 + '~': 'sub', # noqa: E241 + '^': 'sup' # noqa: E241 } tag = qtags[tag] @@ -1229,7 +1229,7 @@ def noTextile(self, text): def fTextile(self, match): before, notextile, after = match.groups() - if after is None: # pragma: no branch + if after is None: # pragma: no branch after = '' before, after = self.getSpecialOptions(before, after) return ''.join([before, self.shelve(notextile), after]) @@ -1331,7 +1331,7 @@ def placeNoteLists(self, text): else: self.unreferencedNotes[label] = info - if o: # pragma: no branch + if o: # pragma: no branch # sort o by key o = OrderedDict(sorted(o.items(), key=lambda t: t[0])) self.notes = o @@ -1347,9 +1347,9 @@ def fNoteLists(self, match): index = '{0}{1}{2}'.format(g_links, extras, start_char) result = '' - if index not in self.notelist_cache: # pragma: no branch + if index not in self.notelist_cache: # pragma: no branch o = [] - if self.notes: # pragma: no branch + if self.notes: # pragma: no branch for seq, info in self.notes.items(): links = self.makeBackrefLink(info, g_links, start_char) atts = '' @@ -1417,7 +1417,7 @@ def fParseNoteDefs(self, m): self.linkPrefix, self._increment_link_index())} # Ignores subsequent defs using the same label - if 'def' not in self.notes[label]: # pragma: no branch + if 'def' not in self.notes[label]: # pragma: no branch self.notes[label]['def'] = { 'atts': pba(att, restricted=self.restricted), 'content': self.graf(content), 'link': link} From 6846b686e2f0f6bf58b9f222b73830005f8fb078 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 18:47:46 -0500 Subject: [PATCH 132/228] flake8: warning cleanup --- textile/core.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/textile/core.py b/textile/core.py index 323533b..1457c64 100644 --- a/textile/core.py +++ b/textile/core.py @@ -1030,8 +1030,9 @@ def encode_url(self, url): # encode each component scheme = parsed.scheme user = netloc_parsed['user'] and quote(netloc_parsed['user']) - password = (netloc_parsed['password'] and - quote(netloc_parsed['password'])) + password = ( + netloc_parsed['password'] and quote(netloc_parsed['password']) + ) host = netloc_parsed['host'] port = netloc_parsed['port'] and netloc_parsed['port'] # the below splits the path portion of the url by slashes, translates From ad26eefcaada82d3e3eb480e75129beefaebb732 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 18:49:08 -0500 Subject: [PATCH 133/228] flake8: comment cleanup --- textile/__main__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/textile/__main__.py b/textile/__main__.py index 1845961..210c147 100644 --- a/textile/__main__.py +++ b/textile/__main__.py @@ -33,5 +33,5 @@ def main(): outfile.write(output) -if __name__ == '__main__': #pragma: no cover +if __name__ == '__main__': # pragma: no cover main() From dd317e3db1b07628e8d0635414ce6500dc661a2f Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 19:01:03 -0500 Subject: [PATCH 134/228] flake8: mostly indentation cleanup --- textile/objects/block.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/textile/objects/block.py b/textile/objects/block.py index de993e8..7aca9dd 100644 --- a/textile/objects/block.py +++ b/textile/objects/block.py @@ -71,12 +71,11 @@ def process(self): else: supp_id = parse_attributes('(#fn{0})'.format(fnid), restricted=self.textile.restricted) - if '^' not in self.atts: sup = generate_tag('sup', fns.group('fnid'), supp_id) else: fnrev = generate_tag('a', fns.group('fnid'), {'href': - '#fnrev{0}'.format(fnid)}) + '#fnrev{0}'.format(fnid)}) sup = generate_tag('sup', fnrev, supp_id) self.content = '{0} {1}'.format(sup, self.content) From 98c0697bd289a5dbaa04a3ed8d024cfadc726269 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 19:07:22 -0500 Subject: [PATCH 135/228] flake8: indentation cleanup --- textile/objects/block.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/textile/objects/block.py b/textile/objects/block.py index 7aca9dd..8f44ad1 100644 --- a/textile/objects/block.py +++ b/textile/objects/block.py @@ -40,7 +40,7 @@ def process(self): [{space}]+ # whitespace ends def marker (?P<content>.*)$ # content""".format( space=regex_snippets['space'], cls=cls_re_s), - flags=re.X | re.U) + flags=re.X | re.U) notedef = notedef_re.sub(self.textile.fParseNoteDefs, self.content) # It will be empty if the regex matched and ate it. @@ -49,13 +49,13 @@ def process(self): self.eat = True fns = re.search(r'fn(?P<fnid>{0}+)'.format(regex_snippets['digit']), - self.tag, flags=re.U) + self.tag, flags=re.U) if fns: self.tag = 'p' fnid = self.textile.fn.get(fns.group('fnid'), None) if fnid is None: fnid = '{0}{1}'.format(self.textile.linkPrefix, - self.textile._increment_link_index()) + self.textile._increment_link_index()) # If there is an author-specified ID goes on the wrapper & the # auto-id gets pushed to the <sup> From 22ac8cd7c64758fff8b851f4835ee2742155aba4 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 19:07:44 -0500 Subject: [PATCH 136/228] flake8: indentation and whitespace cleanup --- textile/objects/table.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/textile/objects/table.py b/textile/objects/table.py index bb8e299..5dc295d 100644 --- a/textile/objects/table.py +++ b/textile/objects/table.py @@ -140,8 +140,8 @@ def process(self): if rgrp: groups.append('\n\t{0}'.format(rgrp.process())) - content = '{0}{1}{2}{3}\n\t'.format(self.caption, self.colgroup, - ''.join(groups), ''.join(self.content)) + content = '{0}{1}{2}{3}\n\t'.format( + self.caption, self.colgroup, ''.join(groups), ''.join(self.content)) tbl = generate_tag('table', content, self.attributes) return '\t{0}\n\n'.format(tbl) From b2024314f2535d95f5f7deb03a935196d2994f41 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 19:09:31 -0500 Subject: [PATCH 137/228] continuing to resolve conflicts while rebasing --- textile/objects/table.py | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/textile/objects/table.py b/textile/objects/table.py index 5dc295d..7a3d0ce 100644 --- a/textile/objects/table.py +++ b/textile/objects/table.py @@ -3,10 +3,9 @@ from xml.etree import ElementTree -from textile.regex_strings import ( - align_re_s, cls_re_s, regex_snippets, table_span_re_s, - valign_re_s, pnct_re_s) -from textile.utils import generate_tag, parse_attributes, pba +from textile.regex_strings import (align_re_s, cls_re_s, regex_snippets, + table_span_re_s, valign_re_s, pnct_re_s) +from textile.utils import generate_tag, parse_attributes try: import regex as re @@ -75,8 +74,9 @@ def process(self): # search the row for a table group - thead, tfoot, or tbody grpmatchpattern = (r"(:?^\|(?P<part>{v})(?P<rgrpatts>{s}{a}{c})" - r"\.\s*$\n)?^(?P<row>.*)").format(**{'v': valign_re_s, 's': - table_span_re_s, 'a': align_re_s, 'c': cls_re_s}) + r"\.\s*$\n)?^(?P<row>.*)").format( + **{'v': valign_re_s, 's': table_span_re_s, + 'a': align_re_s, 'c': cls_re_s}) grpmatch_re = re.compile(grpmatchpattern, re.S | re.M) grpmatch = grpmatch_re.match(row.lstrip()) @@ -106,8 +106,9 @@ def process(self): ctag = 'th' cmtch = re.search(r'^(?P<catts>_?{0}{1}{2}\. )' - '(?P<cell>.*)'.format(table_span_re_s, align_re_s, - cls_re_s), cell, flags=re.S) + '(?P<cell>.*)'.format( + table_span_re_s, align_re_s, cls_re_s), + cell, flags=re.S) if cmtch: catts = cmtch.group('catts') cell_atts = parse_attributes(catts, 'td', restricted=self.textile.restricted) @@ -117,7 +118,7 @@ def process(self): if not self.textile.lite: a_pattern = r'(?P<space>{0}*)(?P<cell>.*)'.format( - regex_snippets['space']) + regex_snippets['space']) a = re.search(a_pattern, cell, flags=re.S) cell = self.textile.redcloth_list(a.group('cell')) cell = self.textile.textileLists(cell) From 7aa2af24b9d4d780f4206ec35407f19cd4abd573 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 19:09:55 -0500 Subject: [PATCH 138/228] flake8: whitespace cleanup --- setup.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 80488d2..b405f28 100644 --- a/setup.py +++ b/setup.py @@ -10,6 +10,7 @@ def get_version(): return variables.get('VERSION') raise RuntimeError('No version info found.') + setup( name='textile', version=get_version(), @@ -38,7 +39,7 @@ def get_version(): install_requires=[ 'html5lib>=1.0.1', 'regex>1.0; implementation_name != "pypy"', - ], + ], extras_require={ 'develop': ['pytest', 'pytest-cov'], 'imagesize': ['Pillow>=3.0.0'], From 62e64c3f0afc9aaeede3364af065247c70f5b700 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 19:16:22 -0500 Subject: [PATCH 139/228] flake8: whitespace cleanup --- tests/test_block.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_block.py b/tests/test_block.py index 613bf41..93d3c17 100644 --- a/tests/test_block.py +++ b/tests/test_block.py @@ -69,6 +69,7 @@ def test_blockcode_comment(): result = t.parse(input) assert result == expect + def test_extended_pre_block_with_many_newlines(): """Extra newlines in an extended pre block should not get cut down to only two.""" From 92b8fdb399c4312b90ce591a86fe2f8263781a7e Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 19:17:30 -0500 Subject: [PATCH 140/228] flake8: whitespace cleanup --- tests/test_block.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/tests/test_block.py b/tests/test_block.py index 93d3c17..691d21a 100644 --- a/tests/test_block.py +++ b/tests/test_block.py @@ -8,6 +8,7 @@ except ImportError: from ordereddict import OrderedDict + def test_block(): t = textile.Textile() result = t.block('h1. foobar baby') @@ -16,14 +17,14 @@ def test_block(): b = Block(t, "bq", "", None, "", "Hello BlockQuote") expect = ('blockquote', OrderedDict(), 'p', OrderedDict(), - 'Hello BlockQuote') + 'Hello BlockQuote') result = (b.outer_tag, b.outer_atts, b.inner_tag, b.inner_atts, b.content) assert result == expect b = Block(t, "bq", "", None, "http://google.com", "Hello BlockQuote") expect = ('blockquote', OrderedDict([('cite', - '{0.uid}{0.refIndex}:url'.format(t))]), 'p', OrderedDict(), - 'Hello BlockQuote') + '{0.uid}{0.refIndex}:url'.format(t))]), 'p', OrderedDict(), + 'Hello BlockQuote') result = (b.outer_tag, b.outer_atts, b.inner_tag, b.inner_atts, b.content) assert result == expect @@ -39,6 +40,7 @@ def test_block(): result = (b.outer_tag, b.outer_atts, b.inner_tag, b.inner_atts, b.content) assert result == expect + def test_block_tags_false(): t = textile.Textile(block_tags=False) assert t.block_tags is False @@ -47,6 +49,7 @@ def test_block_tags_false(): expect = 'test' assert result == expect + def test_blockcode_extended(): input = 'bc.. text\nmoretext\n\nevenmoretext\n\nmoremoretext\n\np. test' expect = '<pre><code>text\nmoretext\n\nevenmoretext\n\nmoremoretext</code></pre>\n\n\t<p>test</p>' @@ -54,6 +57,7 @@ def test_blockcode_extended(): result = t.parse(input) assert result == expect + def test_blockcode_in_README(): with open('README.textile') as f: readme = ''.join(f.readlines()) @@ -62,6 +66,7 @@ def test_blockcode_in_README(): expect = ''.join(f.readlines()) assert result == expect + def test_blockcode_comment(): input = '###.. block comment\nanother line\n\np. New line' expect = '\t<p>New line</p>' From 1ee596229be05950a1e1addcfb3226d4e4e4e794 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 19:17:53 -0500 Subject: [PATCH 141/228] flake8: whitespace cleanup --- tests/test_image.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_image.py b/tests/test_image.py index aad39e2..b746292 100644 --- a/tests/test_image.py +++ b/tests/test_image.py @@ -1,5 +1,6 @@ from textile import Textile + def test_image(): t = Textile() result = t.image('!/imgs/myphoto.jpg!:http://jsamsa.com') @@ -17,5 +18,5 @@ def test_image(): t = Textile(rel='nofollow') result = t.image('!/imgs/myphoto.jpg!:http://jsamsa.com') expect = ('<a href="{0}1:url" rel="nofollow"><img alt="" src="{0}2:url" ' - '/></a>'.format(t.uid)) + '/></a>'.format(t.uid)) assert result == expect From d4d0e3970975f8577ad653f8084ddcde2eb8dde1 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 19:18:41 -0500 Subject: [PATCH 142/228] flake8: whitespace cleanup --- tests/test_retrieve.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_retrieve.py b/tests/test_retrieve.py index 10bd173..a416524 100644 --- a/tests/test_retrieve.py +++ b/tests/test_retrieve.py @@ -1,5 +1,6 @@ from textile import Textile + def test_retrieve(): t = Textile() id = t.shelve("foobar") From a37b57a15691b16dc72caf2b11da73d7480c6e3c Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 19:22:20 -0500 Subject: [PATCH 143/228] flake8: whitespace cleanup --- tests/test_textile.py | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/tests/test_textile.py b/tests/test_textile.py index 7c0ebde..0b2abca 100644 --- a/tests/test_textile.py +++ b/tests/test_textile.py @@ -4,10 +4,12 @@ import re import textile + def test_FootnoteReference(): html = textile.textile('YACC[1]') assert re.search(r'^\t<p><span class="caps">YACC</span><sup class="footnote" id="fnrev([a-f0-9]{32})-1"><a href="#fn\1-1">1</a></sup></p>', html) is not None + def test_Footnote(): html = textile.textile('This is covered elsewhere[1].\n\nfn1. Down here, in fact.\n\nfn2. Here is another footnote.') assert re.search(r'^\t<p>This is covered elsewhere<sup class="footnote" id="fnrev([a-f0-9]{32})-1"><a href="#fn\1-1">1</a></sup>.</p>\n\n\t<p class="footnote" id="fn\1-1"><sup>1</sup> Down here, in fact.</p>\n\n\t<p class="footnote" id="fn\1-2"><sup>2</sup> Here is another footnote.</p>$', html) is not None @@ -24,6 +26,7 @@ def test_Footnote(): html = textile.textile('''See[4!] for details.\n\nfn4^. Here are the details.''') assert re.search(r'^\t<p>See<sup class="footnote" id="fnrev([a-f0-9]{32})-1">4</sup> for details.</p>\n\n\t<p class="footnote" id="fn\1-1"><sup><a href="#fnrev\1-1">4</a></sup> Here are the details.</p>$', html) is not None + def test_issue_35(): result = textile.textile('"z"') expect = '\t<p>“z”</p>' @@ -33,8 +36,9 @@ def test_issue_35(): expect = '\t<p>“ z”</p>' assert result == expect + def test_restricted(): - #Note that the HTML is escaped, thus rendering the <script> tag harmless. + # Note that the HTML is escaped, thus rendering the <script> tag harmless. test = "Here is some text.\n<script>alert('hello world')</script>" result = textile.textile_restricted(test) expect = "\t<p>Here is some text.<br />\n<script>alert(‘hello world’)</script></p>" @@ -93,10 +97,12 @@ def test_restricted(): assert result == expect + def test_unicode_footnote(): html = textile.textile('текст[1]') assert re.compile(r'^\t<p>текст<sup class="footnote" id="fnrev([a-f0-9]{32})-1"><a href="#fn\1-1">1</a></sup></p>$', re.U).search(html) is not None + def test_autolinking(): test = """some text "test":http://www.google.com http://www.google.com "$":http://www.google.com""" result = """\t<p>some text <a href="http://www.google.com">test</a> http://www.google.com <a href="http://www.google.com">www.google.com</a></p>""" @@ -104,6 +110,7 @@ def test_autolinking(): assert result == expect + def test_sanitize(): test = "a paragraph of benign text" result = "\t<p>a paragraph of benign text</p>" @@ -120,14 +127,16 @@ def test_sanitize(): expect = textile.Textile(html_type='html5').parse(test, sanitize=True) assert result == expect + def test_imagesize(): - PIL = pytest.importorskip('PIL') # noqa: F841 + PIL = pytest.importorskip('PIL') # noqa: F841 test = "!http://www.google.com/intl/en_ALL/images/srpr/logo1w.png!" result = '\t<p><img alt="" height="95" src="http://www.google.com/intl/en_ALL/images/srpr/logo1w.png" width="275" /></p>' expect = textile.Textile(get_sizes=True).parse(test) assert result == expect + def test_endnotes_simple(): test = """Scientists say the moon is slowly shrinking[#my_first_label].\n\nnotelist!.\n\nnote#my_first_label Over the past billion years, about a quarter of the moon's 4.5 billion-year lifespan, it has shrunk about 200 meters (700 feet) in diameter.""" html = textile.textile(test) @@ -135,6 +144,7 @@ def test_endnotes_simple(): result_re = re.compile(result_pattern) assert result_re.search(html) is not None + def test_endnotes_complex(): test = """Tim Berners-Lee is one of the pioneer voices in favour of Net Neutrality[#netneutral] and has expressed the view that ISPs should supply "connectivity with no strings attached"[#netneutral!] [#tbl_quote]\n\nBerners-Lee admitted that the forward slashes ("//") in a web address were actually unnecessary. He told the newspaper that he could easily have designed URLs not to have the forward slashes. "... it seemed like a good idea at the time,"[#slashes]\n\nnote#netneutral. "Web creator rejects net tracking":http://news.bbc.co.uk/2/hi/technology/7613201.stm. BBC. 15 September 2008\n\nnote#tbl_quote. "Web inventor's warning on spy software":http://www.telegraph.co.uk/news/uknews/1581938/Web-inventor%27s-warning-on-spy-software.html. The Daily Telegraph (London). 25 May 2008\n\nnote#slashes. "Berners-Lee 'sorry' for slashes":http://news.bbc.co.uk/1/hi/technology/8306631.stm. BBC. 14 October 2009\n\nnotelist.""" html = textile.textile(test) @@ -142,6 +152,7 @@ def test_endnotes_complex(): result_re = re.compile(result_pattern) assert result_re.search(html) is not None + def test_endnotes_unreferenced_note(): test = """Scientists say[#lavader] the moon is quite small. But I, for one, don't believe them. Others claim it to be made of cheese[#aardman]. If this proves true I suspect we are in for troubled times[#apollo13] as people argue over their "share" of the moon's cheese. In the end, its limited size[#lavader] may prove problematic.\n\nnote#lavader(noteclass). "Proof of the small moon hypothesis":http://antwrp.gsfc.nasa.gov/apod/ap080801.html. Copyright(c) Laurent Laveder\n\nnote#aardman(#noteid). "Proof of a cheese moon":http://www.imdb.com/title/tt0104361\n\nnote#apollo13. After all, things do go "wrong":http://en.wikipedia.org/wiki/Apollo_13#The_oxygen_tank_incident.\n\nnotelist{padding:1em; margin:1em; border-bottom:1px solid gray}.\n\nnotelist{padding:1em; margin:1em; border-bottom:1px solid gray}:§^.\n\nnotelist{padding:1em; margin:1em; border-bottom:1px solid gray}:‡""" html = textile.textile(test) @@ -149,6 +160,7 @@ def test_endnotes_unreferenced_note(): result_re = re.compile(result_pattern, re.U) assert result_re.search(html) is not None + def test_endnotes_malformed(): test = """Scientists say[#lavader] the moon is quite small. But I, for one, don't believe them. Others claim it to be made of cheese[#aardman]. If this proves true I suspect we are in for troubled times[#apollo13!] as people argue over their "share" of the moon's cheese. In the end, its limited size[#lavader] may prove problematic.\n\nnote#unused An unreferenced note.\n\nnote#lavader^ "Proof of the small moon hypothesis":http://antwrp.gsfc.nasa.gov/apod/ap080801.html. Copyright(c) Laurent Laveder\n\nnote#aardman^ "Proof of a cheese moon":http://www.imdb.com/title/tt0104361\n\nnote#apollo13^ After all, things do go "wrong":http://en.wikipedia.org/wiki/Apollo_13#The_oxygen_tank_incident.\n\nnotelist{padding:1em; margin:1em; border-bottom:1px solid gray}:α!+""" html = textile.textile(test) @@ -156,6 +168,7 @@ def test_endnotes_malformed(): result_re = re.compile(result_pattern, re.U) assert result_re.search(html) is not None + def test_endnotes_undefined_note(): test = """Scientists say the moon is slowly shrinking[#my_first_label].\n\nnotelist!.""" html = textile.textile(test) @@ -163,6 +176,7 @@ def test_endnotes_undefined_note(): result_re = re.compile(result_pattern) assert result_re.search(html) is not None + def test_encode_url(): # I tried adding these as doctests, but the unicode tests weren't # returning the correct results. @@ -198,21 +212,25 @@ def test_encode_url(): eurl = t.encode_url(url) assert eurl == result + def test_footnote_crosslink(): html = textile.textile('''See[2] for details, and later, reference it again[2].\n\nfn2^(footy#otherid)[en]. Here are the details.''') searchstring = r'\t<p>See<sup class="footnote" id="fnrev([a-f0-9]{32})-1"><a href="#fn\1-1">2</a></sup> for details, and later, reference it again<sup class="footnote"><a href="#fn\1-1">2</a></sup>.</p>\n\n\t<p class="footy" id="otherid" lang="en"><sup id="fn\1-1"><a href="#fnrev\1-1">2</a></sup> Here are the details.</p>$' assert re.compile(searchstring).search(html) is not None + def test_footnote_without_reflink(): html = textile.textile('''See[3!] for details.\n\nfn3. Here are the details.''') searchstring = r'^\t<p>See<sup class="footnote" id="fnrev([a-f0-9]{32})-1">3</sup> for details.</p>\n\n\t<p class="footnote" id="fn\1-1"><sup>3</sup> Here are the details.</p>$' assert re.compile(searchstring).search(html) is not None + def testSquareBrackets(): html = textile.textile("""1[^st^], 2[^nd^], 3[^rd^]. 2 log[~n~]\n\nA close[!http://textpattern.com/favicon.ico!]image.\nA tight["text":http://textpattern.com/]link.\nA ["footnoted link":http://textpattern.com/][182].""") searchstring = r'^\t<p>1<sup>st</sup>, 2<sup>nd</sup>, 3<sup>rd</sup>. 2 log<sub>n</sub></p>\n\n\t<p>A close<img alt="" src="http://textpattern.com/favicon.ico" />image.<br />\nA tight<a href="http://textpattern.com/">text</a>link.<br />\nA <a href="http://textpattern.com/">footnoted link</a><sup class="footnote" id="fnrev([a-f0-9]{32})-1"><a href="#fn\1-1">182</a></sup>.</p>' assert re.compile(searchstring).search(html) is not None + def test_html5(): """docstring for testHTML5""" @@ -221,6 +239,7 @@ def test_html5(): expect = textile.textile(test, html_type="html5") assert result == expect + def test_relURL(): t = textile.Textile() t.restricted = True From f1be07b6ef643c0d50a58a643279abac2f1b9d85 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 19:23:30 -0500 Subject: [PATCH 144/228] flake8: whitespace cleanup --- tests/test_urls.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/test_urls.py b/tests/test_urls.py index 39ff163..1cd09f9 100644 --- a/tests/test_urls.py +++ b/tests/test_urls.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- from textile import Textile + def test_urls(): t = Textile() assert t.relURL("http://www.google.com/") == 'http://www.google.com/' @@ -53,12 +54,14 @@ def test_urls(): expect = '\t<p>A link that <a href="/test/">contains a\nnewline</a> raises an exception.</p>' assert result == expect + def test_rel_attribute(): t = Textile(rel='nofollow') result = t.parse('"$":http://domain.tld') expect = '\t<p><a href="http://domain.tld" rel="nofollow">domain.tld</a></p>' assert result == expect + def test_quotes_in_link_text(): """quotes in link text are tricky.""" test = '""this is a quote in link text"":url' From ae01636248e974b2cc9b5028d570d9a7e7d03ab3 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 19:24:15 -0500 Subject: [PATCH 145/228] flake8: whitespace cleanup --- tests/test_lists.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_lists.py b/tests/test_lists.py index 4e85f4c..06d13c3 100644 --- a/tests/test_lists.py +++ b/tests/test_lists.py @@ -1,5 +1,6 @@ from textile import Textile + def test_lists(): t = Textile() result = t.textileLists("* one\n* two\n* three") From bcee7e9e12addf2289779d5f480e5bd05f86b15c Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 19:24:51 -0500 Subject: [PATCH 146/228] flake8: whitespace cleanup --- tests/test_span.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_span.py b/tests/test_span.py index bafe01c..7ae5b4b 100644 --- a/tests/test_span.py +++ b/tests/test_span.py @@ -1,10 +1,11 @@ from textile import Textile + def test_span(): t = Textile() result = t.retrieveTags(t.span("hello %(bob)span *strong* and **bold**% goodbye")) expect = ('hello <span class="bob">span <strong>strong</strong> and ' - '<b>bold</b></span> goodbye') + '<b>bold</b></span> goodbye') assert result == expect result = t.retrieveTags(t.span('%:http://domain.tld test%')) From b673713b21163d0492d697805cc41fe0aff895da Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 19:34:54 -0500 Subject: [PATCH 147/228] flake8: whitespace cleanup --- tests/test_github_issues.py | 44 ++++++++++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 8 deletions(-) diff --git a/tests/test_github_issues.py b/tests/test_github_issues.py index 2507e5f..c08d067 100644 --- a/tests/test_github_issues.py +++ b/tests/test_github_issues.py @@ -3,51 +3,60 @@ import textile + def test_github_issue_16(): result = textile.textile('"$":http://google.com "$":https://google.com "$":mailto:blackhole@sun.comet') expect = '\t<p><a href="http://google.com">google.com</a> <a href="https://google.com">google.com</a> <a href="mailto:blackhole%40sun.comet">blackhole@sun.comet</a></p>' assert result == expect + def test_github_issue_17(): result = textile.textile('!http://www.ox.ac.uk/favicon.ico!') expect = '\t<p><img alt="" src="http://www.ox.ac.uk/favicon.ico" /></p>' assert result == expect + def test_github_issue_20(): text = 'This is a link to a ["Wikipedia article about Textile":http://en.wikipedia.org/wiki/Textile_(markup_language)].' result = textile.textile(text) expect = '\t<p>This is a link to a <a href="http://en.wikipedia.org/wiki/Textile_%28markup_language%29">Wikipedia article about Textile</a>.</p>' assert result == expect + def test_github_issue_21(): - text = '''h1. xml example + text = ('''h1. xml example -bc. +bc. ''' + ''' <foo> bar -</foo>''' +</foo>''') result = textile.textile(text) expect = '\t<h1>xml example</h1>\n\n<pre><code>\n<foo>\n bar\n</foo></code></pre>' assert result == expect + def test_github_issue_22(): text = '''_(artist-name)Ty Segall_’s''' result = textile.textile(text) expect = '\t<p><em class="artist-name">Ty Segall</em>’s</p>' assert result == expect + def test_github_issue_26(): text = '' result = textile.textile(text) expect = '' assert result == expect + def test_github_issue_27(): test = """* Folders with ":" in their names are displayed with a forward slash "/" instead. (Filed as "#4581709":/test/link, which was considered "normal behaviour" - quote: "Please note that Finder presents the 'Carbon filesystem' view, regardless of the underlying filesystem.")""" result = textile.textile(test) expect = """\t<ul>\n\t\t<li>Folders with “:” in their names are displayed with a forward slash “/” instead. (Filed as <a href="/test/link">#4581709</a>, which was considered “normal behaviour” – quote: “Please note that Finder presents the ‘Carbon filesystem’ view, regardless of the underlying filesystem.”)</li>\n\t</ul>""" assert result == expect + def test_github_issue_28(): test = """So here I am porting my ancient "newspipe":newspipe "front-end":blog/2006/09/30/0950 to "Snakelets":Snakelets and "Python":Python, and I've just trimmed down over 20 lines of "PHP":PHP down to essentially one line of "BeautifulSoup":BeautifulSoup retrieval: @@ -80,23 +89,26 @@ def parseWapProfile(self, url): \t<p>Of course there’s a lot more error handling to do (and useful data to glean off the <a href="XML"><span class="caps">XML</span></a>), but being able to cut through all the usual parsing crap is immensely gratifying.</p>""") assert result == expect + def test_github_issue_30(): - text ='"Tëxtíle (Tëxtíle)":http://lala.com' + text = '"Tëxtíle (Tëxtíle)":http://lala.com' result = textile.textile(text) expect = '\t<p><a href="http://lala.com" title="Tëxtíle">Tëxtíle</a></p>' assert result == expect - text ='!http://lala.com/lol.gif(♡ imáges)!' + text = '!http://lala.com/lol.gif(♡ imáges)!' result = textile.textile(text) expect = '\t<p><img alt="♡ imáges" src="http://lala.com/lol.gif" title="♡ imáges" /></p>' assert result == expect + def test_github_issue_36(): text = '"Chögyam Trungpa":https://www.google.com/search?q=Chögyam+Trungpa' result = textile.textile(text) expect = '\t<p><a href="https://www.google.com/search?q=Chögyam+Trungpa">Chögyam Trungpa</a></p>' assert result == expect + def test_github_issue_37(): text = '# xxx\n# yyy\n*blah*' result = textile.textile(text) @@ -118,24 +130,28 @@ def test_github_issue_37(): \t</ul>''' assert result == expect + def test_github_issue_40(): text = '\r\n' result = textile.textile(text) expect = '\r\n' assert result == expect + def test_github_issue_42(): text = '!./image.png!' result = textile.textile(text) expect = '\t<p><img alt="" src="./image.png" /></p>' assert result == expect + def test_github_issue_43(): text = 'pre. smart ‘quotes’ are not smart!' result = textile.textile(text) expect = '<pre>smart ‘quotes’ are not smart!</pre>' assert result == expect + def test_github_issue_45(): """Incorrect transform unicode url""" text = '"test":https://myabstractwiki.ru/index.php/%D0%97%D0%B0%D0%B3%D0%BB%D0%B0%D0%B2%D0%BD%D0%B0%D1%8F_%D1%81%D1%82%D1%80%D0%B0%D0%BD%D0%B8%D1%86%D0%B0' @@ -143,6 +159,7 @@ def test_github_issue_45(): expect = '\t<p><a href="https://myabstractwiki.ru/index.php/%D0%97%D0%B0%D0%B3%D0%BB%D0%B0%D0%B2%D0%BD%D0%B0%D1%8F_%D1%81%D1%82%D1%80%D0%B0%D0%BD%D0%B8%D1%86%D0%B0">test</a></p>' assert result == expect + def test_github_issue_46(): """Key error on mal-formed numbered lists. CAUTION: both the input and the ouput are ugly.""" @@ -153,6 +170,7 @@ def test_github_issue_46(): result = textile.textile(text) assert result == expect + def test_github_issue_47(): """Incorrect wrap pre-formatted value""" text = '''pre.. word @@ -172,6 +190,7 @@ def test_github_issue_47(): yet anothe word</pre>''' assert result == expect + def test_github_issue_49(): """Key error on russian hash-route link""" s = '"link":https://ru.vuejs.org/v2/guide/components.html#Входные-параметры' @@ -179,6 +198,7 @@ def test_github_issue_49(): expect = '\t<p><a href="https://ru.vuejs.org/v2/guide/components.html#Входные-параметры">link</a></p>' assert result == expect + def test_github_issue_50(): """Incorrect wrap code with Java generics in pre""" test = ('pre.. public class Tynopet<T extends Framework> {}\n\nfinal ' @@ -189,6 +209,7 @@ def test_github_issue_50(): 'ArrayList<>();</pre>') assert result == expect + def test_github_issue_51(): """Link build with $ sign without "http" prefix broken.""" test = '"$":www.google.com.br' @@ -196,6 +217,7 @@ def test_github_issue_51(): expect = '\t<p><a href="www.google.com.br">www.google.com.br</a></p>' assert result == expect + def test_github_issue_52(): """Table build without space after aligment raise a AttributeError.""" test = '|=.First Header |=. Second Header |' @@ -205,6 +227,7 @@ def test_github_issue_52(): '\n\t\t</tr>\n\t</table>') assert result == expect + def test_github_issue_55(): """Incorrect handling of quote entities in extended pre block""" test = ('pre.. this is the first line\n\nbut "quotes" in an extended pre ' @@ -258,15 +281,17 @@ def test_github_issue_55(): 'return configs;\n}\n}</pre>') assert result == expect + def test_github_issue_56(): """Empty description lists throw error""" result = textile.textile("- :=\n-") expect = '<dl>\n</dl>' assert result == expect + def test_github_pull_61(): """Fixed code block multiline encoding on quotes/span""" - test = '''bc.. This is some TEXT inside a "Code BLOCK" + test = ('''bc.. This is some TEXT inside a "Code BLOCK" { if (JSON) { @@ -275,11 +300,12 @@ def test_github_pull_61(): } } -Back to 10-4 CAPS +Back to 10-4 CAPS ''' + ''' p.. Some multiline Paragragh -Here is some output!!! "Some" CAPS''' +Here is some output!!! "Some" CAPS''') expect = '''<pre><code>This is some TEXT inside a "Code BLOCK" @@ -299,6 +325,7 @@ def test_github_pull_61(): result = t.parse(test) assert result == expect + def test_github_pull_62(): """Fix for paragraph multiline, only last paragraph is rendered correctly""" @@ -341,6 +368,7 @@ def test_github_pull_62(): result = t.parse(test) assert result == expect + def test_github_pull_63(): """Forgot to set multiline_para to False""" test = '''p.. First one 'is' From f15dc31edef29df3b140070be1d64f3e6205272e Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 19:36:35 -0500 Subject: [PATCH 148/228] flake8: whitespace cleanup --- tests/test_cli.py | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/test_cli.py b/tests/test_cli.py index 5f6e501..57b2d5f 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -3,28 +3,30 @@ import textile + def test_console_script(): command = [sys.executable, '-m', 'textile', 'README.textile'] try: result = subprocess.check_output(command) except AttributeError: command[2] = 'textile.__main__' - result = subprocess.Popen(command, - stdout=subprocess.PIPE).communicate()[0] + result = subprocess.Popen( + command, stdout=subprocess.PIPE).communicate()[0] with open('tests/fixtures/README.txt') as f: expect = ''.join(f.readlines()) if type(result) == bytes: result = result.decode('utf-8') assert result == expect + def test_version_string(): command = [sys.executable, '-m', 'textile', '-v'] try: result = subprocess.check_output(command) except AttributeError: command[2] = 'textile.__main__' - result = subprocess.Popen(command, - stdout=subprocess.PIPE).communicate()[0] + result = subprocess.Popen( + command, stdout=subprocess.PIPE).communicate()[0] if type(result) == bytes: result = result.decode('utf-8') assert result.strip() == textile.__version__ From 8abe7f4e7b8acbf0efd3b25123221e6c16ff5aa0 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 19:52:50 -0500 Subject: [PATCH 149/228] flake8: whitespace cleanup --- tests/test_attributes.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_attributes.py b/tests/test_attributes.py index 0f5d019..b07712f 100644 --- a/tests/test_attributes.py +++ b/tests/test_attributes.py @@ -1,5 +1,6 @@ from textile.utils import parse_attributes + def test_parse_attributes(): assert parse_attributes('\\1', element='td') == {'colspan': '1'} assert parse_attributes('/1', element='td') == {'rowspan': '1'} From ed28e7155fc48b13aeccf6d5160bec9d1553e91e Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 19:53:42 -0500 Subject: [PATCH 150/228] flake8: whitespace cleanup --- tests/test_getimagesize.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_getimagesize.py b/tests/test_getimagesize.py index 43f85e3..3a3c0a9 100644 --- a/tests/test_getimagesize.py +++ b/tests/test_getimagesize.py @@ -3,6 +3,7 @@ PIL = pytest.importorskip('PIL') + def test_imagesize(): assert getimagesize("http://www.google.com/intl/en_ALL/images/logo.gif") == (276, 110) assert getimagesize("http://bad.domain/") == '' From 3c222b214ff9392623c2b4be856eeccb7eee5f1b Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 19:55:41 -0500 Subject: [PATCH 151/228] flake8: whitespace cleanup --- tests/test_glyphs.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_glyphs.py b/tests/test_glyphs.py index 56b0d27..ed50ad5 100644 --- a/tests/test_glyphs.py +++ b/tests/test_glyphs.py @@ -1,5 +1,6 @@ from textile import Textile + def test_glyphs(): t = Textile() From 4304da9394c5d33f7724692797364b1172ea2082 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 19:57:52 -0500 Subject: [PATCH 152/228] flake8: whitespace cleanup --- tests/test_subclassing.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_subclassing.py b/tests/test_subclassing.py index 9235e03..a7db99a 100644 --- a/tests/test_subclassing.py +++ b/tests/test_subclassing.py @@ -1,10 +1,10 @@ import textile + def test_change_glyphs(): class TextilePL(textile.Textile): glyph_definitions = dict(textile.Textile.glyph_definitions, - quote_double_open = '„' - ) + quote_double_open='„') test = 'Test "quotes".' expect = '\t<p>Test „quotes”.</p>' From 5c663e39ca1f7551f8e5304245abf9d5ea2e5d53 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 19:58:40 -0500 Subject: [PATCH 153/228] flake8: whitespace cleanup --- tests/test_imagesize.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/test_imagesize.py b/tests/test_imagesize.py index 0a89b43..e7d9d88 100644 --- a/tests/test_imagesize.py +++ b/tests/test_imagesize.py @@ -1,10 +1,11 @@ import textile + def test_imagesize(): imgurl = 'http://www.google.com/intl/en_ALL/images/srpr/logo1w.png' result = textile.tools.imagesize.getimagesize(imgurl) try: - import PIL # noqa: F401 + import PIL # noqa: F401 expect = (275, 95) assert result == expect From 0b66de8799a06a38068ea8c418d28d7bec080eb3 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 20:17:17 -0500 Subject: [PATCH 154/228] flake8: whitespace cleanup --- tests/test_textilefactory.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_textilefactory.py b/tests/test_textilefactory.py index 846b927..e9fc027 100644 --- a/tests/test_textilefactory.py +++ b/tests/test_textilefactory.py @@ -1,6 +1,7 @@ from textile import textilefactory import pytest + def test_TextileFactory(): f = textilefactory.TextileFactory() result = f.process("some text here") From 343e26d8a00e61a510b380145a213828ee4bae96 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 20:18:01 -0500 Subject: [PATCH 155/228] flake8: whitespace cleanup --- tests/test_getRefs.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_getRefs.py b/tests/test_getRefs.py index d3cfcd7..8a22d4f 100644 --- a/tests/test_getRefs.py +++ b/tests/test_getRefs.py @@ -1,5 +1,6 @@ from textile import Textile + def test_getRefs(): t = Textile() result = t.getRefs("some text [Google]http://www.google.com") From fddda0ca1e73f73c588c2889b0a465a91f9d5f19 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 20:18:34 -0500 Subject: [PATCH 156/228] flake8: whitespace cleanup --- tests/test_footnoteRef.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_footnoteRef.py b/tests/test_footnoteRef.py index c973ee7..5ac2ea4 100644 --- a/tests/test_footnoteRef.py +++ b/tests/test_footnoteRef.py @@ -1,5 +1,6 @@ from textile import Textile + def test_footnoteRef(): t = Textile() result = t.footnoteRef('foo[1]') From 739ee86698c6bf4357c9211365410c4f55f12569 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 20:19:14 -0500 Subject: [PATCH 157/228] flake8: whitespace cleanup --- tests/test_table.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_table.py b/tests/test_table.py index 0a3cb0d..1ea34e9 100644 --- a/tests/test_table.py +++ b/tests/test_table.py @@ -1,5 +1,6 @@ from textile import Textile + def test_table(): t = Textile() result = t.table('(rowclass). |one|two|three|\n|a|b|c|') From 0def35bc88b2b8c0f0385106787dd254bf6da838 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 20:21:12 -0500 Subject: [PATCH 158/228] flake8: whitespace cleanup --- tests/test_values.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/test_values.py b/tests/test_values.py index 5d15950..7ebd2c7 100644 --- a/tests/test_values.py +++ b/tests/test_values.py @@ -506,12 +506,14 @@ ) + @pytest.mark.parametrize("input, expected_output", xhtml_known_values) def test_KnownValuesXHTML(input, expected_output): # XHTML output = textile.textile(input, html_type='xhtml') assert output == expected_output + @pytest.mark.parametrize("input, expected_output", html_known_values) def test_KnownValuesHTML(input, expected_output): # HTML5 From 5eea5a77cbdab22fd3c381410b75c5a2077d82ae Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 20:21:42 -0500 Subject: [PATCH 159/228] flake8: whitespace cleanup --- tests/test_values.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/test_values.py b/tests/test_values.py index 7ebd2c7..f08ac6f 100644 --- a/tests/test_values.py +++ b/tests/test_values.py @@ -35,7 +35,7 @@ ('h3. Header 3', '\t<h3>Header 3</h3>'), ('An old text\n\nbq. A block quotation.\n\nAny old text''', - '\t<p>An old text</p>\n\n\t<blockquote>\n\t\t<p>A block quotation.</p>\n\t</blockquote>\n\n\t<p>Any old text</p>'), + '\t<p>An old text</p>\n\n\t<blockquote>\n\t\t<p>A block quotation.</p>\n\t</blockquote>\n\n\t<p>Any old text</p>'), ('I _believe_ every word.', '\t<p>I <em>believe</em> every word.</p>'), @@ -70,8 +70,8 @@ ('p[fr]. rouge', '\t<p lang="fr">rouge</p>'), ('I seriously *{color:red}blushed*\nwhen I _(big)sprouted_ that\ncorn stalk from my\n%[es]cabeza%.', - '\t<p>I seriously <strong style="color:red;">blushed</strong><br />\nwhen I <em class="big">sprouted</em>' - ' that<br />\ncorn stalk from my<br />\n<span lang="es">cabeza</span>.</p>'), + '\t<p>I seriously <strong style="color:red;">blushed</strong><br />\nwhen I <em class="big">sprouted</em>' + ' that<br />\ncorn stalk from my<br />\n<span lang="es">cabeza</span>.</p>'), ('p<. align left', '\t<p style="text-align:left;">align left</p>'), @@ -226,7 +226,7 @@ ("""table(#dvds){border-collapse:collapse}. Great films on DVD employing Textile summary, caption, thead, tfoot, two tbody elements and colgroups\n|={font-size:140%;margin-bottom:15px}. DVDs with two Textiled tbody elements\n|:\\3. 100 |{background:#ddd}|250||50|300|\n|^(header).\n|_. Title |_. Starring |_. Director |_. Writer |_. Notes |\n|~(footer).\n|\\5=. This is the tfoot, centred |\n|-(toplist){background:#c5f7f6}.\n| _The Usual Suspects_ | Benicio Del Toro, Gabriel Byrne, Stephen Baldwin, Kevin Spacey | Bryan Singer | Chris McQaurrie | One of the finest films ever made |\n| _Se7en_ | Morgan Freeman, Brad Pitt, Kevin Spacey | David Fincher | Andrew Kevin Walker | Great psychological thriller |\n| _Primer_ | David Sullivan, Shane Carruth | Shane Carruth | Shane Carruth | Amazing insight into trust and human psychology <br />rather than science fiction. Terrific! |\n| _District 9_ | Sharlto Copley, Jason Cope | Neill Blomkamp | Neill Blomkamp, Terri Tatchell | Social commentary layered on thick,\nbut boy is it done well |\n|-(medlist){background:#e7e895;}.\n| _Arlington Road_ | Tim Robbins, Jeff Bridges | Mark Pellington | Ehren Kruger | Awesome study in neighbourly relations |\n| _Phone Booth_ | Colin Farrell, Kiefer Sutherland, Forest Whitaker | Joel Schumacher | Larry Cohen | Edge-of-the-seat stuff in this\nshort but brilliantly executed thriller |""", """\t<table id="dvds" style="border-collapse:collapse;" summary="Great films on DVD employing Textile summary, caption, thead, tfoot, two tbody elements and colgroups">\n\t<caption style="font-size:140%; margin-bottom:15px;"><span class="caps">DVD</span>s with two Textiled tbody elements</caption>\n\t<colgroup span="3" width="100">\n\t<col style="background:#ddd;" />\n\t<col width="250" />\n\t<col />\n\t<col width="50" />\n\t<col width="300" />\n\t</colgroup>\n\t<thead class="header">\n\t\t<tr>\n\t\t\t<th>Title </th>\n\t\t\t<th>Starring </th>\n\t\t\t<th>Director </th>\n\t\t\t<th>Writer </th>\n\t\t\t<th>Notes </th>\n\t\t</tr>\n\t</thead>\n\t<tfoot class="footer">\n\t\t<tr>\n\t\t\t<td colspan="5" style="text-align:center;">This is the tfoot, centred </td>\n\t\t</tr>\n\t</tfoot>\n\t<tbody class="toplist" style="background:#c5f7f6;">\n\t\t<tr>\n\t\t\t<td> <em>The Usual Suspects</em> </td>\n\t\t\t<td> Benicio Del Toro, Gabriel Byrne, Stephen Baldwin, Kevin Spacey </td>\n\t\t\t<td> Bryan Singer </td>\n\t\t\t<td> Chris McQaurrie </td>\n\t\t\t<td> One of the finest films ever made </td>\n\t\t</tr>\n\t\t<tr>\n\t\t\t<td> <em>Se7en</em> </td>\n\t\t\t<td> Morgan Freeman, Brad Pitt, Kevin Spacey </td>\n\t\t\t<td> David Fincher </td>\n\t\t\t<td> Andrew Kevin Walker </td>\n\t\t\t<td> Great psychological thriller </td>\n\t\t</tr>\n\t\t<tr>\n\t\t\t<td> <em>Primer</em> </td>\n\t\t\t<td> David Sullivan, Shane Carruth </td>\n\t\t\t<td> Shane Carruth </td>\n\t\t\t<td> Shane Carruth </td>\n\t\t\t<td> Amazing insight into trust and human psychology <br />\nrather than science fiction. Terrific! </td>\n\t\t</tr>\n\t\t<tr>\n\t\t\t<td> <em>District 9</em> </td>\n\t\t\t<td> Sharlto Copley, Jason Cope </td>\n\t\t\t<td> Neill Blomkamp </td>\n\t\t\t<td> Neill Blomkamp, Terri Tatchell </td>\n\t\t\t<td> Social commentary layered on thick,<br />\nbut boy is it done well </td>\n\t\t</tr>\n\t</tbody>\n\t<tbody class="medlist" style="background:#e7e895;">\n\t\t<tr>\n\t\t\t<td> <em>Arlington Road</em> </td>\n\t\t\t<td> Tim Robbins, Jeff Bridges </td>\n\t\t\t<td> Mark Pellington </td>\n\t\t\t<td> Ehren Kruger </td>\n\t\t\t<td> Awesome study in neighbourly relations </td>\n\t\t</tr>\n\t\t<tr>\n\t\t\t<td> <em>Phone Booth</em> </td>\n\t\t\t<td> Colin Farrell, Kiefer Sutherland, Forest Whitaker </td>\n\t\t\t<td> Joel Schumacher </td>\n\t\t\t<td> Larry Cohen </td>\n\t\t\t<td> Edge-of-the-seat stuff in this<br />\nshort but brilliantly executed thriller </td>\n\t\t</tr>\n\t</tbody>\n\t</table>"""), ("""-(hot) *coffee* := Hot _and_ black\n-(hot#tea) tea := Also hot, but a little less black\n-(cold) milk := Nourishing beverage for baby cows.\nCold drink that goes great with cookies. =:\n\n-(hot) coffee := Hot and black\n-(hot#tea) tea := Also hot, but a little less black\n-(cold) milk :=\nNourishing beverage for baby cows.\nCold drink that goes great with cookies. =:""", - """<dl>\n\t<dt class="hot"><strong>coffee</strong></dt>\n\t<dd>Hot <em>and</em> black</dd>\n\t<dt class="hot" id="tea">tea</dt>\n\t<dd>Also hot, but a little less black</dd>\n\t<dt class="cold">milk</dt>\n\t<dd>Nourishing beverage for baby cows.<br />\nCold drink that goes great with cookies.</dd>\n</dl>\n\n<dl>\n\t<dt class="hot">coffee</dt>\n\t<dd>Hot and black</dd>\n\t<dt class="hot" id="tea">tea</dt>\n\t<dd>Also hot, but a little less black</dd>\n\t<dt class="cold">milk</dt>\n\t<dd><p>Nourishing beverage for baby cows.<br />\nCold drink that goes great with cookies.</p></dd>\n</dl>"""), + """<dl>\n\t<dt class="hot"><strong>coffee</strong></dt>\n\t<dd>Hot <em>and</em> black</dd>\n\t<dt class="hot" id="tea">tea</dt>\n\t<dd>Also hot, but a little less black</dd>\n\t<dt class="cold">milk</dt>\n\t<dd>Nourishing beverage for baby cows.<br />\nCold drink that goes great with cookies.</dd>\n</dl>\n\n<dl>\n\t<dt class="hot">coffee</dt>\n\t<dd>Hot and black</dd>\n\t<dt class="hot" id="tea">tea</dt>\n\t<dd>Also hot, but a little less black</dd>\n\t<dt class="cold">milk</dt>\n\t<dd><p>Nourishing beverage for baby cows.<br />\nCold drink that goes great with cookies.</p></dd>\n</dl>"""), (""";(class#id) Term 1\n: Def 1\n: Def 2\n: Def 3""", """\t<dl class="class" id="id">\n\t\t<dt>Term 1</dt>\n\t\t<dd>Def 1</dd>\n\t\t<dd>Def 2</dd>\n\t\t<dd>Def 3</dd>\n\t</dl>"""), ("""*Here is a comment*\n\nHere is *(class)a comment*\n\n*(class)Here is a class* that is a little extended and is\n*followed* by a strong word!\n\nbc. ; Content-type: text/javascript\n; Cache-Control: no-store, no-cache, must-revalidate, pre-check=0, post-check=0, max-age=0\n; Expires: Sat, 24 Jul 2003 05:00:00 GMT\n; Last-Modified: Wed, 1 Jan 2025 05:00:00 GMT\n; Pragma: no-cache\n\n*123 test*\n\n*test 123*\n\n**123 test**\n\n**test 123**""", @@ -266,8 +266,8 @@ ('I __know__.\nI **really** __know__.', '\t<p>I <i>know</i>.<br />\nI <b>really</b> <i>know</i>.</p>'), ("I'm %{color:red}unaware%\nof most soft drinks.", '\t<p>I’m <span style="color:red;">unaware</span><br />\nof most soft drinks.</p>'), ('I seriously *{color:red}blushed*\nwhen I _(big)sprouted_ that\ncorn stalk from my\n%[es]cabeza%.', - '\t<p>I seriously <strong style="color:red;">blushed</strong><br />\nwhen I <em class="big">sprouted</em>' - ' that<br />\ncorn stalk from my<br />\n<span lang="es">cabeza</span>.</p>'), + '\t<p>I seriously <strong style="color:red;">blushed</strong><br />\nwhen I <em class="big">sprouted</em>' + ' that<br />\ncorn stalk from my<br />\n<span lang="es">cabeza</span>.</p>'), ('<pre>\n<code>\na.gsub!( /</, "" )\n</code>\n</pre>', '<pre>\n<code>\na.gsub!( /</, "" )\n</code>\n</pre>'), ('<div style="float:right;">\n\nh3. Sidebar\n\n"Hobix":http://hobix.com/\n"Ruby":http://ruby-lang.org/\n\n</div>\n\n' From e0e488916e007595806364bb49eb175f53ce9243 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 20:22:50 -0500 Subject: [PATCH 160/228] flake8: whitespace cleanup --- tests/test_utils.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/test_utils.py b/tests/test_utils.py index 7f386a9..a6e88f8 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -3,21 +3,25 @@ from textile import utils + def test_encode_html(): result = utils.encode_html('''this is a "test" of text that's safe to ''' - 'put in an <html> attribute.') + 'put in an <html> attribute.') expect = ('this is a "test" of text that's safe to put in ' - 'an <html> attribute.') + 'an <html> attribute.') assert result == expect + def test_has_raw_text(): assert utils.has_raw_text('<p>foo bar biz baz</p>') is False assert utils.has_raw_text(' why yes, yes it does') is True + def test_is_rel_url(): assert utils.is_rel_url("http://www.google.com/") is False assert utils.is_rel_url("/foo") is True + def test_generate_tag(): result = utils.generate_tag('span', 'inner text', {'class': 'test'}) expect = '<span class="test">inner text</span>' From 1281ede405ae71b365c8c20ea02480006b7d543e Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 20:25:27 -0500 Subject: [PATCH 161/228] turn flake8 warnings and errors back on --- .flake8 | 3 --- 1 file changed, 3 deletions(-) diff --git a/.flake8 b/.flake8 index 07c24d1..9f8ccc3 100644 --- a/.flake8 +++ b/.flake8 @@ -2,8 +2,5 @@ ignore = # line too long E501 - # temporarily ignore warnings and errors - W - E exclude = build/ From e60b72fe625a71289c731d8363a335e5c5fc7be2 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 20:35:36 -0500 Subject: [PATCH 162/228] use environment vars as intended maybe this will fix a warning about unexpected inputs ??? --- .github/workflows/lint_and_test.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.github/workflows/lint_and_test.yml b/.github/workflows/lint_and_test.yml index 437820d..f82f4c2 100644 --- a/.github/workflows/lint_and_test.yml +++ b/.github/workflows/lint_and_test.yml @@ -20,9 +20,10 @@ jobs: - name: Python flake8 Lint uses: py-actions/flake8@v1.2.0 - name: Install dependencies + env: + IMAGESIZE: ${{ matrix.image-size }} run: | imagesize='' - IMAGESIZE=${{ matrix.image-size }} pip install -U pytest pytest-cov coverage codecov if [[ $IMAGESIZE == true ]] ; then imagesize='[imagesize]' ; fi pip install -e ".${imagesize}" From bda9a67d44011734bf66ebd6b8a9599963b2ac2b Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 20:42:24 -0500 Subject: [PATCH 163/228] further testing of environment variables --- .github/workflows/lint_and_test.yml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/lint_and_test.yml b/.github/workflows/lint_and_test.yml index f82f4c2..5ba1e86 100644 --- a/.github/workflows/lint_and_test.yml +++ b/.github/workflows/lint_and_test.yml @@ -9,7 +9,7 @@ jobs: strategy: matrix: python-version: [3.5, 3.6, 3.7, 3.8, 3.9] - image-size: ['true', 'false'] + image_size: ['true', 'false'] steps: - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} @@ -20,12 +20,10 @@ jobs: - name: Python flake8 Lint uses: py-actions/flake8@v1.2.0 - name: Install dependencies - env: - IMAGESIZE: ${{ matrix.image-size }} run: | imagesize='' pip install -U pytest pytest-cov coverage codecov - if [[ $IMAGESIZE == true ]] ; then imagesize='[imagesize]' ; fi + if [[ $INPUT_IMAGE_SIZE == true ]] ; then imagesize='[imagesize]' ; fi pip install -e ".${imagesize}" - name: run tests run: | From 45257b6d8ddae53c281490c95b9d3a3ecb83cf7d Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 20:44:45 -0500 Subject: [PATCH 164/228] This is probably the source of the `unexpected inputs` warning --- .github/workflows/lint_and_test.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.github/workflows/lint_and_test.yml b/.github/workflows/lint_and_test.yml index 5ba1e86..47ce8cf 100644 --- a/.github/workflows/lint_and_test.yml +++ b/.github/workflows/lint_and_test.yml @@ -16,7 +16,6 @@ jobs: uses: actions/setup-python@v2 with: python-version: ${{ matrix.python-version }} - image-size: ${{ matrix.image-size }} - name: Python flake8 Lint uses: py-actions/flake8@v1.2.0 - name: Install dependencies From dd5982ee3345a638fb6db15547f1feb132b4b064 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 28 Nov 2021 20:49:10 -0500 Subject: [PATCH 165/228] add pypy3 to github actions --- .github/workflows/lint_and_test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint_and_test.yml b/.github/workflows/lint_and_test.yml index 47ce8cf..0db7ea8 100644 --- a/.github/workflows/lint_and_test.yml +++ b/.github/workflows/lint_and_test.yml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.5, 3.6, 3.7, 3.8, 3.9] + python-version: [3.5, 3.6, 3.7, 3.8, 3.9, pypy3] image_size: ['true', 'false'] steps: - uses: actions/checkout@v2 From 060e7f46846d427e6674af9ddcb7ced8e1f2f7f3 Mon Sep 17 00:00:00 2001 From: Brad Schoening <5796692+bschoening@users.noreply.github.com> Date: Mon, 22 Jul 2024 22:23:50 -0400 Subject: [PATCH 166/228] Update setup.py to support Python 3.8..3.12 --- setup.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index b405f28..cbbb774 100644 --- a/setup.py +++ b/setup.py @@ -28,11 +28,11 @@ def get_version(): 'Programming Language :: Python', 'Programming Language :: Python :: 3', 'Programming Language :: Python :: 3 :: Only', - 'Programming Language :: Python :: 3.5', - 'Programming Language :: Python :: 3.6', - 'Programming Language :: Python :: 3.7', 'Programming Language :: Python :: 3.8', 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.10', + 'Programming Language :: Python :: 3.11', + 'Programming Language :: Python :: 3.12', 'Topic :: Software Development :: Libraries :: Python Modules', ], keywords='textile,text,html markup', From 3eb20497fec2bc31ee6184d2e64a2abab3568ab6 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Mon, 5 Aug 2024 11:18:12 -0400 Subject: [PATCH 167/228] update versions to test against --- .github/workflows/lint_and_test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint_and_test.yml b/.github/workflows/lint_and_test.yml index 0db7ea8..f488521 100644 --- a/.github/workflows/lint_and_test.yml +++ b/.github/workflows/lint_and_test.yml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.5, 3.6, 3.7, 3.8, 3.9, pypy3] + python-version: [3.8, 3.9, 3.10, 3.11, 3.12, pypy3] image_size: ['true', 'false'] steps: - uses: actions/checkout@v2 From 3fc933a17814adf94a46f0fc651f1eb388ad2f77 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Mon, 5 Aug 2024 11:22:46 -0400 Subject: [PATCH 168/228] make sure versions are interpreted as strings 3.10 was being interpreted as 3.1 with a trailing 0 --- .github/workflows/lint_and_test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint_and_test.yml b/.github/workflows/lint_and_test.yml index f488521..935d688 100644 --- a/.github/workflows/lint_and_test.yml +++ b/.github/workflows/lint_and_test.yml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: [3.8, 3.9, 3.10, 3.11, 3.12, pypy3] + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "pypy3"] image_size: ['true', 'false'] steps: - uses: actions/checkout@v2 From fe144a8ae0947cf941197eb0804984a4b4812cdc Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Mon, 5 Aug 2024 11:26:09 -0400 Subject: [PATCH 169/228] use isinstance for type comparison fixes a flake8 failure --- tests/test_cli.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/test_cli.py b/tests/test_cli.py index 57b2d5f..5e6ab79 100644 --- a/tests/test_cli.py +++ b/tests/test_cli.py @@ -14,7 +14,7 @@ def test_console_script(): command, stdout=subprocess.PIPE).communicate()[0] with open('tests/fixtures/README.txt') as f: expect = ''.join(f.readlines()) - if type(result) == bytes: + if isinstance(result, bytes): result = result.decode('utf-8') assert result == expect @@ -27,6 +27,6 @@ def test_version_string(): command[2] = 'textile.__main__' result = subprocess.Popen( command, stdout=subprocess.PIPE).communicate()[0] - if type(result) == bytes: + if isinstance(result, bytes): result = result.decode('utf-8') assert result.strip() == textile.__version__ From dd353101a7116acd96594afac624d52d1c310b76 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Mon, 5 Aug 2024 11:36:54 -0400 Subject: [PATCH 170/228] try the latest version of the github action --- .github/workflows/lint_and_test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint_and_test.yml b/.github/workflows/lint_and_test.yml index 935d688..96bfd6d 100644 --- a/.github/workflows/lint_and_test.yml +++ b/.github/workflows/lint_and_test.yml @@ -17,7 +17,7 @@ jobs: with: python-version: ${{ matrix.python-version }} - name: Python flake8 Lint - uses: py-actions/flake8@v1.2.0 + uses: py-actions/flake8@v2.3.0 - name: Install dependencies run: | imagesize='' From 5b44180a58def4527c29750cde59e2a8a482687e Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Mon, 5 Aug 2024 11:41:42 -0400 Subject: [PATCH 171/228] update other actions as well --- .github/workflows/lint_and_test.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/lint_and_test.yml b/.github/workflows/lint_and_test.yml index 96bfd6d..c4d4aab 100644 --- a/.github/workflows/lint_and_test.yml +++ b/.github/workflows/lint_and_test.yml @@ -11,9 +11,9 @@ jobs: python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "pypy3"] image_size: ['true', 'false'] steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 - name: Set up Python ${{ matrix.python-version }} - uses: actions/setup-python@v2 + uses: actions/setup-python@v5 with: python-version: ${{ matrix.python-version }} - name: Python flake8 Lint From 9bb449a97e149428951d122156346cebbd7501a3 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Mon, 5 Aug 2024 11:54:17 -0400 Subject: [PATCH 172/228] fix version spec for pypy --- .github/workflows/lint_and_test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint_and_test.yml b/.github/workflows/lint_and_test.yml index c4d4aab..cc2690b 100644 --- a/.github/workflows/lint_and_test.yml +++ b/.github/workflows/lint_and_test.yml @@ -8,7 +8,7 @@ jobs: runs-on: ubuntu-latest strategy: matrix: - python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "pypy3"] + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "pypy3.10"] image_size: ['true', 'false'] steps: - uses: actions/checkout@v4 From 56c912b568ce24404f1291ddb068904b87bd7631 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Mon, 5 Aug 2024 11:59:57 -0400 Subject: [PATCH 173/228] update to the latest version of the codecov action --- .github/workflows/lint_and_test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint_and_test.yml b/.github/workflows/lint_and_test.yml index cc2690b..8cfec1f 100644 --- a/.github/workflows/lint_and_test.yml +++ b/.github/workflows/lint_and_test.yml @@ -28,4 +28,4 @@ jobs: run: | pytest - name: Codecov - uses: codecov/codecov-action@v2.1.0 + uses: codecov/codecov-action@v4 From dac1716210fe6d9b4c631ce3b8b861c75b381149 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Mon, 5 Aug 2024 12:07:53 -0400 Subject: [PATCH 174/228] add codecov token which is stored as a secret --- .github/workflows/lint_and_test.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/lint_and_test.yml b/.github/workflows/lint_and_test.yml index 8cfec1f..44a7c91 100644 --- a/.github/workflows/lint_and_test.yml +++ b/.github/workflows/lint_and_test.yml @@ -29,3 +29,5 @@ jobs: pytest - name: Codecov uses: codecov/codecov-action@v4 + env: + CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} From 07782366f00bcc723033a9aa42b72eb16de6ff0e Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Mon, 5 Aug 2024 12:58:55 -0400 Subject: [PATCH 175/228] update badge to github actions --- README.textile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.textile b/README.textile index 98f4fbd..797516c 100644 --- a/README.textile +++ b/README.textile @@ -1,4 +1,4 @@ -!https://travis-ci.org/textile/python-textile.svg!:https://travis-ci.org/textile/python-textile !https://codecov.io/github/textile/python-textile/coverage.svg!:https://codecov.io/github/textile/python-textile !https://img.shields.io/pypi/pyversions/textile! !https://img.shields.io/pypi/wheel/textile! +!https://github.com/textile/python-textile/actions/workflows/lint_and_test.yml/badge.svg(python-textile)!:https://github.com/textile/python-textile/actions/workflows/lint_and_test.yml !https://codecov.io/github/textile/python-textile/coverage.svg!:https://codecov.io/github/textile/python-textile !https://img.shields.io/pypi/pyversions/textile! !https://img.shields.io/pypi/wheel/textile! h1. python-textile From 47a03ebbec92576fcdef1b681884b2cf3d16079f Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Mon, 5 Aug 2024 13:01:08 -0400 Subject: [PATCH 176/228] update fixture to reflect README changes --- tests/fixtures/README.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/fixtures/README.txt b/tests/fixtures/README.txt index 949db70..a485943 100644 --- a/tests/fixtures/README.txt +++ b/tests/fixtures/README.txt @@ -1,4 +1,4 @@ - <p><a href="https://travis-ci.org/textile/python-textile"><img alt="" src="https://travis-ci.org/textile/python-textile.svg" /></a> <a href="https://codecov.io/github/textile/python-textile"><img alt="" src="https://codecov.io/github/textile/python-textile/coverage.svg" /></a> <img alt="" src="https://img.shields.io/pypi/pyversions/textile" /> <img alt="" src="https://img.shields.io/pypi/wheel/textile" /></p> + <p><a href="https://github.com/textile/python-textile/actions/workflows/lint_and_test.yml"><img alt="python-textile" src="https://github.com/textile/python-textile/actions/workflows/lint_and_test.yml/badge.svg" title="python-textile" /></a> <a href="https://codecov.io/github/textile/python-textile"><img alt="" src="https://codecov.io/github/textile/python-textile/coverage.svg" /></a> <img alt="" src="https://img.shields.io/pypi/pyversions/textile" /> <img alt="" src="https://img.shields.io/pypi/wheel/textile" /></p> <h1>python-textile</h1> From c4308890fecc070622b963557507f98509e69460 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Mon, 5 Aug 2024 13:59:53 -0400 Subject: [PATCH 177/228] update minimum supported python version --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index cbbb774..b14b9a6 100644 --- a/setup.py +++ b/setup.py @@ -1,5 +1,5 @@ -from setuptools import setup, find_packages import os +from setuptools import setup, find_packages def get_version(): @@ -48,5 +48,5 @@ def get_version(): tests_require=['pytest', 'pytest-cov'], include_package_data=True, zip_safe=False, - python_requires='>=3.5', + python_requires='>=3.8', ) From f8533fae9964f744105a01769213ca00dd9d1ffb Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Mon, 5 Aug 2024 14:15:18 -0400 Subject: [PATCH 178/228] codecov is reporting missing coverage in this file testing to see if moving this import will have any affect --- textile/tools/imagesize.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/textile/tools/imagesize.py b/textile/tools/imagesize.py index 6fba73e..ec724a2 100644 --- a/textile/tools/imagesize.py +++ b/textile/tools/imagesize.py @@ -1,3 +1,5 @@ +from urllib.request import urlopen + def getimagesize(url): """ Attempts to determine an image's width and height, and returns a tuple, @@ -11,8 +13,6 @@ def getimagesize(url): except ImportError: return '' - from urllib.request import urlopen - try: p = ImageFile.Parser() f = urlopen(url) From 1f32b74d233a3e3f6bfe134ea251ba5c4ab608ab Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Mon, 5 Aug 2024 14:17:00 -0400 Subject: [PATCH 179/228] flake8 insists on two new lines --- textile/tools/imagesize.py | 1 + 1 file changed, 1 insertion(+) diff --git a/textile/tools/imagesize.py b/textile/tools/imagesize.py index ec724a2..1d758cc 100644 --- a/textile/tools/imagesize.py +++ b/textile/tools/imagesize.py @@ -1,5 +1,6 @@ from urllib.request import urlopen + def getimagesize(url): """ Attempts to determine an image's width and height, and returns a tuple, From db58e41ab37a3f0a158d4ace15a51dbbbfcf665e Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Mon, 5 Aug 2024 14:23:05 -0400 Subject: [PATCH 180/228] setting this back to what it was --- textile/tools/imagesize.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/textile/tools/imagesize.py b/textile/tools/imagesize.py index 1d758cc..6fba73e 100644 --- a/textile/tools/imagesize.py +++ b/textile/tools/imagesize.py @@ -1,6 +1,3 @@ -from urllib.request import urlopen - - def getimagesize(url): """ Attempts to determine an image's width and height, and returns a tuple, @@ -14,6 +11,8 @@ def getimagesize(url): except ImportError: return '' + from urllib.request import urlopen + try: p = ImageFile.Parser() f = urlopen(url) From 88042c5591818d4e4a6acf810029e7038e0aca5e Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Mon, 5 Aug 2024 14:23:21 -0400 Subject: [PATCH 181/228] debugging the value of imagesize --- .github/workflows/lint_and_test.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/lint_and_test.yml b/.github/workflows/lint_and_test.yml index 44a7c91..e932372 100644 --- a/.github/workflows/lint_and_test.yml +++ b/.github/workflows/lint_and_test.yml @@ -24,6 +24,8 @@ jobs: pip install -U pytest pytest-cov coverage codecov if [[ $INPUT_IMAGE_SIZE == true ]] ; then imagesize='[imagesize]' ; fi pip install -e ".${imagesize}" + echo "INPUT_IMAGE_SIZE: ${INPUT_IMAGE_SIZE}" + echo "imagesize: ${imagesize}" - name: run tests run: | pytest From 9c23fdff323c62430f8633bbd90201895f338d0b Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Mon, 5 Aug 2024 14:25:23 -0400 Subject: [PATCH 182/228] more debugging --- .github/workflows/lint_and_test.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/lint_and_test.yml b/.github/workflows/lint_and_test.yml index e932372..f4795b2 100644 --- a/.github/workflows/lint_and_test.yml +++ b/.github/workflows/lint_and_test.yml @@ -24,6 +24,7 @@ jobs: pip install -U pytest pytest-cov coverage codecov if [[ $INPUT_IMAGE_SIZE == true ]] ; then imagesize='[imagesize]' ; fi pip install -e ".${imagesize}" + echo "image_size: ${image_size}" echo "INPUT_IMAGE_SIZE: ${INPUT_IMAGE_SIZE}" echo "imagesize: ${imagesize}" - name: run tests From 889432773d7a53dc23ffdfcbae43156bdca24b28 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Mon, 5 Aug 2024 14:30:29 -0400 Subject: [PATCH 183/228] maybe this is the right way to reference matrix values??? --- .github/workflows/lint_and_test.yml | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/.github/workflows/lint_and_test.yml b/.github/workflows/lint_and_test.yml index f4795b2..047e6d8 100644 --- a/.github/workflows/lint_and_test.yml +++ b/.github/workflows/lint_and_test.yml @@ -24,9 +24,7 @@ jobs: pip install -U pytest pytest-cov coverage codecov if [[ $INPUT_IMAGE_SIZE == true ]] ; then imagesize='[imagesize]' ; fi pip install -e ".${imagesize}" - echo "image_size: ${image_size}" - echo "INPUT_IMAGE_SIZE: ${INPUT_IMAGE_SIZE}" - echo "imagesize: ${imagesize}" + echo "image_size: ${{ matrix.image_size }}" - name: run tests run: | pytest From c55d83b16203cd75047b0acc57cad0a892b73250 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Mon, 5 Aug 2024 14:32:17 -0400 Subject: [PATCH 184/228] correct way to look up matrix value --- .github/workflows/lint_and_test.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/lint_and_test.yml b/.github/workflows/lint_and_test.yml index 047e6d8..14ced37 100644 --- a/.github/workflows/lint_and_test.yml +++ b/.github/workflows/lint_and_test.yml @@ -22,9 +22,8 @@ jobs: run: | imagesize='' pip install -U pytest pytest-cov coverage codecov - if [[ $INPUT_IMAGE_SIZE == true ]] ; then imagesize='[imagesize]' ; fi + if [[ ${{ matrix.image_size }} == true ]] ; then imagesize='[imagesize]' ; fi pip install -e ".${imagesize}" - echo "image_size: ${{ matrix.image_size }}" - name: run tests run: | pytest From abb0581805cb05301a5c6d06e2f604aec7f923b2 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Tue, 6 Aug 2024 14:55:42 -0400 Subject: [PATCH 185/228] flake8 cleanup --- tests/test_values.py | 4 ++-- textile/core.py | 14 ++++---------- textile/objects/table.py | 2 +- textile/utils.py | 4 ++-- 4 files changed, 9 insertions(+), 15 deletions(-) diff --git a/tests/test_values.py b/tests/test_values.py index f08ac6f..62c797d 100644 --- a/tests/test_values.py +++ b/tests/test_values.py @@ -430,8 +430,8 @@ '\t<h1>Two line with <strong>strong</strong><br />\nheading</h1>'), # Non-standalone ampersands should not be escaped (("“<span lang=\"en\">test</span>”\n\n" - "“<span lang=\"en\">test</span>”\n\n" - " <span lang=\"en\">test</span> \n"), + "“<span lang=\"en\">test</span>”\n\n" + " <span lang=\"en\">test</span> \n"), ("\t<p>“<span lang=\"en\">test</span>”</p>\n\n" "\t<p>“<span lang=\"en\">test</span>”</p>\n\n" "\t<p> <span lang=\"en\">test</span> </p>")), diff --git a/textile/core.py b/textile/core.py index 1457c64..9c93ad7 100644 --- a/textile/core.py +++ b/textile/core.py @@ -209,11 +209,6 @@ def __init__(self, restricted=False, lite=False, noimage=False, self.refIndex = 0 self.block_tags = block_tags - cur = r'' - if regex_snippets['cur']: # pragma: no branch - cur = r'(?:[{0}]{1}*)?'.format(regex_snippets['cur'], - regex_snippets['space']) - self.glyph_replacers = make_glyph_replacers( html_type, self.uid, self.glyph_definitions) @@ -424,7 +419,7 @@ def fPBr(self, m): re.compile(r"<br[ ]*/?>{0}*\n(?![{0}|])".format(regex_snippets['space']), re.I) .sub("\n", content)) - content = re.compile(r"\n(?![\s|])").sub('<br />',content) + content = re.compile(r"\n(?![\s|])").sub('<br />', content) return '<{0}{1}>{2}{3}'.format(m.group(1), m.group(2), content, m.group(4)) def doBr(self, match): @@ -1122,11 +1117,11 @@ def storeTags(self, opentag, closetag=''): tags = {} self.refIndex += 1 self.refCache[self.refIndex] = opentag - tags['open'] = self.uid + str(self.refIndex) + ':ospan '; + tags['open'] = self.uid + str(self.refIndex) + ':ospan ' self.refIndex += 1 self.refCache[self.refIndex] = closetag - tags['close'] = ' ' + self.uid + str(self.refIndex) + ':cspan'; + tags['close'] = ' ' + self.uid + str(self.refIndex) + ':cspan' return tags def retrieveTags(self, text): @@ -1139,7 +1134,6 @@ def retrieveTags(self, text): def fRetrieveTags(self, match): return self.refCache[int(match.group('token'))] - def image(self, text): pattern = re.compile(r""" (?:[\[{{])? # pre @@ -1363,7 +1357,7 @@ def fNoteLists(self, match): content) else: li = ('\t\t<li{0}>{1} Undefined Note [#{2}].</li>' - ).format(atts, links, info['seq']) + ).format(atts, links, info['seq']) o.append(li) if '+' == extras and self.unreferencedNotes: for seq, info in self.unreferencedNotes.items(): diff --git a/textile/objects/table.py b/textile/objects/table.py index 7a3d0ce..ba034f2 100644 --- a/textile/objects/table.py +++ b/textile/objects/table.py @@ -5,7 +5,7 @@ from textile.regex_strings import (align_re_s, cls_re_s, regex_snippets, table_span_re_s, valign_re_s, pnct_re_s) -from textile.utils import generate_tag, parse_attributes +from textile.utils import generate_tag, parse_attributes, pba try: import regex as re diff --git a/textile/utils.py b/textile/utils.py index 9a459a9..1a04182 100644 --- a/textile/utils.py +++ b/textile/utils.py @@ -22,11 +22,12 @@ # else the regex module doesn't properly match pre-s. It only matches the # p in pre. re.compile(r'<(pre|p|blockquote|div|form|table|ul|ol|dl|h[1-6])[^>]*?>.*</\1>', - re.S), + re.S), re.compile(r'<(hr|br)[^>]*?/>'), re.compile(r'<!--.*?-->'), ) + def decode_high(text): """Decode encoded HTML entities.""" text = '&#{0};'.format(text) @@ -88,7 +89,6 @@ def has_raw_text(text): return r != '' - def is_rel_url(url): """Identify relative urls.""" (scheme, netloc) = urlparse(url)[0:2] From 82fe31acdd6333ca6b5f732e798190b504e55fcb Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Tue, 6 Aug 2024 15:02:52 -0400 Subject: [PATCH 186/228] fix colgroup handling --- textile/objects/table.py | 22 ++++++++++------------ 1 file changed, 10 insertions(+), 12 deletions(-) diff --git a/textile/objects/table.py b/textile/objects/table.py index ba034f2..2471d3c 100644 --- a/textile/objects/table.py +++ b/textile/objects/table.py @@ -57,19 +57,17 @@ def process(self): # Colgroup -- A colgroup row will not necessarily end with a |. # Hence it may include the next row of actual table data. - - gmtch = self.colgroup_re.match(row) - if gmtch: - cols = gmtch.group(1).replace('.', "") - for idx, col in enumerate(cols.split("|")): - gatts = pba(col.strip(), 'col', restricted=self.textile.restricted) - self.colgroup += '\t<col{0}\n'.format( - ('group' + gatts + '>') if idx == 0 else (gatts + ' />')) - self.colgroup += '\t</colgroup>' - nl_index = row.find('\n') - if nl_index >= 0: - row = row[nl_index:].lstrip() + if row[:2] == '|:': + if '\n' in row: + colgroup_data, row = row[2:].split('\n') else: + colgroup_data, row = row[2:], '' + colgroup_atts, cols = colgroup_data, None + if '|' in colgroup_data: + colgroup_atts, cols = colgroup_data.split('|', 1) + colgrp = Colgroup(cols, colgroup_atts, restricted=self.textile.restricted) + self.colgroup = colgrp.process() + if row == '': continue # search the row for a table group - thead, tfoot, or tbody From 57e01fcfc89e1ff5dc6d04a86660f24567fe44b4 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Tue, 6 Aug 2024 15:04:27 -0400 Subject: [PATCH 187/228] remove unused import --- textile/objects/table.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/textile/objects/table.py b/textile/objects/table.py index 2471d3c..96adaed 100644 --- a/textile/objects/table.py +++ b/textile/objects/table.py @@ -5,7 +5,7 @@ from textile.regex_strings import (align_re_s, cls_re_s, regex_snippets, table_span_re_s, valign_re_s, pnct_re_s) -from textile.utils import generate_tag, parse_attributes, pba +from textile.utils import generate_tag, parse_attributes try: import regex as re From 063aaab3fa8ba039557db658aa9297d100ce6841 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Tue, 6 Aug 2024 15:22:02 -0400 Subject: [PATCH 188/228] fix formatting of captions and colgroups --- textile/objects/table.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/textile/objects/table.py b/textile/objects/table.py index 96adaed..fcecb06 100644 --- a/textile/objects/table.py +++ b/textile/objects/table.py @@ -152,7 +152,7 @@ def __init__(self, capts, cap, row, restricted): def process(self, cap): tag = generate_tag('caption', cap.strip(), self.attributes) - return '\t{0}\n\t'.format(tag) + return '\t{0}\n'.format(tag) class Colgroup(object): @@ -181,7 +181,7 @@ def process(self): # tab between cols and a newline at the end xml_declaration = "<?xml version='1.0' encoding='UTF-8'?>\n" colgrp = colgrp.replace(xml_declaration, '') - return colgrp.replace('><', '>\n\t<') + return f'\t{colgrp.replace('><', '>\n\t<')}' class Row(object): From 036f4ba34f6466b12d166735932db5ce00f53d37 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Tue, 6 Aug 2024 15:25:01 -0400 Subject: [PATCH 189/228] fix f-string formatting --- textile/objects/table.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/textile/objects/table.py b/textile/objects/table.py index fcecb06..ac1da76 100644 --- a/textile/objects/table.py +++ b/textile/objects/table.py @@ -181,7 +181,7 @@ def process(self): # tab between cols and a newline at the end xml_declaration = "<?xml version='1.0' encoding='UTF-8'?>\n" colgrp = colgrp.replace(xml_declaration, '') - return f'\t{colgrp.replace('><', '>\n\t<')}' + return f"\t{colgrp.replace('><', '>\n\t<')}" class Row(object): From 6e041ab237b2536e23f65cf3c87ede1aa3ffd19f Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Tue, 6 Aug 2024 15:26:57 -0400 Subject: [PATCH 190/228] further f-string formatting fix --- textile/objects/table.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/textile/objects/table.py b/textile/objects/table.py index ac1da76..42644b5 100644 --- a/textile/objects/table.py +++ b/textile/objects/table.py @@ -181,7 +181,8 @@ def process(self): # tab between cols and a newline at the end xml_declaration = "<?xml version='1.0' encoding='UTF-8'?>\n" colgrp = colgrp.replace(xml_declaration, '') - return f"\t{colgrp.replace('><', '>\n\t<')}" + colgrp = colgrp.replace('><', '>\n\t<') + return f"\t{colgrp}" class Row(object): From a10ace1feccf1296f53ae39234bf8e5c73dac772 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Wed, 7 Aug 2024 15:01:30 -0400 Subject: [PATCH 191/228] add a test for a table which has a colgroup but no caption --- tests/test_values.py | 8 +++++++- textile/objects/table.py | 4 ++-- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/tests/test_values.py b/tests/test_values.py index 62c797d..957a42e 100644 --- a/tests/test_values.py +++ b/tests/test_values.py @@ -503,7 +503,13 @@ # Table column with an emphasis should not be confused with a heading ('|_touch_ this!| _touch_ this! |', '\t<table>\n\t\t<tr>\n\t\t\t<td><em>touch</em> this!</td>\n\t\t\t<td> <em>touch</em> this! </td>\n\t\t</tr>\n\t</table>'), - + # Table with colgroup but no caption + (("|:\\5. 80 |\x20\n" + "|a|b|c|d|e|\x20\n"), + ('\t<table>\n' + '\t<colgroup span="5" width="80">\n\t</colgroup>\n' + '\t\t<tr>\n\t\t\t<td>a</td>\n\t\t\t<td>b</td>\n\t\t\t<td>c</td>\n\t\t\t<td>d</td>\n\t\t\t<td>e</td>\n\t\t</tr>\n' + '\t</table>')), ) diff --git a/textile/objects/table.py b/textile/objects/table.py index 42644b5..a257bd0 100644 --- a/textile/objects/table.py +++ b/textile/objects/table.py @@ -152,7 +152,7 @@ def __init__(self, capts, cap, row, restricted): def process(self, cap): tag = generate_tag('caption', cap.strip(), self.attributes) - return '\t{0}\n'.format(tag) + return '\t{0}'.format(tag) class Colgroup(object): @@ -182,7 +182,7 @@ def process(self): xml_declaration = "<?xml version='1.0' encoding='UTF-8'?>\n" colgrp = colgrp.replace(xml_declaration, '') colgrp = colgrp.replace('><', '>\n\t<') - return f"\t{colgrp}" + return f"\n\t{colgrp}" class Row(object): From 0c2c0fa376faa0f457c7da4a09da83e978b1dc2b Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Wed, 7 Aug 2024 15:40:31 -0400 Subject: [PATCH 192/228] confirm that the extra newline from captions without colgroups is fixed --- tests/test_textile.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_textile.py b/tests/test_textile.py index 0b2abca..8a5e4f8 100644 --- a/tests/test_textile.py +++ b/tests/test_textile.py @@ -76,7 +76,6 @@ def test_restricted(): expect = '''\ \t<table> \t<caption>Your caption goes here</caption> - \t<tfoot> \t\t<tr> \t\t\t<td>A footer </td> From 9c4013c22993219771bdc88a47d1652850a08b8c Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Wed, 7 Aug 2024 16:17:27 -0400 Subject: [PATCH 193/228] hopefully this will cover a missing line in codecov --- tests/test_values.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_values.py b/tests/test_values.py index 957a42e..8b6d36b 100644 --- a/tests/test_values.py +++ b/tests/test_values.py @@ -247,6 +247,7 @@ """\t<h2>A definition list</h2>\n\n\t<dl class="class" id="id">\n\t\t<dt>Term 1</dt>\n\t\t<dd>Def 1</dd>\n\t\t<dd>Def 2</dd>\n\t\t<dd>Def 3\n\t\t<dl>\n\t\t\t<dt>Center</dt>\n\t\t\t<dt><acronym title="Why Em Cee Ayy"><span class="caps">NATO</span></acronym></dt>\n\t\t\t<dd>Subdef 1</dd>\n\t\t\t<dd>Subdef 2\n\t\t\t<dl>\n\t\t\t\t<dt>SubSub Term</dt>\n\t\t\t\t<dd>SubSub Def 1</dd>\n\t\t\t\t<dd>SubSub Def 2</dd>\n\t\t\t\t<dd>Subsub Def 3<br />\nWith newline</dd>\n\t\t\t\t<dd>Subsub Def 4</dd>\n\t\t\t</dl></dd>\n\t\t\t<dd>Subdef 3</dd>\n\t\t</dl></dd>\n\t\t<dd><span class="caps">DEF</span> 4</dd>\n\t\t<dt>Term 2</dt>\n\t\t<dd>Another def</dd>\n\t\t<dd>And another</dd>\n\t\t<dd>One more\n\t\t<dl>\n\t\t\t<dd>A def without a term</dd>\n\t\t\t<dd>More defness</dd>\n\t\t</dl></dd>\n\t\t<dt>Third term for good measure</dt>\n\t\t<dd>My definition of a boombastic jazz</dd>\n\t</dl>"""), ("""###. Here's a comment.\n\nh3. Hello\n\n###. And\nanother\none.\n\nGoodbye.""", """\t<h3>Hello</h3>\n\n\t<p>Goodbye.</p>"""), ("""h2. A Definition list which covers the instance where a new definition list is created with a term without a definition\n\n- term :=\n- term2 := def""", """\t<h2>A Definition list which covers the instance where a new definition list is created with a term without a definition</h2>\n\n<dl>\n\t<dt>term</dt>\n\t<dt>term2</dt>\n\t<dd>def</dd>\n</dl>"""), + ("""h2. A definition list where one item in the list is missing a definition for the term\n\n- Markdown := a perfectly cromulent plaintext markup system\n- Textile :=""", """\t<h2>A definition list where one item in the list is missing a definition for the term</h2>\n\n<dl>\n\t<dt>Markdown</dt>\n\t<dd>a perfectly cromulent plaintext markup system</dd>\n\t<dt>Textile</dt>\n</dl>"""), ('!{height:20px;width:20px;}https://1.gravatar.com/avatar/!', '\t<p><img alt="" src="https://1.gravatar.com/avatar/" style="height:20px; width:20px;" /></p>'), ('& test', '\t<p>& test</p>'), From a169a7c508b978e82c0816a8f9ffe30a0938f43e Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Wed, 7 Aug 2024 16:36:23 -0400 Subject: [PATCH 194/228] I don't think we have to worry about a possibly malformed term --- textile/core.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/textile/core.py b/textile/core.py index 9c93ad7..6454187 100644 --- a/textile/core.py +++ b/textile/core.py @@ -1276,10 +1276,7 @@ def fRCList(self, match): .format(regex_snippets['space']), content, re.S) - if xm: - term, definition, _ = xm.groups() - else: - term, definition = content, '' + term, definition, _ = xm.groups() # cleanup term = term.strip() definition = definition.strip(' ') From 52c1b8a4be6eb2e0df294c0b4c1ea0c751e8722e Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Wed, 7 Aug 2024 16:40:52 -0400 Subject: [PATCH 195/228] If only I had read the test on the line immediately above this --- tests/test_values.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/test_values.py b/tests/test_values.py index 8b6d36b..957a42e 100644 --- a/tests/test_values.py +++ b/tests/test_values.py @@ -247,7 +247,6 @@ """\t<h2>A definition list</h2>\n\n\t<dl class="class" id="id">\n\t\t<dt>Term 1</dt>\n\t\t<dd>Def 1</dd>\n\t\t<dd>Def 2</dd>\n\t\t<dd>Def 3\n\t\t<dl>\n\t\t\t<dt>Center</dt>\n\t\t\t<dt><acronym title="Why Em Cee Ayy"><span class="caps">NATO</span></acronym></dt>\n\t\t\t<dd>Subdef 1</dd>\n\t\t\t<dd>Subdef 2\n\t\t\t<dl>\n\t\t\t\t<dt>SubSub Term</dt>\n\t\t\t\t<dd>SubSub Def 1</dd>\n\t\t\t\t<dd>SubSub Def 2</dd>\n\t\t\t\t<dd>Subsub Def 3<br />\nWith newline</dd>\n\t\t\t\t<dd>Subsub Def 4</dd>\n\t\t\t</dl></dd>\n\t\t\t<dd>Subdef 3</dd>\n\t\t</dl></dd>\n\t\t<dd><span class="caps">DEF</span> 4</dd>\n\t\t<dt>Term 2</dt>\n\t\t<dd>Another def</dd>\n\t\t<dd>And another</dd>\n\t\t<dd>One more\n\t\t<dl>\n\t\t\t<dd>A def without a term</dd>\n\t\t\t<dd>More defness</dd>\n\t\t</dl></dd>\n\t\t<dt>Third term for good measure</dt>\n\t\t<dd>My definition of a boombastic jazz</dd>\n\t</dl>"""), ("""###. Here's a comment.\n\nh3. Hello\n\n###. And\nanother\none.\n\nGoodbye.""", """\t<h3>Hello</h3>\n\n\t<p>Goodbye.</p>"""), ("""h2. A Definition list which covers the instance where a new definition list is created with a term without a definition\n\n- term :=\n- term2 := def""", """\t<h2>A Definition list which covers the instance where a new definition list is created with a term without a definition</h2>\n\n<dl>\n\t<dt>term</dt>\n\t<dt>term2</dt>\n\t<dd>def</dd>\n</dl>"""), - ("""h2. A definition list where one item in the list is missing a definition for the term\n\n- Markdown := a perfectly cromulent plaintext markup system\n- Textile :=""", """\t<h2>A definition list where one item in the list is missing a definition for the term</h2>\n\n<dl>\n\t<dt>Markdown</dt>\n\t<dd>a perfectly cromulent plaintext markup system</dd>\n\t<dt>Textile</dt>\n</dl>"""), ('!{height:20px;width:20px;}https://1.gravatar.com/avatar/!', '\t<p><img alt="" src="https://1.gravatar.com/avatar/" style="height:20px; width:20px;" /></p>'), ('& test', '\t<p>& test</p>'), From 164c09e572b37dddb5bb4e724fa2f3c4fb320c5d Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Wed, 7 Aug 2024 18:37:37 -0400 Subject: [PATCH 196/228] add test cases to increase code coverage for parse_attributes --- tests/test_attributes.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/tests/test_attributes.py b/tests/test_attributes.py index b07712f..fed235d 100644 --- a/tests/test_attributes.py +++ b/tests/test_attributes.py @@ -1,3 +1,4 @@ +from typing import OrderedDict from textile.utils import parse_attributes @@ -13,3 +14,11 @@ def test_parse_attributes(): assert parse_attributes('<') == {'style': 'text-align:left;'} assert parse_attributes('(c#i)') == {'class': 'c', 'id': 'i'} assert parse_attributes('\\2 100', element='col') == {'span': '2', 'width': '100'} + + +def test_parse_attributes_edge_cases(): + result = parse_attributes('(:c#i)') + expect = OrderedDict({'id': 'i'}) + assert result == expect + + assert parse_attributes('(<)') == OrderedDict() From d3cc63cfc07782ea2a8fd4a0b06c1e7ddc1c4fc9 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Wed, 7 Aug 2024 18:47:50 -0400 Subject: [PATCH 197/228] move human_readable_url function into utils --- textile/core.py | 13 +++---------- textile/utils.py | 8 ++++++++ 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/textile/core.py b/textile/core.py index 6454187..ea94ac1 100644 --- a/textile/core.py +++ b/textile/core.py @@ -25,8 +25,9 @@ from textile.regex_strings import (align_re_s, cls_re_s, pnct_re_s, regex_snippets, syms_re_s, table_span_re_s) from textile.utils import (decode_high, encode_high, encode_html, generate_tag, - has_raw_text, is_rel_url, is_valid_url, list_type, - normalize_newlines, parse_attributes, pba) + has_raw_text, human_readable_url, is_rel_url, + is_valid_url, list_type, normalize_newlines, + parse_attributes, pba) from textile.objects import Block, Table try: @@ -139,14 +140,6 @@ def make_glyph_replacers(html_type, uid, glyph_defs): for (regex_obj, replacement) in pre_result] -def human_readable_url(url): - if "://" in url: - url = url.split("://")[1] - elif ":" in url: - url = url.split(":")[1] - return url - - class Textile(object): restricted_url_schemes = ('http', 'https', 'ftp', 'mailto') unrestricted_url_schemes = restricted_url_schemes + ( diff --git a/textile/utils.py b/textile/utils.py index 1a04182..7045bcb 100644 --- a/textile/utils.py +++ b/textile/utils.py @@ -89,6 +89,14 @@ def has_raw_text(text): return r != '' +def human_readable_url(url): + if "://" in url: + url = url.split("://")[1] + elif ":" in url: + url = url.split(":")[1] + return url + + def is_rel_url(url): """Identify relative urls.""" (scheme, netloc) = urlparse(url)[0:2] From f10d0aea01ff915876a012ba17b801ae35a15aa2 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Wed, 7 Aug 2024 18:59:53 -0400 Subject: [PATCH 198/228] add human_readable_url edge case test to increase coverage --- tests/test_utils.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/test_utils.py b/tests/test_utils.py index a6e88f8..6ddb93f 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -32,3 +32,7 @@ def test_generate_tag(): expect = '<a href="http://de.wikipedia.org/wiki/%C3%C9bermensch">Übermensch</a>' result = utils.generate_tag('a', text, attributes) assert result == expect + + +def test_human_readable_url_edge_case(): + assert utils.human_readable_url('tel:1-800-555-1212') == '1-800-555-1212' From 4d6fd237f2a06e860e931a9ace9c48b575ca2cb4 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Wed, 7 Aug 2024 19:25:04 -0400 Subject: [PATCH 199/228] closing the final edge case for complete coverage of human_readable_url --- tests/test_utils.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/test_utils.py b/tests/test_utils.py index 6ddb93f..b2e455b 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -35,4 +35,5 @@ def test_generate_tag(): def test_human_readable_url_edge_case(): + assert utils.human_readable_url('google.com') == 'google.com' assert utils.human_readable_url('tel:1-800-555-1212') == '1-800-555-1212' From 08dc3dc9ca0ddfae0072d395141b474a3672c8ec Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Thu, 8 Aug 2024 12:57:57 -0400 Subject: [PATCH 200/228] update README regarding Python version support --- README.textile | 2 +- tests/fixtures/README.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.textile b/README.textile index 797516c..7362c73 100644 --- a/README.textile +++ b/README.textile @@ -42,7 +42,7 @@ bc.. import textile h3. Notes: -* Active development supports Python 3.5 or later. +* Active development supports Python 3.8 or later. h3. Running Tests diff --git a/tests/fixtures/README.txt b/tests/fixtures/README.txt index a485943..fd5ce43 100644 --- a/tests/fixtures/README.txt +++ b/tests/fixtures/README.txt @@ -47,7 +47,7 @@ <h3>Notes:</h3> <ul> - <li>Active development supports Python 3.5 or later.</li> + <li>Active development supports Python 3.8 or later.</li> </ul> <h3>Running Tests</h3> From dbdf43031b0c237b5117767356f78eeeab31ac12 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Thu, 8 Aug 2024 12:59:26 -0400 Subject: [PATCH 201/228] update CHANGELOG in preparation for next release --- CHANGELOG.textile | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/CHANGELOG.textile b/CHANGELOG.textile index 43beedc..e13f6f5 100644 --- a/CHANGELOG.textile +++ b/CHANGELOG.textile @@ -1,5 +1,12 @@ h1. Textile Changelog +h2. Version 4.0.3 +* Update supported Python versions to 3.8 - 3.12 ("#83":https://github.com/textile/python-textile/issues/83) +* Bugfixes: +** Wrong HTML output when "bc.." is the very last in the document ("#81":https://github.com/textile/python-textile/issues/81) +* Other: +** Use github actions instead of travis for automated testing + h2. Version 4.0.2 * Bugfixes: ** Support non-http schemas in url refs ("#75":https://github.com/textile/python-textile/pull/75) From 247dbfad5909da3579367619242990b036213377 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Thu, 8 Aug 2024 13:05:28 -0400 Subject: [PATCH 202/228] update url for textile-lang --- README.textile | 2 +- tests/fixtures/README.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.textile b/README.textile index 7362c73..1bb9087 100644 --- a/README.textile +++ b/README.textile @@ -2,7 +2,7 @@ h1. python-textile -python-textile is a Python port of "Textile":http://txstyle.org/, Dean Allen's humane web text generator. +python-textile is a Python port of "Textile":https://textile-lang.com/, Dean Allen's humane web text generator. h2. Installation diff --git a/tests/fixtures/README.txt b/tests/fixtures/README.txt index fd5ce43..01d0209 100644 --- a/tests/fixtures/README.txt +++ b/tests/fixtures/README.txt @@ -2,7 +2,7 @@ <h1>python-textile</h1> - <p>python-textile is a Python port of <a href="http://txstyle.org/">Textile</a>, Dean Allen’s humane web text generator.</p> + <p>python-textile is a Python port of <a href="https://textile-lang.com/">Textile</a>, Dean Allen’s humane web text generator.</p> <h2>Installation</h2> From 8b26327ad665cd48e990d92a41f044d711d062e8 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Thu, 8 Aug 2024 13:30:10 -0400 Subject: [PATCH 203/228] update contributors list --- CONTRIBUTORS.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CONTRIBUTORS.txt b/CONTRIBUTORS.txt index 5624ae6..90d949a 100644 --- a/CONTRIBUTORS.txt +++ b/CONTRIBUTORS.txt @@ -7,4 +7,6 @@ Alex Shiels Jason Samsa Kurt Raschke Dave Brondsema -Dmitry Shachnev \ No newline at end of file +Dmitry Shachnev +Kirill Mavreshko +Brad Schoening \ No newline at end of file From 739f758befd3fe349f179c37a435ecaf2ebb2e7e Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Thu, 8 Aug 2024 15:46:51 -0400 Subject: [PATCH 204/228] replace old setup.py config with pyproject.toml --- MANIFEST.in | 2 -- pyproject.toml | 43 +++++++++++++++++++++++++++++++++++++++++ setup.py | 52 -------------------------------------------------- 3 files changed, 43 insertions(+), 54 deletions(-) delete mode 100644 MANIFEST.in create mode 100644 pyproject.toml delete mode 100644 setup.py diff --git a/MANIFEST.in b/MANIFEST.in deleted file mode 100644 index 5ca56e8..0000000 --- a/MANIFEST.in +++ /dev/null @@ -1,2 +0,0 @@ -include MANIFEST.in -include tests/fixtures/README.txt diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..fdb4bd7 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,43 @@ +[build-system] +requires = ["setuptools", "setuptools-scm"] +build-backend = "setuptools.build_meta" + +[project] +name = "textile" +authors = [ + { name = "Dennis Burke", email = "ikirudennis@gmail.com"} +] +description = 'Textile processing for python.' +classifiers = [ + 'Development Status :: 5 - Production/Stable', + 'Environment :: Web Environment', + 'Intended Audience :: Developers', + 'License :: OSI Approved :: BSD License', + 'Operating System :: OS Independent', + 'Programming Language :: Python', + 'Programming Language :: Python :: 3', + 'Programming Language :: Python :: 3 :: Only', + 'Programming Language :: Python :: 3.8', + 'Programming Language :: Python :: 3.9', + 'Programming Language :: Python :: 3.10', + 'Programming Language :: Python :: 3.11', + 'Programming Language :: Python :: 3.12', + 'Topic :: Software Development :: Libraries :: Python Modules', +] +dynamic = ["version",] +dependencies = [ + 'html5lib>=1.0.1', + 'regex>1.0; implementation_name != "pypy"', +] +requires-python = '>=3.8' +keywords = ['textile', 'text', 'html', 'markup'] + +[project.optional-dependencies] +develop = ['pytest', 'pytest-cov'] +imagesize = ['Pillow>=3.0.0',] + +[python.scripts] +pytextile = "textile.__main__:main" + +[tool.setuptools.dynamic] +version = {attr = "textile.__version__"} diff --git a/setup.py b/setup.py deleted file mode 100644 index b14b9a6..0000000 --- a/setup.py +++ /dev/null @@ -1,52 +0,0 @@ -import os -from setuptools import setup, find_packages - - -def get_version(): - basedir = os.path.dirname(__file__) - with open(os.path.join(basedir, 'textile/version.py')) as f: - variables = {} - exec(f.read(), variables) - return variables.get('VERSION') - raise RuntimeError('No version info found.') - - -setup( - name='textile', - version=get_version(), - author='Dennis Burke', - author_email='ikirudennis@gmail.com', - description='Textile processing for python.', - url='http://github.com/textile/python-textile', - packages=find_packages(), - classifiers=[ - 'Development Status :: 5 - Production/Stable', - 'Environment :: Web Environment', - 'Intended Audience :: Developers', - 'License :: OSI Approved :: BSD License', - 'Operating System :: OS Independent', - 'Programming Language :: Python', - 'Programming Language :: Python :: 3', - 'Programming Language :: Python :: 3 :: Only', - 'Programming Language :: Python :: 3.8', - 'Programming Language :: Python :: 3.9', - 'Programming Language :: Python :: 3.10', - 'Programming Language :: Python :: 3.11', - 'Programming Language :: Python :: 3.12', - 'Topic :: Software Development :: Libraries :: Python Modules', - ], - keywords='textile,text,html markup', - install_requires=[ - 'html5lib>=1.0.1', - 'regex>1.0; implementation_name != "pypy"', - ], - extras_require={ - 'develop': ['pytest', 'pytest-cov'], - 'imagesize': ['Pillow>=3.0.0'], - }, - entry_points={'console_scripts': ['pytextile=textile.__main__:main']}, - tests_require=['pytest', 'pytest-cov'], - include_package_data=True, - zip_safe=False, - python_requires='>=3.8', -) From 8a5053e45c82e68299c2451f79fb0ac8e7b43532 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Thu, 8 Aug 2024 19:17:53 -0400 Subject: [PATCH 205/228] define the urls for the pypi project --- pyproject.toml | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pyproject.toml b/pyproject.toml index fdb4bd7..f8e13ef 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -36,6 +36,11 @@ keywords = ['textile', 'text', 'html', 'markup'] develop = ['pytest', 'pytest-cov'] imagesize = ['Pillow>=3.0.0',] +[project.urls] +Homepage = "https://github.com/textile/python-textile" +Repository = "https://github.com/textile/python-textile.git" +Issues = "https://github.com/textile/python-textile/issues" + [python.scripts] pytextile = "textile.__main__:main" From c758c633f56271ea58c27d3c09736996fa0dab61 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Fri, 9 Aug 2024 11:27:55 -0400 Subject: [PATCH 206/228] replace html5lib with nh3 We're only using the santizer part of html5lib, and it's being deprecated. It seems nh3 is the recommended replacement at this time. This change eliminates a series of Deprecation Warnings. --- pyproject.toml | 2 +- tests/test_textile.py | 2 +- textile/core.py | 11 ++++++----- textile/tools/sanitizer.py | 11 ----------- 4 files changed, 8 insertions(+), 18 deletions(-) delete mode 100644 textile/tools/sanitizer.py diff --git a/pyproject.toml b/pyproject.toml index f8e13ef..5ef4a96 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -26,7 +26,7 @@ classifiers = [ ] dynamic = ["version",] dependencies = [ - 'html5lib>=1.0.1', + 'nh3', 'regex>1.0; implementation_name != "pypy"', ] requires-python = '>=3.8' diff --git a/tests/test_textile.py b/tests/test_textile.py index 8a5e4f8..a4ec90b 100644 --- a/tests/test_textile.py +++ b/tests/test_textile.py @@ -117,7 +117,7 @@ def test_sanitize(): assert result == expect test = """<p style="width: expression(alert('evil'));">a paragraph of evil text</p>""" - result = '<p style="">a paragraph of evil text</p>' + result = '<p>a paragraph of evil text</p>' expect = textile.Textile().parse(test, sanitize=True) assert result == expect diff --git a/textile/core.py b/textile/core.py index ea94ac1..e9d2955 100644 --- a/textile/core.py +++ b/textile/core.py @@ -20,8 +20,9 @@ import uuid from urllib.parse import urlparse, urlsplit, urlunsplit, quote, unquote from collections import OrderedDict +from nh3 import clean -from textile.tools import sanitizer, imagesize +from textile.tools import imagesize from textile.regex_strings import (align_re_s, cls_re_s, pnct_re_s, regex_snippets, syms_re_s, table_span_re_s) from textile.utils import (decode_high, encode_high, encode_html, generate_tag, @@ -236,12 +237,12 @@ def parse(self, text, rel=None, sanitize=False): if self.block_tags: if self.lite: - self.blocktag_whitelist = ['bq', 'p'] + self.blocktag_allowlist = set(['bq', 'p', 'br']) text = self.block(text) else: - self.blocktag_whitelist = ['bq', 'p', 'bc', 'notextile', + self.blocktag_allowlist = set(['bq', 'p', 'br', 'bc', 'notextile', 'pre', 'h[1-6]', 'fn{0}+'.format( - regex_snippets['digit']), '###'] + regex_snippets['digit']), '###']) text = self.block(text) text = self.placeNoteLists(text) else: @@ -263,7 +264,7 @@ def parse(self, text, rel=None, sanitize=False): text = text.replace('{0}:glyph:'.format(self.uid), '') if sanitize: - text = sanitizer.sanitize(text) + text = clean(text, tags=self.blocktag_allowlist) text = self.retrieveTags(text) text = self.retrieveURLs(text) diff --git a/textile/tools/sanitizer.py b/textile/tools/sanitizer.py deleted file mode 100644 index 3c7209c..0000000 --- a/textile/tools/sanitizer.py +++ /dev/null @@ -1,11 +0,0 @@ -def sanitize(string): - """ - Ensure that the text does not contain any malicious HTML code which might - break the page. - """ - from html5lib import parseFragment, serialize - - parsed = parseFragment(string) - clean = serialize(parsed, sanitize=True, omit_optional_tags=False, - quote_attr_values='always') - return clean From f6fbd530384d131be15eee73c88b4fa60c4de112 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Fri, 9 Aug 2024 11:33:00 -0400 Subject: [PATCH 207/228] replace str.format with f-string --- textile/core.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/textile/core.py b/textile/core.py index e9d2955..1b8cd66 100644 --- a/textile/core.py +++ b/textile/core.py @@ -241,8 +241,8 @@ def parse(self, text, rel=None, sanitize=False): text = self.block(text) else: self.blocktag_allowlist = set(['bq', 'p', 'br', 'bc', 'notextile', - 'pre', 'h[1-6]', 'fn{0}+'.format( - regex_snippets['digit']), '###']) + 'pre', 'h[1-6]', + f'fn{regex_snippets['digit']}+', '###']) text = self.block(text) text = self.placeNoteLists(text) else: From 01b3e73a2fc97eecf774adeda3a9004b706fe181 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Fri, 9 Aug 2024 11:34:37 -0400 Subject: [PATCH 208/228] fix formatting --- textile/core.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/textile/core.py b/textile/core.py index 1b8cd66..711d08c 100644 --- a/textile/core.py +++ b/textile/core.py @@ -242,7 +242,7 @@ def parse(self, text, rel=None, sanitize=False): else: self.blocktag_allowlist = set(['bq', 'p', 'br', 'bc', 'notextile', 'pre', 'h[1-6]', - f'fn{regex_snippets['digit']}+', '###']) + f"fn{regex_snippets['digit']}+", '###']) text = self.block(text) text = self.placeNoteLists(text) else: From a716dfaece13f5f77c57d02afd79fe973a2744e2 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Fri, 9 Aug 2024 12:21:01 -0400 Subject: [PATCH 209/228] add nh3 to build requirements --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 5ef4a96..08ded12 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,5 @@ [build-system] -requires = ["setuptools", "setuptools-scm"] +requires = ["setuptools", "setuptools-scm", "nh3"] build-backend = "setuptools.build_meta" [project] From a47bd97e7244c4881fe25f50e414616a9a7ac4a3 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Fri, 9 Aug 2024 12:39:29 -0400 Subject: [PATCH 210/228] update README to reflect change of dependencies --- README.textile | 2 +- tests/fixtures/README.txt | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/README.textile b/README.textile index 1bb9087..49df25e 100644 --- a/README.textile +++ b/README.textile @@ -9,7 +9,7 @@ h2. Installation @pip install textile@ Dependencies: -* "html5lib":https://pypi.org/project/html5lib/ +* "nh3":https://pypi.org/project/nh3/ * "regex":https://pypi.org/project/regex/ (The regex package causes problems with PyPy, and is not installed as a dependency in such environments. If you are upgrading a textile install on PyPy which had regex previously included, you may need to uninstall it.) Optional dependencies include: diff --git a/tests/fixtures/README.txt b/tests/fixtures/README.txt index 01d0209..bb0ad30 100644 --- a/tests/fixtures/README.txt +++ b/tests/fixtures/README.txt @@ -10,7 +10,7 @@ <p>Dependencies: <ul> - <li><a href="https://pypi.org/project/html5lib/">html5lib</a></li> + <li><a href="https://pypi.org/project/nh3/">nh3</a></li> <li><a href="https://pypi.org/project/regex/">regex</a> (The regex package causes problems with PyPy, and is not installed as a dependency in such environments. If you are upgrading a textile install on PyPy which had regex previously included, you may need to uninstall it.)</li> </ul></p> From 9f2b9352de20db5e7ab73f474829a989cffb2b15 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Fri, 9 Aug 2024 14:30:44 -0400 Subject: [PATCH 211/228] remove `from __future__ import unicode_literals` This is effectively a no-op in all supported python versions --- tests/test_block.py | 2 -- tests/test_github_issues.py | 2 -- tests/test_textile.py | 1 - tests/test_utils.py | 2 -- tests/test_values.py | 1 - textile/__init__.py | 2 -- textile/core.py | 2 -- textile/objects/block.py | 2 -- textile/objects/table.py | 2 -- textile/regex_strings.py | 2 -- textile/textilefactory.py | 1 - textile/utils.py | 2 -- 12 files changed, 21 deletions(-) diff --git a/tests/test_block.py b/tests/test_block.py index 691d21a..eed5441 100644 --- a/tests/test_block.py +++ b/tests/test_block.py @@ -1,5 +1,3 @@ -from __future__ import unicode_literals - import textile from textile.objects import Block diff --git a/tests/test_github_issues.py b/tests/test_github_issues.py index c08d067..6808054 100644 --- a/tests/test_github_issues.py +++ b/tests/test_github_issues.py @@ -1,6 +1,4 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals - import textile diff --git a/tests/test_textile.py b/tests/test_textile.py index a4ec90b..84e9ddf 100644 --- a/tests/test_textile.py +++ b/tests/test_textile.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals import pytest import re import textile diff --git a/tests/test_utils.py b/tests/test_utils.py index b2e455b..952c7b4 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -1,6 +1,4 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals - from textile import utils diff --git a/tests/test_values.py b/tests/test_values.py index 957a42e..7c19e11 100644 --- a/tests/test_values.py +++ b/tests/test_values.py @@ -1,5 +1,4 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals import textile import pytest diff --git a/textile/__init__.py b/textile/__init__.py index 08a242f..1641873 100644 --- a/textile/__init__.py +++ b/textile/__init__.py @@ -1,5 +1,3 @@ -from __future__ import unicode_literals - from .core import textile, textile_restricted, Textile # noqa: F401 from .version import VERSION diff --git a/textile/core.py b/textile/core.py index 711d08c..6d2c1e7 100644 --- a/textile/core.py +++ b/textile/core.py @@ -1,6 +1,4 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals - __copyright__ = """ Copyright (c) 2009, Jason Samsa, http://jsamsa.com/ Copyright (c) 2010, Kurt Raschke <kurt@kurtraschke.com> diff --git a/textile/objects/block.py b/textile/objects/block.py index 8f44ad1..6d611ed 100644 --- a/textile/objects/block.py +++ b/textile/objects/block.py @@ -1,6 +1,4 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals - from collections import OrderedDict try: import regex as re diff --git a/textile/objects/table.py b/textile/objects/table.py index a257bd0..72781ad 100644 --- a/textile/objects/table.py +++ b/textile/objects/table.py @@ -1,6 +1,4 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals - from xml.etree import ElementTree from textile.regex_strings import (align_re_s, cls_re_s, regex_snippets, diff --git a/textile/regex_strings.py b/textile/regex_strings.py index 2e096fb..c3691bb 100644 --- a/textile/regex_strings.py +++ b/textile/regex_strings.py @@ -1,6 +1,4 @@ # -*- coding: utf-8 -*- -from __future__ import unicode_literals - try: # Use regex module for matching uppercase characters if installed, # otherwise fall back to finding all the uppercase chars in a loop. diff --git a/textile/textilefactory.py b/textile/textilefactory.py index e5e2458..3892988 100644 --- a/textile/textilefactory.py +++ b/textile/textilefactory.py @@ -1,4 +1,3 @@ -from __future__ import unicode_literals from .core import Textile diff --git a/textile/utils.py b/textile/utils.py index 7045bcb..b4516ea 100644 --- a/textile/utils.py +++ b/textile/utils.py @@ -1,5 +1,3 @@ -from __future__ import unicode_literals - try: import regex as re except ImportError: From 6544c68dcf96b93d353f906e60cdd8e76aa2ff13 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Fri, 9 Aug 2024 14:35:17 -0400 Subject: [PATCH 212/228] remove unused variable --- textile/core.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/textile/core.py b/textile/core.py index 6d2c1e7..8efdbea 100644 --- a/textile/core.py +++ b/textile/core.py @@ -149,8 +149,6 @@ class Textile(object): note_index = 1 - doctype_whitelist = ['xhtml', 'html5'] - glyph_definitions = { 'quote_single_open': '‘', # noqa: E241 'quote_single_close': '’', # noqa: E241 From 8b262811853a6ce2c71d05fcc40dd3a9a3d0eab5 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Fri, 9 Aug 2024 14:58:22 -0400 Subject: [PATCH 213/228] move getimagesize into utils And now the tools directory is no longer needed. --- tests/test_getimagesize.py | 2 +- tests/test_imagesize.py | 2 +- textile/core.py | 9 ++++----- textile/textilefactory.py | 5 +---- textile/tools/__init__.py | 0 textile/tools/imagesize.py | 27 --------------------------- textile/utils.py | 29 +++++++++++++++++++++++++++++ 7 files changed, 36 insertions(+), 38 deletions(-) delete mode 100644 textile/tools/__init__.py delete mode 100644 textile/tools/imagesize.py diff --git a/tests/test_getimagesize.py b/tests/test_getimagesize.py index 3a3c0a9..4cafc9d 100644 --- a/tests/test_getimagesize.py +++ b/tests/test_getimagesize.py @@ -1,4 +1,4 @@ -from textile.tools.imagesize import getimagesize +from textile.utils import getimagesize import pytest PIL = pytest.importorskip('PIL') diff --git a/tests/test_imagesize.py b/tests/test_imagesize.py index e7d9d88..cb3ad68 100644 --- a/tests/test_imagesize.py +++ b/tests/test_imagesize.py @@ -3,7 +3,7 @@ def test_imagesize(): imgurl = 'http://www.google.com/intl/en_ALL/images/srpr/logo1w.png' - result = textile.tools.imagesize.getimagesize(imgurl) + result = textile.utils.getimagesize(imgurl) try: import PIL # noqa: F401 diff --git a/textile/core.py b/textile/core.py index 8efdbea..4a2594f 100644 --- a/textile/core.py +++ b/textile/core.py @@ -20,13 +20,12 @@ from collections import OrderedDict from nh3 import clean -from textile.tools import imagesize from textile.regex_strings import (align_re_s, cls_re_s, pnct_re_s, regex_snippets, syms_re_s, table_span_re_s) from textile.utils import (decode_high, encode_high, encode_html, generate_tag, - has_raw_text, human_readable_url, is_rel_url, - is_valid_url, list_type, normalize_newlines, - parse_attributes, pba) + getimagesize, has_raw_text, human_readable_url, + is_rel_url, is_valid_url, list_type, + normalize_newlines, parse_attributes, pba) from textile.objects import Block, Table try: @@ -1152,7 +1151,7 @@ def fImage(self, match): title = '' if not is_rel_url(url) and self.get_sizes: - size = imagesize.getimagesize(url) + size = getimagesize(url) if href: href = self.shelveURL(href) diff --git a/textile/textilefactory.py b/textile/textilefactory.py index 3892988..402bf86 100644 --- a/textile/textilefactory.py +++ b/textile/textilefactory.py @@ -20,10 +20,7 @@ def __init__(self, restricted=False, lite=False, sanitize=False, self.method_parms['rel'] = 'nofollow' if noimage is None: - if restricted: - noimage = True - else: - noimage = False + noimage = bool(restricted) self.class_parms['noimage'] = noimage self.method_parms['sanitize'] = sanitize diff --git a/textile/tools/__init__.py b/textile/tools/__init__.py deleted file mode 100644 index e69de29..0000000 diff --git a/textile/tools/imagesize.py b/textile/tools/imagesize.py deleted file mode 100644 index 6fba73e..0000000 --- a/textile/tools/imagesize.py +++ /dev/null @@ -1,27 +0,0 @@ -def getimagesize(url): - """ - Attempts to determine an image's width and height, and returns a tuple, - (width, height), in pixels or an empty string in case of failure. - Requires that PIL is installed. - - """ - - try: - from PIL import ImageFile - except ImportError: - return '' - - from urllib.request import urlopen - - try: - p = ImageFile.Parser() - f = urlopen(url) - while True: - s = f.read(1024) - if not s: - break - p.feed(s) - if p.image: - return p.image.size - except (IOError, ValueError): - return '' diff --git a/textile/utils.py b/textile/utils.py index b4516ea..578af4e 100644 --- a/textile/utils.py +++ b/textile/utils.py @@ -79,6 +79,35 @@ def generate_tag(tag, content, attributes=None): return element_text +def getimagesize(url): + """ + Attempts to determine an image's width and height, and returns a tuple, + (width, height), in pixels or an empty string in case of failure. + Requires that PIL is installed. + + """ + + try: + from PIL import ImageFile + except ImportError: + return '' + + from urllib.request import urlopen + + try: + p = ImageFile.Parser() + f = urlopen(url) + while True: + s = f.read(1024) + if not s: + break + p.feed(s) + if p.image: + return p.image.size + except (IOError, ValueError): + return '' + + def has_raw_text(text): """checks whether the text has text not already enclosed by a block tag""" r = text.strip() From 05ec535ea96c4a5ac4fa41ea59b49933e5d6718b Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Fri, 9 Aug 2024 15:31:49 -0400 Subject: [PATCH 214/228] update CHANGELOG --- CHANGELOG.textile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.textile b/CHANGELOG.textile index e13f6f5..a3ff742 100644 --- a/CHANGELOG.textile +++ b/CHANGELOG.textile @@ -2,6 +2,8 @@ h1. Textile Changelog h2. Version 4.0.3 * Update supported Python versions to 3.8 - 3.12 ("#83":https://github.com/textile/python-textile/issues/83) +* Replace html5lib with nh3 for html sanitization +* General code cleanup * Bugfixes: ** Wrong HTML output when "bc.." is the very last in the document ("#81":https://github.com/textile/python-textile/issues/81) * Other: From 8498638d1d550131d4676f602443f5d128b40f92 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sat, 17 Aug 2024 15:56:40 -0400 Subject: [PATCH 215/228] typo fix to allow pytextile cli tool installation --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 08ded12..15f27c2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -41,7 +41,7 @@ Homepage = "https://github.com/textile/python-textile" Repository = "https://github.com/textile/python-textile.git" Issues = "https://github.com/textile/python-textile/issues" -[python.scripts] +[project.scripts] pytextile = "textile.__main__:main" [tool.setuptools.dynamic] From e1de4a6e75c4533ac8786ba0620c7fd0b7b4851e Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 18 Aug 2024 15:00:31 -0400 Subject: [PATCH 216/228] set project keywords to current values on pypi --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 15f27c2..5e7c2db 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -30,7 +30,7 @@ dependencies = [ 'regex>1.0; implementation_name != "pypy"', ] requires-python = '>=3.8' -keywords = ['textile', 'text', 'html', 'markup'] +keywords = ['textile', 'text', 'html markup'] [project.optional-dependencies] develop = ['pytest', 'pytest-cov'] From a3709a07bfeec29958d8059ea2d882157436bf00 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 18 Aug 2024 15:04:53 -0400 Subject: [PATCH 217/228] attempt adding rendered README to pypi project page --- pyproject.toml | 1 + 1 file changed, 1 insertion(+) diff --git a/pyproject.toml b/pyproject.toml index 5e7c2db..a2774e7 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,6 +31,7 @@ dependencies = [ ] requires-python = '>=3.8' keywords = ['textile', 'text', 'html markup'] +readme = {file = 'tests/fixtures/README.txt', content-type = 'text/plain'} [project.optional-dependencies] develop = ['pytest', 'pytest-cov'] From 51fa5c9e29a96bb2ce851d4358f204479471b61a Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 18 Aug 2024 18:52:15 -0400 Subject: [PATCH 218/228] fix formatting in README --- README.textile | 4 ++-- tests/fixtures/README.txt | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/README.textile b/README.textile index 49df25e..958ea63 100644 --- a/README.textile +++ b/README.textile @@ -50,8 +50,8 @@ To run the test suite, use pytest. `pytest-cov` is required as well. When textile is installed locally: -bc.. pytest +bc. pytest When textile is not installed locally: -bc.. PYTHONPATH=. pytest +bc. PYTHONPATH=. pytest diff --git a/tests/fixtures/README.txt b/tests/fixtures/README.txt index bb0ad30..515cf86 100644 --- a/tests/fixtures/README.txt +++ b/tests/fixtures/README.txt @@ -56,8 +56,8 @@ <p>When textile is installed locally:</p> -<pre><code>pytest +<pre><code>pytest</code></pre> -When textile is not installed locally:</code></pre> + <p>When textile is not installed locally:</p> <pre><code>PYTHONPATH=. pytest</code></pre> \ No newline at end of file From 8aa13cb6389d7f2e3806c6cf97b11fad68357359 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 18 Aug 2024 18:52:50 -0400 Subject: [PATCH 219/228] upload a pandoc-converted version of the README to pypi --- pyproject.toml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index a2774e7..50e2a57 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,7 +31,10 @@ dependencies = [ ] requires-python = '>=3.8' keywords = ['textile', 'text', 'html markup'] -readme = {file = 'tests/fixtures/README.txt', content-type = 'text/plain'} +# use pandoc to generate a markdown version of the README before uploading: +# pandoc README.textile -o README.md +# this kinda hurts to have to do this. +readme = {file = 'README.md', content-type = 'text/markdown'} [project.optional-dependencies] develop = ['pytest', 'pytest-cov'] From 2f6f4de49d017a8aebef23a42768d3d1e487c894 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sun, 18 Aug 2024 19:11:41 -0400 Subject: [PATCH 220/228] ignore converted README.md --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 2ea0352..9cc80d7 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,4 @@ develop-eggs .DS_Store *.swp .tox +README.md From 37225e1091197d37b373390b933548979cfd3502 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Mon, 19 Aug 2024 14:57:41 -0400 Subject: [PATCH 221/228] a better way to upload the README to pypi --- pyproject.toml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 50e2a57..caa03da 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -31,10 +31,10 @@ dependencies = [ ] requires-python = '>=3.8' keywords = ['textile', 'text', 'html markup'] -# use pandoc to generate a markdown version of the README before uploading: -# pandoc README.textile -o README.md -# this kinda hurts to have to do this. -readme = {file = 'README.md', content-type = 'text/markdown'} +# Use the following command to generate a README.txt which is compatible with +# pypi's readme rendering: +# pytextile README.textile | sed -e 's/^\t//' > README.txt +readme = {file = 'README.txt', content-type = 'text/markdown'} [project.optional-dependencies] develop = ['pytest', 'pytest-cov'] From 53fb39ee2a08af904be1e069e04574e11b8016c0 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Mon, 19 Aug 2024 14:58:36 -0400 Subject: [PATCH 222/228] create a Makefile to ease building a release --- Makefile | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 Makefile diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..fff9565 --- /dev/null +++ b/Makefile @@ -0,0 +1,18 @@ +clean: + rm README.txt + rm -rf ./dist ./build + +generate_pypi_README: + ${VIRTUAL_ENV}/bin/pytextile README.textile | sed -e 's/^\t//' > README.txt + +build: generate_pypi_README + python -m build + +upload_to_test: build + twine check ./dist/* + twine upload --repository test_textile ./dist/* + +upload_to_prod: build + twine check ./dist/* + # for now, don't actually upload to prod PyPI, just output the command to do so. + @echo "twine upload --repository textile ./dist/*" From 0abea01888582c899202da6e5deceee50c60c797 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Mon, 19 Aug 2024 16:29:43 -0400 Subject: [PATCH 223/228] update which pypi README file to ignore --- .gitignore | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 9cc80d7..7f97eb6 100644 --- a/.gitignore +++ b/.gitignore @@ -21,4 +21,4 @@ develop-eggs .DS_Store *.swp .tox -README.md +README.txt From f24f52fdd4410a382a96276a11f5b4e235d3438b Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Mon, 19 Aug 2024 18:07:36 -0400 Subject: [PATCH 224/228] better name for the github action we're not building, we're linting and testing --- .github/workflows/lint_and_test.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/lint_and_test.yml b/.github/workflows/lint_and_test.yml index 14ced37..d6e13da 100644 --- a/.github/workflows/lint_and_test.yml +++ b/.github/workflows/lint_and_test.yml @@ -4,7 +4,7 @@ name: python-textile on: [push] jobs: - build: + lint_and_test: runs-on: ubuntu-latest strategy: matrix: From e596255998c0a46bae29ad48733ccf8f735adc7d Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Thu, 19 Sep 2024 16:58:53 -0400 Subject: [PATCH 225/228] add an __init__.py to the tests directory Without this, when running tests locally, I get a warning from coverage about `no data collected.` It's not happening with the automated tests in github actions, and googling the error seems to indicate that some people need this while others don't. Not sure what's up, but at least, it shouldn't hurt anything --- tests/__init__.py | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/__init__.py diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 From a5cef1b554aea531f96e8bada992878b39d3ac11 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sat, 21 Sep 2024 15:30:28 -0400 Subject: [PATCH 226/228] bumping the version number for test.pypi upload testing --- textile/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/textile/version.py b/textile/version.py index f3c42a7..0374ae8 100644 --- a/textile/version.py +++ b/textile/version.py @@ -1 +1 @@ -VERSION = '4.0.2' +VERSION = '4.0.3rc1' From e876ddd5a470b6899dd955e1ff505fac8ca7c917 Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sat, 21 Sep 2024 16:06:00 -0400 Subject: [PATCH 227/228] a better way to handle the `make clean` task Previously, make clean would fail if README.txt did not exist. This is the recommended way to get around this and it just continues on silently if there's nothing to be done --- Makefile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Makefile b/Makefile index fff9565..7570eac 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,6 @@ clean: - rm README.txt - rm -rf ./dist ./build + $(RM) README.txt + $(RM) -r ./dist ./build generate_pypi_README: ${VIRTUAL_ENV}/bin/pytextile README.textile | sed -e 's/^\t//' > README.txt From 64ab026e8a5f9cfd06186fe8a19bb64cd0218a1f Mon Sep 17 00:00:00 2001 From: Dennis Burke <dennisburke@prodigy.net> Date: Sat, 21 Sep 2024 16:09:01 -0400 Subject: [PATCH 228/228] prepare the final version number for release --- textile/version.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/textile/version.py b/textile/version.py index 0374ae8..ad53acb 100644 --- a/textile/version.py +++ b/textile/version.py @@ -1 +1 @@ -VERSION = '4.0.3rc1' +VERSION = '4.0.3'