├── .gitignore ├── .travis.yml ├── CHANGELOG ├── MANIFEST.in ├── README.md ├── TODO.md ├── pyfmt.py ├── requirements.txt ├── setup.cfg ├── setup.py └── test_pyfmt.py /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by .ignore support plugin (hsz.mobi) 2 | ### Python template 3 | # Byte-compiled / optimized / DLL files 4 | __pycache__/ 5 | *.py[cod] 6 | *$py.class 7 | 8 | # C extensions 9 | *.so 10 | 11 | # Distribution / packaging 12 | .Python 13 | env/ 14 | build/ 15 | develop-eggs/ 16 | dist/ 17 | downloads/ 18 | eggs/ 19 | .eggs/ 20 | lib/ 21 | lib64/ 22 | parts/ 23 | sdist/ 24 | var/ 25 | *.egg-info/ 26 | .installed.cfg 27 | *.egg 28 | 29 | # PyInstaller 30 | # Usually these files are written by a python script from a template 31 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 32 | *.manifest 33 | *.spec 34 | 35 | # Installer logs 36 | pip-log.txt 37 | pip-delete-this-directory.txt 38 | 39 | # Unit test / coverage reports 40 | htmlcov/ 41 | .tox/ 42 | .coverage 43 | .coverage.* 44 | .cache 45 | nosetests.xml 46 | coverage.xml 47 | *,cover 48 | .hypothesis/ 49 | 50 | # Translations 51 | *.mo 52 | *.pot 53 | 54 | # Django stuff: 55 | *.log 56 | local_settings.py 57 | 58 | # Flask stuff: 59 | instance/ 60 | .webassets-cache 61 | 62 | # Scrapy stuff: 63 | .scrapy 64 | 65 | # Sphinx documentation 66 | docs/_build/ 67 | 68 | # PyBuilder 69 | target/ 70 | 71 | # IPython Notebook 72 | .ipynb_checkpoints 73 | 74 | # pyenv 75 | .python-version 76 | 77 | # celery beat schedule file 78 | celerybeat-schedule 79 | 80 | # dotenv 81 | .env 82 | 83 | # virtualenv 84 | venv/ 85 | ENV/ 86 | env_pyfmt 87 | 88 | # Spyder project settings 89 | .spyderproject 90 | 91 | # Rope project settings 92 | .ropeproject 93 | 94 | # PyCharm project 95 | .idea/*.* 96 | vcs.xml -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: python 2 | install: 3 | - pip install -r requirements.txt 4 | - pip install pytest python-coveralls pytest-cov 5 | python: 6 | - "2.7" 7 | - "3.4" 8 | script: 9 | - py.test test_pyfmt.py --cov pyfmt --cov-report term-missing 10 | after_success: 11 | - coveralls 12 | notifications: 13 | irc: "chat.freenode.net#baron" 14 | -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | Changelog 2 | ========= 3 | 4 | 0.1 (2014-09-07) 5 | ---------------- 6 | 7 | - Init 8 | -------------------------------------------------------------------------------- /MANIFEST.in: -------------------------------------------------------------------------------- 1 | include README.md CHANGELOG 2 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | PyFmt 2 | ====== 3 | 4 | `Pyfmt` is an autoformatting source code tool for `python`, in more or less the 5 | same spirit than [gofmt](http://golang.org/cmd/gofmt/). It follows the `pep8` and 6 | uses [Baron](https://github.com/Psycojoker/baron) to do its work in one pass. 7 | 8 | **`Pyfmt` is in its early stage of development, it already do a good job at 9 | formatting most of python code but it doesn't handle yet splitting a too long 10 | line and might end up putting back into one line a line that you have split.** 11 | But it should be fine for ~80% of the cases. 12 | 13 | Feedback is very welcome. 14 | 15 | You can see it in action [here](https://github.com/Psycojoker/pyfmt/commit/145a186b00f842d62be71959f698f84b033310ff). 16 | 17 | Installation 18 | ============ 19 | `PyFmt` can be installed using `$ pip install pyfmt` 20 | 21 | Usage 22 | ===== 23 | 24 | pyfmt file.py # output to standard output 25 | pyfmt -i file.py # replace the content of the file, like -i of sed 26 | 27 | From `python`: 28 | 29 | ```python 30 | from pyfmt import format_code 31 | 32 | format_code(source_code) 33 | ``` 34 | 35 | Community 36 | ========= 37 | 38 | You can reach us on [irc.freenode.net#baron](https://webchat.freenode.net/?channels=%23baron) 39 | 40 | Tests 41 | ===== 42 | You can run the tests using `$ py.test test_pyfmt.py` 43 | 44 | Operations 45 | ========== 46 | 47 | Things that `pyfmt` do (if it's not already done in the code): 48 | 49 | * render ALL nodes of the python language according to the `pep8` 50 | * if a datastructure is indented, keep the indentation and indent it according to the pep8 51 | * put 2 spaces before comments after code, put a space after the "#" of the comment (don't do for shebang) 52 | * split compound statements written on one line on two lines (example: `if a: pass` -> `if a:\n` pass", same for every other statements that wait for a block of code) 53 | * replace `stuff` by repr(stuff) 54 | * split multiple import across multiple lines 55 | * replace tabs with space 56 | * correctly indent the whole file using 4 spaces 57 | * convert windows `'\r\n`' to `'\n'` 58 | * if not present, put two blank lines around functions or class definitions at the root level of the file 59 | * if not present, put one blank line around method definition in a class 60 | * replace <> with != 61 | 62 | Things that `pyfmt` don't do or don't do yet and that can be annoying: 63 | 64 | * properly formatting the content of a "from x import (lot of stuff)" 65 | * properly splitting too long lines, it may ends up putting back on one line a splitted line 66 | * removing extra blank lines 67 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | # ToDo 2 | 3 | ### Important 4 | 5 | - create formatting metrics according to rendering order for every node: https://github.com/PyCQA/baron/blob/master/baron/render.py#L114 6 | i.e., format code with specific style, distinct for every developer 7 | 8 | - format any node with gathering metrics 9 | 10 | - format whole module with gathering metrics 11 | 12 | - set default style as PEP8 like in yapf: https://github.com/google/yapf/blob/master/yapf/yapflib/style.py -------------------------------------------------------------------------------- /pyfmt.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | import os 4 | import re 5 | import sys 6 | import argparse 7 | import baron 8 | 9 | 10 | python_version = sys.version_info[0] 11 | python_subversion = sys.version_info[1] 12 | string_instance = str if python_version == 3 else basestring 13 | 14 | 15 | def d(j): 16 | import json 17 | print(json.dumps(j, indent=4)) 18 | 19 | 20 | dumpers = {} 21 | 22 | 23 | def node(key=""): 24 | def wrap(func): 25 | if not key: 26 | dumpers[func.__name__ if not func.__name__.endswith( 27 | "_") else func.__name__[:-1]] = func 28 | 29 | dumpers[key] = func 30 | return func 31 | return wrap 32 | 33 | 34 | def find(node_type, tree): 35 | if isinstance(tree, dict): 36 | if tree.get("type") == node_type: 37 | return tree 38 | for i in tree.values(): 39 | result = find(node_type, i) 40 | if result is not None: 41 | return result 42 | elif isinstance(tree, list): 43 | for i in tree: 44 | result = find(node_type, i) 45 | if result is not None: 46 | return result 47 | return None 48 | 49 | 50 | class Dumper(object): 51 | 52 | def __init__(self): 53 | self._current_indent = "" # we always start at the level 0 54 | self.previous = None 55 | self.stack = [] 56 | self.indentation_stack = [] 57 | self.number_of_endl = 0 58 | 59 | def maybe_backslash(self, formatting, default): 60 | if formatting and "\\" in formatting[0]["value"]: 61 | return formatting[0]["value"] 62 | else: 63 | return default 64 | 65 | def dump_node(self, node): 66 | self.stack.append(node) 67 | if node["type"] == "endl": 68 | self.number_of_endl += 1 69 | elif node["type"] not in ('space', 'comment'): 70 | self.number_of_endl = 0 71 | to_return = "".join(list(dumpers[node["type"]](self, node))) 72 | self.stack.pop() 73 | return to_return 74 | 75 | def dump_node_list(self, node_list): 76 | to_return = "" 77 | for node in node_list: 78 | to_return += self.dump_node(node) 79 | self.previous = node 80 | return to_return 81 | 82 | def dump_root(self, node_list): 83 | to_return = "" 84 | previous_is_function = False 85 | for statement_number, node in enumerate(node_list): 86 | if node["type"] not in ('endl', 'comment', 'space'): 87 | if node["type"] in ("def", "class") and self.number_of_endl != 3 and statement_number != 0: 88 | to_return += "\n"*(3 - self.number_of_endl) 89 | previous_is_function = True 90 | elif previous_is_function: 91 | previous_is_function = False 92 | to_return += "\n" * (3 - self.number_of_endl) 93 | 94 | to_return += self.dump_node(node) 95 | self.previous = node 96 | return to_return 97 | 98 | def dump_suite(self, node_list): 99 | if node_list and node_list[0]["type"] != "endl": 100 | node_list = [ 101 | {"type": "endl", "formatting": [], "value": "\n", "indent": self._current_indent + " "}] + node_list 102 | return self.dump_node_list(node_list) 103 | 104 | def dump_class_body(self, node_list): 105 | if node_list and node_list[0]["type"] != "endl": 106 | node_list = [ 107 | {"type": "endl", "formatting": [], "value": "\n", "indent": self._current_indent + " "}] + node_list 108 | 109 | to_return = "" 110 | previous_is_function = False 111 | for statement_number, node in enumerate(node_list): 112 | if node["type"] not in ('endl', 'comment', 'space'): 113 | if node["type"] == "def" and self.number_of_endl != 3 and statement_number != 1: 114 | to_return = re.sub(' *$', '', to_return) 115 | to_return += "\n" * \ 116 | (2 - self.number_of_endl) + self._current_indent 117 | previous_is_function = True 118 | elif previous_is_function: 119 | previous_is_function = False 120 | to_return += "\n" * (2 - self.number_of_endl) 121 | to_return += self.dump_node(node) 122 | self.previous = node 123 | return to_return 124 | 125 | @node() 126 | def endl(self, node): 127 | # replace tab with space 128 | indentation = node["indent"].replace("\t", " " * 8) 129 | 130 | # reindentation rules 131 | # self.indentation_stack store tuples ('found intentation', 'correct 132 | # indentation') 133 | if len(indentation) == 0: 134 | pass 135 | 136 | elif len(self.indentation_stack) == 0: 137 | if len(indentation) != 4: 138 | self.indentation_stack.append((indentation, " " * 4)) 139 | indentation = " " * 4 140 | else: 141 | self.indentation_stack.append((indentation, indentation)) 142 | 143 | elif indentation > self.indentation_stack[-1][0]: 144 | if indentation != self.indentation_stack[-1][1] + " " * 4: 145 | self.indentation_stack.append( 146 | (indentation, self.indentation_stack[-1][1] + " " * 4)) 147 | indentation = self.indentation_stack[-2][1] + " " * 4 148 | else: 149 | self.indentation_stack.append((indentation, indentation)) 150 | 151 | elif indentation < self.indentation_stack[-1][0]: 152 | while self.indentation_stack and indentation != self.indentation_stack[-1][0]: 153 | self.indentation_stack.pop() 154 | if not self.indentation_stack: 155 | self.indentation = "" 156 | elif indentation != self.indentation_stack[-1][1]: 157 | indentation = self.indentation_stack[-1][1] 158 | 159 | elif indentation == self.indentation_stack[-1][0]: 160 | indentation = self.indentation_stack[-1][1] 161 | 162 | self._current_indent = indentation 163 | yield self.dump_node_list(node["formatting"]) 164 | yield "\n" 165 | yield indentation 166 | 167 | @node() 168 | def ternary_operator(self, node): 169 | yield self.dump_node(node["first"]) 170 | yield " if " 171 | yield self.dump_node(node["value"]) 172 | yield " else " 173 | yield self.dump_node(node["second"]) 174 | 175 | @node("int") 176 | @node("name") 177 | @node("hexa") 178 | @node("octa") 179 | @node("float") 180 | @node("space") 181 | @node("binary") 182 | @node("complex") 183 | @node("float_exponant") 184 | @node("left_parenthesis") 185 | @node("right_parenthesis") 186 | def get_value(self, node): 187 | yield node["value"] 188 | 189 | @node("break") 190 | @node("continue") 191 | @node("pass") 192 | def get_type(self, node): 193 | yield node["type"] 194 | 195 | @node("star") 196 | @node("string") 197 | @node("raw_string") 198 | @node("binary_string") 199 | @node("unicode_string") 200 | @node("binary_raw_string") 201 | @node("unicode_raw_string") 202 | def generic(self, node): 203 | if find('endl', node["first_formatting"]): 204 | yield self.dump_node_list(node["first_formatting"]) 205 | yield node["value"] 206 | if find('endl', node["second_formatting"]): 207 | yield self.dump_node_list(node["second_formatting"]) 208 | 209 | @node() 210 | def comment(self, node): 211 | if self.previous and self.previous["type"] != "endl": 212 | yield " " 213 | if node["value"].startswith(("# ", "##", "#!")) or len(node["value"]) == 1: 214 | yield node["value"] 215 | else: 216 | yield "# " + node["value"][1:] 217 | 218 | @node() 219 | def ellipsis(self, node): 220 | yield "..." 221 | 222 | @node() 223 | def dot(self, node): 224 | yield "." 225 | 226 | @node() 227 | def semicolon(self, node): 228 | yield "\n" + self._current_indent 229 | 230 | @node() 231 | def comma(self, node): 232 | yield ", " 233 | 234 | @node() 235 | def call(self, node): 236 | yield "(" 237 | yield self.dump_node_list(node["value"]) 238 | yield ")" 239 | 240 | @node() 241 | def decorator(self, node): 242 | yield "@" 243 | yield self.dump_node(node["value"]) 244 | if node["call"]: 245 | yield self.dump_node(node["call"]) 246 | 247 | @node() 248 | def class_(self, node): 249 | yield self.dump_node_list(node["decorators"]) 250 | yield "class " 251 | yield node["name"] 252 | if node["parenthesis"]: 253 | yield "(" 254 | yield self.dump_node_list(node["inherit_from"]) 255 | if node["parenthesis"]: 256 | yield ")" 257 | yield ":" 258 | self.previous = node 259 | yield self.dump_class_body(node["value"]) 260 | 261 | @node() 262 | def repr(self, node): 263 | yield "repr(" 264 | yield self.dump_node_list(node["value"]) 265 | yield ")" 266 | 267 | @node() 268 | def list_(self, node): 269 | yield "[" 270 | yield self._dump_data_structure_body(node) 271 | yield "]" 272 | 273 | def _dump_data_structure_body(self, node): 274 | if find('endl', node['value']): 275 | return "".join(list(self.dump_data_structure(content=node["value"], 276 | indent=self._current_indent))) 277 | else: 278 | return re.sub("([^\n ]) +$", "\g<1>", self.dump_node_list(node["value"])) 279 | 280 | @node() 281 | def associative_parenthesis(self, node): 282 | yield "(" 283 | yield self.dump_node(node["value"]) 284 | yield ")" 285 | 286 | @node() 287 | def tuple_(self, node): 288 | if node["with_parenthesis"]: 289 | yield "(" 290 | yield self._dump_data_structure_body(node) 291 | if node["with_parenthesis"]: 292 | yield ")" 293 | 294 | @node() 295 | def def_(self, node): 296 | yield self.dump_node_list(node["decorators"]) 297 | yield "def " 298 | yield node["name"] 299 | yield "(" 300 | yield self.dump_node_list(node["arguments"]) 301 | yield "):" 302 | self.previous = node 303 | yield self.dump_suite(node["value"]) 304 | 305 | @node() 306 | def call_argument(self, node): 307 | if node["name"]: 308 | yield node["name"] 309 | yield "=" 310 | yield self.dump_node(node["value"]) 311 | else: 312 | yield self.dump_node(node["value"]) 313 | 314 | @node() 315 | def def_argument(self, node): 316 | if node["value"]: 317 | yield node["name"] 318 | yield "=" 319 | yield self.dump_node(node["value"]) 320 | elif isinstance(node["name"], string_instance): 321 | yield node["name"] 322 | else: 323 | yield self.dump_node(node["name"]) 324 | 325 | @node() 326 | def list_argument(self, node): 327 | yield "*" 328 | yield self.dump_node(node["value"]) 329 | 330 | @node() 331 | def dict_argument(self, node): 332 | yield "**" 333 | yield self.dump_node(node["value"]) 334 | 335 | @node() 336 | def return_(self, node): 337 | yield "return" 338 | if node["value"]: 339 | yield " " 340 | yield self.dump_node(node["value"]) 341 | 342 | @node() 343 | def raise_(self, node): 344 | yield "raise" 345 | if node["value"]: 346 | yield " " 347 | yield self.dump_node(node["value"]) 348 | if node["instance"]: 349 | yield "," 350 | yield " " 351 | yield self.dump_node(node["instance"]) 352 | if node["traceback"]: 353 | yield "," 354 | yield " " 355 | yield self.dump_node(node["traceback"]) 356 | 357 | @node() 358 | def assert_(self, node): 359 | yield "assert " 360 | yield self.dump_node(node["value"]) 361 | if node["message"]: 362 | yield ", " 363 | yield self.dump_node(node["message"]) 364 | 365 | @node("dotted_name") 366 | @node("ifelseblock") 367 | @node("atomtrailers") 368 | @node("string_chain") 369 | def dump_node_list_value(self, node): 370 | yield self.dump_node_list(node["value"]) 371 | 372 | @node() 373 | def set_comprehension(self, node): 374 | yield "{" 375 | yield self.dump_node(node["result"]) 376 | yield self.dump_node_list(node["generators"]) 377 | yield "}" 378 | 379 | @node() 380 | def dict_comprehension(self, node): 381 | yield "{" 382 | yield self.dump_node(node["result"]["key"]) 383 | yield ": " 384 | yield self.dump_node(node["result"]["value"]) 385 | yield self.dump_node_list(node["generators"]) 386 | yield "}" 387 | 388 | @node() 389 | def argument_generator_comprehension(self, node): 390 | yield self.dump_node(node["result"]) 391 | yield self.dump_node_list(node["generators"]) 392 | 393 | @node() 394 | def generator_comprehension(self, node): 395 | yield "(" 396 | yield self.dump_node(node["result"]) 397 | yield self.dump_node_list(node["generators"]) 398 | yield ")" 399 | 400 | @node() 401 | def list_comprehension(self, node): 402 | yield "[" 403 | yield self.dump_node(node["result"]) 404 | yield self.dump_node_list(node["generators"]) 405 | yield "]" 406 | 407 | @node() 408 | def comprehension_loop(self, node): 409 | "for x in x" 410 | yield " for " 411 | yield self.dump_node(node["iterator"]) 412 | yield " in " 413 | if isinstance(node["target"], list): 414 | yield self.dump_node_list(node["target"]) 415 | else: 416 | yield self.dump_node(node["target"]) 417 | yield self.dump_node_list(node["ifs"]) 418 | 419 | @node() 420 | def comprehension_if(self, node): 421 | yield " if " 422 | yield self.dump_node(node["value"]) 423 | 424 | @node() 425 | def getitem(self, node): 426 | yield "[" 427 | yield self.dump_node(node["value"]) 428 | yield "]" 429 | 430 | @node() 431 | def slice(self, node): 432 | if node["lower"]: 433 | yield self.dump_node(node["lower"]) 434 | yield self.dump_node_list(node["first_formatting"]) 435 | yield ":" 436 | yield self.dump_node_list(node["second_formatting"]) 437 | if node["upper"]: 438 | yield self.dump_node(node["upper"]) 439 | 440 | if node["has_two_colons"]: 441 | yield self.dump_node_list(node["third_formatting"]) 442 | yield ":" 443 | yield self.dump_node_list(node["fourth_formatting"]) 444 | if node["step"]: 445 | yield self.dump_node(node["step"]) 446 | 447 | @node() 448 | def assignment(self, node): 449 | yield self.dump_node(node["target"]) 450 | yield " " 451 | if node.get("operator"): 452 | # FIXME should probably be a different node type 453 | yield node["operator"] 454 | yield "= " 455 | yield self.dump_node(node["value"]) 456 | 457 | @node() 458 | def unitary_operator(self, node): 459 | yield node["value"] 460 | if node["value"] == "not": 461 | yield " " 462 | yield self.dump_node(node["target"]) 463 | 464 | @node() 465 | def comparison(self, node): 466 | yield self.dump_node(node["first"]) 467 | yield self.maybe_backslash(node["first_formatting"], " ") 468 | yield self.dump_node(node["value"]) 469 | yield self.maybe_backslash(node["second_formatting"], " ") 470 | yield self.dump_node(node["second"]) 471 | 472 | @node("binary_operator") 473 | @node("boolean_operator") 474 | def binary_operator(self, node): 475 | yield self.dump_node(node["first"]) 476 | yield self.maybe_backslash(node["first_formatting"], " ") 477 | yield node["value"].replace("<>", "!=") 478 | yield self.maybe_backslash(node["second_formatting"], " ") 479 | yield self.dump_node(node["second"]) 480 | 481 | @node() 482 | def comparison_operator(self, node): 483 | yield node["first"].replace("<>", "!=") 484 | if node["second"]: 485 | yield " " 486 | yield node["second"] 487 | 488 | @node() 489 | def with_(self, node): 490 | yield "with " 491 | yield self.dump_node_list(node["contexts"]) 492 | yield ":" 493 | self.previous = node 494 | yield self.dump_suite(node["value"]) 495 | 496 | @node() 497 | def with_context_item(self, node): 498 | yield self.dump_node(node["value"]) 499 | if node["as"]: 500 | yield " as " 501 | yield self.dump_node(node["as"]) 502 | 503 | @node() 504 | def del_(self, node): 505 | yield "del " 506 | yield self.dump_node(node["value"]) 507 | 508 | @node() 509 | def yield_(self, node): 510 | yield "yield" 511 | if node["value"]: 512 | yield " " 513 | yield self.dump_node(node["value"]) 514 | 515 | @node() 516 | def yield_atom(self, node): 517 | yield "(yield " 518 | if node["value"]: 519 | yield self.dump_node(node["value"]) 520 | yield ")" 521 | 522 | @node() 523 | def exec_(self, node): 524 | yield "exec " 525 | yield self.dump_node(node["value"]) 526 | if node["globals"]: 527 | yield " in " 528 | yield self.dump_node(node["globals"]) 529 | if node["locals"]: 530 | yield ", " 531 | yield self.dump_node(node["locals"]) 532 | 533 | @node() 534 | def global_(self, node): 535 | yield "global " 536 | yield self.dump_node_list(node["value"]) 537 | 538 | @node() 539 | def while_(self, node): 540 | yield "while " 541 | yield self.dump_node(node["test"]) 542 | yield ":" 543 | self.previous = node 544 | yield self.dump_suite(node["value"]) 545 | if node["else"]: 546 | yield self.dump_node(node["else"]) 547 | 548 | @node() 549 | def for_(self, node): 550 | yield "for " 551 | yield self.dump_node(node["iterator"]) 552 | yield " in " 553 | yield self.dump_node(node["target"]) 554 | yield ":" 555 | self.previous = node 556 | yield self.dump_suite(node["value"]) 557 | if node["else"]: 558 | yield self.dump_node(node["else"]) 559 | 560 | @node() 561 | def if_(self, node): 562 | yield "if " 563 | yield self.dump_node(node["test"]) 564 | yield ":" 565 | self.previous = node 566 | yield self.dump_suite(node["value"]) 567 | 568 | @node() 569 | def elif_(self, node): 570 | yield "elif " 571 | yield self.dump_node(node["test"]) 572 | yield ":" 573 | self.previous = node 574 | yield self.dump_suite(node["value"]) 575 | 576 | @node() 577 | def else_(self, node): 578 | yield "else:" 579 | self.previous = node 580 | yield self.dump_suite(node["value"]) 581 | 582 | @node() 583 | def lambda_(self, node): 584 | yield "lambda" 585 | if node["arguments"]: 586 | yield " " 587 | yield self.dump_node_list(node["arguments"]) 588 | yield ": " 589 | yield self.dump_node(node["value"]) 590 | 591 | @node() 592 | def try_(self, node): 593 | yield "try:" 594 | self.previous = node 595 | yield self.dump_suite(node["value"]) 596 | yield self.dump_node_list(node["excepts"]) 597 | if node["else"]: 598 | yield self.dump_node(node["else"]) 599 | if node["finally"]: 600 | yield self.dump_node(node["finally"]) 601 | 602 | @node() 603 | def except_(self, node): 604 | yield "except" 605 | if node["exception"]: 606 | yield " " 607 | yield self.dump_node(node["exception"]) 608 | if node["delimiter"]: 609 | if node["delimiter"] == "as": 610 | yield " " 611 | yield node["delimiter"] 612 | yield " " 613 | yield self.dump_node(node["target"]) 614 | yield ":" 615 | self.previous = node 616 | yield self.dump_suite(node["value"]) 617 | 618 | @node() 619 | def finally_(self, node): 620 | yield "finally:" 621 | self.previous = node 622 | yield self.dump_suite(node["value"]) 623 | 624 | @node("dict") 625 | @node("set") 626 | def dict_or_set(self, node): 627 | yield "{" 628 | yield self._dump_data_structure_body(node) 629 | yield "}" 630 | 631 | @node() 632 | def dictitem(self, node): 633 | yield self.dump_node(node["key"]) 634 | yield ": " 635 | yield self.dump_node(node["value"]) 636 | 637 | @node() 638 | def import_(self, node): 639 | to_yield = [] 640 | for i in filter(lambda x: x["type"] != "comma", node["value"]): 641 | to_yield.append("import " + self.dump_node(i)) 642 | yield ("\n" + self._current_indent).join(to_yield) 643 | 644 | @node() 645 | def from_import(self, node): 646 | yield "from " 647 | yield self.dump_node_list(node["value"]) 648 | yield " import " 649 | yield self.dump_node_list(node["targets"]) 650 | 651 | @node() 652 | def dotted_as_name(self, node): 653 | yield self.dump_node_list(node["value"]) 654 | if node["target"]: 655 | yield " as " 656 | yield node["target"] 657 | 658 | @node() 659 | def name_as_name(self, node): 660 | yield node["value"] 661 | if node["target"]: 662 | yield " as " 663 | yield node["target"] 664 | 665 | @node() 666 | def print_(self, node): 667 | yield "print" 668 | if node["destination"]: 669 | yield " >>" 670 | yield self.dump_node(node["destination"]) 671 | if node["value"]: 672 | to_yield = self.dump_node_list(node["value"]) 673 | if to_yield.startswith("(") and to_yield.endswith(")") and "," not in to_yield: 674 | pass 675 | elif node["value"][0]["type"] != "comma": 676 | yield " " 677 | yield to_yield 678 | 679 | dumps = dump_node_list 680 | 681 | def dump_data_structure(self, content, indent): 682 | yield "\n " + indent 683 | to_yield = "" 684 | self._current_indent = indent + " " 685 | for i in content: 686 | if i["type"] != "comma": 687 | to_yield += self.dump_node(i) 688 | else: 689 | to_yield += ",\n " + indent 690 | to_yield = to_yield.rstrip() 691 | yield to_yield 692 | yield "\n" + indent 693 | self._current_indent = self._current_indent[:-4] 694 | 695 | 696 | def format_code(source_code): 697 | return Dumper().dump_root(baron.parse(source_code)) 698 | 699 | 700 | def main(): 701 | parser = argparse.ArgumentParser( 702 | description='Auto format a python file following the pep8 convention.') 703 | parser.add_argument( 704 | 'file_name', metavar='file_name', type=str, help='file name') 705 | parser.add_argument('-i', dest='in_place', action='store_true', 706 | default=False, help='in place modification, like sed') 707 | 708 | args = parser.parse_args() 709 | if not os.path.exists(args.file_name): 710 | sys.stderr.write( 711 | "Error: the file '%s' does not exist.\n" % args.file_name) 712 | sys.exit(1) 713 | 714 | if not args.in_place: 715 | sys.stdout.write(format_code(open(args.file_name, "r").read())) 716 | else: 717 | result = format_code(open(args.file_name, "r").read()) 718 | open(args.file_name, "w").write(result) 719 | 720 | 721 | if __name__ == '__main__': 722 | main() 723 | -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | baron 2 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [wheel] 2 | universal = 1 3 | 4 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | # -*- coding:Utf-8 -*- 3 | 4 | from setuptools import setup 5 | 6 | try: 7 | from pypandoc import convert 8 | read_md = lambda f: convert(f, 'rst') 9 | except ImportError: 10 | print("warning: pypandoc module not found, could not convert Markdown to RST") 11 | read_md = lambda f: open(f, 'r').read() 12 | 13 | 14 | setup(name='pyfmt', 15 | version='0.1', 16 | description='automatic code formatter for python following pep8 using baron FST, like gofmt', 17 | author='Laurent Peuch', 18 | long_description=read_md("README.md") + "\n\n" + open("CHANGELOG", "r").read(), 19 | author_email='cortex@worlddomination.be', 20 | url='https://github.com/Psycojoker/pyfmt', 21 | install_requires=['baron<0.5,<=0.4'], 22 | license='gplv3+', 23 | keywords='pep8 formatting baron fst code fmt gofmt', 24 | py_modules=['pyfmt'], 25 | entry_points={ 26 | 'console_scripts': [ 27 | 'pyfmt = pyfmt:main', 28 | ] 29 | }, 30 | classifiers=[ 31 | 'Development Status :: 4 - Beta', 32 | 'Environment :: Console', 33 | 'Intended Audience :: Developers', 34 | 'License :: OSI Approved :: GNU General Public License v3 or later (GPLv3+)', 35 | 'Programming Language :: Python :: 2.7', 36 | 'Topic :: Software Development :: Libraries', 37 | 'Topic :: Software Development :: Quality Assurance', 38 | 'Topic :: Utilities', 39 | ] 40 | ) 41 | -------------------------------------------------------------------------------- /test_pyfmt.py: -------------------------------------------------------------------------------- 1 | import baron 2 | from pyfmt import format_code, find 3 | 4 | 5 | def test_empty(): 6 | assert format_code("") == "" 7 | 8 | 9 | def test_ternary_operator(): 10 | assert format_code("a if b else c") == "a if b else c" 11 | 12 | 13 | def test_ellipsis(): 14 | assert format_code("a[. . .]") == "a[...]" 15 | 16 | 17 | def test_dot(): 18 | assert format_code("a . b") == "a.b" 19 | 20 | 21 | def test_comma(): 22 | assert format_code("a , c") == "a, c" 23 | 24 | 25 | def test_call(): 26 | assert format_code("a ( b )") == "a(b)" 27 | 28 | 29 | def test_class_simple(): 30 | assert format_code("class A :\n pass") == "class A:\n pass\n" 31 | assert format_code("class A ( ) :\n pass") == "class A():\n pass\n" 32 | assert format_code("class A ( zob ) :\n pass") == "class A(zob):\n pass\n" 33 | assert format_code("class A ( zob , plop ) :\n pass") == "class A(zob, plop):\n pass\n" 34 | 35 | 36 | def test_repr(): 37 | assert format_code("` a `") == "repr(a)" 38 | 39 | 40 | def test_list(): 41 | assert format_code("[ a , b , c ]") == "[a, b, c]" 42 | 43 | 44 | def test_associative_parenthesis(): 45 | assert format_code("( b )") == "(b)" 46 | 47 | 48 | def test_tuple(): 49 | assert format_code("a , b") == "a, b" 50 | assert format_code("( a , b )") == "(a, b)" 51 | 52 | 53 | def test_funcdef(): 54 | assert format_code("def a ( ) :\n pass") == "def a():\n pass\n" 55 | 56 | 57 | def test_call_argument(): 58 | assert format_code("a( b = c )") == "a(b=c)" 59 | 60 | 61 | def test_def_arguments(): 62 | assert format_code("def a( b =False):\n pass") == "def a(b=False):\n pass\n" 63 | 64 | 65 | def test_def_arguments_list_argument(): 66 | assert format_code("def a( * b ):\n pass") == "def a(*b):\n pass\n" 67 | 68 | 69 | def test_def_arguments_dict_argument(): 70 | assert format_code("def a( ** b ):\n pass") == "def a(**b):\n pass\n" 71 | 72 | 73 | def test_return(): 74 | assert format_code("return") == "return" 75 | assert format_code("return a") == "return a" 76 | 77 | 78 | def test_raise(): 79 | assert format_code("raise Exception()") == "raise Exception()" 80 | assert format_code("raise Exception() , b") == "raise Exception(), b" 81 | assert format_code("raise Exception() , b , c") == "raise Exception(), b, c" 82 | 83 | 84 | def test_assert(): 85 | assert format_code("assert a") == "assert a" 86 | assert format_code("assert a , b") == "assert a, b" 87 | 88 | 89 | def test_set_comprehension(): 90 | assert format_code("{ x for x in x }") == "{x for x in x}" 91 | 92 | 93 | def test_dict_comprehension(): 94 | assert format_code("{ x : z for x in x }") == "{x: z for x in x}" 95 | 96 | 97 | def test_generator_comprehension(): 98 | assert format_code("( x for x in x )") == "(x for x in x)" 99 | 100 | 101 | def test_list_comprehension(): 102 | assert format_code("[ x for x in x ]") == "[x for x in x]" 103 | 104 | 105 | def test_if_comprehension(): 106 | assert format_code("[ x for x in x if x]") == "[x for x in x if x]" 107 | 108 | 109 | def test_getitem(): 110 | assert format_code("a[ b ]") == "a[b]" 111 | 112 | 113 | def test_assignement(): 114 | assert format_code("a = b") == "a = b" 115 | 116 | 117 | def test_augassign(): 118 | assert format_code("a += b") == "a += b" 119 | 120 | 121 | def test_unitary_operator(): 122 | assert format_code("- a") == "-a" 123 | 124 | 125 | def test_binary_operator(): 126 | assert format_code("a + b") == "a + b" 127 | 128 | 129 | def test_comparison(): 130 | assert format_code("a > b") == "a > b" 131 | 132 | 133 | def test_boolean_operator(): 134 | assert format_code("a and b") == "a and b" 135 | 136 | 137 | def test_not_in(): 138 | assert format_code("a not in b") == "a not in b" 139 | 140 | 141 | def test_with(): 142 | assert format_code("with a :\n pass") == "with a:\n pass\n" 143 | 144 | 145 | def test_with_as(): 146 | assert format_code("with a as b :\n pass") == "with a as b:\n pass\n" 147 | 148 | 149 | def test_del(): 150 | assert format_code("del a") == "del a" 151 | 152 | 153 | def test_yield(): 154 | assert format_code("yield") == "yield" 155 | assert format_code("yield a") == "yield a" 156 | 157 | 158 | def test_yield_atom(): 159 | assert format_code("a = ( yield a )") == "a = (yield a)" 160 | 161 | 162 | def test_exec(): 163 | assert format_code("exec a") == "exec a" 164 | assert format_code("exec a in b") == "exec a in b" 165 | assert format_code("exec a in b , c") == "exec a in b, c" 166 | 167 | 168 | def test_global(): 169 | assert format_code("global a") == "global a" 170 | 171 | 172 | def test_while(): 173 | assert format_code("while a :\n pass") == "while a:\n pass\n" 174 | 175 | 176 | def test_for(): 177 | assert format_code("for a in b :\n pass") == "for a in b:\n pass\n" 178 | 179 | 180 | def test_if(): 181 | assert format_code("if a :\n pass") == "if a:\n pass\n" 182 | 183 | 184 | def test_elif(): 185 | assert format_code("if a :\n pass\nelif b :\n pass\n") == "if a:\n pass\nelif b:\n pass\n" 186 | 187 | 188 | def test_else(): 189 | assert format_code("if a :\n pass\nelse :\n pass\n") == "if a:\n pass\nelse:\n pass\n" 190 | 191 | 192 | def test_lambda(): 193 | assert format_code("lambda : a") == "lambda: a" 194 | assert format_code("lambda a : a") == "lambda a: a" 195 | 196 | 197 | def test_try(): 198 | assert format_code("try :\n pass\nexcept:\n pass\n") == "try:\n pass\nexcept:\n pass\n" 199 | 200 | 201 | def test_except(): 202 | assert format_code("try:\n pass\nexcept :\n pass\n") == "try:\n pass\nexcept:\n pass\n" 203 | assert format_code("try:\n pass\nexcept a :\n pass\n") == "try:\n pass\nexcept a:\n pass\n" 204 | assert format_code("try:\n pass\nexcept a , b :\n pass\n") == "try:\n pass\nexcept a, b:\n pass\n" 205 | assert format_code("try:\n pass\nexcept a as b :\n pass\n") == "try:\n pass\nexcept a as b:\n pass\n" 206 | 207 | 208 | def test_finally(): 209 | assert format_code("try:\n pass\nfinally :\n pass\n") == "try:\n pass\nfinally:\n pass\n" 210 | 211 | 212 | def test_dict(): 213 | assert format_code("{ }") == "{}" 214 | assert format_code("{ a : b }") == "{a: b}" 215 | assert format_code("{ a : b , c : d }") == "{a: b, c: d}" 216 | 217 | 218 | def test_import(): 219 | assert format_code("import a") == "import a" 220 | 221 | 222 | def test_import_dot(): 223 | assert format_code("import a . b . c") == "import a.b.c" 224 | 225 | 226 | def test_from_import(): 227 | assert format_code("from . a import b , c") == "from .a import b, c" 228 | 229 | 230 | def test_import_as(): 231 | assert format_code("import a as b") == "import a as b" 232 | assert format_code("from c import a as b") == "from c import a as b" 233 | 234 | 235 | def test_print(): 236 | assert format_code("print a") == "print a" 237 | assert format_code("print >> a , b") == "print >>a, b" 238 | assert format_code("print >> a , b , c , d") == "print >>a, b, c, d" 239 | 240 | 241 | def test_find_endl_empty_string(): 242 | assert find('endl', "") == None 243 | 244 | 245 | def test_find_endl_dict_not_good(): 246 | assert find('endl', {'type': 'pouet'}) == None 247 | 248 | 249 | def test_find_endl_empty_list(): 250 | assert find('endl', []) == None 251 | 252 | 253 | def test_find_endl_list_not_good(): 254 | assert find('endl', [{'type': 'pouet'}]) == None 255 | 256 | 257 | def test_find_endl_dict_good(): 258 | assert find('endl', {'type': 'endl'}) == {'type': 'endl'} 259 | 260 | 261 | def test_find_endl_list_one(): 262 | assert find('endl', [{'type': 'endl'}]) == {'type': 'endl'} 263 | 264 | 265 | def test_find_endl_list_two(): 266 | assert find('endl', [{'type': 'not_endl'}, {'type': 'endl'}]) == {'type': 'endl'} 267 | 268 | 269 | def test_find_endl_first_one(): 270 | assert find('endl', [{'type': 'endl', 'stuff': 'is_the_first'}, {'type': 'endl'}]) == {'type': 'endl', 'stuff': 'is_the_first'} 271 | 272 | 273 | def test_find_endl_recursive(): 274 | assert find('endl', {'type': 'pouet', 'stuff': {'type': 'endl'}}) == {'type': 'endl'} 275 | assert find('endl', {'type': 'pouet', 'stuff': [{'type': 'endl'}]}) == {'type': 'endl'} 276 | 277 | 278 | def test_find_endl_functionnal(): 279 | assert find('endl', baron.parse("[a, b, c]")[0]["value"]) == None 280 | assert find('endl', baron.parse("[a, b,\n c]")[0]["value"]) == {'formatting': [], 'indent': ' ', 'type': 'endl', 'value': '\n'} 281 | 282 | 283 | def test_preserve_indent_in_list_in_root(): 284 | assert format_code("[\n a,\n b,\n]") == "[\n a,\n b,\n]" 285 | 286 | 287 | def test_preserve_indent_in_list_indented(): 288 | assert format_code("if a:\n a = [\n a,\n b,\n ]") == "if a:\n a = [\n a,\n b,\n ]\n" 289 | 290 | 291 | def test_preserve_indent_in_set_in_root(): 292 | assert format_code("{\n a,\n b,\n}") == "{\n a,\n b,\n}" 293 | 294 | 295 | def test_preserve_indent_in_set_indented(): 296 | assert format_code("if a:\n a = {\n a,\n b,\n }") == "if a:\n a = {\n a,\n b,\n }\n" 297 | 298 | 299 | def test_preserve_indent_in_dict_in_root(): 300 | assert format_code("{\n a,\n b,\n}") == "{\n a,\n b,\n}" 301 | 302 | 303 | def test_preserve_indent_in_dict_indented(): 304 | assert format_code("if a:\n a = {\n a,\n b,\n }") == "if a:\n a = {\n a,\n b,\n }\n" 305 | 306 | 307 | def test_preserve_indent_in_tuple_in_root(): 308 | assert format_code("(\n a,\n b,\n)") == "(\n a,\n b,\n)" 309 | 310 | 311 | def test_preserve_indent_in_tuple_indented(): 312 | assert format_code("if a:\n a = (\n a,\n b,\n )") == "if a:\n a = (\n a,\n b,\n )\n" 313 | 314 | 315 | nested_data_structure = """ 316 | a = [ 317 | { 318 | a, 319 | b, 320 | }, 321 | ] 322 | """ 323 | 324 | 325 | def test_nested_data_structure(): 326 | assert format_code(nested_data_structure) == nested_data_structure 327 | 328 | 329 | def test_not_keyword(): 330 | assert format_code("not a") == "not a" 331 | 332 | 333 | def test_comment(): 334 | assert format_code("#!/bin/bash") == "#!/bin/bash" 335 | assert format_code("#pouet") == "# pouet" 336 | assert format_code("##pouet") == "##pouet" 337 | 338 | 339 | def test_decorator(): 340 | assert format_code("@a\ndef a():\n pass") == "@a\ndef a():\n pass\n" 341 | assert format_code("@a\nclass a():\n pass") == "@a\nclass a():\n pass\n" 342 | 343 | 344 | def test_string_formatting_special_case(): 345 | assert format_code("'a' + 1") == "'a' + 1" 346 | assert format_code("'a'\n'b'") == "'a'\n'b'" 347 | 348 | 349 | def test_before_comment_formatting(): 350 | assert format_code("a # b") == "a # b" 351 | 352 | 353 | def test_comment_not_empty_line(): 354 | assert format_code("'1' # b") == "'1' # b" 355 | 356 | 357 | def test_comment_empty_line(): 358 | assert format_code("# b") == "# b" 359 | 360 | 361 | def test_comment_after_colon_statement(): 362 | assert format_code("try:# pouet\n pass\nexcept:\n pass\n") == "try: # pouet\n pass\nexcept:\n pass\n" 363 | 364 | 365 | def test_comment_empty_line_indented(): 366 | assert format_code("if a:\n # pouet\n pass\n") == "if a:\n # pouet\n pass\n" 367 | 368 | 369 | def test_add_endl_one_line_suite_if(): 370 | assert format_code("if a: pass") == "if a:\n pass\n" 371 | 372 | 373 | def test_add_endl_one_line_suite_elif(): 374 | assert format_code("if a:\n pass\nelif a: pass") == "if a:\n pass\nelif a:\n pass\n" 375 | 376 | 377 | def test_add_endl_one_line_suite_else(): 378 | assert format_code("if a:\n pass\nelse: pass") == "if a:\n pass\nelse:\n pass\n" 379 | 380 | 381 | def test_add_endl_one_line_suite_for(): 382 | assert format_code("for a in a: pass") == "for a in a:\n pass\n" 383 | 384 | 385 | def test_add_endl_one_line_suite_try(): 386 | assert format_code("try: pass\nexcept:\n pass\n") == "try:\n pass\nexcept:\n pass\n" 387 | 388 | 389 | def test_add_endl_one_line_suite_except(): 390 | assert format_code("try:\n pass\nexcept: pass\n") == "try:\n pass\nexcept:\n pass\n" 391 | 392 | 393 | def test_add_endl_one_line_suite_finally(): 394 | assert format_code("try:\n pass\nfinally: pass\n") == "try:\n pass\nfinally:\n pass\n" 395 | 396 | 397 | def test_add_endl_one_line_suite_with(): 398 | assert format_code("with a: pass") == "with a:\n pass\n" 399 | 400 | 401 | def test_add_endl_one_line_suite_class(): 402 | assert format_code("class a: pass") == "class a:\n pass\n" 403 | 404 | 405 | def test_add_endl_one_line_suite_while(): 406 | assert format_code("while a: pass") == "while a:\n pass\n" 407 | 408 | 409 | def test_add_endl_one_line_suite_funcdef(): 410 | assert format_code("def a(): pass") == "def a():\n pass\n" 411 | 412 | 413 | def test_replace_repr_by_function_call(): 414 | assert format_code("`a`") == "repr(a)" 415 | 416 | 417 | one_line_import = "import a, b.e.f, c as pouet, d.d as i" 418 | split_import = """\ 419 | import a 420 | import b.e.f 421 | import c as pouet 422 | import d.d as i""" 423 | 424 | 425 | one_line_import_indented = "if a:\n import a, b.e.f, c as pouet, d.d as i" 426 | split_import_indented = """\ 427 | if a: 428 | import a 429 | import b.e.f 430 | import c as pouet 431 | import d.d as i 432 | """ 433 | 434 | 435 | def test_split_import(): 436 | assert format_code(one_line_import) == split_import 437 | 438 | 439 | def test_split_import_indented(): 440 | assert format_code(one_line_import_indented) == split_import_indented 441 | 442 | 443 | comment_2_spaces_target = """\ 444 | class A:# stuff 445 | def a():# stuff 446 | try:# stuff 447 | pass 448 | except:# stuff 449 | pass 450 | finally:# stuff 451 | pass 452 | for a in a:# stuff 453 | pass 454 | """ 455 | 456 | 457 | comment_2_spaces_target_result = """\ 458 | class A: # stuff 459 | def a(): # stuff 460 | try: # stuff 461 | pass 462 | except: # stuff 463 | pass 464 | finally: # stuff 465 | pass 466 | for a in a: # stuff 467 | pass 468 | """ 469 | 470 | 471 | def test_comment_indented_after_try(): 472 | assert format_code(comment_2_spaces_target) == comment_2_spaces_target_result 473 | 474 | 475 | def test_tuple_trailing(): 476 | assert format_code("(3,)") == "(3,)" 477 | 478 | 479 | def test_list_trailing(): 480 | assert format_code("[3,]") == "[3,]" 481 | 482 | 483 | def test_set_trailing(): 484 | assert format_code("{3,}") == "{3,}" 485 | 486 | 487 | def test_dict_trailing(): 488 | assert format_code("{3: 3,}") == "{3: 3,}" 489 | 490 | 491 | def test_empty_comment_no_space(): 492 | assert format_code("#") == "#" 493 | 494 | 495 | def test_simily_print_function_stuff(): 496 | assert format_code("print(a)") == "print(a)" 497 | 498 | 499 | def test_replace_tabs(): 500 | assert format_code("if a:\n if b:\n pass\n\n") == "if a:\n if b:\n pass\n\n" 501 | 502 | bad_indentation = """\ 503 | if a: 504 | if b: 505 | if c: 506 | if d: 507 | pouet 508 | plop 509 | pop 510 | """ 511 | 512 | bad_indentation_fixed = """\ 513 | if a: 514 | if b: 515 | if c: 516 | if d: 517 | pouet 518 | plop 519 | pop 520 | """ 521 | 522 | 523 | def test_fix_bad_indentation_simple_too_small(): 524 | assert format_code("if a:\n pass") == "if a:\n pass\n" 525 | 526 | 527 | def test_fix_bad_indentation_simple_too_big(): 528 | assert format_code("if a:\n pass") == "if a:\n pass\n" 529 | 530 | 531 | def test_fix_indentation_complex(): 532 | print("result:") 533 | print(format_code(bad_indentation)) 534 | print("expected:") 535 | print(bad_indentation_fixed) 536 | assert format_code(bad_indentation) == bad_indentation_fixed 537 | 538 | bug_reindent_tabs = """ 539 | if b: 540 | if a: 541 | pass 542 | pass 543 | pass 544 | """ 545 | 546 | bug_reindent_tabs_fixed = """ 547 | if b: 548 | if a: 549 | pass 550 | pass 551 | pass 552 | """ 553 | 554 | 555 | def test_bug_reindent_tabs(): 556 | assert format_code(bug_reindent_tabs) == bug_reindent_tabs_fixed 557 | 558 | 559 | root_level_function = """ 560 | pouet 561 | import a 562 | def plop(): 563 | pass 564 | plop 565 | pop 566 | """ 567 | 568 | 569 | root_level_function_fixed = """ 570 | pouet 571 | import a 572 | 573 | 574 | def plop(): 575 | pass 576 | 577 | 578 | plop 579 | pop 580 | """ 581 | 582 | 583 | def test_blank_lines_arround_functions_first_level(): 584 | assert format_code(root_level_function) == root_level_function_fixed 585 | 586 | 587 | def test_blank_lines_arround_class_first_level(): 588 | assert format_code(root_level_function.replace('def', 'class')) == root_level_function_fixed.replace('def', 'class') 589 | 590 | 591 | def test_replace_windows_endl(): 592 | assert format_code("\r\n") == "\n" 593 | 594 | 595 | class_level_function = """\ 596 | class A: 597 | def plop(): 598 | pass 599 | def pop(): 600 | pass 601 | def ploup(): 602 | pass 603 | """ 604 | 605 | 606 | class_level_function_fixed = """\ 607 | class A: 608 | def plop(): 609 | pass 610 | 611 | def pop(): 612 | pass 613 | 614 | def ploup(): 615 | pass 616 | """ 617 | 618 | 619 | def test_blank_lines_arround_methods(): 620 | assert format_code(class_level_function) == class_level_function_fixed 621 | 622 | 623 | def test_split_semicolon(): 624 | assert format_code("a;b") == "a\nb" 625 | 626 | 627 | def test_split_semicolon_indented(): 628 | assert format_code("\n a;b") == "\n a\n b" 629 | 630 | 631 | def test_replace_old_comparison_operator(): 632 | assert format_code("a <> b") == "a != b" 633 | 634 | 635 | comment_previous_endl_indent = """\ 636 | class A: 637 | a = b 638 | # should not be indented 639 | b = c 640 | """ 641 | 642 | 643 | def test_comment_previous_endl_indent_regression_test(): 644 | assert format_code(comment_previous_endl_indent) == comment_previous_endl_indent 645 | 646 | 647 | def test_respect_backslash(): 648 | respect_backslash = "a ==\\\n b" 649 | assert format_code(respect_backslash) == respect_backslash 650 | respect_backslash = "a \\\n== b" 651 | assert format_code(respect_backslash) == respect_backslash 652 | 653 | 654 | # Disable test because the many line statement is a big subject 655 | # def test_on_Self(): 656 | # assert format_code(open("./pyfmt.py", "r").read()) == open("./pyfmt.py", "r").read() 657 | 658 | 659 | def test_on_self_tests(): 660 | assert format_code(open("./test_pyfmt.py", "r").read()) == open("./test_pyfmt.py", "r").read() 661 | --------------------------------------------------------------------------------