Skip to content

Commit e79358e

Browse files
First release v0.1.0 (#1)
* First release v0.1.0 * readme update * Analysis -> Analyzer * use color * item analyzer name change * Session & Refactoring * Remove command add * Test run fix * refactor source refactor * Refactor refactor, more case support * 3.8+ * Include & Exclude * Skip node * remove codacy * update * main exit code update * Rules add * Mis Typing * v0.1.0
1 parent 56f778a commit e79358e

23 files changed

Lines changed: 1086 additions & 16 deletions

.github/workflows/tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ jobs:
66
strategy:
77
matrix:
88
os: [ubuntu-latest, macos-latest, windows-latest]
9-
python-version: [3.6, 3.7, 3.8, 3.9]
9+
python-version: [3.8, 3.9]
1010
steps:
1111
- uses: actions/checkout@v2.3.4
1212

.pre-commit-config.yaml

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,6 @@ repos:
2727
rev: v0.800
2828
hooks:
2929
- id: mypy
30-
args: [--no-strict-optional, --ignore-missing-imports]
3130

3231
- repo: https://github.com/pre-commit/mirrors-prettier
3332
rev: v2.2.1
@@ -47,9 +46,21 @@ repos:
4746
rev: v2.10.0
4847
hooks:
4948
- id: pyupgrade
50-
args: [--py36-plus]
49+
args: [--py38-plus]
5150

52-
- repo: https://github.com/isidentical/teyit
53-
rev: 2eaaabed645b33822de0e310851c0c7abfaa500b
51+
- repo: https://github.com/pycqa/flake8
52+
rev: "3.9.0"
5453
hooks:
55-
- id: teyit
54+
- id: flake8
55+
56+
- repo: local
57+
hooks:
58+
- id: pyall
59+
name: pyall
60+
description:
61+
"Pyall is a linter that tries to keep the __all __ in your Python modules
62+
always up to date."
63+
entry: python -m pyall --refactor
64+
language: python
65+
language_version: python3
66+
types: [python]

README.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
![pyall](https://raw.githubusercontent.com/hakancelik96/pyall/master/images/logo/pyall.png)
2+
3+
**Pyall is a linter that tries to keep the **all ** in your Python modules always up to
4+
date.**
5+
6+
[![pre-commit.ci status](https://results.pre-commit.ci/badge/github/hakancelik96/pyall/master.svg)](https://results.pre-commit.ci/latest/github/hakancelik96/pyall/master)
7+
[![test](https://github.com/hakancelik96/pyall/actions/workflows/tests.yml/badge.svg)](https://github.com/hakancelik96/pyall/actions/workflows/tests.yml)
8+
9+
[![Pypi](https://img.shields.io/pypi/v/pyall)](https://pypi.org/project/pyall/)
10+
![PyPI - Python Version](https://img.shields.io/pypi/pyversions/pyall)
11+
[![Downloads](https://static.pepy.tech/personalized-badge/pyall?period=total&units=none&left_color=grey&right_color=red&left_text=downloads)](https://pepy.tech/project/pyall)
12+
[![License](https://img.shields.io/github/license/hakancelik96/pyall.svg)](https://github.com/hakancelik96/pyall/blob/master/LICENSE)
13+
14+
[![Forks](https://img.shields.io/github/forks/hakancelik96/pyall)](https://github.com/hakancelik96/pyall/fork)
15+
[![Issues](https://img.shields.io/github/issues/hakancelik96/pyall)](https://github.com/hakancelik96/pyall/issues)
16+
[![Stars](https://img.shields.io/github/stars/hakancelik96/pyall)](https://github.com/hakancelik96/pyall/stargazers)
17+
18+
[![Codecov](https://codecov.io/gh/hakancelik96/pyall/branch/master/graph/badge.svg)](https://codecov.io/gh/hakancelik96/pyall)
19+
[![Contributors](https://img.shields.io/github/contributors/hakancelik96/pyall)](https://github.com/hakancelik96/pyall/graphs/contributors)
20+
[![Last Commit](https://img.shields.io/github/last-commit/hakancelik96/pyall.svg)](https://github.com/hakancelik96/pyall/commits/master)
21+
22+
For more information see: https://pyall.hakancelik.dev/

docs/CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,3 +3,5 @@
33
All notable changes to this project will be documented in this file.
44

55
## [Unreleased] - ././2021
6+
7+
- [Version V0.1.0 by @hakancelik96](https://github.com/hakancelik96/pyall/pull/1)

docs/CONTRIBUTING.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,7 @@ $ git rebase upstream/master
3535

3636
## Testing
3737

38-
Firstly make sure you have 3.6, 3.7, 3.8 and 3.9 python versions installed on your
39-
system.
38+
Firstly make sure you have 3.8 and 3.9 python versions installed on your system.
4039

4140
After typing your codes, you should run the tests by typing the following command.
4241

docs/README.md

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
date.**
77

88
[![pre-commit.ci status](https://results.pre-commit.ci/badge/github/hakancelik96/pyall/master.svg)](https://results.pre-commit.ci/latest/github/hakancelik96/pyall/master)
9-
![test](https://github.com/hakancelik96/pyall/workflows/Test/badge.svg)
9+
[![test](https://github.com/hakancelik96/pyall/actions/workflows/tests.yml/badge.svg)](https://github.com/hakancelik96/pyall/actions/workflows/tests.yml)
1010

1111
[![Pypi](https://img.shields.io/pypi/v/pyall)](https://pypi.org/project/pyall/)
1212
![PyPI - Python Version](https://img.shields.io/pypi/pyversions/pyall)
@@ -17,16 +17,54 @@ date.**
1717
[![Issues](https://img.shields.io/github/issues/hakancelik96/pyall)](https://github.com/hakancelik96/pyall/issues)
1818
[![Stars](https://img.shields.io/github/stars/hakancelik96/pyall)](https://github.com/hakancelik96/pyall/stargazers)
1919

20-
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/3fbd4686e97b4e19906ca2fa933e4cfc)](https://app.codacy.com/manual/hakancelik96/pyall?utm_source=github.com&utm_medium=referral&utm_content=hakancelik96/pyall&utm_campaign=Badge_Grade_Settings)
2120
[![Codecov](https://codecov.io/gh/hakancelik96/pyall/branch/master/graph/badge.svg)](https://codecov.io/gh/hakancelik96/pyall)
2221
[![Contributors](https://img.shields.io/github/contributors/hakancelik96/pyall)](https://github.com/hakancelik96/pyall/graphs/contributors)
2322
[![Last Commit](https://img.shields.io/github/last-commit/hakancelik96/pyall.svg)](https://github.com/hakancelik96/pyall/commits/master)
2423

2524
## Installation
2625

27-
pyall requires Python 3.6+ and can be easily installed using most common Python
26+
pyall requires Python 3.8+ and can be easily installed using most common Python
2827
packaging tools. We recommend installing the latest stable release from PyPI with pip:
2928

3029
```shell
3130
$ pip install pyall
3231
```
32+
33+
### Command line options
34+
35+
You can list many options by running pyall --help
36+
37+
```
38+
usage: Pyall [-h] [-r] [-d] [--include include] [--exclude exclude] [-v] [sources [sources ...]]
39+
40+
Pyall is a linter that tries to keep the __all __ in your Python modules always up to date.
41+
42+
positional arguments:
43+
sources Enter the directories and file paths you want to analyze.
44+
45+
optional arguments:
46+
-h, --help show this help message and exit
47+
-r, --refactor Auto-sync __all__ list in python modules automatically.
48+
-d, --diff Prints a diff of all the changes Pyall would make to a file.
49+
--include include File include pattern.
50+
--exclude exclude File exclude pattern.
51+
-v, --version Prints version of pyall
52+
```
53+
54+
### Adding pre-commit plugins to your project
55+
56+
Once you have [pre-commit](https://pre-commit.com/)
57+
[installed](https://pre-commit.com/#install), adding pre-commit plugins to your project
58+
is done with the .pre-commit-config.yaml configuration file.
59+
60+
Add a file called .pre-commit-config.yaml to the root of your project. The pre-commit
61+
config file describes what repositories and hooks are installed.
62+
63+
```yaml
64+
repos:
65+
- repo: https://github.com/hakancelik96/pyall
66+
rev: stable
67+
hooks:
68+
- id: pyall
69+
args: [--refactor]
70+
```

pyall/__main__.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import sys
2+
3+
from pyall.main import main
4+
5+
if __name__ == "__main__":
6+
sys.exit(main())

pyall/analysis.py

Whitespace-only changes.

pyall/analyzer.py

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
import ast
2+
import io
3+
import re
4+
import tokenize
5+
from dataclasses import dataclass, field
6+
from typing import Set
7+
8+
from pyall import constants as C
9+
from pyall.relate import relate
10+
from pyall.rule import Rule
11+
12+
__all__ = ["Analyzer"]
13+
14+
15+
@dataclass
16+
class _AllItemAnalyzer(ast.NodeVisitor):
17+
actual_all: Set[str] = field(default_factory=set)
18+
classes: Set[str] = field(default_factory=set)
19+
functions: Set[str] = field(default_factory=set)
20+
variables: Set[str] = field(default_factory=set)
21+
22+
@Rule.apply
23+
def visit_ClassDef(self, node: ast.ClassDef) -> None:
24+
self.classes.add(node.name)
25+
self.generic_visit(node)
26+
27+
@Rule.apply
28+
def visit_FunctionDef(self, node: C.ASTFunctionT) -> None:
29+
self.functions.add(node.name)
30+
self.generic_visit(node)
31+
32+
visit_AsyncFunctionDef = visit_FunctionDef
33+
34+
@Rule.apply
35+
def visit_Name(self, node: ast.Name) -> None:
36+
self.variables.add(node.id)
37+
self.generic_visit(node)
38+
39+
@Rule.apply
40+
def visit_Assign(self, node: ast.Assign) -> None:
41+
assert isinstance(node.value, ast.List)
42+
for item in node.value.elts:
43+
if isinstance(item, ast.Constant):
44+
self.actual_all.add(str(item.value))
45+
elif isinstance(item, ast.Str):
46+
self.actual_all.add(item.s)
47+
self.generic_visit(node)
48+
49+
@Rule.apply
50+
def visit_Expr(self, node: ast.Expr) -> None:
51+
assert isinstance(node.value, ast.Call)
52+
assert isinstance(node.value.func, ast.Attribute)
53+
if node.value.func.attr == "append":
54+
for arg in node.value.args:
55+
if isinstance(arg, ast.Constant):
56+
self.actual_all.add(str(arg.value))
57+
elif isinstance(arg, ast.Str):
58+
self.actual_all.add(arg.s)
59+
elif node.value.func.attr == "extend":
60+
for arg in node.value.args:
61+
if isinstance(arg, ast.List):
62+
for item in arg.elts:
63+
if isinstance(item, ast.Constant):
64+
self.actual_all.add(str(item.value))
65+
elif isinstance(item, ast.Str):
66+
self.actual_all.add(item.s)
67+
self.generic_visit(node)
68+
69+
70+
@dataclass
71+
class Analyzer:
72+
source: str
73+
all_item_analyzer: _AllItemAnalyzer = field(
74+
init=False, default_factory=_AllItemAnalyzer
75+
)
76+
77+
def traverse(self) -> None:
78+
tree = ast.parse(self.source)
79+
relate(tree)
80+
self.set_extra_attr(tree)
81+
self.all_item_analyzer.visit(tree)
82+
83+
def set_extra_attr(self, tree: ast.AST) -> None:
84+
skip, add = set(), set()
85+
readline = io.StringIO(self.source).readline
86+
for _, _, start, _, line in tokenize.generate_tokens(readline):
87+
if re.search(C.SKIP_COMMENTS_REGEX_PATTERN, line, re.IGNORECASE):
88+
lineno = start[0]
89+
skip.add(lineno)
90+
if re.search(C.ADD_COMMENTS_REGEX_PATTERN, line, re.IGNORECASE):
91+
lineno = start[0]
92+
add.add(lineno)
93+
for node in ast.walk(tree):
94+
if isinstance(node, C.ALL_NODE) and node.lineno in skip:
95+
node.skip = True # type: ignore
96+
else:
97+
node.skip = False # type: ignore
98+
99+
if isinstance(node, C.ALL_NODE) and node.lineno in add:
100+
node.add = True # type: ignore
101+
102+
@property
103+
def actual_all(self):
104+
return sorted(self.all_item_analyzer.actual_all)
105+
106+
@property
107+
def classes(self):
108+
return sorted(self.all_item_analyzer.classes)
109+
110+
@property
111+
def functions(self):
112+
return sorted(self.all_item_analyzer.functions)
113+
114+
@property
115+
def variables(self):
116+
return sorted(self.all_item_analyzer.variables)
117+
118+
@property
119+
def expected_all(self):
120+
return sorted(self.classes + self.functions + self.variables)

pyall/color.py

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import sys
2+
from typing import Tuple
3+
4+
__all__ = [
5+
"BLACK",
6+
"BLUE",
7+
"BOLD_WHITE",
8+
"CYAN",
9+
"GREEN",
10+
"MAGENTA",
11+
"RED",
12+
"RESET",
13+
"TERMINAL_SUPPORT_COLOR",
14+
"WHITE",
15+
"YELLOW",
16+
"diff",
17+
"paint",
18+
]
19+
20+
if sys.platform == "win32": # pragma: no cover (windows)
21+
22+
def _enable() -> None:
23+
from ctypes import POINTER, WINFUNCTYPE, WinError, windll
24+
from ctypes.wintypes import BOOL, DWORD, HANDLE
25+
26+
STD_ERROR_HANDLE = -12
27+
ENABLE_VIRTUAL_TERMINAL_PROCESSING = 4
28+
29+
def bool_errcheck(result, func, args):
30+
if not result:
31+
raise WinError()
32+
return args
33+
34+
GetStdHandle = WINFUNCTYPE(HANDLE, DWORD)(
35+
("GetStdHandle", windll.kernel32),
36+
((1, "nStdHandle"),),
37+
)
38+
39+
GetConsoleMode = WINFUNCTYPE(BOOL, HANDLE, POINTER(DWORD))(
40+
("GetConsoleMode", windll.kernel32),
41+
((1, "hConsoleHandle"), (2, "lpMode")),
42+
)
43+
GetConsoleMode.errcheck = bool_errcheck
44+
45+
SetConsoleMode = WINFUNCTYPE(BOOL, HANDLE, DWORD)(
46+
("SetConsoleMode", windll.kernel32),
47+
((1, "hConsoleHandle"), (1, "dwMode")),
48+
)
49+
SetConsoleMode.errcheck = bool_errcheck
50+
51+
# As of Windows 10, the Windows console supports (some) ANSI escape
52+
# sequences, but it needs to be enabled using `SetConsoleMode` first.
53+
#
54+
# More info on the escape sequences supported:
55+
# https://msdn.microsoft.com/en-us/library/windows/desktop/mt638032(v=vs.85).aspx
56+
stderr = GetStdHandle(STD_ERROR_HANDLE)
57+
flags = GetConsoleMode(stderr)
58+
SetConsoleMode(stderr, flags | ENABLE_VIRTUAL_TERMINAL_PROCESSING)
59+
60+
try:
61+
_enable()
62+
except OSError:
63+
TERMINAL_SUPPORT_COLOR = False
64+
else:
65+
TERMINAL_SUPPORT_COLOR = True
66+
else: # pragma: win32 no cover
67+
TERMINAL_SUPPORT_COLOR = True
68+
69+
RESET = "\033[0m"
70+
BLACK = "\033[30m"
71+
RED = "\033[31m"
72+
GREEN = "\033[32m"
73+
YELLOW = "\033[33m"
74+
BLUE = "\033[34m"
75+
MAGENTA = "\033[35m"
76+
CYAN = "\033[36m"
77+
WHITE = "\033[97m"
78+
BOLD_WHITE = "\033[1;37m"
79+
80+
81+
def paint(content: str, color: str) -> str:
82+
if TERMINAL_SUPPORT_COLOR:
83+
return color + content + RESET
84+
else:
85+
return content
86+
87+
88+
def diff(content: Tuple[str, ...]) -> str: # pragma: no cover
89+
lines = list(content)
90+
for i, line in enumerate(lines):
91+
if line.startswith("+++") or line.startswith("---"):
92+
lines[i] = paint(line, BOLD_WHITE)
93+
elif line.startswith("@@"):
94+
lines[i] = paint(line, CYAN)
95+
elif line.startswith("+"):
96+
lines[i] = paint(line, GREEN)
97+
elif line.startswith("-"):
98+
lines[i] = paint(line, RED)
99+
return "\n".join(lines)

0 commit comments

Comments
 (0)