pytidylib-0.3.2/0000755000076500000240000000000013012735764013772 5ustar jasonstaff00000000000000pytidylib-0.3.2/docs/0000755000076500000240000000000013012735764014722 5ustar jasonstaff00000000000000pytidylib-0.3.2/docs/rst/0000755000076500000240000000000013012735764015532 5ustar jasonstaff00000000000000pytidylib-0.3.2/docs/rst/conf.py0000644000076500000240000000035312771050061017021 0ustar jasonstaff00000000000000# Configuration file for Sphinx documentation tool extensions = ['sphinx.ext.autodoc'] master_doc = "index" project = "pytidylib" copyright = "2009-2014 Jason Stitt" version = "0.1" language = "en" html_title = "pytidylib module" pytidylib-0.3.2/docs/rst/index.rst0000644000076500000240000001302012771050061017356 0ustar jasonstaff00000000000000PyTidyLib: A Python Interface to HTML Tidy ------------------------------------------ `PyTidyLib`_ is a Python package that wraps the `HTML Tidy`_ library. This allows you, from Python code, to "fix" invalid (X)HTML markup. Some of the library's many capabilities include: * Clean up unclosed tags and unescaped characters such as ampersands * Output HTML 4 or XHTML, strict or transitional, and add missing doctypes * Convert named entities to numeric entities, which can then be used in XML documents without an HTML doctype. * Clean up HTML from programs such as Word (to an extent) * Indent the output, including proper (i.e. no) indenting for ``pre`` elements, which some (X)HTML indenting code overlooks. As of the latest PyTidyLib maintenance updates, HTML Tidy itself has currently not been updated since 2008, and it may have trouble with newer HTML. This is just a thin Python wrapper around HTML Tidy, which is a separate project. As of 0.2.3, both Python 2 and Python 3 are supported with passing tests. Naming conventions ================== `HTML Tidy`_ is a longstanding open-source library written in C that implements the actual functionality of cleaning up (X)HTML markup. It provides a shared library (``so``, ``dll``, or ``dylib``) that can variously be called ``tidy``, ``libtidy``, or ``tidylib``, as well as a command-line executable named ``tidy``. For clarity, this document will consistently refer to it by the project name, HTML Tidy. `PyTidyLib`_ is the name of the Python package discussed here. As this is the package name, ``pip install pytidylib`` is correct (they are case-insenstive). The *module* name is ``tidylib``, so ``import tidylib`` is correct in Python code. This document will consistently use the package name, PyTidyLib, outside of code examples. Installing HTML Tidy ==================== You must have both `HTML Tidy`_ and `PyTidyLib`_ installed in order to use the functionality described here. There is no affiliation between the two projects. The following briefly outlines what you must do to install HTML Tidy. See the `HTML Tidy`_ web site for more information. **Linux/BSD or similar:** First, try to use your distribution's package management system (``apt-get``, ``yum``, etc.) to install HTML Tidy. It might go under the name ``libtidy``, ``tidylib``, ``tidy``, or something similar. Otherwise see *Building from Source*, below. **OS X:** You may already have HTML Tidy installed. In the Terminal, run ``locate libtidy`` and see if you get any results, which should end in ``dylib``. Otherwise see *Building from Source*, below. **Windows:** (Do not use pre-0.2.0 PyTidyLib.) You may be able to find prebuild DLLs. The DLL sources that were linked to in previous versions of this documentation have since gone 404 without obvious replacements. Once you have a DLL (which may be named ``tidy.dll``, ``libtidy.dll``, or ``tidylib.dll``), you must place it in a directory on your system path. If you are running Python from the command-line, placing the DLL in the present working directory will work, but this is unreliable otherwise (e.g. for server software). See the articles `How to set the path in Windows 2000/Windows XP `_ (ComputerHope.com) and `Modify a Users Path in Windows Vista `_ (Question Defense) for more information on your system path. **Building from Source:** The HTML Tidy developers have chosen to make the source code downloadable *only* through CVS, and not from the web site. Use the following CVS checkout at the command line:: cvs -z3 -d:pserver:anonymous@tidy.cvs.sourceforge.net:/cvsroot/tidy co -P tidy Then see the instructions packaged with the source code or on the `HTML Tidy`_ web site. Installing PyTidyLib ==================== PyTidyLib is available on the Python Package Index:: pip install pytidylib You can also download the latest source distribution from PyPI manually. Small example of use ==================== The following code cleans up an invalid HTML document and sets an option:: from tidylib import tidy_document document, errors = tidy_document('''

fõo ''', options={'numeric-entities':1}) print document print errors Configuration options ===================== The Python interface allows you to pass options directly to HTML Tidy. For a complete list of options, see the `HTML Tidy Configuration Options Quick Reference`_ or, from the command line, run ``tidy -help-config``. .. _`HTML Tidy Configuration Options Quick Reference`: http://tidy.sourceforge.net/docs/quickref.html This module sets certain default options, as follows:: BASE_OPTIONS = { "indent": 1, # Pretty; not too much of a performance hit "tidy-mark": 0, # No tidy meta tag in output "wrap": 0, # No wrapping "alt-text": "", # Help ensure validation "doctype": 'strict', # Little sense in transitional for tool-generated markup... "force-output": 1, # May not get what you expect but you will get something } If you do not like these options to be set for you, do the following after importing ``tidylib``:: tidylib.BASE_OPTIONS = {} Function reference ================== .. autofunction:: tidylib.tidy_document .. autofunction:: tidylib.tidy_fragment .. autofunction:: tidylib.release_tidy_doc .. _`HTML Tidy`: http://tidy.sourceforge.net/ .. _`PyTidyLib`: http://countergram.com/open-source/pytidylib/ pytidylib-0.3.2/LICENSE0000644000076500000240000000204012771050061014762 0ustar jasonstaff00000000000000Copyright 2009-2014 Jason Stitt Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. pytidylib-0.3.2/MANIFEST.in0000644000076500000240000000040512771050061015516 0ustar jasonstaff00000000000000include README include LICENSE include MANIFEST.in include tidylib/*.py include tests/*.py include *.py include docs/pytidylib.pdf include docs/html/*.html include docs/html/*.js include docs/html/_static/*.* include docs/html/_sources/*.* include docs/rst/*.* pytidylib-0.3.2/PKG-INFO0000644000076500000240000000552513012735764015076 0ustar jasonstaff00000000000000Metadata-Version: 1.1 Name: pytidylib Version: 0.3.2 Summary: Python wrapper for HTML Tidy (tidylib) on Python 2 and 3 Home-page: http://countergram.com/open-source/pytidylib/ Author: Jason Stitt Author-email: js@jasonstitt.com License: UNKNOWN Description: `PyTidyLib`_ is a Python package that wraps the `HTML Tidy`_ library. This allows you, from Python code, to "fix" invalid (X)HTML markup. Some of the library's many capabilities include: * Clean up unclosed tags and unescaped characters such as ampersands * Output HTML 4 or XHTML, strict or transitional, and add missing doctypes * Convert named entities to numeric entities, which can then be used in XML documents without an HTML doctype. * Clean up HTML from programs such as Word (to an extent) * Indent the output, including proper (i.e. no) indenting for ``pre`` elements, which some (X)HTML indenting code overlooks. Changes ======= * 0.3.2: Initialization bug fix * 0.3.1: find_library support while still allowing a list of library names * 0.3.0: Refactored to use Tidy and PersistentTidy classes while keeping the functional interface (which will lazily create a global Tidy() object) for backward compatibility. You can now pass a list of library names and base options when instantiating Tidy. The keep_doc argument is now deprecated and does nothing; use PersistentTidy. * 0.2.4: Bugfix for a strange memory allocation corner case in Tidy. * 0.2.3: Python 3 support (2 + 3 cross compatible) with passing Tox tests. Small example of use ==================== The following code cleans up an invalid HTML document and sets an option:: from tidylib import tidy_document document, errors = tidy_document('''

fõo ''', options={'numeric-entities':1}) print document print errors Docs ==== Documentation is shipped with the source distribution and is available at the `PyTidyLib`_ web page. .. _`HTML Tidy`: http://tidy.sourceforge.net/ .. _`PyTidyLib`: http://countergram.com/open-source/pytidylib/ Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Environment :: Other Environment Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: MIT License Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 3 Classifier: Natural Language :: English Classifier: Topic :: Utilities Classifier: Topic :: Text Processing :: Markup :: HTML Classifier: Topic :: Text Processing :: Markup :: XML pytidylib-0.3.2/README0000644000076500000240000000046012771050061014641 0ustar jasonstaff00000000000000This is a Python wrapper around the HTML Tidy library. Quick start example: from tidylib import Tidy tidy = Tidy() document, errors = tidy.tidy_document('

fõo ', options={'alt-text': 'baz'}) print(document) print(errors) For full documentation, see the docs/ directory. pytidylib-0.3.2/setup.py0000644000076500000240000000721713012735740015505 0ustar jasonstaff00000000000000# Copyright 2009-2015 Jason Stitt # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. from distutils.core import setup longdesc = """\ `PyTidyLib`_ is a Python package that wraps the `HTML Tidy`_ library. This allows you, from Python code, to "fix" invalid (X)HTML markup. Some of the library's many capabilities include: * Clean up unclosed tags and unescaped characters such as ampersands * Output HTML 4 or XHTML, strict or transitional, and add missing doctypes * Convert named entities to numeric entities, which can then be used in XML documents without an HTML doctype. * Clean up HTML from programs such as Word (to an extent) * Indent the output, including proper (i.e. no) indenting for ``pre`` elements, which some (X)HTML indenting code overlooks. Changes ======= * 0.3.2: Initialization bug fix * 0.3.1: find_library support while still allowing a list of library names * 0.3.0: Refactored to use Tidy and PersistentTidy classes while keeping the functional interface (which will lazily create a global Tidy() object) for backward compatibility. You can now pass a list of library names and base options when instantiating Tidy. The keep_doc argument is now deprecated and does nothing; use PersistentTidy. * 0.2.4: Bugfix for a strange memory allocation corner case in Tidy. * 0.2.3: Python 3 support (2 + 3 cross compatible) with passing Tox tests. Small example of use ==================== The following code cleans up an invalid HTML document and sets an option:: from tidylib import tidy_document document, errors = tidy_document('''

fõo ''', options={'numeric-entities':1}) print document print errors Docs ==== Documentation is shipped with the source distribution and is available at the `PyTidyLib`_ web page. .. _`HTML Tidy`: http://tidy.sourceforge.net/ .. _`PyTidyLib`: http://countergram.com/open-source/pytidylib/ """ VERSION = "0.3.2" setup( name="pytidylib", version=VERSION, description="Python wrapper for HTML Tidy (tidylib) on Python 2 and 3", long_description=longdesc, author="Jason Stitt", author_email="js@jasonstitt.com", url="http://countergram.com/open-source/pytidylib/", packages=['tidylib'], classifiers=[ 'Development Status :: 5 - Production/Stable', 'Environment :: Other Environment', 'Intended Audience :: Developers', 'License :: OSI Approved :: MIT License', 'Programming Language :: Python', 'Programming Language :: Python :: 3', 'Natural Language :: English', 'Topic :: Utilities', 'Topic :: Text Processing :: Markup :: HTML', 'Topic :: Text Processing :: Markup :: XML', ], ) pytidylib-0.3.2/tests/0000755000076500000240000000000013012735764015134 5ustar jasonstaff00000000000000pytidylib-0.3.2/tests/__init__.py0000644000076500000240000000000012771050061017222 0ustar jasonstaff00000000000000pytidylib-0.3.2/tests/test_docs.py0000644000076500000240000000767112773075420017507 0ustar jasonstaff00000000000000# -*- coding: utf-8 -*- # Copyright 2009-2014 Jason Stitt # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. from __future__ import unicode_literals import unittest from tidylib import Tidy, PersistentTidy, tidy_document DOC = u''' %s ''' class TestDocs1(unittest.TestCase): """ Test some sample documents """ def test_p_element_closed(self): h = "

hello" expected = DOC % '''

\n hello\n

''' doc, err = tidy_document(h) self.assertEqual(doc, expected) def test_alt_added_to_img(self): h = "" expected = DOC % '''bar''' doc, err = tidy_document(h, {'alt-text': 'bar'}) self.assertEqual(doc, expected) def test_entity_preserved_using_bytes(self): h = b"é" expected = (DOC % "é").encode('utf-8') doc, err = tidy_document(h, {'preserve-entities': 1}) self.assertEqual(doc, expected) def test_numeric_entities_using_bytes(self): h = b"é" expected = (DOC % "é").encode('utf-8') doc, err = tidy_document(h, {'numeric-entities': 1, 'output-encoding': 'ascii'}) self.assertEqual(doc, expected) def test_non_ascii_preserved(self): h = u"unicode string ß" expected = DOC % h doc, err = tidy_document(h) self.assertEqual(doc, expected) def test_large_document(self): h = u"A" * 10000 expected = DOC % h doc, err = tidy_document(h) self.assertEqual(doc, expected) def test_can_use_two_tidy_instances(self): t1 = Tidy() t2 = Tidy() self.assertEqual(t1.tidy_document(DOC % 'a')[0], DOC % 'a') self.assertEqual(t2.tidy_document(DOC % 'b')[0], DOC % 'b') def test_tidy_doesnt_persist_options(self): tidy = Tidy() # This option makes it a fragment doc, err = tidy.tidy_document(DOC % 'a', {'show-body-only': 1}) self.assertEqual(doc, 'a\n') doc, err = tidy.tidy_document(DOC % 'a') self.assertEqual(doc, DOC % 'a') def test_persistent_tidy_does_persist_options(self): tidy = PersistentTidy() # This option makes it a fragment doc, err = tidy.tidy_document(DOC % 'a', {'show-body-only': 1}) self.assertEqual(doc, 'a\n') doc, err = tidy.tidy_document(DOC % 'a') self.assertEqual(doc, 'a\n') def test_xmlns_large_document_xml_corner_case(self): # Test for a super weird edge case in Tidy that can cause it to return # the wrong required buffer size. body = 'A' + 'A' * 7937 html = '' + body doc, err = tidy_document(html, {'output-xml': 1}) self.assertEqual(doc.strip()[-7:], "") if __name__ == '__main__': unittest.main() pytidylib-0.3.2/tests/test_fragments.py0000644000076500000240000000445512773075420020542 0ustar jasonstaff00000000000000# -*- coding: utf-8 -*- # Copyright 2009-2014 Jason Stitt # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. from __future__ import unicode_literals import unittest from tidylib import tidy_fragment class TestFrags1(unittest.TestCase): """ Test some sample fragment documents """ def test_p_element_closed(self): h = "

hello" expected = '''

\n hello\n

''' doc, err = tidy_fragment(h) self.assertEqual(doc, expected) def test_alt_added_to_img(self): h = "" expected = '''bar''' doc, err = tidy_fragment(h, {'alt-text': 'bar'}) self.assertEqual(doc, expected) def test_entity_preserved_using_bytes(self): h = b"é" expected = b"é" doc, err = tidy_fragment(h, {'preserve-entities': 1}) self.assertEqual(doc, expected) def test_numeric_entities_using_bytes(self): h = b"é" expected = b"é" doc, err = tidy_fragment(h, {'numeric-entities': 1, 'output-encoding': 'ascii'}) self.assertEqual(doc, expected) def test_non_ascii_preserved(self): h = u"unicode string ß" expected = h doc, err = tidy_fragment(h) self.assertEqual(doc, expected) if __name__ == '__main__': unittest.main() pytidylib-0.3.2/tests/test_init.py0000644000076500000240000000254413012735312017502 0ustar jasonstaff00000000000000# -*- coding: utf-8 -*- # Copyright 2009-2016 Jason Stitt # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. from __future__ import unicode_literals import unittest from tidylib import Tidy, PersistentTidy, tidy_document class TestDocs1(unittest.TestCase): def test_not_find_lib(self): with self.assertRaises(OSError): tidy = Tidy(lib_names=[]) pytidylib-0.3.2/tests/test_memory.py0000644000076500000240000000324612771050061020051 0ustar jasonstaff00000000000000# Copyright 2009-2014 Jason Stitt # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. import unittest from tidylib import tidy_document, tidy_fragment, sink try: xrange except NameError: xrange = range class TestSinkMemory(unittest.TestCase): """ Make sure error sinks are cleared properly """ def test_tidy_document(self): h = "

hello" for i in xrange(100): doc, err = tidy_document(h) self.assertEqual(sink.sinks, {}) def test_tidy_fragment(self): h = "

hello" for i in xrange(100): doc, err = tidy_fragment(h) self.assertEqual(sink.sinks, {}) if __name__ == '__main__': unittest.main() pytidylib-0.3.2/tests/threadsafety.py0000644000076500000240000000374212771050061020166 0ustar jasonstaff00000000000000# Copyright 2009-2014 Jason Stitt # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. import threading from Queue import Queue from tidylib import tidy_document error_queue = Queue() DOC = ''' hello, world ''' SAMPLE = "hello, world" NUM_THREADS = 100 NUM_TRIES = 100 class TidyingThread(threading.Thread): def run(self): for x in xrange(NUM_TRIES): output, errors = tidy_document(SAMPLE, keep_doc=True) if output != DOC: error_queue.put(output) def run_test(): threads = [] for i in xrange(NUM_THREADS): t = TidyingThread() threads.append(t) t.start() for t in threads: t.join() if __name__ == '__main__': run_test() if not error_queue.empty(): print("About %s errors out of %s" % (error_queue.qsize(), NUM_THREADS * NUM_TRIES)) print(error_queue.get()) pytidylib-0.3.2/tidylib/0000755000076500000240000000000013012735764015432 5ustar jasonstaff00000000000000pytidylib-0.3.2/tidylib/__init__.py0000644000076500000240000000012712771050061017532 0ustar jasonstaff00000000000000from .tidy import Tidy, PersistentTidy, tidy_document, tidy_fragment, release_tidy_doc pytidylib-0.3.2/tidylib/sink.py0000644000076500000240000000700212771050061016736 0ustar jasonstaff00000000000000# Copyright 2009-2014 Jason Stitt # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. import ctypes import sys import threading import platform try: from cStringIO import StringIO except ImportError: try: from StringIO import StringIO except ImportError: from io import StringIO __all__ = ['Sink', 'create_sink', 'destroy_sink'] # -------------------------------------------------------------------------- # # Globals sinks = {} # of int: Sink last_sink_id = 0 sink_id_lock = threading.Lock() # -------------------------------------------------------------------------- # # ctypes type definitions # Fix for Windows b/c tidy uses stdcall on Windows if "Windows" == platform.system(): functype = ctypes.WINFUNCTYPE else: functype = ctypes.CFUNCTYPE PutByteType = functype(None, ctypes.c_int, ctypes.c_char) class TidyOutputSink(ctypes.Structure): """ Mirrors the _TidyOutputSink structure in tidy.h """ _fields_ = [ ('sinkData', ctypes.c_void_p), ('putByte', PutByteType) ] # -------------------------------------------------------------------------- # # Python interface class Sink(object): """ Represent a buffer to which Tidy writes errors with a callback function """ def __init__(self, sink_id): self.data = StringIO() self.sink_id = sink_id self.struct = TidyOutputSink() self.struct.sinkData = ctypes.cast( ctypes.pointer(ctypes.c_int(sink_id)), ctypes.c_void_p) # Windows fix write_func = self.data.write # Avoid 2 attr accesses per byte def put_byte(sink_id, byte): # We don't need sink_id because we have a separate put_byte # function for each sink write_func(byte.decode('utf-8')) self.struct.putByte = PutByteType(put_byte) self._as_parameter_ = ctypes.byref(self.struct) def __str__(self): return self.data.getvalue() def create_sink(): """ Return a new Sink with a numeric ID incremented in a threadsafe way """ global last_sink_id, sink_id_lock, sinks sink_id_lock.acquire() try: this_sink_id = last_sink_id last_sink_id = (last_sink_id + 1) % sys.maxsize # If you have more than maxint sinks open at a time, you're screwed finally: sink_id_lock.release() sink = Sink(this_sink_id) sinks[this_sink_id] = sink return sink def destroy_sink(sink): """ Free a Sink object by eliminating the reference from the global map """ global sinks del sinks[sink.sink_id] del sink pytidylib-0.3.2/tidylib/tidy.py0000644000076500000240000002205213012735506016750 0ustar jasonstaff00000000000000# Copyright 2009-2015 Jason Stitt # # Permission is hereby granted, free of charge, to any person obtaining a copy # of this software and associated documentation files (the "Software"), to deal # in the Software without restriction, including without limitation the rights # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell # copies of the Software, and to permit persons to whom the Software is # furnished to do so, subject to the following conditions: # # The above copyright notice and this permission notice shall be included in # all copies or substantial portions of the Software. # # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN # THE SOFTWARE. import ctypes import ctypes.util import threading import platform import warnings from contextlib import contextmanager from .sink import create_sink, destroy_sink __all__ = ['Tidy', 'PersistentTidy'] # Default search order for library names if nothing is passed in LIB_NAMES = ['libtidy', 'libtidy.so', 'libtidy-0.99.so.0', 'cygtidy-0-99-0', 'tidylib', 'libtidy.dylib', 'tidy'] # Error code from library ENOMEM = -12 # Default options; can be overriden with argument to Tidy() BASE_OPTIONS = { "indent": 1, # Pretty; not too much of a performance hit "tidy-mark": 0, # No tidy meta tag in output "wrap": 0, # No wrapping "alt-text": "", # Help ensure validation "doctype": 'strict', # Little sense in transitional for tool-generated markup... "force-output": 1, # May not get what you expect but you will get something } KEEP_DOC_WARNING = "keep_doc and release_tidy_doc are no longer used. Create a PersistentTidy object instead." # Fix for Windows b/c tidy uses stdcall on Windows if "Windows" == platform.system(): load_library = ctypes.windll.LoadLibrary else: load_library = ctypes.cdll.LoadLibrary # -------------------------------------------------------------------------- # # 3.x/2.x cross-compatibility try: unicode # 2.x def is_unicode(obj): return isinstance(obj, unicode) def encode_key_value(k, v): return unicode(k).encode('utf-8'), unicode(v).encode('utf-8') except NameError: # 3.x def is_unicode(obj): return isinstance(obj, str) def encode_key_value(k, v): return str(k).encode('utf-8'), str(v).encode('utf-8') # -------------------------------------------------------------------------- # # The main python interface class Tidy(object): """ Wrapper around the HTML Tidy library for cleaning up possibly invalid HTML and XHTML. """ def __init__(self, lib_names=None): self._tidy = None if lib_names is None: lib_names = ctypes.util.find_library('tidy') or LIB_NAMES if isinstance(lib_names, str): lib_names = [lib_names] for name in lib_names: try: self._tidy = load_library(name) break except OSError: continue if self._tidy is None: raise OSError( "Could not load libtidy using any of these names: " + ",".join(lib_names)) self._tidy.tidyCreate.restype = ctypes.POINTER(ctypes.c_void_p) # Fix for 64-bit systems @contextmanager def _doc_and_sink(self): " Create and cleanup a Tidy document and error sink " doc = self._tidy.tidyCreate() sink = create_sink() self._tidy.tidySetErrorSink(doc, sink) yield (doc, sink) destroy_sink(sink) self._tidy.tidyRelease(doc) def tidy_document(self, text, options=None): """ Run a string with markup through HTML Tidy; return the corrected one and any error output. text: The markup, which may be anything from an empty string to a complete (X)HTML document. If you pass in a unicode type (py3 str, py2 unicode) you get one back out, and tidy will have some options set that may affect behavior (e.g. named entities converted to plain unicode characters). If you pass in a bytes type (py3 bytes, py2 str) you will get one of those back. options (dict): Options passed directly to HTML Tidy; see the HTML Tidy docs (http://tidy.sourceforge.net/docs/quickref.html) or run tidy -help-config from the command line. returns (str, str): The tidied markup and unparsed warning/error messages. Warnings and errors are returned just as tidylib returns them. """ # Unicode approach is to encode as string, then decode libtidy output use_unicode = False if is_unicode(text): use_unicode = True text = text.encode('utf-8') with self._doc_and_sink() as (doc, sink): tidy_options = dict(BASE_OPTIONS) if options: tidy_options.update(options) if use_unicode: tidy_options['input-encoding'] = 'utf8' tidy_options['output-encoding'] = 'utf8' for key in tidy_options: value = tidy_options[key] key = key.replace('_', '-') if value is None: value = '' key, value = encode_key_value(key, value) self._tidy.tidyOptParseValue(doc, key, value) error = str(sink) if error: raise ValueError("(tidylib) " + error) self._tidy.tidyParseString(doc, text) self._tidy.tidyCleanAndRepair(doc) # Guess at buffer size; tidy returns ENOMEM if the buffer is too # small and puts the required size into out_length out_length = ctypes.c_int(8192) out = ctypes.c_buffer(out_length.value) while ENOMEM == self._tidy.tidySaveString(doc, out, ctypes.byref(out_length)): out = ctypes.c_buffer(out_length.value) document = out.value if use_unicode: document = document.decode('utf-8') errors = str(sink) return (document, errors) def tidy_fragment(self, text, options=None): """ Tidy a string with markup and return only the contents. HTML Tidy normally returns a full (X)HTML document; this function returns only the contents of the element and is meant to be used for snippets. Calling tidy_fragment on elements that don't go in the , like , will produce incorrect behavior. Arguments and return value are the same as tidy_document. Note that HTML Tidy will always complain about the lack of a doctype and <title> element in fragments, and these errors are not stripped out for you. """ options = dict(options) if options else dict() options["show-body-only"] = 1 document, errors = self.tidy_document(text, options) document = document.strip() return document, errors class PersistentTidy(Tidy): """ Functions the same as the Tidy class but keeps a persistent reference to one Tidy document object. This increases performance slightly when tidying many documents in a row. It also persists all options (not just the base options) between runs, which could lead to unexpected behavior. If you plan to use different options on each run with PersistentTidy, set all options that could change on every call. Note that passing in unicode text will result in the input-encoding and output-encoding options being automatically set. Thread-local storage is used for the document object (one document per thread). """ def __init__(self, lib_names=None): Tidy.__init__(self, lib_names) self._local = threading.local() self._local.doc = self._tidy.tidyCreate() def __del__(self): self._tidy.tidyRelease(self._local.doc) @contextmanager def _doc_and_sink(self): " Create and cleanup an error sink but use the persistent doc object " sink = create_sink() self._tidy.tidySetErrorSink(self._local.doc, sink) yield (self._local.doc, sink) destroy_sink(sink) def tidy_document(text, options=None, keep_doc=False): if keep_doc: warnings.warn(KEEP_DOC_WARNING, DeprecationWarning, stacklevel=2) return get_module_tidy().tidy_document(text, options) def tidy_fragment(text, options=None, keep_doc=False): if keep_doc: warnings.warn(KEEP_DOC_WARNING, DeprecationWarning, stacklevel=2) return get_module_tidy().tidy_fragment(text, options) def get_module_tidy(): global _tidy if '_tidy' not in globals(): _tidy = Tidy() return _tidy def release_tidy_doc(): warnings.warn(KEEP_DOC_WARNING, DeprecationWarning, stacklevel=2) ��������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������������