1
0
Fork 0
mirror of https://github.com/ohmyzsh/ohmyzsh.git synced 2024-11-23 22:30:07 +00:00
ohmyzsh/tools/alias_collision/check_alias_collision.py

167 lines
4.6 KiB
Python
Raw Normal View History

2022-04-15 11:34:17 +00:00
"""Check for alias collisions within the codebase"""
2022-11-22 15:24:15 +00:00
from argparse import ArgumentDefaultsHelpFormatter, ArgumentParser
2022-04-15 11:34:17 +00:00
from pathlib import Path
2024-02-19 14:19:49 +00:00
from dataclasses import dataclass
2024-02-02 12:48:57 +00:00
from typing import List, Dict
2022-04-15 11:34:17 +00:00
import itertools
import re
2024-02-02 18:54:53 +00:00
import json
2022-04-15 11:34:17 +00:00
ERROR_MESSAGE_TEMPLATE = (
"Alias `%s` defined in `%s` already exists as alias `%s` in `%s`."
)
2022-04-15 11:34:17 +00:00
2024-02-19 14:19:49 +00:00
KNOWN_COLLISIONS_PATH = Path(__file__).resolve().parent / "known_collisions.json"
2024-02-02 13:13:12 +00:00
2022-04-15 11:34:17 +00:00
def dir_path(path_string: str) -> Path:
if Path(path_string).is_dir():
return Path(path_string)
else:
raise NotADirectoryError(path_string)
def parse_arguments():
parser = ArgumentParser(
description=__doc__,
formatter_class=ArgumentDefaultsHelpFormatter,
)
parser.add_argument(
"folder",
type=dir_path,
help="Folder to check",
)
2024-02-19 14:19:49 +00:00
group = parser.add_mutually_exclusive_group(required=True)
group.add_argument(
2024-02-02 18:54:53 +00:00
"--known-collisions",
type=Path,
2024-02-19 14:19:49 +00:00
default=None,
help="Json-serialized list of known collision to compare to",
)
group.add_argument(
"--known-collisions-output-path",
type=Path,
default=KNOWN_COLLISIONS_PATH,
help="Output path for a json-serialized list of known collisions",
2024-02-02 18:54:53 +00:00
)
2022-04-15 11:34:17 +00:00
return parser.parse_args()
@dataclass(frozen=True)
class Alias:
alias: str
value: str
module: Path
2024-02-19 14:19:49 +00:00
def to_dict(self) -> Dict:
return {
"alias": self.alias,
"value": self.value,
"module": str(self.module),
}
2022-04-15 11:34:17 +00:00
@dataclass(frozen=True)
class Collision:
existing_alias: Alias
new_alias: Alias
2024-02-02 18:54:53 +00:00
def is_new_collision(self, known_collision_aliases: List[str]) -> bool:
return self.new_alias.alias not in known_collision_aliases
@classmethod
def from_dict(cls, collision_dict: Dict) -> "Collision":
return cls(
Alias(**collision_dict["existing_alias"]),
Alias(**collision_dict["new_alias"]),
)
2024-02-02 13:13:12 +00:00
2024-02-19 14:19:49 +00:00
def to_dict(self) -> Dict:
return {
"existing_alias": self.existing_alias.to_dict(),
"new_alias": self.new_alias.to_dict(),
}
2024-02-02 12:48:57 +00:00
def find_aliases_in_file(file: Path) -> List[Alias]:
2022-04-15 11:34:17 +00:00
matches = re.findall(r"^alias (.*)='(.*)'", file.read_text(), re.M)
return [Alias(match[0], match[1], file) for match in matches]
2024-02-02 18:54:53 +00:00
def load_known_collisions(collision_file: Path) -> List[Collision]:
collision_list = json.loads(collision_file.read_text())
return [Collision.from_dict(collision_dict) for collision_dict in collision_list]
def find_all_aliases(path: Path) -> list:
2024-02-02 13:13:12 +00:00
aliases = [find_aliases_in_file(file) for file in path.rglob("*.zsh")]
2022-04-15 11:34:17 +00:00
return list(itertools.chain(*aliases))
2024-02-02 12:48:57 +00:00
def check_for_duplicates(aliases: List[Alias]) -> List[Collision]:
2022-11-22 15:24:15 +00:00
elements = {}
collisions = []
2022-04-15 11:34:17 +00:00
for alias in aliases:
if alias.alias in elements:
existing = elements[alias.alias]
collisions.append(Collision(existing, alias))
else:
elements[alias.alias] = alias
return collisions
2024-02-02 12:48:57 +00:00
def print_collisions(collisions: Dict[Alias, Alias]) -> None:
if collisions:
print(f"Found {len(collisions)} alias collisions:\n")
for collision in collisions:
print(
2022-04-15 11:34:17 +00:00
ERROR_MESSAGE_TEMPLATE
% (
f"{collision.new_alias.alias}={collision.new_alias.value}",
collision.new_alias.module.name,
f"{collision.existing_alias.alias}={collision.existing_alias.value}",
collision.existing_alias.module.name,
2022-04-15 11:34:17 +00:00
)
)
print("\nConsider renaming your aliases.")
else:
print("Found no collisions")
2022-04-15 11:34:17 +00:00
2024-02-19 14:19:49 +00:00
def check_for_new_collisions(
known_collisions: Path, collisions: List[Collision]
) -> List[Collision]:
known_collisions = load_known_collisions(known_collisions)
2024-02-02 18:54:53 +00:00
known_collision_aliases = [
collision.new_alias.alias for collision in known_collisions
]
2024-02-19 14:19:49 +00:00
return [
2024-02-02 13:13:12 +00:00
collision
for collision in collisions
2024-02-02 18:54:53 +00:00
if collision.is_new_collision(known_collision_aliases)
2024-02-02 13:13:12 +00:00
]
2024-02-02 18:54:53 +00:00
2024-02-19 14:19:49 +00:00
def main() -> int:
"""main"""
args = parse_arguments()
aliases = find_all_aliases(args.folder)
collisions = check_for_duplicates(aliases)
if args.known_collisions is not None:
new_collisions = check_for_new_collisions(args.known_collisions, collisions)
print_collisions(new_collisions)
return -1 if new_collisions else 0
args.known_collisions_output_path.write_text(
json.dumps([collision.to_dict() for collision in collisions])
)
return 0
2022-04-15 11:34:17 +00:00
if __name__ == "__main__":
2024-02-02 18:54:53 +00:00
exit(main())