sqltidy 0.2.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. sqltidy-0.2.2/LICENSE +21 -0
  2. sqltidy-0.2.2/PKG-INFO +26 -0
  3. sqltidy-0.2.2/README.md +15 -0
  4. sqltidy-0.2.2/pyproject.toml +21 -0
  5. sqltidy-0.2.2/setup.cfg +4 -0
  6. sqltidy-0.2.2/sqltidy/__init__.py +5 -0
  7. sqltidy-0.2.2/sqltidy/api.py +41 -0
  8. sqltidy-0.2.2/sqltidy/cli.py +185 -0
  9. sqltidy-0.2.2/sqltidy/config.py +19 -0
  10. sqltidy-0.2.2/sqltidy/core.py +47 -0
  11. sqltidy-0.2.2/sqltidy/generator.py +210 -0
  12. sqltidy-0.2.2/sqltidy/rules/__init__.py +3 -0
  13. sqltidy-0.2.2/sqltidy/rules/base.py +14 -0
  14. sqltidy-0.2.2/sqltidy/rules/loader.py +88 -0
  15. sqltidy-0.2.2/sqltidy/rules/rewrite/__init__.py +1 -0
  16. sqltidy-0.2.2/sqltidy/rules/rewrite/alias_style_abc.py +124 -0
  17. sqltidy-0.2.2/sqltidy/rules/rewrite/alias_style_t_numeric.py +118 -0
  18. sqltidy-0.2.2/sqltidy/rules/rewrite/subquery_to_cte.py +111 -0
  19. sqltidy-0.2.2/sqltidy/rules/rules.py +253 -0
  20. sqltidy-0.2.2/sqltidy/rules/tidy/__init__.py +1 -0
  21. sqltidy-0.2.2/sqltidy/rules/tidy/compact_whitespace.py +16 -0
  22. sqltidy-0.2.2/sqltidy/rules/tidy/indent_select_columns.py +52 -0
  23. sqltidy-0.2.2/sqltidy/rules/tidy/leading_commas.py +57 -0
  24. sqltidy-0.2.2/sqltidy/rules/tidy/newline_after_select.py +28 -0
  25. sqltidy-0.2.2/sqltidy/rules/tidy/uppercase_keywords.py +17 -0
  26. sqltidy-0.2.2/sqltidy/tokenizer.py +44 -0
  27. sqltidy-0.2.2/sqltidy.egg-info/PKG-INFO +26 -0
  28. sqltidy-0.2.2/sqltidy.egg-info/SOURCES.txt +29 -0
  29. sqltidy-0.2.2/sqltidy.egg-info/dependency_links.txt +1 -0
  30. sqltidy-0.2.2/sqltidy.egg-info/entry_points.txt +2 -0
  31. sqltidy-0.2.2/sqltidy.egg-info/top_level.txt +1 -0
sqltidy-0.2.2/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Jayden Rasband
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
sqltidy-0.2.2/PKG-INFO ADDED
@@ -0,0 +1,26 @@
1
+ Metadata-Version: 2.4
2
+ Name: sqltidy
3
+ Version: 0.2.2
4
+ Summary: SQL Formatting Made Easy
5
+ Author: Jayden Rasband
6
+ Project-URL: Source, https://github.com/jrasband-dev/sqltidy
7
+ Project-URL: Documentation, https://jrasband-dev.github.io/sqltidy/
8
+ Description-Content-Type: text/markdown
9
+ License-File: LICENSE
10
+ Dynamic: license-file
11
+
12
+ # SQLTidy
13
+
14
+ [![PyPI version](https://img.shields.io/pypi/v/sqltidy.svg)](https://pypi.org/project/sqltidy/)
15
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
16
+ [![Documentation Status](https://img.shields.io/badge/docs-online-brightgreen.svg)](https://jrasband-dev.github.io/sqltidy/)
17
+
18
+ Tidy your SQL Scripts
19
+
20
+ For full details and usage, see the [official documentation](https://jrasband-dev.github.io/sqltidy/).
21
+
22
+ ## Getting Started
23
+
24
+ ```bash
25
+ pip install sqltidy
26
+ ```
@@ -0,0 +1,15 @@
1
+ # SQLTidy
2
+
3
+ [![PyPI version](https://img.shields.io/pypi/v/sqltidy.svg)](https://pypi.org/project/sqltidy/)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](LICENSE)
5
+ [![Documentation Status](https://img.shields.io/badge/docs-online-brightgreen.svg)](https://jrasband-dev.github.io/sqltidy/)
6
+
7
+ Tidy your SQL Scripts
8
+
9
+ For full details and usage, see the [official documentation](https://jrasband-dev.github.io/sqltidy/).
10
+
11
+ ## Getting Started
12
+
13
+ ```bash
14
+ pip install sqltidy
15
+ ```
@@ -0,0 +1,21 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [tool.setuptools.dynamic]
6
+ version = {attr = "sqltidy.__version__"}
7
+
8
+ [project]
9
+ name = "sqltidy"
10
+ dynamic = ["version"]
11
+ description = "SQL Formatting Made Easy"
12
+ readme = "README.md"
13
+ authors = [{name = "Jayden Rasband"}]
14
+ dependencies = []
15
+
16
+ [project.urls]
17
+ "Source" = "https://github.com/jrasband-dev/sqltidy"
18
+ "Documentation" = "https://jrasband-dev.github.io/sqltidy/"
19
+
20
+ [project.scripts]
21
+ sqltidy = "sqltidy.cli:main"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,5 @@
1
+ from .api import format_sql, register_plugin, clear_plugins
2
+
3
+ __version__ = "0.2.2"
4
+
5
+ __all__ = ["format_sql", "register_plugin", "clear_plugins", "__version__"]
@@ -0,0 +1,41 @@
1
+ from .config import TidyConfig
2
+ from .rules.base import BaseRule
3
+ from .core import SQLFormatter
4
+
5
+ # In-memory list to hold extra plugin rules registered at runtime
6
+ _extra_plugins = []
7
+
8
+ def register_plugin(rule: BaseRule):
9
+ """
10
+ Register a plugin rule at runtime.
11
+
12
+ Args:
13
+ rule (BaseRule): An instance of a rule to apply.
14
+ """
15
+ if not isinstance(rule, BaseRule):
16
+ raise TypeError("Plugin must be an instance of BaseRule")
17
+ _extra_plugins.append(rule)
18
+
19
+ def clear_plugins():
20
+ """
21
+ Clear all runtime-registered plugin rules.
22
+ """
23
+ _extra_plugins.clear()
24
+
25
+ def format_sql(sql: str, config: TidyConfig = None) -> str:
26
+ """
27
+ Format a SQL string using all registered rules, including runtime plugins.
28
+
29
+ Args:
30
+ sql (str): The SQL string to format.
31
+ config (TidyConfig, optional): Formatter configuration.
32
+
33
+ Returns:
34
+ str: Formatted SQL string.
35
+ """
36
+ formatter = SQLFormatter(config=config)
37
+
38
+ # Inject runtime plugins into the formatter
39
+ formatter.rules.extend(_extra_plugins)
40
+
41
+ return formatter.format(sql)
@@ -0,0 +1,185 @@
1
+ import argparse
2
+ import sys
3
+ import json
4
+ from . import __version__
5
+ from .api import format_sql
6
+ from .config import TidyConfig, RewriteConfig
7
+ from .generator import run_generator, load_config_file
8
+
9
+
10
+ def create_tidy_config_from_file(config_file: str) -> TidyConfig:
11
+ """
12
+ Load TidyConfig from a JSON configuration file.
13
+
14
+ Args:
15
+ config_file: Path to the configuration JSON file
16
+
17
+ Returns:
18
+ TidyConfig: Configuration object with loaded values
19
+ """
20
+ try:
21
+ config_data = load_config_file(config_file)
22
+
23
+ # Create TidyConfig with loaded values (no nesting needed)
24
+ return TidyConfig(**config_data)
25
+ except (FileNotFoundError, json.JSONDecodeError) as e:
26
+ print(f"Error loading config file: {e}", file=sys.stderr)
27
+ sys.exit(1)
28
+
29
+
30
+ def create_rewrite_config_from_file(config_file: str) -> RewriteConfig:
31
+ """
32
+ Load RewriteConfig from a JSON configuration file.
33
+
34
+ Args:
35
+ config_file: Path to the configuration JSON file
36
+
37
+ Returns:
38
+ RewriteConfig: Configuration object with loaded values
39
+ """
40
+ try:
41
+ config_data = load_config_file(config_file)
42
+
43
+ # Create RewriteConfig with loaded values (no nesting needed)
44
+ return RewriteConfig(**config_data)
45
+ except (FileNotFoundError, json.JSONDecodeError) as e:
46
+ print(f"Error loading config file: {e}", file=sys.stderr)
47
+ sys.exit(1)
48
+
49
+
50
+ def main():
51
+ parser = argparse.ArgumentParser(
52
+ description="A SQL formatting tool"
53
+ )
54
+
55
+ parser.add_argument(
56
+ "-v", "--version",
57
+ action="version",
58
+ version=f"%(prog)s {__version__}"
59
+ )
60
+
61
+ # create subparsers for subcommands
62
+ subparsers = parser.add_subparsers(title='Commands', dest="command", required=True)
63
+
64
+ # -------------------
65
+ # tidy Command
66
+ # -------------------
67
+ tidy_parser = subparsers.add_parser(
68
+ name="tidy",
69
+ help="Format a SQL file",
70
+ description="Format SQL with tidy rules."
71
+ )
72
+
73
+ tidy_input_group = tidy_parser.add_argument_group(title='Input')
74
+ tidy_input_group.add_argument("input", nargs="?", help="SQL file to format")
75
+
76
+ tidy_parameter_group = tidy_parser.add_argument_group('Parameters')
77
+ tidy_parameter_group.add_argument("-o", "--output", help="Output file")
78
+ tidy_parameter_group.add_argument("-cfg","--rules", help="Path to custom rules json file")
79
+
80
+
81
+
82
+ # -------------------
83
+ # rewrite Command
84
+ # -------------------
85
+
86
+ rewrite_parser = subparsers.add_parser(
87
+ "rewrite",
88
+ help="Rewrite SQL queries",
89
+ description="Rewrite SQL queries according to specified rules"
90
+ )
91
+
92
+ rewrite_input_group = rewrite_parser.add_argument_group(title='Input')
93
+ rewrite_input_group.add_argument("input", nargs="?", help="SQL file to rewrite")
94
+
95
+ rewrite_parameter_group = rewrite_parser.add_argument_group('Parameters')
96
+ rewrite_parameter_group.add_argument("-o", "--output", help="Output file")
97
+ rewrite_parameter_group.add_argument("-cfg", "--rules", help="Path to custom rules json file")
98
+ # Use config.py defaults for rewrite behavior. No CLI enable/disable flags are provided.
99
+ rewrite_parameter_group.add_argument("--tidy", action="store_true", help="Apply tidy rules after rewriting")
100
+
101
+
102
+ # -------------------
103
+ # config Command
104
+ # -------------------
105
+ config_parser = subparsers.add_parser(
106
+ "config",
107
+ help="Interactive config generator",
108
+ description="Launch an interactive configuration generator for sqltidy"
109
+ )
110
+ # You can add config-specific arguments here if needed
111
+
112
+
113
+
114
+
115
+
116
+
117
+
118
+ # -------------------
119
+ # parse arguments
120
+ # -------------------
121
+ args = parser.parse_args()
122
+
123
+ if args.command == "config":
124
+ run_generator()
125
+ return
126
+
127
+ # tidy command
128
+ if args.command == "tidy":
129
+ if args.input:
130
+ with open(args.input, "r", encoding="utf-8") as f:
131
+ sql = f.read()
132
+ else:
133
+ sql = sys.stdin.read()
134
+
135
+ # Load config from file if provided, otherwise use defaults
136
+ if args.rules:
137
+ config = create_tidy_config_from_file(args.rules)
138
+ else:
139
+ config = TidyConfig()
140
+
141
+ formatted_sql = format_sql(sql, config=config)
142
+
143
+ if args.output:
144
+ with open(args.output, "w", encoding="utf-8") as f:
145
+ f.write(formatted_sql)
146
+ elif args.input:
147
+ # overwrite input file if no output specified
148
+ with open(args.input, "w", encoding="utf-8") as f:
149
+ f.write(formatted_sql)
150
+ else:
151
+ print(formatted_sql)
152
+
153
+ # rewrite command
154
+ if args.command == "rewrite":
155
+ if args.input:
156
+ with open(args.input, "r", encoding="utf-8") as f:
157
+ sql = f.read()
158
+ else:
159
+ sql = sys.stdin.read()
160
+
161
+ # Load config from file if provided, otherwise use defaults
162
+ if args.rules:
163
+ config = create_rewrite_config_from_file(args.rules)
164
+ else:
165
+ config = RewriteConfig()
166
+
167
+ formatted_sql = format_sql(sql, config=config)
168
+
169
+ # Apply tidy rules if requested
170
+ if args.tidy:
171
+ tidy_config = TidyConfig()
172
+ formatted_sql = format_sql(formatted_sql, config=tidy_config)
173
+
174
+ if args.output:
175
+ with open(args.output, "w", encoding="utf-8") as f:
176
+ f.write(formatted_sql)
177
+ elif args.input:
178
+ # overwrite input file if no output specified
179
+ with open(args.input, "w", encoding="utf-8") as f:
180
+ f.write(formatted_sql)
181
+ else:
182
+ print(formatted_sql)
183
+
184
+ if __name__ == "__main__":
185
+ main()
@@ -0,0 +1,19 @@
1
+ # Default Rules
2
+
3
+ from dataclasses import dataclass, field
4
+
5
+ @dataclass
6
+ class TidyConfig:
7
+ uppercase_keywords: bool = True
8
+ newline_after_select: bool = True
9
+ compact: bool = True
10
+ leading_commas: bool = True
11
+ indent_select_columns: bool = True
12
+
13
+
14
+
15
+ @dataclass
16
+ class RewriteConfig:
17
+ enable_subquery_to_cte: bool = True
18
+ enable_alias_style_abc: bool = False
19
+ enable_alias_style_t_numeric: bool = False
@@ -0,0 +1,47 @@
1
+ # sqltidy/core.py
2
+ import re
3
+ from typing import List
4
+ from .config import TidyConfig
5
+ from .tokenizer import TOKEN_RE
6
+
7
+ class SQLFormatter:
8
+ """Main SQL formatting engine."""
9
+
10
+ def __init__(self, config: TidyConfig = None):
11
+ from .rules import load_rules
12
+ from .rules.base import FormatterContext
13
+ self.ctx = FormatterContext(config or TidyConfig())
14
+ self.rules = load_rules()
15
+
16
+ def tokenize(self, sql: str) -> List[str]:
17
+ """Convert raw SQL into proper tokens without external dependencies."""
18
+ tokens = []
19
+ for groups in TOKEN_RE.findall(sql):
20
+ # Find the first non-empty capturing group
21
+ for t in groups:
22
+ if t == "":
23
+ continue
24
+ # normalize whitespace
25
+ if t.isspace():
26
+ if "\n" in t:
27
+ tokens.append("\n")
28
+ else:
29
+ tokens.append(" ")
30
+ else:
31
+ tokens.append(t)
32
+ break
33
+
34
+ return tokens
35
+
36
+ def format(self, sql: str) -> str:
37
+ tokens = self.tokenize(sql)
38
+
39
+ # Apply your custom rules
40
+ for rule in sorted(self.rules, key=lambda r: getattr(r, "order", 100)):
41
+ tokens = rule.apply(tokens, self.ctx)
42
+
43
+ return self.join_tokens(tokens)
44
+
45
+ def join_tokens(self, tokens: List[str]) -> str:
46
+ """Reassemble tokens into formatted SQL text."""
47
+ return "".join(tokens).strip()
@@ -0,0 +1,210 @@
1
+ """
2
+ Interactive configuration generator for sqltidy.
3
+ Generates config files that can override default settings.
4
+ """
5
+
6
+ import json
7
+ from dataclasses import asdict
8
+ from pathlib import Path
9
+ from typing import Dict, Any
10
+ from .config import TidyConfig, RewriteConfig
11
+
12
+
13
+ TIDY_DESCRIPTIONS = {
14
+ "uppercase_keywords": "Convert SQL keywords to uppercase? (e.g., SELECT, FROM, WHERE)",
15
+ "newline_after_select": "Add newline after SELECT keyword?",
16
+ "compact": "Use compact formatting (reduce unnecessary whitespace)?",
17
+ "leading_commas": "Use leading commas in column lists? (e.g., col1\\n , col2\\n , col3)",
18
+ "indent_select_columns": "Indent SELECT columns on separate lines?",
19
+ }
20
+
21
+ REWRITE_DESCRIPTIONS = {
22
+ "enable_subquery_to_cte": "Convert subqueries to Common Table Expressions (CTEs)?",
23
+ "enable_alias_style_abc": "Apply uppercase A, B, C ... table aliases?",
24
+ "enable_alias_style_t_numeric": "Apply uppercase T1, T2, T3 ... table aliases?",
25
+ }
26
+
27
+
28
+ def prompt_yes_no(question: str, default: bool = True) -> bool:
29
+ """
30
+ Prompt user for a yes/no question.
31
+
32
+ Args:
33
+ question: The question to ask
34
+ default: The default value if user just presses enter
35
+
36
+ Returns:
37
+ bool: The user's choice
38
+ """
39
+ default_str = "[Y/n]" if default else "[y/N]"
40
+ while True:
41
+ response = input(f"{question} {default_str}: ").strip().lower()
42
+ if not response:
43
+ return default
44
+ if response in ("y", "yes"):
45
+ return True
46
+ if response in ("n", "no"):
47
+ return False
48
+ print("Please enter 'y' or 'n'")
49
+
50
+
51
+ def generate_tidy_config() -> Dict[str, Any]:
52
+ """
53
+ Interactively generate a TidyConfig.
54
+ Automatically includes any new boolean fields added to TidyConfig.
55
+ """
56
+ print("\n" + "=" * 60)
57
+ print("TIDY CONFIGURATION GENERATOR")
58
+ print("=" * 60)
59
+ print("Configure SQL formatting rules for the 'tidy' command.\n")
60
+
61
+ config = {}
62
+ default_config = asdict(TidyConfig())
63
+
64
+ for field_name, default_value in default_config.items():
65
+ if not isinstance(default_value, bool):
66
+ continue # only prompt for boolean options
67
+ question = TIDY_DESCRIPTIONS.get(
68
+ field_name,
69
+ f"Enable {field_name.replace('_', ' ')}?"
70
+ )
71
+ config[field_name] = prompt_yes_no(question, default=default_value)
72
+
73
+ return config
74
+
75
+
76
+ def generate_rewrite_config() -> Dict[str, Any]:
77
+ """
78
+ Interactively generate a RewriteConfig.
79
+ Automatically includes any new boolean fields added to RewriteConfig.
80
+ """
81
+ print("\n" + "=" * 60)
82
+ print("REWRITE CONFIGURATION GENERATOR")
83
+ print("=" * 60)
84
+ print("Configure SQL rewriting rules for the 'rewrite' command.\n")
85
+
86
+ config = {}
87
+ default_config = asdict(RewriteConfig())
88
+
89
+ for field_name, default_value in default_config.items():
90
+ if not isinstance(default_value, bool):
91
+ continue # only prompt for boolean options
92
+ question = REWRITE_DESCRIPTIONS.get(
93
+ field_name,
94
+ f"Enable {field_name.replace('_', ' ')}?"
95
+ )
96
+ config[field_name] = prompt_yes_no(question, default=default_value)
97
+
98
+ return config
99
+
100
+
101
+ def select_config_type() -> str:
102
+ """
103
+ Prompt user to select which config type to generate.
104
+
105
+ Returns:
106
+ str: "tidy" or "rewrite"
107
+ """
108
+ print("\n" + "=" * 60)
109
+ print("SQLTIDY CONFIGURATION GENERATOR")
110
+ print("=" * 60)
111
+ print("\nSelect which configuration to generate:\n")
112
+ print("1. Tidy configuration")
113
+ print("2. Rewrite configuration")
114
+
115
+ while True:
116
+ choice = input("\nEnter your choice (1-2): ").strip()
117
+ if choice == "1":
118
+ return "tidy"
119
+ elif choice == "2":
120
+ return "rewrite"
121
+ print("Please enter 1 or 2")
122
+
123
+
124
+ def get_output_filename(config_type: str) -> str:
125
+ """
126
+ Prompt user for output filename.
127
+
128
+ Args:
129
+ config_type: Type of config ("tidy" or "rewrite")
130
+
131
+ Returns:
132
+ str: Output filename
133
+ """
134
+ if config_type == "tidy":
135
+ default = "tidy_config.json"
136
+ else:
137
+ default = "rewrite_config.json"
138
+
139
+ filename = input(f"\nOutput filename [{default}]: ").strip()
140
+ return filename if filename else default
141
+
142
+
143
+ def save_config(config_data: Dict[str, Any], filename: str) -> Path:
144
+ """
145
+ Save configuration to a JSON file.
146
+
147
+ Args:
148
+ config_data: Configuration dictionary
149
+ filename: Output filename
150
+
151
+ Returns:
152
+ Path: Path to the saved file
153
+ """
154
+ filepath = Path(filename)
155
+
156
+ with open(filepath, "w", encoding="utf-8") as f:
157
+ json.dump(config_data, f, indent=2)
158
+
159
+ return filepath
160
+
161
+
162
+ def run_generator():
163
+ """
164
+ Run the interactive configuration generator.
165
+ """
166
+ try:
167
+ config_type = select_config_type()
168
+
169
+ if config_type == "tidy":
170
+ config_data = generate_tidy_config()
171
+ else:
172
+ config_data = generate_rewrite_config()
173
+
174
+ # Get output filename
175
+ output_file = get_output_filename(config_type)
176
+
177
+ # Save config
178
+ filepath = save_config(config_data, output_file)
179
+
180
+ print("\n" + "=" * 60)
181
+ print("✓ Configuration saved successfully!")
182
+ print(f"File: {filepath.absolute()}")
183
+ print("=" * 60)
184
+ print("\nUsage:")
185
+ if config_type == "tidy":
186
+ print(f" sqltidy tidy -cfg {output_file} <input_file>")
187
+ else:
188
+ print(f" sqltidy rewrite -cfg {output_file} <input_file>")
189
+ print()
190
+
191
+ except KeyboardInterrupt:
192
+ print("\n\nConfiguration generation cancelled.")
193
+ return
194
+ except Exception as e:
195
+ print(f"\nError: {e}")
196
+ raise
197
+
198
+
199
+ def load_config_file(filepath: str) -> Dict[str, Any]:
200
+ """
201
+ Load configuration from a JSON file.
202
+
203
+ Args:
204
+ filepath: Path to the configuration file
205
+
206
+ Returns:
207
+ dict: Configuration data
208
+ """
209
+ with open(filepath, "r", encoding="utf-8") as f:
210
+ return json.load(f)
@@ -0,0 +1,3 @@
1
+ from .loader import load_rules
2
+
3
+ __all__ = ["load_rules"]
@@ -0,0 +1,14 @@
1
+ from typing import List
2
+ from ..config import TidyConfig
3
+
4
+ class FormatterContext:
5
+ """Holds configuration for the formatting run."""
6
+ def __init__(self, config: TidyConfig):
7
+ self.config = config
8
+
9
+ class BaseRule:
10
+ """All rules must inherit from this."""
11
+ order = 100
12
+ rule_type = None # "tidy" or "rewrite"
13
+ def apply(self, tokens: List[str], ctx: FormatterContext) -> List[str]:
14
+ raise NotImplementedError