Skip to content

Commit

Permalink
ENH: sort JSON document case-insensitive (#197)
Browse files Browse the repository at this point in the history
  • Loading branch information
redeboer authored Oct 6, 2023
1 parent e3696d8 commit 1042c08
Show file tree
Hide file tree
Showing 5 changed files with 69 additions and 18 deletions.
2 changes: 1 addition & 1 deletion docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -117,10 +117,10 @@ def get_version(package_name: str) -> str:
"colon_fence",
]
nitpick_ignore = [
("py:class", "repoma.utilities.precommit.T"),
("py:class", "tomlkit.container.Container"),
]
nitpick_ignore_regex = [
("py:class", r"^.*.[A-Z]$"),
(r"py:.*", r"ruamel\.yaml\..*"),
]
nitpicky = True
Expand Down
32 changes: 17 additions & 15 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -87,6 +87,7 @@ reportPrivateImportUsage = false
reportPrivateUsage = false
reportUnboundVariable = false
reportUnknownArgumentType = false
reportUnknownLambdaType = false
reportUnknownMemberType = false
reportUnknownParameterType = false
reportUnknownVariableType = false
Expand Down Expand Up @@ -151,21 +152,22 @@ extend-select = [
"YTT",
]
ignore = [
"C408", # Unnecessary dict call - rewrite as a literal
"D101", # class docstring
"D102", # method docstring
"D103", # function docstring
"D105", # magic method docstring
"D107", # init docstring
"D203", # conflicts with D211
"D213", # multi-line docstring should start at the second line
"D407", # missing dashed underline after section
"D416", # section name does not have to end with a colon
"E501", # handled by black formatting
"PLR0913", # sympy functions
"PLW2901", # often used for xreplace
"S301", # allow pickle
"SIM108", # if-else-block-instead-of-if-exp
"C408",
"D101",
"D102",
"D103",
"D105",
"D107",
"D203",
"D213",
"D407",
"D416",
"E501",
"PLR0913",
"PLW2901",
"S301",
"SIM108",
"UP036",
]
show-fixes = true
src = [
Expand Down
1 change: 1 addition & 0 deletions setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ install_requires =
PyYAML
ruamel.yaml # better YAML dumping
tomlkit
typing-extensions; python_version<"3.7.0"
packages = find:
package_dir =
=src
Expand Down
2 changes: 2 additions & 0 deletions src/repoma/check_dev_files/ruff.py
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,8 @@ def _update_ruff_settings() -> None:
"E501", # line-width already handled by black
"SIM108", # allow if-else blocks
]
if "3.6" in get_supported_python_versions():
extend_ignore.append("UP036")
ignores = sorted({*settings.get("ignore", []), *extend_ignore})
minimal_settings = {
"extend-select": __get_selected_ruff_rules(),
Expand Down
50 changes: 48 additions & 2 deletions src/repoma/utilities/vscode.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,23 @@
"""Helper functions for modifying a VSCode configuration."""

import collections
import json
import sys
from collections import abc
from copy import deepcopy
from pathlib import Path
from typing import Iterable, Union
from typing import Dict, Iterable, List, TypeVar, Union, overload

from repoma.errors import PrecommitError
from repoma.utilities.executor import Executor

from . import CONFIG_PATH

if sys.version_info < (3, 7):
from typing_extensions import OrderedDict
else:
from typing import OrderedDict


def remove_setting(key: Union[str, dict]) -> None:
old = __load_config(CONFIG_PATH.vscode_settings, create=True)
Expand Down Expand Up @@ -110,10 +118,48 @@ def _remove() -> None:

def __dump_config(config: dict, path: Path) -> None:
with open(path, "w") as stream:
json.dump(config, stream, indent=2, sort_keys=True)
json.dump(sort_case_insensitive(config), stream, indent=2)
stream.write("\n")


K = TypeVar("K")
V = TypeVar("V")


@overload
def sort_case_insensitive(dct: Dict[K, V]) -> OrderedDict[K, V]: ... # type: ignore[misc]
@overload
def sort_case_insensitive(dct: str) -> str: ... # type: ignore[misc]
@overload
def sort_case_insensitive(dct: Iterable[K]) -> List[K]: ... # type: ignore[misc]
@overload
def sort_case_insensitive(dct: K) -> K: ...
def sort_case_insensitive(dct): # type: ignore[no-untyped-def]
"""Order a `dict` by key, **case-insensitive**.
This function is implemented in order to :func:`~json.dump` a JSON file with a
sorting that is the same as `the one used by VS Code
<https://code.visualstudio.com/updates/v1_76#_jsonc-document-sorting>`_.
>>> sort_case_insensitive(
... {
... "cSpell.enabled": True,
... "coverage-gutters": ["test", "coverage.xml"],
... }
... )
OrderedDict([('coverage-gutters', ['coverage.xml', 'test']), ('cSpell.enabled', True)])
"""
if isinstance(dct, abc.Mapping):
return collections.OrderedDict(
{k: sort_case_insensitive(dct[k]) for k in sorted(dct, key=str.lower)}
)
if isinstance(dct, str):
return dct
if isinstance(dct, abc.Iterable):
return sorted(dct, key=lambda t: str(t).lower())
return dct


def __load_config(path: Path, create: bool = False) -> dict:
if not path.exists() and create:
path.parent.mkdir(exist_ok=True)
Expand Down

0 comments on commit 1042c08

Please sign in to comment.