Skip to content

Commit

Permalink
feat(formatter): initial setup and config handling for formatter
Browse files Browse the repository at this point in the history
  • Loading branch information
0xtekgrinder committed Sep 22, 2024
1 parent 9576370 commit 22c12d2
Show file tree
Hide file tree
Showing 13 changed files with 215 additions and 1 deletion.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@

**/target

.idea
.idea
__pycache__
Empty file added libs/formatter/README.md
Empty file.
Empty file.
50 changes: 50 additions & 0 deletions libs/formatter/converter/format.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
def node_to_string(node):
ast_type = node["ast_type"]
if ast_type == "Module":
return node_to_string(node["body"])
elif ast_type == "FunctionDef":
return f"def {node['name']}({node_to_string(node['args'])}):"
elif ast_type == "arguments":
return ", ".join([node_to_string(arg) for arg in node["args"]])
elif ast_type == "arg":
return node["arg"]
elif ast_type == "Expr":
return node_to_string(node["value"])
elif ast_type == "Call":
return f"{node_to_string(node['func'])}({node_to_string(node['args'])})"
elif ast_type == "Name":
return node["id"]
elif ast_type == "Constant":
return str(node["value"])
elif ast_type == "BinOp":
return f"{node_to_string(node['left'])} {node['op']} {node_to_string(node['right'])}"
elif ast_type == "Return":
return f"return {node_to_string(node['value'])}"
elif ast_type == "Assign":
return f"{node_to_string(node['targets'])} = {node_to_string(node['value'])}"
elif ast_type == "List":
return f"[{node_to_string(node['elts'])}]"
elif ast_type == "Subscript":
return f"{node_to_string(node['value'])}[{node_to_string(node['slice'])}]"
elif ast_type == "Index":
return node_to_string(node["value"])
elif ast_type == "Slice":
return f"{node_to_string(node['lower'])}:{node_to_string(node['upper'])}"
elif ast_type == "Attribute":
return f"{node_to_string(node['value'])}.{node['attr']}"
elif ast_type == "If":
return f"if {node_to_string(node['test'])}:"
elif ast_type == "Compare":
return f"{node_to_string(node['left'])} {node_to_string(node['ops'])} {node_to_string(node['comparators'])}"
elif ast_type == "Eq":
return "=="
elif ast_type == "Gt":
return ">"
elif ast_type == "Lt":
return "<"
elif ast_type == "GtE":
return ">="

def convert_ast_to_string(ast, config):
## Handle the config and ast tabs
return node_to_string(ast["ast"])
2 changes: 2 additions & 0 deletions libs/formatter/converter/format2.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
def convert_ast_to_string(ast, config):
return ""
Empty file.
42 changes: 42 additions & 0 deletions libs/formatter/formatter/lib.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
from ast_extractor.ast import filepath_vyper_to_ast
from converter.format2 import convert_ast_to_string
from difflib import unified_diff
import os

def diff_files(original, formatted):
original_lines = original.splitlines()
formatted_lines = formatted.splitlines()

diff = unified_diff(original_lines, formatted_lines, lineterm='')
return '\n'.join(list(diff))

def format_file(file_path, config, check, raw):
ast = filepath_vyper_to_ast(file_path)
if ast is not None:
formatted_code = convert_ast_to_string(ast, config)
if check:
with open(file_path) as reader:
if raw:
return formatted_code
else:
original_code = reader.read()
diff = diff_files(original_code, formatted_code)
if diff != "":
return diff
else:
with open(file_path, "w") as writer:
writer.write(formatted_code)

def format_root(root, config, check, raw):
output = []
if os.path.isdir(root):
for root, _, files in os.walk(root):
for file in files:
if file.endswith(".vy"):
file_path = os.path.join(root, file)
output_file = format_file(file_path, config, check, raw)
if output_file is not None:
output.append({"filename": file_path, "output": output_file})
else:
raise ValueError("Root path is not a directory")
return output
31 changes: 31 additions & 0 deletions libs/formatter/formatter/main.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import argparse
import os

from rules.config import init_config
from .lib import format_root

def start() -> None:
parser = argparse.ArgumentParser(description='Vyper Formatter')
parser.add_argument('--root', type=str, help="The project's root path to format\n\nBy default root of the current working directory", default=".")
parser.add_argument('--check', action='store_true', help="Run in 'check' mode.\n\nExits with 0 if input is formatted correctly. Exits with 1 if formatting is required", default=False)
parser.add_argument('--raw', action='store_true', help="In 'check' and stdin modes, outputs raw formatted code instead of the diff", default=False)
parser.add_argument('--config', type=str, help="Path to the configuration file\n\nBy default search for a `vyfmt.toml` inside the defined root", default=None)
args = parser.parse_args()

# Read root directory and check if it exists
if not os.path.exists(args.root):
print(f"Error: Root path '{args.root}' does not exist")
exit(1)

config = init_config(args.root, args.config)
output = format_root(args.root, config, args.check, args.raw)

if args.check:
if len(output) != 0:
for file in output:
print(file["filename"])
print(file["output"])
print()
exit(1)
else:
exit(0)
30 changes: 30 additions & 0 deletions libs/formatter/poetry.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

19 changes: 19 additions & 0 deletions libs/formatter/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
[tool.poetry]
name = "formatter"
version = "0.1.0"
description = ""
authors = ["0xtekgrinder <0xtekgrinder@protonmail.com>"]
readme = "README.md"

[tool.poetry.dependencies]
python = "^3.10"
ast-extractor = {path = "../../libs/ast-extractor"}
tomli = "^2.0.1"


[build-system]
requires = ["poetry-core"]
build-backend = "poetry.core.masonry.api"

[tool.poetry.scripts]
start = "formatter.main:start"
Empty file.
39 changes: 39 additions & 0 deletions libs/formatter/rules/config.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import os
import tomli

rules_types = [
{ "field": "indentation", "type": int, "default": 4 }
]

def init_config(root, config_file):
if config_file is None:
config_file = os.path.join(root, 'vyfmt.toml')

config = {}
modified_fields = []
if os.path.exists(config_file):
with open(config_file) as reader:
content = reader.read()
parsed_file = tomli.loads(content)

for rule in rules_types:
field = rule["field"]
if field in parsed_file:
if type(parsed_file[field]) != rule["type"]:
raise TypeError(f"Field '{field}' in configuration file is not of type '{rule['type']}'")
else:
config[field] = parsed_file[field]
modified_fields.append(field)

for rule in rules_types:
field = rule["field"]
if field not in modified_fields:
config[field] = rule["default"]

return config






Empty file.

0 comments on commit 22c12d2

Please sign in to comment.