diff --git a/app.py b/app.py index c8e188d28..f0d2ca18f 100644 --- a/app.py +++ b/app.py @@ -1,3 +1,5 @@ +"""Web app that serves proselint's API.""" + from flask import Flask, request, make_response, current_app import subprocess import uuid @@ -13,6 +15,7 @@ def crossdomain(origin=None, methods=None, headers=None, max_age=21600, attach_to_all=True, automatic_options=True): + """Allow cross-domain requests.""" if methods is not None: methods = ', '.join(sorted(x.upper() for x in methods)) if headers is not None and not isinstance(headers, basestring): @@ -54,9 +57,10 @@ def wrapped_function(*args, **kwargs): @app.route('/v1/', methods=["GET"]) -@crossdomain(origin='*', headers=['Origin, X-Requested-With, Content-Type, Accept']) +@crossdomain( + origin='*', headers=['Origin, X-Requested-With, Content-Type, Accept']) def lint(): - + """Run proselint on the provided text and return the results.""" id = uuid.uuid4() filename = os.path.join("tmp", "{}.md".format(id)) diff --git a/demo.md b/demo.md index ded6bc152..d073e3dcb 100644 --- a/demo.md +++ b/demo.md @@ -1,11 +1,11 @@ # Consistency -This is a sentence. One space after a period. -This is a sentence. Two spaces after a period. -This is a sentence. Two spaces after a period. -This is a sentence. One space after a period. -This is a sentence. One space after a period. -This is a sentence. One space after a period. -This is a sentence. One space after a period. +This is a sentence. One space after a period. +This is a sentence. Two spaces after a period. +This is a sentence. Two spaces after a period. +This is a sentence. One space after a period. +This is a sentence. One space after a period. +This is a sentence. One space after a period. +This is a sentence. One space after a period. centre centre center organize, organized, organizing, organise recognize, recognise, recognise, recognise @@ -102,3 +102,6 @@ Copy­right © 2015 (R) The Corporation This is so awesome... Bose-Einstein condensate + +# Denizen labels +He's a Michiganite diff --git a/proselint/__init__.py b/proselint/__init__.py index e69de29bb..9cd43158a 100644 --- a/proselint/__init__.py +++ b/proselint/__init__.py @@ -0,0 +1 @@ +"""Proselint applies advice from great writers to your writing.""" diff --git a/proselint/checks/__init__.py b/proselint/checks/__init__.py index e69de29bb..a568e93b2 100644 --- a/proselint/checks/__init__.py +++ b/proselint/checks/__init__.py @@ -0,0 +1 @@ +"""All the checks are organized into modules and places here.""" diff --git a/proselint/checks/butterick/__init__.py b/proselint/checks/butterick/__init__.py index e69de29bb..2aa8c9979 100644 --- a/proselint/checks/butterick/__init__.py +++ b/proselint/checks/butterick/__init__.py @@ -0,0 +1 @@ +"""Advice from Butterick's Practical Typography.""" diff --git a/proselint/checks/butterick/symbols.py b/proselint/checks/butterick/symbols.py index 8eb10d194..f9a81a435 100644 --- a/proselint/checks/butterick/symbols.py +++ b/proselint/checks/butterick/symbols.py @@ -20,17 +20,24 @@ @memoize def check(text): - + """Check the text.""" err = "BTR100" symbols = [ - ["\s\(c\)\s", u"'{}' is a goofy alphabetic approximation. Use ©."], - ["\s\(TM\)\s", u"'{}' is a goofy alphabetic approximation. Use ™."], - ["\s\(R\)\s", u"'{}' is a goofy alphabetic approximation. Use ®."], - [u"[Cc]opy­right ©", u"'{}' is redundant. Use the word or the symbol."], - [r"\.\.\.", u"'...' is an approximation, use the ellipsis symbol '…'."], - [u"[A-Z][a-z]{1,10}[-\u2014][A-Z][a-z]{1,10}", u"Use an en dash (–) to separate names."], - ["\. {3}", "More than two spaces after the period; use 1 or 2."], + ["\s\(c\)\s", + u"'{}' is a goofy alphabetic approximation. Use ©."], + ["\s\(TM\)\s", + u"'{}' is a goofy alphabetic approximation. Use ™."], + ["\s\(R\)\s", + u"'{}' is a goofy alphabetic approximation. Use ®."], + [u"[Cc]opy­right ©", + u"'{}' is redundant. Use the word or the symbol."], + [r"\.\.\.", + u"'...' is an approximation, use the ellipsis symbol '…'."], + [u"[A-Z][a-z]{1,10}[-\u2014][A-Z][a-z]{1,10}", + u"Use an en dash (–) to separate names."], + ["\. {3}", + "More than two spaces after the period; use 1 or 2."], ] errors = [] diff --git a/proselint/checks/consistency/__init__.py b/proselint/checks/consistency/__init__.py index e69de29bb..902e36d94 100644 --- a/proselint/checks/consistency/__init__.py +++ b/proselint/checks/consistency/__init__.py @@ -0,0 +1 @@ +"""Various consistency checks.""" diff --git a/proselint/checks/consistency/spacing.py b/proselint/checks/consistency/spacing.py index faa9757c1..db7a65b64 100644 --- a/proselint/checks/consistency/spacing.py +++ b/proselint/checks/consistency/spacing.py @@ -20,8 +20,9 @@ @memoize def check(text): - + """Check the text.""" err = "CST200" msg = "Inconsistent spacing after period (1 vs. 2 spaces)." - return consistency_check(text, [["[^\w\s] [A-Z]", "[^\w\s] [A-Z]"]], err, msg) + regex = "[^\w\s] [A-Z]", "[^\w\s] [A-Z]" + return consistency_check(text, [regex], err, msg) diff --git a/proselint/checks/consistency/spelling.py b/proselint/checks/consistency/spelling.py index 21333c993..8c0a6a217 100644 --- a/proselint/checks/consistency/spelling.py +++ b/proselint/checks/consistency/spelling.py @@ -26,7 +26,7 @@ @memoize def check(text): - + """Check the text.""" err = "IEL100" msg = "Inconsistent spelling of '{}' (vs. '{}')." diff --git a/proselint/checks/example/__init__.py b/proselint/checks/example/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/proselint/checks/garner/__init__.py b/proselint/checks/garner/__init__.py index e69de29bb..ae2ec6164 100644 --- a/proselint/checks/garner/__init__.py +++ b/proselint/checks/garner/__init__.py @@ -0,0 +1 @@ +"""Advice from Garner's Modern Ameican Usage.""" diff --git a/proselint/checks/garner/a_vs_an.py b/proselint/checks/garner/a_vs_an.py index 92319db22..17ce5aff0 100644 --- a/proselint/checks/garner/a_vs_an.py +++ b/proselint/checks/garner/a_vs_an.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -"""MAU100: Misuse of 'a' vs. 'an'. +u"""MAU100: Misuse of 'a' vs. 'an'. --- layout: post @@ -21,7 +21,7 @@ @memoize def check(text): - + """Define the check.""" err = "MAU101" msg_a = "'a' should be 'an'." msg_an = "'an' should be 'a'." @@ -52,7 +52,6 @@ def check(text): @memoize def starts_with_vowel_sound(word): """Check whether the word starts with a vowel sound.""" - # Get the pronunciations of the word. if 'd' not in globals(): import nltk @@ -80,6 +79,7 @@ def starts_with_vowel_sound(word): def initialize(): + """Initialize the cache of pronunciations.""" print "Running initialization for garner.a_vs_an (may take a few minutes)" from nltk.corpus import cmudict diff --git a/proselint/checks/garner/airlinese.py b/proselint/checks/garner/airlinese.py index abf7387bf..f1c11bd56 100644 --- a/proselint/checks/garner/airlinese.py +++ b/proselint/checks/garner/airlinese.py @@ -14,12 +14,12 @@ Airlinese. """ -from proselint.tools import blacklist, memoize +from proselint.tools import existence_check, memoize @memoize def check(text): - + """Check the text.""" err = "MAU108" msg = u"'{}' is airlinese." @@ -28,4 +28,4 @@ def check(text): "momentarily", ] - return blacklist(text, airlinese, err, msg) + return existence_check(text, airlinese, err, msg) diff --git a/proselint/checks/garner/archaism.py b/proselint/checks/garner/archaism.py index 8dcae64e7..5f4f05bdb 100644 --- a/proselint/checks/garner/archaism.py +++ b/proselint/checks/garner/archaism.py @@ -14,12 +14,12 @@ Archaism. """ -from proselint.tools import blacklist, memoize +from proselint.tools import existence_check, memoize @memoize def check(text): - + """Check the text.""" err = "MAU103" msg = u"'{}' is archaic." @@ -78,7 +78,8 @@ def check(text): # "demean", when used to mean "to behave" in legal contexts # "by the bye", # variant, modern is "by the by" # "comptroller" # in british english - # "abortive" Abortive is archaic in reference to abortions of fetuses, except in the sense “causing an abortion.” + # "abortive" Abortive is archaic in reference to abortions of fetuses, + # except in the sense “causing an abortion.” ] - return blacklist(text, archaisms, err, msg) + return existence_check(text, archaisms, err, msg) diff --git a/proselint/checks/garner/cliches.py b/proselint/checks/garner/cliches.py index c4bf34167..f1d1de0b8 100644 --- a/proselint/checks/garner/cliches.py +++ b/proselint/checks/garner/cliches.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -"""MAU100: Cliches. +u"""MAU100: Cliches. --- layout: post @@ -20,7 +20,7 @@ @memoize def check(text): - + """Check the text.""" err = "MAU101" msg = u"'{}' is cliché." diff --git a/proselint/checks/garner/commercialese.py b/proselint/checks/garner/commercialese.py index 97f4b2972..8a5071174 100644 --- a/proselint/checks/garner/commercialese.py +++ b/proselint/checks/garner/commercialese.py @@ -14,12 +14,12 @@ Archaism. """ -from proselint.tools import blacklist, memoize +from proselint.tools import existence_check, memoize @memoize def check(text): - + """Check the text.""" err = "MAU104" msg = u"'{}' is commercialese." @@ -51,4 +51,4 @@ def check(text): "yours of even date" ] - return blacklist(text, commercialese, err, msg) + return existence_check(text, commercialese, err, msg) diff --git a/proselint/checks/garner/denizen_labels.py b/proselint/checks/garner/denizen_labels.py index b59053466..2b5c07c26 100644 --- a/proselint/checks/garner/denizen_labels.py +++ b/proselint/checks/garner/denizen_labels.py @@ -14,8 +14,7 @@ Denizen labels. """ -import re -from proselint.tools import memoize +from proselint.tools import memoize, existence_check @memoize @@ -32,8 +31,5 @@ def check(text): ] errors = [] for p in preferences: - for r in p[1]: - for m in re.finditer("\s{}\s".format(r), text, flags=re.IGNORECASE): - errors.append((m.start()+1, m.end(), err, msg.format(p[0]))) - + errors += existence_check(text, p[1], err, msg) return errors diff --git a/proselint/checks/garner/illogic.py b/proselint/checks/garner/illogic.py index 8c9ddf3f8..82f1f480c 100644 --- a/proselint/checks/garner/illogic.py +++ b/proselint/checks/garner/illogic.py @@ -14,12 +14,12 @@ Archaism. """ -from proselint.tools import blacklist, memoize +from proselint.tools import existence_check, memoize @memoize def check(text): - + """Check the text.""" err = "MAU105" msg = u"'{}' is illogical." @@ -34,4 +34,4 @@ def check(text): "no longer requires oxygen", ] - return blacklist(text, illogics, err, msg) + return existence_check(text, illogics, err, msg) diff --git a/proselint/checks/garner/preferred_forms.py b/proselint/checks/garner/preferred_forms.py index f80c521d1..6b6ea8f1b 100644 --- a/proselint/checks/garner/preferred_forms.py +++ b/proselint/checks/garner/preferred_forms.py @@ -160,8 +160,7 @@ def check(text): errors = [] for p in preferences: for r in p[1]: - for m in re.finditer("\s{}\s".format(r), text, flags=re.IGNORECASE): - + for m in re.finditer("\s{}\s".format(r), text, flags=re.I): errors.append((m.start()+1, m.end(), err, msg.format(p[0]))) return errors diff --git a/proselint/checks/inprogress/er_vs_or.py b/proselint/checks/inprogress/er_vs_or.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/proselint/checks/inprogress/example_check.py b/proselint/checks/inprogress/example_check.py index 3dbac995b..b4367d24b 100644 --- a/proselint/checks/inprogress/example_check.py +++ b/proselint/checks/inprogress/example_check.py @@ -16,11 +16,12 @@ """ from proselint.tools import reverse -def check(text): - - reversed_text = reverse(text) +def check(text): + """Check the text.""" error_code = "PL000" msg = "First line always has an error." + reverse(text) + return [(1, 1, error_code, msg)] diff --git a/proselint/checks/inprogress/hyphenation.py b/proselint/checks/inprogress/hyphenation.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/proselint/checks/inprogress/needless_variants.py b/proselint/checks/inprogress/needless_variants.py deleted file mode 100644 index 39cdd0ded..000000000 --- a/proselint/checks/inprogress/needless_variants.py +++ /dev/null @@ -1 +0,0 @@ -- diff --git a/proselint/checks/misc/__init__.py b/proselint/checks/misc/__init__.py index e69de29bb..c291a5abe 100644 --- a/proselint/checks/misc/__init__.py +++ b/proselint/checks/misc/__init__.py @@ -0,0 +1 @@ +"""Miscellaneous advice not otherwise categorized.""" diff --git a/proselint/checks/misc/annotations.py b/proselint/checks/misc/annotations.py index 4e2015465..4aad069f9 100644 --- a/proselint/checks/misc/annotations.py +++ b/proselint/checks/misc/annotations.py @@ -14,12 +14,12 @@ Annotation left in text. """ -from proselint.tools import blacklist, memoize +from proselint.tools import existence_check, memoize @memoize def check(text): - + """Check the text.""" err = "ANN100" msg = u"Annotation left in text." @@ -32,4 +32,4 @@ def check(text): "FIX THIS", ] - return blacklist(text, annotations, err, msg, ignore_case=False) + return existence_check(text, annotations, err, msg, ignore_case=False) diff --git a/proselint/checks/misc/chatspeak.py b/proselint/checks/misc/chatspeak.py index 8065e686a..078174ffa 100644 --- a/proselint/checks/misc/chatspeak.py +++ b/proselint/checks/misc/chatspeak.py @@ -14,12 +14,12 @@ Chatspeak. """ -from proselint.tools import blacklist, memoize +from proselint.tools import existence_check, memoize @memoize def check(text): - + """Check the text.""" err = "MSC104" msg = u"'{}' is chatspeak. Write it out." @@ -49,4 +49,4 @@ def check(text): "XOXO" ] - return blacklist(text, words, err, msg) + return existence_check(text, words, err, msg) diff --git a/proselint/checks/misc/creditcard.py b/proselint/checks/misc/creditcard.py index e153c7c03..2bbe7f130 100644 --- a/proselint/checks/misc/creditcard.py +++ b/proselint/checks/misc/creditcard.py @@ -14,12 +14,12 @@ Credit card number printed. """ -from proselint.tools import blacklist, memoize +from proselint.tools import existence_check, memoize @memoize def check(text): - + """Check the text.""" err = "MSC102" msg = u"Don't put credit card numbers in plain text." @@ -31,4 +31,4 @@ def check(text): "6011\d{12}", ] - return blacklist(text, credit_card_numbers, err, msg) + return existence_check(text, credit_card_numbers, err, msg) diff --git a/proselint/checks/misc/symbols.py b/proselint/checks/misc/currency.py similarity index 71% rename from proselint/checks/misc/symbols.py rename to proselint/checks/misc/currency.py index bcfc209bc..d0826f843 100644 --- a/proselint/checks/misc/symbols.py +++ b/proselint/checks/misc/currency.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -"""MSC110: Symbols. +"""MSC110: Currency. --- layout: post @@ -14,12 +14,12 @@ Symbols. """ -from proselint.tools import blacklist, memoize +from proselint.tools import existence_check, memoize @memoize def check(text): - + """Check the text.""" err = "MSC110" msg = u"Incorrent use of symbols in {}." @@ -27,4 +27,4 @@ def check(text): "\$[\d]* ?(?:dollars|usd|us dollars)" ] - return blacklist(text, symbols, err, msg) + return existence_check(text, symbols, err, msg) diff --git a/proselint/checks/misc/hyperbolic.py b/proselint/checks/misc/hyperbolic.py index 8a56b3998..cf22bdf09 100644 --- a/proselint/checks/misc/hyperbolic.py +++ b/proselint/checks/misc/hyperbolic.py @@ -14,12 +14,12 @@ Hyperbolic language. """ -from proselint.tools import blacklist, memoize +from proselint.tools import existence_check, memoize @memoize def check(text): - + """Check the text.""" err = "MSC100" msg = u"'{}' is hyperbolic." @@ -28,4 +28,4 @@ def check(text): "[a-z]*\?{2,}" ] - return blacklist(text, words, err, msg) + return existence_check(text, words, err, msg) diff --git a/proselint/checks/misc/password.py b/proselint/checks/misc/password.py index ef7beed8a..13a5a1118 100644 --- a/proselint/checks/misc/password.py +++ b/proselint/checks/misc/password.py @@ -13,11 +13,12 @@ Don't put pass """ -from proselint.tools import blacklist, memoize +from proselint.tools import existence_check, memoize @memoize def check(text): + """Check the text.""" err = "MSC103" msg = u"Don't put passwords in plain text." @@ -30,4 +31,4 @@ def check(text): "my password's{}".format(pwd_regex), ] - return blacklist(text, password, err, msg) + return existence_check(text, password, err, msg) diff --git a/proselint/checks/misc/yelling.py b/proselint/checks/misc/yelling.py index 84d77000f..662fc1cad 100644 --- a/proselint/checks/misc/yelling.py +++ b/proselint/checks/misc/yelling.py @@ -14,13 +14,14 @@ Too much yelling. """ -from proselint.tools import blacklist, memoize +from proselint.tools import existence_check, memoize @memoize def check(text): - + """Check the text.""" err = "MAU103" msg = u"Too much yelling." - return blacklist(text, ["[A-Z]+ [A-Z]+ [A-Z]+"], err, msg, ignore_case=False) + regex = "[A-Z]+ [A-Z]+ [A-Z]+" + return existence_check(text, [regex], err, msg, ignore_case=False) diff --git a/proselint/checks/orwell/__init__.py b/proselint/checks/orwell/__init__.py index e69de29bb..e45b51ccd 100644 --- a/proselint/checks/orwell/__init__.py +++ b/proselint/checks/orwell/__init__.py @@ -0,0 +1 @@ +"""Advice from George Orwell.""" diff --git a/proselint/checks/pinker/__init__.py b/proselint/checks/pinker/__init__.py index e69de29bb..938fd2791 100644 --- a/proselint/checks/pinker/__init__.py +++ b/proselint/checks/pinker/__init__.py @@ -0,0 +1 @@ +"""Advice from Steve Pinker.""" diff --git a/proselint/checks/strunkwhite/__init__.py b/proselint/checks/strunkwhite/__init__.py index e69de29bb..d5a04b020 100644 --- a/proselint/checks/strunkwhite/__init__.py +++ b/proselint/checks/strunkwhite/__init__.py @@ -0,0 +1 @@ +"""Advice from Strunk & White.""" diff --git a/proselint/checks/strunkwhite/elementary_composition.py b/proselint/checks/strunkwhite/elementary_composition.py index a7f6a08ad..f8f0c4645 100644 --- a/proselint/checks/strunkwhite/elementary_composition.py +++ b/proselint/checks/strunkwhite/elementary_composition.py @@ -14,11 +14,14 @@ 1. Choose a suitable design and hold to it. * MDPNB: Sounds like a principle of `consistency`. 2. Make the paragraph the unit of composition. - * MDPNB: This can be generalized to say something about variability in the length of paragraphs and sentences. When any device is too often used it becomes a mannerism. + * MDPNB: This can be generalized to say something about variability in the + length of paragraphs and sentences. When any device is too often used it + becomes a mannerism. * MDPNB: Sounds like a principle of `variation`. 3. Use the active voice. 4. Put statements in positive form. - * MDPNB: In some cases this will apply as an invective against the use of a double negative. + * MDPNB: In some cases this will apply as an invective against the use of + a double negative. * Ex: He was not very often on time. -> He usually came late. * Ex: 4.1. Placing negative and positive in oposition makes for a stronger structure. @@ -45,21 +48,21 @@ from proselint.tools import memoize import re -# recomment when done -# @memoize + +@memoize def check(text): """Suggest the preferred forms.""" err = "STW102" msg = "'{}' is better than '{}'." bad_forms = [ - # Put statements in positive form + # Put statements in positive form ["dishonest", ["not honest"]], ["trifling", ["not important"]], ["forgot", ["did not remember"]], ["ignored", ["did not pay (any )?attention to"]], ["distrusted", ["did not have much confidence in"]], - # Omit needless words + # Omit needless words ["whether", ["the question as to whether"]], ["no doubt", ["there is no doubt but that"]], ["used for fuel", ["used for fuel purposes"]], @@ -81,8 +84,8 @@ def check(text): errors = [] for p in bad_forms: for r in p[1]: - for m in re.finditer("\s{}\s".format(r), text, flags=re.IGNORECASE): - - errors.append((m.start()+1, m.end(), err, msg.format(p[0], m.group(0)))) + for m in re.finditer("\s{}\s".format(r), text, flags=re.I): + e = (m.start()+1, m.end(), err, msg.format(p[0], m.group(0))) + errors.append(e) return errors diff --git a/proselint/checks/strunkwhite/elementary_usage.py b/proselint/checks/strunkwhite/elementary_usage.py index db6b79ee6..094f71085 100644 --- a/proselint/checks/strunkwhite/elementary_usage.py +++ b/proselint/checks/strunkwhite/elementary_usage.py @@ -12,20 +12,26 @@ Strunk & White say: 1. Form the possesive singular of nouns by adding 's. -2. In a series of three or more terms with a conjunction, use a comma after each term except the last. -3. Enclose parenthetic expressions between commas. ("This rule is difficult to apply.") +2. In a series of three or more terms with a conjunction, use a comma after +each term except the last. +3. Enclose parenthetic expressions between commas. ("This rule is difficult to +apply.") 4. Place a comma before a conjunction introducing an independent clause. 5. Do not join independent clauses with a comma; use a semi-colon. Or a period. 6. Do not break sentences in two. Do not use periods for the role of commas. -7. Use a colon after an independent clause if you introduce: a list of particulars, an appositive, an application, or an illustative quotation. -8. Use a dash to set off an aburpt break or interruption and to announce a long appositive or summary. -9. The number of the subject determines the number of the verb. (MDPNB: This will require nltk & syntactic parsing) +7. Use a colon after an independent clause if you introduce: a list of +particulars, an appositive, an application, or an illustative quotation. +8. Use a dash to set off an aburpt break or interruption and to announce a +long appositive or summary. +9. The number of the subject determines the number of the verb. (MDPNB: This +will require nltk & syntactic parsing) 10. Use the proper case(grammatical gender) of pronouns. - * MDPNB: hard case: "Give this work to whoever looks idle." `whoever looks idle` is the object of `to`. -11. A participial phrase at the beginning of a sentence must refer to the grammatical subject. - - + * MDPNB: hard case: "Give this work to whoever looks idle." `whoever looks + idle` is the object of `to`. +11. A participial phrase at the beginning of a sentence must refer to the +grammatical subject. """ + # from proselint.tools import memoize # import re diff --git a/proselint/checks/strunkwhite/greylist.py b/proselint/checks/strunkwhite/greylist.py index 56a135c14..0fafa0c9d 100644 --- a/proselint/checks/strunkwhite/greylist.py +++ b/proselint/checks/strunkwhite/greylist.py @@ -20,6 +20,7 @@ @memoize def check(text): + """Check the text.""" err = "STW100" msg = "Use of '{}'. {}" diff --git a/proselint/checks/wallace/__init__.py b/proselint/checks/wallace/__init__.py index e69de29bb..29dc13804 100644 --- a/proselint/checks/wallace/__init__.py +++ b/proselint/checks/wallace/__init__.py @@ -0,0 +1 @@ +"""Advice from Davis Foster Wallace.""" diff --git a/proselint/checks/wallace/tense_present.py b/proselint/checks/wallace/tense_present.py index 9bb7d1baf..7cc0048f4 100644 --- a/proselint/checks/wallace/tense_present.py +++ b/proselint/checks/wallace/tense_present.py @@ -20,7 +20,7 @@ @memoize def check(text): - + """Check the text.""" err = "DFW201" msg = u"'{}'." @@ -40,7 +40,7 @@ def check(text): errors = [] for i in illogics: - for m in re.finditer(u"\s{}\s".format(i), text, flags=re.UNICODE | re.IGNORECASE): + for m in re.finditer(u"\s{}\s".format(i), text, flags=re.U | re.I): txt = m.group(0).strip() errors.append((m.start(), m.end(), err, msg.format(txt))) diff --git a/proselint/checks/wallace/uncomparables.py b/proselint/checks/wallace/uncomparables.py index ccb1d5a0d..1ee88f4c6 100644 --- a/proselint/checks/wallace/uncomparables.py +++ b/proselint/checks/wallace/uncomparables.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -"""DFW200: Comparing uncomparables. +u"""DFW200: Comparing uncomparables. --- layout: post @@ -51,7 +51,7 @@ @memoize def check(text): - + """Check the text.""" err = "DFW200" msg = "Comparison of an uncomparable: {} is not comparable." diff --git a/proselint/checks/writegood/__init__.py b/proselint/checks/writegood/__init__.py index e69de29bb..36faf4ba8 100644 --- a/proselint/checks/writegood/__init__.py +++ b/proselint/checks/writegood/__init__.py @@ -0,0 +1 @@ +"""Advice from the write-good package.""" diff --git a/proselint/checks/writegood/cliches.py b/proselint/checks/writegood/cliches.py index 259e34035..e317bc491 100644 --- a/proselint/checks/writegood/cliches.py +++ b/proselint/checks/writegood/cliches.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -"""WGD101: Clichés. +u"""WGD101: Clichés. --- layout: post @@ -14,13 +14,13 @@ Cliches are cliché. """ -from proselint.tools import blacklist, memoize +from proselint.tools import existence_check, memoize import re @memoize def check(text): - + """Check the text.""" err = "WGD101" msg = u"'{}' is a cliché." diff --git a/proselint/checks/writegood/lexical_illusions.py b/proselint/checks/writegood/lexical_illusions.py index 6bf3286c2..6ac027756 100644 --- a/proselint/checks/writegood/lexical_illusions.py +++ b/proselint/checks/writegood/lexical_illusions.py @@ -14,12 +14,12 @@ and this happens most often between line breaks. """ -from proselint.tools import blacklist, memoize +from proselint.tools import existence_check, memoize @memoize def check(text): - + """Check the text.""" err = "WGD105" msg = u"There's a lexical illusion here: a word is repeated." @@ -27,4 +27,4 @@ def check(text): "the\sthe" ] - return blacklist(text, commercialese, err, msg) + return existence_check(text, commercialese, err, msg) diff --git a/proselint/command_line.py b/proselint/command_line.py index f043763ec..1dc916e2a 100644 --- a/proselint/command_line.py +++ b/proselint/command_line.py @@ -10,6 +10,7 @@ import pkgutil import codecs import subprocess +import ntpath base_url = "prose.lifelinter.com/" @@ -17,8 +18,8 @@ def log_error(filename, line, column, error_code, msg): - """Print a message to the command line.""" - click.echo(filename + ":" + + """Print the error to the command line.""" + click.echo(ntpath.basename(filename) + ":" + str(1 + line) + ":" + str(1 + column) + ": " + error_code + " " + @@ -26,6 +27,7 @@ def log_error(filename, line, column, error_code, msg): def run_initialization(): + """Run the initialization method for each check.""" for loader, module_name, is_pkg in pkgutil.walk_packages(pl.__path__): module = loader.find_module(module_name).load_module(module_name) @@ -37,8 +39,8 @@ def run_initialization(): pass -def lint(filename): - +def lint(path): + """Run the linter on the file with the given path.""" # Extract functions from the checks folder. checks = [] for loader, module_name, is_pkg in pkgutil.walk_packages(pl.__path__): @@ -52,7 +54,7 @@ def lint(filename): pass # Apply all the checks. - with codecs.open(filename, "r", encoding='utf-8') as f: + with codecs.open(path, "r", encoding='utf-8') as f: text = f.read() errors = [] for check in checks: @@ -65,7 +67,7 @@ def lint(filename): for error in errors: (start, end, error_code, msg) = error (line, column) = line_and_column(text, start) - log_error(filename, line, column, error_code, msg) + log_error(path, line, column, error_code, msg) return errors @@ -76,13 +78,13 @@ def lint(filename): @click.option('--debug/--d', default=False) @click.argument('file', default=False) def proselint(file=None, version=None, initialize=None, debug=None): - """Run the linter.""" - + """Define the linter command line API.""" # Return the version number. if version: print "v0.0.1" return + # Run the intialization. if initialize: run_initialization() return @@ -92,8 +94,9 @@ def proselint(file=None, version=None, initialize=None, debug=None): subprocess.call("find . -name '*.pyc' -delete", shell=True) subprocess.call("find . -name '*.check' -delete", shell=True) + # Use the demo file by default. if not file: - file = "demo.md" + file = os.path.join(os.path.dirname(proselint_path), "demo.md") return lint(file) diff --git a/proselint/tools.py b/proselint/tools.py index 48f46e068..11d215f36 100644 --- a/proselint/tools.py +++ b/proselint/tools.py @@ -70,8 +70,7 @@ def line_and_column(text, position): def consistency_check(text, word_pairs, err, msg): - """Build a consistency checker for the given word_pairs""" - + """Build a consistency checker for the given word_pairs.""" errors = [] for w in word_pairs: match1 = [m for m in re.finditer(w[0], text)] @@ -91,9 +90,8 @@ def consistency_check(text, word_pairs, err, msg): return errors -def blacklist(text, list, err, msg, ignore_case=True, unicode=False): +def existence_check(text, list, err, msg, ignore_case=True, unicode=False): """Build a checker that blacklists certain words.""" - flags = 0 if ignore_case and unicode: diff --git a/scripts/generate_posts.py b/scripts/generate_posts.py index 2660aab15..c85f8a2af 100755 --- a/scripts/generate_posts.py +++ b/scripts/generate_posts.py @@ -1,3 +1,5 @@ +"""Generate blog posts from check docstrings.""" + import os import ast import datetime @@ -10,7 +12,17 @@ def is_check(fn): - return fn[-3:] == ".py" and (not fn[-11:] == "__init__.py") and (not "inprogress" in fn) + """Check whether a file contains a check.""" + if not fn[-3:] == ".py": + return False + + if fn[-11:] == "__init__.py": + return False + + if "inprogress" in fn: + return False + + return True for root, subdirs, files in listing: for file in files: diff --git a/setup.py b/setup.py index 40c83198f..ebc45a914 100644 --- a/setup.py +++ b/setup.py @@ -1,3 +1,5 @@ +"""Installation script for proselint.""" + from setuptools import setup setup( diff --git a/tests/check.py b/tests/check.py index 94dc9c4f5..d9d004637 100644 --- a/tests/check.py +++ b/tests/check.py @@ -1,16 +1,22 @@ +"""Check that a check is working.""" + from unittest import TestCase import os class Check(TestCase): + """All tests inherit from Check.""" + __test__ = False @property def this_check(self): + """The specific check.""" raise NotImplementedError def check(self, lst): + """Check if the test runs cleanly on the given text.""" if isinstance(lst, basestring): lst = [lst] @@ -21,6 +27,7 @@ def check(self, lst): return len(errors[0]) == 0 def test_wpe(self): + """Check whether the check is too noisy.""" min_wpe = 50 examples_dir = os.path.join(os.getcwd(), "tests", "samples") @@ -36,7 +43,7 @@ def test_wpe(self): num_words = len(text.split(' ')) try: - wpe = 1.0*num_words / num_errors + wpe = 1.0 * num_words / num_errors except ZeroDivisionError: wpe = float('Inf') diff --git a/tests/test_mau_a_vs_an.py b/tests/test_mau_a_vs_an.py index e765e904a..618823789 100644 --- a/tests/test_mau_a_vs_an.py +++ b/tests/test_mau_a_vs_an.py @@ -1,3 +1,5 @@ +"""Unit tests for MAU101.""" + from check import Check from proselint.checks import mau_a_vs_an as chk diff --git a/tests/test_strunk_white_eos.py b/tests/test_strunk_white_eos.py index e6911d8f1..c605dbd9e 100644 --- a/tests/test_strunk_white_eos.py +++ b/tests/test_strunk_white_eos.py @@ -1,21 +1,27 @@ +"""Unit tests for strunk_white_eos.""" + from check import Check from proselint.checks import strunk_white_eos as chk class TestCheck(Check): - __test__ = True + """Define the suite of checks.""" - @property - def this_check(self): - return chk + def __init__(self): + """Initialize the check.""" + self.this_check = chk + + __test__ = True def test_with_utilized(self): + """Don't produce an error when 'use' is used correctly.""" assert self.check( - """I use a hammer to drive nails into wood..""" + "I use a hammer to drive nails into wood." ) def test_no_utilized(self): + """Produce an error when 'utilize' is used in place of 'use'.""" assert not self.check( - """I utilize a hammer to drive nails into wood.""" + "I utilize a hammer to drive nails into wood." )