Skip to content

Commit

Permalink
Merge pull request #53 from jonathangreen/feature/sort-first
Browse files Browse the repository at this point in the history
Add option to sort particular keys first in output
  • Loading branch information
pappasam authored Mar 16, 2023
2 parents 26df965 + 986349c commit 91bfb56
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 8 deletions.
10 changes: 7 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,11 +34,11 @@ This project can be used as either a command line utility or a Python library. R
```console
$ toml-sort --help
usage: toml-sort [-h] [--version] [-o OUTPUT] [-i] [-I] [-a] [--no-sort-tables] [--sort-table-keys]
[--sort-inline-tables] [--sort-inline-arrays] [--no-header] [--no-comments] [--no-header-comments]
[--no-footer-comments] [--no-inline-comments] [--no-block-comments]
[--sort-inline-tables] [--sort-inline-arrays] [--sort-first KEYS] [--no-header] [--no-comments]
[--no-header-comments] [--no-footer-comments] [--no-inline-comments] [--no-block-comments]
[--spaces-before-inline-comment {1,2,3,4}] [--spaces-indent-inline-array {2,4,6,8}]
[--trailing-comma-inline-array] [--check]
[F [F ...]]
[F ...]

Toml sort: a sorting utility for toml files.

Expand All @@ -64,6 +64,8 @@ sort:
--sort-table-keys Sort the keys in tables and arrays of tables (excluding inline tables and arrays).
--sort-inline-tables Sort inline tables.
--sort-inline-arrays Sort inline arrays.
--sort-first KEYS Table keys that will be sorted first in the output. Multiple keys can be given separated by a
comma.

comments:
exclude comments from output
Expand Down Expand Up @@ -120,6 +122,7 @@ no_footer_comments = true
no_inline_comments = true
no_block_comments = true
no_sort_tables = true
sort_first = ["key1", "key2"]
sort_table_keys = true
sort_inline_tables = true
sort_inline_arrays = true
Expand All @@ -137,6 +140,7 @@ Only the following options can be included in an override:

```toml
[tool.tomlsort.overrides."path.to.key"]
first = ["key1", "key2"]
table_keys = true
inline_tables = true
inline_arrays = true
Expand Down
44 changes: 44 additions & 0 deletions tests/examples/sorted/from-toml-lang-first.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# This is a TOML document. Boom.

title = "TOML Example"

[servers]

# You can indent as you please. Tabs or spaces. TOML don't care.
[servers.alpha]
dc = "eqdc10"
ip = "10.0.0.1"

[servers.beta]
country = "中国" # This should be parsed as UTF-8
dc = "eqdc10"
ip = "10.0.0.2"

[[products]]
name = "Hammer"
sku = 738594937

[[products]]
color = "gray"
name = "Nail"
sku = 284758393

[clients]
data = [["delta", "gamma"], [1, 2]] # just an update to make sure parsers support it
# Line breaks are OK when inside arrays
hosts = [
"alpha",
"omega"
]

[database]
ports = [8001, 8001, 8002]
connection_max = 5000
enabled = true # Comment after a boolean
server = "192.168.1.1"

[owner]
name = "Tom Preston-Werner"
dob = 1979-05-27T07:32:00Z # First class dates? Why not?
bio = "GitHub Cofounder & CEO\nLikes tater tots and beer."
organization = "GitHub"
12 changes: 12 additions & 0 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -279,6 +279,7 @@ def test_load_config_file_read():
"[tool.tomlsort]\nspaces_before_inline_comment=4",
{"spaces_before_inline_comment": 4},
),
("[tool.tomlsort]\nsort_first=['x', 'y']", {"sort_first": "x,y"}),
],
)
def test_load_config_file(toml, expected):
Expand Down Expand Up @@ -339,6 +340,17 @@ def test_load_config_file_invalid(toml):
"test.789": SortOverrideConfiguration(inline_arrays=False),
},
),
(
"""
[tool.tomlsort.overrides]
"test.123".first = ["one", "two", "three"]
""",
{
"test.123": SortOverrideConfiguration(
first=["one", "two", "three"]
),
},
),
],
)
def test_load_config_overrides(toml, expected):
Expand Down
18 changes: 18 additions & 0 deletions tests/test_toml_sort.py
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,24 @@ def test_sort_toml_is_str() -> None:
},
},
),
(
"from-toml-lang",
"from-toml-lang-first",
{
"sort_config": SortConfiguration(
inline_arrays=True,
inline_tables=True,
first=["servers", "products"],
),
"format_config": FormattingConfiguration(
spaces_before_inline_comment=1
),
"sort_config_overrides": {
"database": SortOverrideConfiguration(first=["ports"]),
"owner": SortOverrideConfiguration(first=["name", "dob"]),
},
},
),
(
"pyproject-weird-order",
"pyproject-weird-order",
Expand Down
14 changes: 14 additions & 0 deletions toml_sort/cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,6 +124,9 @@ def parse_config(tomlsort_section: TOMLDocument) -> Dict[str, Any]:
validate_and_copy(
config, clean_config, "trailing_comma_inline_array", bool
)
validate_and_copy(config, clean_config, "sort_first", list)
if "sort_first" in clean_config:
clean_config["sort_first"] = ",".join(clean_config["sort_first"])

if config:
printerr(f"Unexpected configuration values: {config}")
Expand Down Expand Up @@ -242,6 +245,16 @@ def get_parser(defaults: Dict[str, Any]) -> ArgumentParser:
help=("Sort inline arrays."),
action="store_true",
)
sort.add_argument(
"--sort-first",
help=(
"Table keys that will be sorted first in the output. Multiple "
"keys can be given separated by a comma."
),
metavar="KEYS",
type=str,
default="",
)
comments = parser.add_argument_group(
"comments", "exclude comments from output"
)
Expand Down Expand Up @@ -386,6 +399,7 @@ def cli( # pylint: disable=too-many-branches
table_keys=bool(args.sort_table_keys or args.all),
inline_tables=bool(args.sort_inline_tables or args.all),
inline_arrays=bool(args.sort_inline_arrays or args.all),
first=args.sort_first.split(","),
),
format_config=FormattingConfiguration(
spaces_before_inline_comment=args.spaces_before_inline_comment,
Expand Down
32 changes: 27 additions & 5 deletions toml_sort/tomlsort.py
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,7 @@ class SortConfiguration:
inline_tables: bool = False
inline_arrays: bool = False
ignore_case: bool = False
first: List[str] = field(default_factory=list)


@dataclass
Expand All @@ -252,6 +253,7 @@ class SortOverrideConfiguration:
table_keys: Optional[bool] = None
inline_tables: Optional[bool] = None
inline_arrays: Optional[bool] = None
first: Optional[List[str]] = None


class TomlSort:
Expand Down Expand Up @@ -440,6 +442,24 @@ def sort_item(

return item

def sort_keys(
self, items: Iterable[TomlSortItem], sort_config: SortConfiguration
) -> List[TomlSortItem]:
"""Sorts Iterable of Tomlsort item based on keys.
The sort respects the sort_config.first setting which allows
overriding the sorted order of keys.
"""

def sort_first(item):
if item.keys.base in sort_config.first:
return sort_config.first.index(item.keys.base)
return len(sort_config.first)

items = sorted(items, key=self.key_sort_func)
items = sorted(items, key=sort_first)
return items

def sort_inline_table(
self, keys: TomlSortKeys, item: Item, indent_depth: int = 0
) -> InlineTable:
Expand All @@ -452,8 +472,9 @@ def sort_inline_table(
for k, v in item.value.body
if not isinstance(v, Whitespace) and k is not None
]
if self.sort_config(keys).inline_tables:
tomlsort_items = sorted(tomlsort_items, key=self.key_sort_func)
sort_config = self.sort_config(keys)
if sort_config.inline_tables:
tomlsort_items = self.sort_keys(tomlsort_items, sort_config)
new_table = InlineTable(
Container(parsed=True), trivia=item.trivia, new=True
)
Expand Down Expand Up @@ -514,6 +535,7 @@ def sorted_children_table(
self, parent_keys: Optional[TomlSortKeys], parent: List[TomlSortItem]
) -> Iterable[TomlSortItem]:
"""Get the sorted children of a table."""
sort_config = self.sort_config(parent_keys)
tables = coalesce_tables(
item for item in parent if isinstance(item.value, (Table, AoT))
)
Expand All @@ -525,12 +547,12 @@ def sorted_children_table(
]
)
non_tables_final = (
sorted(non_tables, key=self.key_sort_func)
if self.sort_config(parent_keys).table_keys
self.sort_keys(non_tables, sort_config)
if sort_config.table_keys
else non_tables
)
tables_final = (
sorted(tables, key=self.key_sort_func)
self.sort_keys(tables, sort_config)
if self.sort_config(parent_keys).tables
else tables
)
Expand Down

0 comments on commit 91bfb56

Please sign in to comment.