yapf_api.py 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. # Copyright 2015 Google Inc. All Rights Reserved.
  2. #
  3. # Licensed under the Apache License, Version 2.0 (the "License");
  4. # you may not use this file except in compliance with the License.
  5. # You may obtain a copy of the License at
  6. #
  7. # http://www.apache.org/licenses/LICENSE-2.0
  8. #
  9. # Unless required by applicable law or agreed to in writing, software
  10. # distributed under the License is distributed on an "AS IS" BASIS,
  11. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. # See the License for the specific language governing permissions and
  13. # limitations under the License.
  14. """Entry points for YAPF.
  15. The main APIs that YAPF exposes to drive the reformatting.
  16. FormatFile(): reformat a file.
  17. FormatCode(): reformat a string of code.
  18. These APIs have some common arguments:
  19. style_config: (string) Either a style name or a path to a file that contains
  20. formatting style settings. If None is specified, use the default style
  21. as set in style.DEFAULT_STYLE_FACTORY
  22. lines: (list of tuples of integers) A list of tuples of lines, [start, end],
  23. that we want to format. The lines are 1-based indexed. It can be used by
  24. third-party code (e.g., IDEs) when reformatting a snippet of code rather
  25. than a whole file.
  26. print_diff: (bool) Instead of returning the reformatted source, return a
  27. diff that turns the formatted source into reformatter source.
  28. verify: (bool) True if reformatted code should be verified for syntax.
  29. """
  30. import codecs
  31. import difflib
  32. import re
  33. from yapf.pyparser import pyparser
  34. from yapf.pytree import blank_line_calculator
  35. from yapf.pytree import comment_splicer
  36. from yapf.pytree import continuation_splicer
  37. from yapf.pytree import pytree_unwrapper
  38. from yapf.pytree import pytree_utils
  39. from yapf.pytree import split_penalty
  40. from yapf.pytree import subtype_assigner
  41. from yapf.yapflib import errors
  42. from yapf.yapflib import file_resources
  43. from yapf.yapflib import identify_container
  44. from yapf.yapflib import reformatter
  45. from yapf.yapflib import style
  46. def FormatFile(filename,
  47. style_config=None,
  48. lines=None,
  49. print_diff=False,
  50. verify=False,
  51. in_place=False,
  52. logger=None):
  53. """Format a single Python file and return the formatted code.
  54. Arguments:
  55. filename: (unicode) The file to reformat.
  56. style_config: (string) Either a style name or a path to a file that contains
  57. formatting style settings. If None is specified, use the default style
  58. as set in style.DEFAULT_STYLE_FACTORY
  59. lines: (list of tuples of integers) A list of tuples of lines, [start, end],
  60. that we want to format. The lines are 1-based indexed. It can be used by
  61. third-party code (e.g., IDEs) when reformatting a snippet of code rather
  62. than a whole file.
  63. print_diff: (bool) Instead of returning the reformatted source, return a
  64. diff that turns the formatted source into reformatter source.
  65. verify: (bool) True if reformatted code should be verified for syntax.
  66. in_place: (bool) If True, write the reformatted code back to the file.
  67. logger: (io streamer) A stream to output logging.
  68. Returns:
  69. Tuple of (reformatted_code, encoding, changed). reformatted_code is None if
  70. the file is successfully written to (having used in_place). reformatted_code
  71. is a diff if print_diff is True.
  72. Raises:
  73. IOError: raised if there was an error reading the file.
  74. ValueError: raised if in_place and print_diff are both specified.
  75. """
  76. if in_place and print_diff:
  77. raise ValueError('Cannot pass both in_place and print_diff.')
  78. original_source, newline, encoding = ReadFile(filename, logger)
  79. reformatted_source, changed = FormatCode(
  80. original_source,
  81. style_config=style_config,
  82. filename=filename,
  83. lines=lines,
  84. print_diff=print_diff,
  85. verify=verify)
  86. if newline != '\n':
  87. reformatted_source = reformatted_source.replace('\n', newline)
  88. if in_place:
  89. if changed:
  90. file_resources.WriteReformattedCode(filename, reformatted_source,
  91. encoding, in_place)
  92. return None, encoding, changed
  93. return reformatted_source, encoding, changed
  94. def FormatTree(tree, style_config=None, lines=None, verify=False):
  95. """Format a parsed lib2to3 pytree.
  96. This provides an alternative entry point to YAPF.
  97. Arguments:
  98. tree: (pytree.Node) The root of the pytree to format.
  99. style_config: (string) Either a style name or a path to a file that contains
  100. formatting style settings. If None is specified, use the default style
  101. as set in style.DEFAULT_STYLE_FACTORY
  102. lines: (list of tuples of integers) A list of tuples of lines, [start, end],
  103. that we want to format. The lines are 1-based indexed. It can be used by
  104. third-party code (e.g., IDEs) when reformatting a snippet of code rather
  105. than a whole file.
  106. verify: (bool) True if reformatted code should be verified for syntax.
  107. Returns:
  108. The source formatted according to the given formatting style.
  109. """
  110. style.SetGlobalStyle(style.CreateStyleFromConfig(style_config))
  111. # Run passes on the tree, modifying it in place.
  112. comment_splicer.SpliceComments(tree)
  113. continuation_splicer.SpliceContinuations(tree)
  114. subtype_assigner.AssignSubtypes(tree)
  115. identify_container.IdentifyContainers(tree)
  116. split_penalty.ComputeSplitPenalties(tree)
  117. blank_line_calculator.CalculateBlankLines(tree)
  118. llines = pytree_unwrapper.UnwrapPyTree(tree)
  119. for lline in llines:
  120. lline.CalculateFormattingInformation()
  121. lines = _LineRangesToSet(lines)
  122. _MarkLinesToFormat(llines, lines)
  123. return reformatter.Reformat(_SplitSemicolons(llines), verify, lines)
  124. def FormatAST(ast, style_config=None, lines=None, verify=False):
  125. """Format a parsed lib2to3 pytree.
  126. This provides an alternative entry point to YAPF.
  127. Arguments:
  128. unformatted_source: (unicode) The code to format.
  129. style_config: (string) Either a style name or a path to a file that contains
  130. formatting style settings. If None is specified, use the default style
  131. as set in style.DEFAULT_STYLE_FACTORY
  132. lines: (list of tuples of integers) A list of tuples of lines, [start, end],
  133. that we want to format. The lines are 1-based indexed. It can be used by
  134. third-party code (e.g., IDEs) when reformatting a snippet of code rather
  135. than a whole file.
  136. verify: (bool) True if reformatted code should be verified for syntax.
  137. Returns:
  138. The source formatted according to the given formatting style.
  139. """
  140. style.SetGlobalStyle(style.CreateStyleFromConfig(style_config))
  141. llines = pyparser.ParseCode(ast)
  142. for lline in llines:
  143. lline.CalculateFormattingInformation()
  144. lines = _LineRangesToSet(lines)
  145. _MarkLinesToFormat(llines, lines)
  146. return reformatter.Reformat(_SplitSemicolons(llines), verify, lines)
  147. def FormatCode(unformatted_source,
  148. filename='<unknown>',
  149. style_config=None,
  150. lines=None,
  151. print_diff=False,
  152. verify=False):
  153. """Format a string of Python code.
  154. This provides an alternative entry point to YAPF.
  155. Arguments:
  156. unformatted_source: (unicode) The code to format.
  157. filename: (unicode) The name of the file being reformatted.
  158. style_config: (string) Either a style name or a path to a file that contains
  159. formatting style settings. If None is specified, use the default style
  160. as set in style.DEFAULT_STYLE_FACTORY
  161. lines: (list of tuples of integers) A list of tuples of lines, [start, end],
  162. that we want to format. The lines are 1-based indexed. It can be used by
  163. third-party code (e.g., IDEs) when reformatting a snippet of code rather
  164. than a whole file.
  165. print_diff: (bool) Instead of returning the reformatted source, return a
  166. diff that turns the formatted source into reformatter source.
  167. verify: (bool) True if reformatted code should be verified for syntax.
  168. Returns:
  169. Tuple of (reformatted_source, changed). reformatted_source conforms to the
  170. desired formatting style. changed is True if the source changed.
  171. """
  172. try:
  173. tree = pytree_utils.ParseCodeToTree(unformatted_source)
  174. except Exception as e:
  175. e.filename = filename
  176. raise errors.YapfError(errors.FormatErrorMsg(e))
  177. reformatted_source = FormatTree(
  178. tree, style_config=style_config, lines=lines, verify=verify)
  179. if unformatted_source == reformatted_source:
  180. return '' if print_diff else reformatted_source, False
  181. if print_diff:
  182. code_diff = _GetUnifiedDiff(
  183. unformatted_source, reformatted_source, filename=filename)
  184. return code_diff, code_diff.strip() != '' # pylint: disable=g-explicit-bool-comparison # noqa
  185. return reformatted_source, True
  186. def ReadFile(filename, logger=None):
  187. """Read the contents of the file.
  188. An optional logger can be specified to emit messages to your favorite logging
  189. stream. If specified, then no exception is raised. This is external so that it
  190. can be used by third-party applications.
  191. Arguments:
  192. filename: (unicode) The name of the file.
  193. logger: (function) A function or lambda that takes a string and emits it.
  194. Returns:
  195. The contents of filename.
  196. Raises:
  197. IOError: raised if there was an error reading the file.
  198. """
  199. try:
  200. encoding = file_resources.FileEncoding(filename)
  201. # Preserves line endings.
  202. with codecs.open(filename, mode='r', encoding=encoding) as fd:
  203. lines = fd.readlines()
  204. line_ending = file_resources.LineEnding(lines)
  205. source = '\n'.join(line.rstrip('\r\n') for line in lines) + '\n'
  206. return source, line_ending, encoding
  207. except IOError as e: # pragma: no cover
  208. if logger:
  209. logger(e)
  210. e.args = (e.args[0], (filename, e.args[1][1], e.args[1][2], e.args[1][3]))
  211. raise
  212. except UnicodeDecodeError as e: # pragma: no cover
  213. if logger:
  214. logger('Could not parse %s! Consider excluding this file with --exclude.',
  215. filename)
  216. logger(e)
  217. e.args = (e.args[0], (filename, e.args[1][1], e.args[1][2], e.args[1][3]))
  218. raise
  219. def _SplitSemicolons(lines):
  220. res = []
  221. for line in lines:
  222. res.extend(line.Split())
  223. return res
  224. DISABLE_PATTERN = r'^#.*\b(?:yapf:\s*disable|fmt: ?off)\b'
  225. ENABLE_PATTERN = r'^#.*\b(?:yapf:\s*enable|fmt: ?on)\b'
  226. def _LineRangesToSet(line_ranges):
  227. """Return a set of lines in the range."""
  228. if line_ranges is None:
  229. return None
  230. line_set = set()
  231. for low, high in sorted(line_ranges):
  232. line_set.update(range(low, high + 1))
  233. return line_set
  234. def _MarkLinesToFormat(llines, lines):
  235. """Skip sections of code that we shouldn't reformat."""
  236. if lines:
  237. for uwline in llines:
  238. uwline.disable = not lines.intersection(
  239. range(uwline.lineno, uwline.last.lineno + 1))
  240. # Now go through the lines and disable any lines explicitly marked as
  241. # disabled.
  242. index = 0
  243. while index < len(llines):
  244. uwline = llines[index]
  245. if uwline.is_comment:
  246. if _DisableYAPF(uwline.first.value.strip()):
  247. index += 1
  248. while index < len(llines):
  249. uwline = llines[index]
  250. line = uwline.first.value.strip()
  251. if uwline.is_comment and _EnableYAPF(line):
  252. if not _DisableYAPF(line):
  253. break
  254. uwline.disable = True
  255. index += 1
  256. elif re.search(DISABLE_PATTERN, uwline.last.value.strip(), re.IGNORECASE):
  257. uwline.disable = True
  258. index += 1
  259. def _DisableYAPF(line):
  260. return (re.search(DISABLE_PATTERN,
  261. line.split('\n')[0].strip(), re.IGNORECASE) or
  262. re.search(DISABLE_PATTERN,
  263. line.split('\n')[-1].strip(), re.IGNORECASE))
  264. def _EnableYAPF(line):
  265. return (re.search(ENABLE_PATTERN,
  266. line.split('\n')[0].strip(), re.IGNORECASE) or
  267. re.search(ENABLE_PATTERN,
  268. line.split('\n')[-1].strip(), re.IGNORECASE))
  269. def _GetUnifiedDiff(before, after, filename='code'):
  270. """Get a unified diff of the changes.
  271. Arguments:
  272. before: (unicode) The original source code.
  273. after: (unicode) The reformatted source code.
  274. filename: (unicode) The code's filename.
  275. Returns:
  276. The unified diff text.
  277. """
  278. before = before.splitlines()
  279. after = after.splitlines()
  280. return '\n'.join(
  281. difflib.unified_diff(
  282. before,
  283. after,
  284. filename,
  285. filename,
  286. '(original)',
  287. '(reformatted)',
  288. lineterm='')) + '\n'