diff options
| author | Sébastien Dailly <sebastien@chimrod.com> | 2014-11-15 21:22:30 +0100 | 
|---|---|---|
| committer | Sébastien Dailly <sebastien@chimrod.com> | 2014-11-15 21:22:30 +0100 | 
| commit | 237ae081dbade817f4a2033b6aa2d3cdeb15b8b2 (patch) | |
| tree | b430183ccc7e5361c33e22184658f84cdb25f4f7 /plugins/typogrify/titlecase | |
| parent | 522cef8dcb4c5b68646d0862545e7e381f539257 (diff) | |
Moved typogrify as plugin
Diffstat (limited to 'plugins/typogrify/titlecase')
| -rwxr-xr-x | plugins/typogrify/titlecase/__init__.py | 101 | ||||
| -rw-r--r-- | plugins/typogrify/titlecase/tests.py | 174 | 
2 files changed, 275 insertions, 0 deletions
diff --git a/plugins/typogrify/titlecase/__init__.py b/plugins/typogrify/titlecase/__init__.py new file mode 100755 index 0000000..aeaca97 --- /dev/null +++ b/plugins/typogrify/titlecase/__init__.py @@ -0,0 +1,101 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# titlecase v0.5.1 +# Copyright (C) 2008-2010, Stuart Colville. +# https://pypi.python.org/pypi/titlecase + +""" +Original Perl version by: John Gruber http://daringfireball.net/ 10 May 2008 +Python version by Stuart Colville http://muffinresearch.co.uk +License: http://www.opensource.org/licenses/mit-license.php +""" + +import re + +__all__ = ['titlecase'] +__version__ = '0.5.1' + +SMALL = 'a|an|and|as|at|but|by|en|for|if|in|of|on|or|the|to|v\.?|via|vs\.?' +PUNCT = r"""!"#$%&'‘()*+,\-./:;?@[\\\]_`{|}~""" + +SMALL_WORDS = re.compile(r'^(%s)$' % SMALL, re.I) +INLINE_PERIOD = re.compile(r'[a-z][.][a-z]', re.I) +UC_ELSEWHERE = re.compile(r'[%s]*?[a-zA-Z]+[A-Z]+?' % PUNCT) +CAPFIRST = re.compile(r"^[%s]*?([A-Za-z])" % PUNCT) +SMALL_FIRST = re.compile(r'^([%s]*)(%s)\b' % (PUNCT, SMALL), re.I) +SMALL_LAST = re.compile(r'\b(%s)[%s]?$' % (SMALL, PUNCT), re.I) +SUBPHRASE = re.compile(r'([:.;?!][ ])(%s)' % SMALL) +APOS_SECOND = re.compile(r"^[dol]{1}['‘]{1}[a-z]+$", re.I) +ALL_CAPS = re.compile(r'^[A-Z\s%s]+$' % PUNCT) +UC_INITIALS = re.compile(r"^(?:[A-Z]{1}\.{1}|[A-Z]{1}\.{1}[A-Z]{1})+$") +MAC_MC = re.compile(r"^([Mm]a?c)(\w+)") + +def titlecase(text): + +    """ +    Titlecases input text + +    This filter changes all words to Title Caps, and attempts to be clever +    about *un*capitalizing SMALL words like a/an/the in the input. + +    The list of "SMALL words" which are not capped comes from +    the New York Times Manual of Style, plus 'vs' and 'v'. + +    """ + +    lines = re.split('[\r\n]+', text) +    processed = [] +    for line in lines: +        all_caps = ALL_CAPS.match(line) +        words = re.split('[\t ]', line) +        tc_line = [] +        for word in words: +            if all_caps: +                if UC_INITIALS.match(word): +                    tc_line.append(word) +                    continue +                else: +                    word = word.lower() + +            if APOS_SECOND.match(word): +                word = word.replace(word[0], word[0].upper()) +                word = word.replace(word[2], word[2].upper()) +                tc_line.append(word) +                continue +            if INLINE_PERIOD.search(word) or UC_ELSEWHERE.match(word): +                tc_line.append(word) +                continue +            if SMALL_WORDS.match(word): +                tc_line.append(word.lower()) +                continue + +            match = MAC_MC.match(word) +            if match: +                tc_line.append("%s%s" % (match.group(1).capitalize(), +                                      match.group(2).capitalize())) +                continue + +            hyphenated = [] +            for item in word.split('-'): +                hyphenated.append(CAPFIRST.sub(lambda m: m.group(0).upper(), item)) +            tc_line.append("-".join(hyphenated)) + + +        result = " ".join(tc_line) + +        result = SMALL_FIRST.sub(lambda m: '%s%s' % ( +            m.group(1), +            m.group(2).capitalize() +        ), result) + +        result = SMALL_LAST.sub(lambda m: m.group(0).capitalize(), result) + +        result = SUBPHRASE.sub(lambda m: '%s%s' % ( +            m.group(1), +            m.group(2).capitalize() +        ), result) + +        processed.append(result) + +    return "\n".join(processed) + diff --git a/plugins/typogrify/titlecase/tests.py b/plugins/typogrify/titlecase/tests.py new file mode 100644 index 0000000..97a45e4 --- /dev/null +++ b/plugins/typogrify/titlecase/tests.py @@ -0,0 +1,174 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- + +"""Tests for titlecase""" + + +import os +import sys +sys.path.insert(0, os.path.join(os.path.dirname(__file__), '../')) + +from titlecase import titlecase + +TEST_DATA = ( +    ( +        "Q&A with steve jobs: 'that's what happens in technology'", +        "Q&A With Steve Jobs: 'That's What Happens in Technology'" +    ), +    ( +        "What is AT&T's problem?", +        "What Is AT&T's Problem?" +    ), +    ( +        "Apple deal with AT&T falls through", +        "Apple Deal With AT&T Falls Through" +    ), +    ( +        "this v that", +        "This v That" +    ), +    ( +        "this v. that", +        "This v. That" +    ), +    ( +        "this vs that", +        "This vs That" +    ), +    ( +        "this vs. that", +        "This vs. That" +    ), +    ( +        "The SEC's Apple probe: what you need to know", +        "The SEC's Apple Probe: What You Need to Know" +    ), +    ( +        "'by the Way, small word at the start but within quotes.'", +        "'By the Way, Small Word at the Start but Within Quotes.'" +    ), +    ( +        "Small word at end is nothing to be afraid of", +        "Small Word at End Is Nothing to Be Afraid Of" +    ), +    ( +        "Starting Sub-Phrase With a Small Word: a Trick, Perhaps?", +        "Starting Sub-Phrase With a Small Word: A Trick, Perhaps?" +    ), +    (     +        "Sub-Phrase With a Small Word in Quotes: 'a Trick, Perhaps?'", +        "Sub-Phrase With a Small Word in Quotes: 'A Trick, Perhaps?'" +    ), +    ( +        'sub-phrase with a small word in quotes: "a trick, perhaps?"', +        'Sub-Phrase With a Small Word in Quotes: "A Trick, Perhaps?"' +    ), +    ( +        '"Nothing to Be Afraid of?"', +        '"Nothing to Be Afraid Of?"' +    ), +    ( +        '"Nothing to be Afraid Of?"', +        '"Nothing to Be Afraid Of?"'     +    ), +    (    +        'a thing', +        'A Thing' +    ), +    ( +        "2lmc Spool: 'gruber on OmniFocus and vapo(u)rware'", +        "2lmc Spool: 'Gruber on OmniFocus and Vapo(u)rware'" +    ), +    ( +        'this is just an example.com', +        'This Is Just an example.com' +    ), +    ( +        'this is something listed on del.icio.us', +        'This Is Something Listed on del.icio.us' +    ), +    ( +        'iTunes should be unmolested', +        'iTunes Should Be Unmolested' +    ), +    ( +        'reading between the lines of steve jobs’s ‘thoughts on music’', +        'Reading Between the Lines of Steve Jobs’s ‘Thoughts on Music’' +    ), +    ( +        'seriously, ‘repair permissions’ is voodoo', +        'Seriously, ‘Repair Permissions’ Is Voodoo' +    ), +    ( +        'generalissimo francisco franco: still dead; kieren McCarthy: still a jackass', +        'Generalissimo Francisco Franco: Still Dead; Kieren McCarthy: Still a Jackass' +    ), +    ( +        "O'Reilly should be untouched", +        "O'Reilly Should Be Untouched" +    ), +    ( +        "my name is o'reilly", +        "My Name Is O'Reilly" +    ), +    ( +        "WASHINGTON, D.C. SHOULD BE FIXED BUT MIGHT BE A PROBLEM", +        "Washington, D.C. Should Be Fixed but Might Be a Problem" +    ), +    ( +        "THIS IS ALL CAPS AND SHOULD BE ADDRESSED", +        "This Is All Caps and Should Be Addressed" +    ), +    ( +        "Mr McTavish went to MacDonalds", +        "Mr McTavish Went to MacDonalds" +    ), +    ( +        "this shouldn't\nget mangled", +        "This Shouldn't\nGet Mangled" +    ), +    (  +        "this is http://foo.com", +        "This Is http://foo.com" +    ) +) + +def test_all_caps_regex(): +    """Test - all capitals regex""" +    from titlecase import ALL_CAPS +    assert bool(ALL_CAPS.match('THIS IS ALL CAPS')) is True + +def test_initials_regex(): +    """Test - uppercase initals regex with A.B""" +    from titlecase import UC_INITIALS +    assert bool(UC_INITIALS.match('A.B')) is True + +def test_initials_regex_2(): +    """Test - uppercase initals regex with A.B.""" +    from titlecase import UC_INITIALS +    assert bool(UC_INITIALS.match('A.B.')) is True + +def test_initials_regex_3(): +    """Test - uppercase initals regex with ABCD""" +    from titlecase import UC_INITIALS +    assert bool(UC_INITIALS.match('ABCD')) is False + +def check_input_matches_expected_output(in_, out): +    """Function yielded by test generator""" +    try : +        assert  titlecase(in_) == out +    except AssertionError: +        print("%s != %s" % (titlecase(in_), out)) +        raise + + +def test_input_output(): +    """Generated tests""" +    for data in TEST_DATA: +        yield check_input_matches_expected_output, data[0], data[1] +        + +if __name__ == "__main__": +    import nose +    nose.main() +  | 
