Package: pymarkdownlnt
PyMarkdownProjectQualityCommunityMaintainersNote To Any ReadersAs part of our effort to clean up the project in preparation for a version 1.0.0
release, we are moving our documentation from plain Markdown files hosted in this
repository GitHub to theReadTheDocsformat. Please bear with us during this transitional period.And On with The Show...PyMarkdown is primarily a Markdown Linter.  To ensure that the Markdownlintingis accomplished successfully, the rules engine that powers the linter uses a Markdown
parser that is bothGitHub Flavored Markdowncompliant andCommonMarkcompliant.  The rules provided in the base application can be easily extended
by writing new plugins and importing them into the rules engine through simple
configuration options.The PyMarkdown project has the following advantages:ConsistencyThis project can examine multiple files and directories with one invocation,
ensuring that all detected Markdown files adhere to the same guidelines.PortableThe linter runs on any system running Python 3.8 or later, with no modifications.StandardizedThe parser that powers the linter is GitHub Flavored Markdown (GFM) compliant.
Due to that foundation, the parser does not guess how some parsers may handle
a given situation, as it has a clear set of rules to follow.AccurateThe parser passes all GFM conformance tests and CommonMark conformance tests.  In
test scenarios that were not present in either set of tests, the CommonMark
0.29.2 release was used to determine the correct parsing.FlexibleEach Markdown document is parsed into an internal token format.  Most of the rules
and made more efficient by leveraging this token format. Where that is not possible,
simple regular expressions and simple algorithms are used on a line-by-line basis.Thoroughly testedThe project currently has over 4100 scenario tests and a coverage percentage of 100%.ExtensibleThe parser for the project adheres to the GFM specification and most of
the rules for the parser leverage the tokens produced by that parser. Therulesthemselves are plugins, so they are extensible by default.
The parser itself will be extended as needed to provide for other Markdownextensionsas needed.VersatileThe PyMarkdown linter can be executed from a script on the command line,
from within another Python program, or using the popular Git Pre-Commit hooks.NoteThis project is currently in beta, and some of these documented things
may not work 100% as advertised until after the final release.  However,
everything should be close enough to done that if you find something missing,
please let us know.GitHub Pre Commit HooksIf you intend to use the PyMarkdown project as a linter for your GitHub Pre-Commit
hooks, then refer tothis documenton how to set that up.
Once configured, you can continue reading at theHow To Use sectionfor more information on the options available for use in your Pre-Commit Hooks.RequirementsThis project required Python 3.8 or later to function.Installationpip install pymarkdownlntHow To UseIf You Get StuckFull help support is available by enteringpymarkdown--helpon the command line and pressing enter.  For an individual command, help is
available by following the command or commands with--helpas follows:pymarkdownscan--helpPrerequisitesVarious sections of this document benefit from having concrete examples to
illustrate how things work. For the following sections, this documentation
will assume that there is a file calledexample-1.mdin a directory calledexamplesthat has the following content:## This is an exampleJust an example.and a file calledexample-2.mdin that same directory that has the
following content:# This is an exampleJust an example.If you prefer concrete files, these files are checked into theexamples directoryof the GitHub project.API SupportWhile the vast majority of people using this application will use it
through the command line, there is an auto-generatedAPI documentanda companion documenton how to use the API.  This
support is very new, but introduced with lots of testing. If there are usability
issues or something you feel is missing, please contact us.Basic ScanningThe PyMarkdown linter is executed by calling the project from the command line
and specifying one or more files and directories to scan for Markdown.mdfiles.
The set of files and/or directories must be prefaced with thescankeyword to
denote that scanning is required. For the examples directory, both this form:pymarkdownscanexamplesand this form:pymarkdownscanexamples/example-1.mdexamples/example-2.mdcan be used to scan both files in the directory.  The only difference between the
two invocations is that the first example will scan every Markdown.mdfile in
theexamplesdirectory, while the second invocation will only scan the two specified
files.  For clarity purposes, if the command line specifies the same file multiple
times, that file name will only be added to the list of files to scan once.If everything is working properly, both of the above scans will produce the following
output:examples/example-1.md:3:16: MD047: Each file should end with a single newline character. (single-trailing-newline)RulesThe PyMarkdown project includes 42 out-of-the-boxrules. These
rules are implemented using a simple plugin system that is documented in thedeveloper documentation.  It is these rules that allow the
PyMarkdown project to scan the various Markdown files, looking for bad patterns
over that set of Markdown documents.Because of the way that the rules are provided, sometimes we refer to the rules
asrulesand sometimes asrule plugins.  Aruleis a specific set of conditions
that trigger the reporting of a violation when those conditions occur.  Arule pluginis the Python class and Python file in which theruleis supplied
to the PyMarkdown application.  Our goal is to try to not use these phrases interchangeably,
but we are only human.  If we do mess up and use the wrong phase, we do apologize.Note that the initial set of rules are modelled after the 42 rules provided by
David Anson'sMarkdown Lintproject.
This decision was made to give Markdown authors that use his project in their IDEs
(such as the MarkdownLint plugin for VSCode that I use), a good grounding in what
they can consistently check for.Rule Violation FormatExecuting either of the above example command lines will produce the following output:/examples/example-1.md:3:16: MD047: Each file should end with a single newline character. (single-trailing-newline)The format of the output for any rules that are triggered is as follows:file-name:line:column: rule-id: description (aliases)file-name- Path to the file that triggered the rule.line/column- Position in the file where the rule was triggered.rule-id- Unique identifier assigned to the rule.description- Human readable description of the rule.aliases- One or more aliases used to reference the rule.For the rule violation that was reported at the start of this section, the first
step in diagnosing that violation is to look at the file/examples/example-1.mdat the end of line 3, which is column 16.  Rulemd047specifies that every file should end with a single newline character, which is
what is reported in the violation's description.  Additionally, it reports that
this rule can also be identified by the more human readable alias ofsingle-trailing-newline.Basic ConfigurationThe most frequently used part of the configuration system is the part that enables
and disables specific rules while scanning the Markdown files.  For example, if
you do not like rule md047 which states that each file must end with a single newline,
you can disable that rule by specifying:pymarkdown-dmd047scan/examplesor:pymarkdown--disable-rulesmd047scan/examplesThe effect of disabling the rule should be evidenced by the scan no longer reporting
any violations of rule md047 against the Markdown fileexample-1.md.Alternatively, rules can also be enabled.  As the modelled base rules for this
project are based off those rules for David Anson's project, rule md002 is disabled
by default in both projects.  Specifically, rule md002 is disabled by default as
rule md041 provides a better implementation of that rule that takes front-matter
into account.  If you prefer rule md002, you can enable it by specifying either:pymarkdown-emd002scan/examplesorpymarkdown--enable-rulesmd002scan/examplesThe effect of enabling the rule is evidenced by the scan reporting a violation of
Rule md002 against Markdown fileexample-1.md:examples/example-1.md:1:1: MD002: First heading of the document should be a top level heading.
 [Expected: h1; Actual: h2] (first-heading-h1, first-header-h1)
examples/example-1.md:3:16: MD047: Each file should end with a single newline character.
(single-trailing-newline)ExtensionsExtensions are any features that are implemented in addition to the baseGitHub Flavored Markdownspecification.
These extensions are documented in theextensionsdocument, including information about how they perform, details on the
extension, and configuration information.Advanced ScanningFor more advanced scanning options, please consult the document
onAdvanced Scanning.  This document includes information
on:Command Line Globbing SupportRecursing DirectoriesPragmas to Disable Rules InlineAdvanced ConfigurationFor more advanced configuration options, please consult the document
onAdvanced Configuration.  This
document includes information on:Command Line SettingsConfiguration File SettingsAvailable Configuration ValuesReturn Code BehaviorAdvanced Rule PluginsFor more information of how to query information on the rule plugins
that are currently loaded, please consult the document onAdvanced Rule Plugins.Open Issues and Future PlansDuring the development phase of this project, it was more useful to have
an actual list of issues to track and prioritize, rather than relying on
GitHub to do all the work. This is the location of the prioritizedIssues List.If you find any issues, please report them using the standard GitHub
issues process.  When our team looks at your issue and triages
it, it will be added to our Issues List with the triaged priority.
For us, this provides transparency as to what we are currently working
on, what is up next, and what our plans are for further development.When Did Things Change?The changelog for this project is maintainedat this location.Still Have Questions?If you still have questions, please consult ourFrequently Asked Questionsdocument.Contact InformationIf you would like to report an issue with the linter, a rule, or
the documentation, please file an issueusing GitHub.If you would like to us to implement a feature that you believe is
important, please file an issueusing GitHubthat includes what you want to add, why you want to add it,
and why it is important.  Please note that the issue will usually be
the start of a conversation, and be ready for more questions.If you would like to contribute to the project in a more
substantial manner, please contact me atjack.de.winteratoutlook.com.Instructions For ContributingDeveloper notes on various topics are kept in the theDeveloper Notesdocument.If you attempting to contribute something to this project,
please follow the steps outlined in theCONTRIBUTING.mdfile.AcknowledgementsCurrently, as a team of one, there are only two big groups of people to
acknowledge.The first, and foremost group, is my immediate family. They
have endured me coming out of my office with my head still in the
clouds, explaining things to them so that I can think more clearly.
While they still do not understand what I am talking about with respect
to this project, I am so grateful to them for allowing me to work "my
process" to figure things out.The second group is the contributors to theCommonMark discussion forum.
While I have raised some issues that were cut and dry, a lot of them
involved significant amount of discussion to figure out what the
right approach is.  Through all those discussions, I rarely, if ever,
felt like they treated me as less than equal, no matter how stupid
my questions were.  For their patience and their professionalism,
thank you.