cleany 0.0.1__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.
cleany-0.0.1/PKG-INFO ADDED
@@ -0,0 +1,17 @@
1
+ Metadata-Version: 2.4
2
+ Name: cleany
3
+ Version: 0.0.1
4
+ Summary: CLI to clean up your Python comments
5
+ Author-email: Ben Weddle <ben.weddle@gmail.com>
6
+ Maintainer-email: Ben Weddle <ben.weddle@gmail.com>
7
+ Project-URL: Repository, https://pointlessapi.com
8
+ Requires-Python: >=3.10
9
+ Description-Content-Type: text/markdown
10
+ Requires-Dist: regex
11
+ Requires-Dist: pytest
12
+ Requires-Dist: ruff
13
+ Requires-Dist: pydantic
14
+
15
+ # Clean Comments
16
+
17
+ clean up your comments, people!
cleany-0.0.1/README.md ADDED
@@ -0,0 +1,3 @@
1
+ # Clean Comments
2
+
3
+ clean up your comments, people!
@@ -0,0 +1,29 @@
1
+ [project]
2
+ name = "cleany"
3
+ version = "0.0.1"
4
+ description = "CLI to clean up your Python comments"
5
+ readme = "README.md"
6
+ authors = [{name = "Ben Weddle", email = "ben.weddle@gmail.com"}]
7
+ maintainers = [{name = "Ben Weddle", email = "ben.weddle@gmail.com"}]
8
+ requires-python = ">=3.10"
9
+
10
+ dependencies = [
11
+ "regex",
12
+ "pytest",
13
+ "ruff",
14
+ "pydantic"
15
+ ]
16
+
17
+ [project.urls]
18
+ Repository = "https://pointlessapi.com"
19
+
20
+ [build-system]
21
+ requires = ["setuptools"]
22
+ build-backend = "setuptools.build_meta"
23
+
24
+ [tool.setuptools.packages.find]
25
+ where = ["src"]
26
+ include = ["cleany*"]
27
+
28
+ [project.scripts]
29
+ cleany = "cleany.cli:main"
cleany-0.0.1/setup.cfg ADDED
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1 @@
1
+ from cleany.cleany import Cleany
@@ -0,0 +1,122 @@
1
+ import subprocess
2
+ import tokenize
3
+ from tokenize import TokenInfo
4
+ import regex
5
+ from pathlib import Path
6
+
7
+ from pydantic import BaseModel, Field
8
+
9
+ grapheme_pattern = regex.compile(r"\X", regex.UNICODE)
10
+ emoji_pattern = regex.compile(r"\p{Extended_Pictographic}")
11
+
12
+ class Cleany(BaseModel):
13
+
14
+ path: Path
15
+ ignore_dir: list = Field(default_factory=list)
16
+ ignore_file: list = Field(default_factory=list)
17
+ nuke: bool = False
18
+ emoji: bool = False
19
+ quiet: bool = False
20
+ list_of_files: list[Path] = Field(default_factory=list)
21
+ total_emojis_removed: int = 0
22
+
23
+ def model_post_init(self, __context):
24
+ self.list_of_files = self.create_list_of_files()
25
+
26
+ def main_loop(self):
27
+ if not any([self.nuke, self.emoji]):
28
+ return print("no modification commands")
29
+ if not self.path.exists():
30
+ return print(f"cannot find matchign directory: {self.path}")
31
+ if len(self.list_of_files) == 0:
32
+ return print(f"no files found in {self.path}")
33
+ for file in self.list_of_files:
34
+ if self.nuke:
35
+ self.nuke_comments(file)
36
+ if self.emoji:
37
+ self.remove_emojis(file)
38
+ print(f"removed {self.total_emojis_removed} emojis")
39
+
40
+ def create_list_of_files(self) -> list[Path]:
41
+ list_of_files: list = []
42
+ for file in self.path.rglob("*.py"):
43
+ if any(part in self.ignore_dir for part in file.parent.parts):
44
+ continue
45
+ if any(str(file).endswith(to_ignore) for to_ignore in self.ignore_file):
46
+ continue
47
+ else:
48
+ list_of_files.append(file)
49
+ return list_of_files
50
+
51
+ def nuke_comments(self, path: Path):
52
+ self.print_to_screen(f"----- scanning comments in {path} -----")
53
+ total_removed: int = 0
54
+ with open(path, "rb") as f:
55
+ tokens = list(tokenize.tokenize(f.readline))
56
+
57
+ new_tokens = []
58
+
59
+ for token in tokens:
60
+ if token.type == tokenize.COMMENT:
61
+ self.print_to_screen(f"removing comment from line {token.start[0]} of {path}")
62
+ total_removed += 1
63
+ pass
64
+ else:
65
+ new_tokens.append(token)
66
+
67
+ if tokens == new_tokens:
68
+ self.print_to_screen(f"----- no comments found in {path} -----")
69
+ self.print_to_screen(statement="")
70
+ return
71
+
72
+ self.print_to_screen(f"removed {total_removed} comments from {path}")
73
+ new_source = tokenize.untokenize(new_tokens)
74
+ path.write_bytes(new_source)
75
+ self.run_ruff(path=path)
76
+
77
+ def remove_emojis(self, path: Path):
78
+ self.print_to_screen(f"----- scanning comments in {path} -----")
79
+ with open(path, "rb") as f:
80
+ tokens = list(tokenize.tokenize(f.readline))
81
+
82
+ new_tokens = []
83
+
84
+ for token in tokens:
85
+ if token.type == tokenize.COMMENT:
86
+ new_comment = self.replace_emojis_in_comment(token)
87
+ token = tokenize.TokenInfo(
88
+ token.type,
89
+ new_comment,
90
+ token.start,
91
+ token.end,
92
+ token.line,
93
+ )
94
+ new_tokens.append(token)
95
+
96
+ if tokens == new_tokens:
97
+ self.print_to_screen(f"----- no emojis found in {path} -----")
98
+ self.print_to_screen(statement="")
99
+ return
100
+
101
+ new_source = tokenize.untokenize(new_tokens)
102
+ path.write_bytes(new_source)
103
+ self.run_ruff(path=path)
104
+
105
+ def replace_emojis_in_comment(self, text: TokenInfo, replacement: str = "") -> str:
106
+ graphemes = grapheme_pattern.findall(text.string)
107
+ new_parts = []
108
+ for g in graphemes:
109
+ if emoji_pattern.search(g):
110
+ new_parts.append(replacement)
111
+ self.print_to_screen(f"removing {g} from line {text.start[0]}")
112
+ self.total_emojis_removed += 1
113
+ else:
114
+ new_parts.append(g)
115
+ return "".join(new_parts)
116
+
117
+ def run_ruff(self, path: Path):
118
+ subprocess.run(["ruff", "format", "--silent", str(path)], check=True)
119
+
120
+ def print_to_screen(self, statement):
121
+ if not self.quiet:
122
+ print(statement)
@@ -0,0 +1,67 @@
1
+ import argparse
2
+ from pathlib import Path
3
+ import sys
4
+
5
+ from cleany.cleany import Cleany
6
+
7
+ def parse_args():
8
+ parser = argparse.ArgumentParser(description="Clean up your comments")
9
+ parser.add_argument(
10
+ "--path",
11
+ type=str,
12
+ default=".",
13
+ help="Path to the directory containing Python files (default: current directory)",
14
+ )
15
+ parser.add_argument(
16
+ "--ignore-dir",
17
+ action="append",
18
+ default=[],
19
+ help="ignore matching directories"
20
+ )
21
+ parser.add_argument(
22
+ "--ignore-file",
23
+ type=str,
24
+ required=False,
25
+ help="ignore matching files"
26
+ )
27
+ parser.add_argument(
28
+ "--nuke",
29
+ action="store_true",
30
+ help="Removes ALL comments",
31
+ )
32
+ parser.add_argument(
33
+ "--emoji",
34
+ action="store_true",
35
+ help="Removes emojis"
36
+ )
37
+ parser.add_argument(
38
+ "--quiet",
39
+ action="store_true",
40
+ help="surpress output"
41
+ )
42
+ return parser.parse_args()
43
+
44
+
45
+ def main():
46
+ args = parse_args()
47
+
48
+ if len(sys.argv) == 1:
49
+ return print("cleany -h for help")
50
+
51
+ ignore_dir = ["venv", "tests"]
52
+ for ignored in args.ignore_dir:
53
+ ignore_dir.append(ignored)
54
+
55
+ ignore_file = []
56
+ if args.ignore_file:
57
+ ignore_file.append(args.ignore_file)
58
+
59
+ cleany = Cleany(
60
+ path=args.path,
61
+ ignore_dir=ignore_dir,
62
+ ignore_file=ignore_file,
63
+ nuke=args.nuke,
64
+ emoji=args.emoji,
65
+ quiet=args.quiet
66
+ )
67
+ cleany.main_loop()
@@ -0,0 +1,17 @@
1
+ Metadata-Version: 2.4
2
+ Name: cleany
3
+ Version: 0.0.1
4
+ Summary: CLI to clean up your Python comments
5
+ Author-email: Ben Weddle <ben.weddle@gmail.com>
6
+ Maintainer-email: Ben Weddle <ben.weddle@gmail.com>
7
+ Project-URL: Repository, https://pointlessapi.com
8
+ Requires-Python: >=3.10
9
+ Description-Content-Type: text/markdown
10
+ Requires-Dist: regex
11
+ Requires-Dist: pytest
12
+ Requires-Dist: ruff
13
+ Requires-Dist: pydantic
14
+
15
+ # Clean Comments
16
+
17
+ clean up your comments, people!
@@ -0,0 +1,12 @@
1
+ README.md
2
+ pyproject.toml
3
+ src/cleany/__init__.py
4
+ src/cleany/cleany.py
5
+ src/cleany/cli.py
6
+ src/cleany.egg-info/PKG-INFO
7
+ src/cleany.egg-info/SOURCES.txt
8
+ src/cleany.egg-info/dependency_links.txt
9
+ src/cleany.egg-info/entry_points.txt
10
+ src/cleany.egg-info/requires.txt
11
+ src/cleany.egg-info/top_level.txt
12
+ tests/test_base.py
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ cleany = cleany.cli:main
@@ -0,0 +1,4 @@
1
+ regex
2
+ pytest
3
+ ruff
4
+ pydantic
@@ -0,0 +1 @@
1
+ cleany
@@ -0,0 +1,66 @@
1
+ import os
2
+ from pathlib import Path
3
+ import subprocess
4
+
5
+ import pytest
6
+
7
+ def test_init_1():
8
+ '''if nothing passed in, should return default help message'''
9
+ output = subprocess.run("cleany", capture_output=True)
10
+ assert "help" in str(output.stdout)
11
+
12
+ def test_init_2():
13
+ '''runs...'''
14
+ subprocess.run(["cleany", "--nuke"])
15
+
16
+ def test_init_3():
17
+ '''wrong type passed to path. should be str'''
18
+ with pytest.raises(Exception):
19
+ subprocess.run(["cleany", "--path", 2])
20
+
21
+ def test_init_4():
22
+ '''if no modification commands passed (emoji, nuke, etc)'''
23
+ output = subprocess.run(["cleany", "--path", "some_dir"], capture_output=True)
24
+ assert "no modification commands" in str(output.stdout)
25
+
26
+ def test_init_5():
27
+ '''if no modification commands passed (emoji, nuke, etc)'''
28
+ output = subprocess.run(["cleany", "--ignore-dir", "some_dir"], capture_output=True)
29
+ assert "no modification commands" in str(output.stdout)
30
+
31
+ def test_init_6():
32
+ '''passing in non-existent directory'''
33
+ output = subprocess.run(["cleany", "--path", "doesnt_exist", "--nuke"], capture_output=True)
34
+ assert "cannot find matchign directory" in str(output.stdout)
35
+
36
+ def test_init_7(tmp_path):
37
+ '''specifying directory with no matching python files'''
38
+ output = subprocess.run(["cleany", "--path", tmp_path, "--nuke"], capture_output=True)
39
+ assert "no files found in" in str(output.stdout)
40
+
41
+ def test_quiet():
42
+ output = subprocess.run(["cleany", "--nuke", "--quiet"], capture_output=True)
43
+ output = str(output.stdout).strip("b''")
44
+ assert len(str(output)) == 0
45
+
46
+ def test_remove_emojis_1(tmp_path):
47
+ pre = Path("tests/fixtures/pre-clean-emojis.py")
48
+ post = Path("tests/fixtures/post-clean-emojis.py")
49
+
50
+ temp: Path = tmp_path / "emoji.py"
51
+ temp.write_text(pre.read_text())
52
+
53
+ subprocess.run(["cleany", "--emoji", "--path", tmp_path])
54
+
55
+ assert temp.read_text() == post.read_text()
56
+
57
+ def test_nuke_1(tmp_path):
58
+ pre = Path("tests/fixtures/pre-clean-nuke.py")
59
+ post = Path("tests/fixtures/post-clean-nuke.py")
60
+
61
+ temp: Path = tmp_path / "nuke.py"
62
+ temp.write_text(pre.read_text())
63
+
64
+ subprocess.run(["cleany", "--nuke", "--path", tmp_path])
65
+
66
+ assert temp.read_text() == post.read_text()