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 +17 -0
- cleany-0.0.1/README.md +3 -0
- cleany-0.0.1/pyproject.toml +29 -0
- cleany-0.0.1/setup.cfg +4 -0
- cleany-0.0.1/src/cleany/__init__.py +1 -0
- cleany-0.0.1/src/cleany/cleany.py +122 -0
- cleany-0.0.1/src/cleany/cli.py +67 -0
- cleany-0.0.1/src/cleany.egg-info/PKG-INFO +17 -0
- cleany-0.0.1/src/cleany.egg-info/SOURCES.txt +12 -0
- cleany-0.0.1/src/cleany.egg-info/dependency_links.txt +1 -0
- cleany-0.0.1/src/cleany.egg-info/entry_points.txt +2 -0
- cleany-0.0.1/src/cleany.egg-info/requires.txt +4 -0
- cleany-0.0.1/src/cleany.egg-info/top_level.txt +1 -0
- cleany-0.0.1/tests/test_base.py +66 -0
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,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 @@
|
|
|
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 @@
|
|
|
1
|
+
|
|
@@ -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()
|