__main__.py 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151
  1. """
  2. Python Markdown
  3. A Python implementation of John Gruber's Markdown.
  4. Documentation: https://python-markdown.github.io/
  5. GitHub: https://github.com/Python-Markdown/markdown/
  6. PyPI: https://pypi.org/project/Markdown/
  7. Started by Manfred Stienstra (http://www.dwerg.net/).
  8. Maintained for a few years by Yuri Takhteyev (http://www.freewisdom.org).
  9. Currently maintained by Waylan Limberg (https://github.com/waylan),
  10. Dmitry Shachnev (https://github.com/mitya57) and Isaac Muse (https://github.com/facelessuser).
  11. Copyright 2007-2018 The Python Markdown Project (v. 1.7 and later)
  12. Copyright 2004, 2005, 2006 Yuri Takhteyev (v. 0.2-1.6b)
  13. Copyright 2004 Manfred Stienstra (the original version)
  14. License: BSD (see LICENSE.md for details).
  15. """
  16. import sys
  17. import optparse
  18. import codecs
  19. import warnings
  20. import markdown
  21. try:
  22. # We use `unsafe_load` because users may need to pass in actual Python
  23. # objects. As this is only available from the CLI, the user has much
  24. # worse problems if an attacker can use this as an attach vector.
  25. from yaml import unsafe_load as yaml_load
  26. except ImportError: # pragma: no cover
  27. try:
  28. # Fall back to PyYAML <5.1
  29. from yaml import load as yaml_load
  30. except ImportError:
  31. # Fall back to JSON
  32. from json import load as yaml_load
  33. import logging
  34. from logging import DEBUG, WARNING, CRITICAL
  35. logger = logging.getLogger('MARKDOWN')
  36. def parse_options(args=None, values=None):
  37. """
  38. Define and parse `optparse` options for command-line usage.
  39. """
  40. usage = """%prog [options] [INPUTFILE]
  41. (STDIN is assumed if no INPUTFILE is given)"""
  42. desc = "A Python implementation of John Gruber's Markdown. " \
  43. "https://Python-Markdown.github.io/"
  44. ver = "%%prog %s" % markdown.__version__
  45. parser = optparse.OptionParser(usage=usage, description=desc, version=ver)
  46. parser.add_option("-f", "--file", dest="filename", default=None,
  47. help="Write output to OUTPUT_FILE. Defaults to STDOUT.",
  48. metavar="OUTPUT_FILE")
  49. parser.add_option("-e", "--encoding", dest="encoding",
  50. help="Encoding for input and output files.",)
  51. parser.add_option("-o", "--output_format", dest="output_format",
  52. default='xhtml', metavar="OUTPUT_FORMAT",
  53. help="Use output format 'xhtml' (default) or 'html'.")
  54. parser.add_option("-n", "--no_lazy_ol", dest="lazy_ol",
  55. action='store_false', default=True,
  56. help="Observe number of first item of ordered lists.")
  57. parser.add_option("-x", "--extension", action="append", dest="extensions",
  58. help="Load extension EXTENSION.", metavar="EXTENSION")
  59. parser.add_option("-c", "--extension_configs",
  60. dest="configfile", default=None,
  61. help="Read extension configurations from CONFIG_FILE. "
  62. "CONFIG_FILE must be of JSON or YAML format. YAML"
  63. "format requires that a python YAML library be "
  64. "installed. The parsed JSON or YAML must result in a "
  65. "python dictionary which would be accepted by the "
  66. "'extension_configs' keyword on the markdown.Markdown "
  67. "class. The extensions must also be loaded with the "
  68. "`--extension` option.",
  69. metavar="CONFIG_FILE")
  70. parser.add_option("-q", "--quiet", default=CRITICAL,
  71. action="store_const", const=CRITICAL+10, dest="verbose",
  72. help="Suppress all warnings.")
  73. parser.add_option("-v", "--verbose",
  74. action="store_const", const=WARNING, dest="verbose",
  75. help="Print all warnings.")
  76. parser.add_option("--noisy",
  77. action="store_const", const=DEBUG, dest="verbose",
  78. help="Print debug messages.")
  79. (options, args) = parser.parse_args(args, values)
  80. if len(args) == 0:
  81. input_file = None
  82. else:
  83. input_file = args[0]
  84. if not options.extensions:
  85. options.extensions = []
  86. extension_configs = {}
  87. if options.configfile:
  88. with codecs.open(
  89. options.configfile, mode="r", encoding=options.encoding
  90. ) as fp:
  91. try:
  92. extension_configs = yaml_load(fp)
  93. except Exception as e:
  94. message = "Failed parsing extension config file: %s" % \
  95. options.configfile
  96. e.args = (message,) + e.args[1:]
  97. raise
  98. opts = {
  99. 'input': input_file,
  100. 'output': options.filename,
  101. 'extensions': options.extensions,
  102. 'extension_configs': extension_configs,
  103. 'encoding': options.encoding,
  104. 'output_format': options.output_format,
  105. 'lazy_ol': options.lazy_ol
  106. }
  107. return opts, options.verbose
  108. def run(): # pragma: no cover
  109. """Run Markdown from the command line."""
  110. # Parse options and adjust logging level if necessary
  111. options, logging_level = parse_options()
  112. if not options:
  113. sys.exit(2)
  114. logger.setLevel(logging_level)
  115. console_handler = logging.StreamHandler()
  116. logger.addHandler(console_handler)
  117. if logging_level <= WARNING:
  118. # Ensure deprecation warnings get displayed
  119. warnings.filterwarnings('default')
  120. logging.captureWarnings(True)
  121. warn_logger = logging.getLogger('py.warnings')
  122. warn_logger.addHandler(console_handler)
  123. # Run
  124. markdown.markdownFromFile(**options)
  125. if __name__ == '__main__': # pragma: no cover
  126. # Support running module as a commandline command.
  127. # `python -m markdown [options] [args]`.
  128. run()