Skip to content

Commit

Permalink
i199 Refactor options & commands & Option color output (#205)
Browse files Browse the repository at this point in the history
  • Loading branch information
hakancelikdev committed Jan 29, 2022
1 parent ecfcb4e commit 04f6a9f
Show file tree
Hide file tree
Showing 19 changed files with 543 additions and 222 deletions.
2 changes: 1 addition & 1 deletion .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -60,4 +60,4 @@ repos:
- id: pyall
args:
- --refactor
- --exclude=tests|setup.py
- --exclude=tests|setup.py|__init__.py|
5 changes: 5 additions & 0 deletions docs/changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,11 @@ All notable changes to this project will be documented in this file.

- []()

## [0.9.4] - 29/January/2021

- [💪 🔥 🧪 i199 Refactor options & commands & Option color output by @hakancelik96](https://github.com/hakancelik96/unimport/pull/205)
- [💪 🔥 Build an Docker image #202 by @hakancelik96](https://github.com/hakancelik96/unimport/issues/202)

## [0.9.2] - 14/September/2021

- 🐞 Fix setup.py
Expand Down
45 changes: 42 additions & 3 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,18 @@ You can automatically delete unused modules from the requirements.txt file
- `unimport --requirements --permission` to refactor permission after seeing the diff.
- `unimport --requirements --remove` to remove automatically.

## Color

> (optional: default `auto`) choices: (always, never, auto)
Select whether to use color in the output.

**Usage**

- `unimport --color always`
- `unimport --color never`
- `unimport --color auto`

## Typing

Unimport can understand that imports are used these cases.
Expand Down Expand Up @@ -351,9 +363,9 @@ class Klass:
You can list many options by running unimport --help

```
usage: unimport [-h] [--check] [-c PATH] [--include include] [--exclude exclude] [--gitignore]
[--ignore-init] [--include-star-import] [-d] [-r | -p] [--requirements] [-v]
[sources [sources ...]]
usage: unimport [-h] [--check] [-c PATH] [--color {auto,always,never}] [--include include] [--exclude exclude] [--gitignore] [--ignore-init] [--include-star-import] [-d]
[-r | -p] [--requirements] [-v]
[sources ...]
A linter, formatter for finding and removing unused import statements.
Expand All @@ -365,6 +377,8 @@ optional arguments:
--check Prints which file the unused imports are in.
-c PATH, --config PATH
Read configuration from PATH.
--color {auto,always,never}
Select whether to use color in the output. Defaults to `auto`.
--include include File include pattern.
--exclude exclude File exclude pattern.
--gitignore Exclude .gitignore patterns. if present.
Expand Down Expand Up @@ -438,3 +452,28 @@ repos:
args:
[--remove, --requirements, --include-star-import, --ignore-init, --gitignore]
```

## Use as a Docker image

Install from the command line:

To use the latest

```
$ docker pull ghcr.io/hakancelik96/unimport:latest
```

To use the stable

```
$ docker pull ghcr.io/hakancelik96/unimport:stable
```

To use the other versions

```
$ docker pull ghcr.io/hakancelik96/unimport:{version_number}
```

For more information see:
https://github.com/hakancelik96/unimport/pkgs/container/unimport
4 changes: 2 additions & 2 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ def get_long_description():
DESCRIPTION = (
"A linter, formatter for finding and removing unused import statements."
)
VERSION = "0.9.2"
VERSION = "0.9.4"


setup(
Expand All @@ -38,7 +38,7 @@ def get_long_description():
license="MIT",
license_file="LICENSE",
python_requires=">=3.6",
packages=["unimport"],
packages=["unimport", "unimport.commands"],
install_requires=[
"libcst>=0.3.7; python_version >= '3.9'",
"libcst>=0.3.0; python_version <= '3.8'",
Expand Down
22 changes: 22 additions & 0 deletions tests/analyzer/test__analyzer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import textwrap
import unittest

from unimport.analyzer import Analyzer
from unimport.statement import Import


class AnalyzerTestCase(unittest.TestCase):
def setUp(self) -> None:
Analyzer.clear()

def test_context_manager(self):
with Analyzer(
source=textwrap.dedent(
"""\
import x
"""
)
):
self.assertEqual(1, len(Import.imports))

self.assertEqual(0, len(Import.imports))
51 changes: 36 additions & 15 deletions tests/test_color.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
import sys
import unittest

from unimport.color import RED, RESET, TERMINAL_SUPPORT_COLOR, paint
import pytest

from unimport.color import RED, RESET, TERMINAL_SUPPORT_COLOR, paint, use_color


class ColorTestCase(unittest.TestCase):
maxDiff = None

def setUp(self):
self.content = "test content"
self.text = "test text"

@unittest.skipUnless(sys.platform == "win32", "requires Windows32")
def test_terminal_support_color_on_win(self):
Expand All @@ -25,17 +27,36 @@ def test_terminal_support_color_on_win(self):
def test_terminal_support_color(self):
self.assertTrue(TERMINAL_SUPPORT_COLOR)

@unittest.skipUnless(sys.platform == "win32", "requires Windows32")
def test_red_paint_on_win(self):
action_content = paint(self.content, RED)
if TERMINAL_SUPPORT_COLOR:
expected_content = RED + self.content + RESET
else:
expected_content = self.content
self.assertEqual(expected_content, action_content)

@unittest.skipUnless(sys.platform != "win32", "requires Windows32")
def test_red_paint(self):
action_content = paint(self.content, RED)
expected_content = RED + self.content + RESET
self.assertEqual(expected_content, action_content)
action_text = paint(self.text, RED)
expected_text = RED + self.text + RESET
self.assertEqual(expected_text, action_text)

def test_use_color_setting_false(self):
action_text = paint(self.text, RED, False)
expected_text = self.text
self.assertEqual(expected_text, action_text)

def test_use_color_setting_true(self):
action_text = paint(self.text, RED, True)
expected_text = RED + self.text + RESET
self.assertEqual(expected_text, action_text)


@pytest.mark.parametrize(
"option,expected_result",
[
("auto", TERMINAL_SUPPORT_COLOR and sys.stderr.isatty()),
("always", True),
("never", False),
],
)
def test_use_color(option, expected_result):
assert expected_result == use_color(option)


def test_use_color_none_of_them():
with pytest.raises(ValueError) as cm:
use_color("none-of-them")

assert "none-of-them" in str(cm.value)
2 changes: 1 addition & 1 deletion tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ class UtilsTestCase(unittest.TestCase):
)

def test_list_paths(self):
self.assertEqual(len(list(utils.list_paths(Path("tests")))), 33)
self.assertEqual(len(list(utils.list_paths(Path("tests")))), 34)
self.assertEqual(
len(list(utils.list_paths(Path("tests/test_config.py")))), 1
)
Expand Down
4 changes: 1 addition & 3 deletions unimport/__main__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import sys

from unimport.main import main

if __name__ == "__main__":
sys.exit(main())
raise SystemExit(main())
7 changes: 7 additions & 0 deletions unimport/analyzer.py
Original file line number Diff line number Diff line change
Expand Up @@ -447,6 +447,13 @@ def __init__(
self.path = path
self.include_star_import = include_star_import

def __enter__(self):
self.traverse()
return self

def __exit__(self, exc_type, exc_value, exc_tb):
self.clear()

def traverse(self) -> None:
if self.skip_file():
return None
Expand Down
37 changes: 31 additions & 6 deletions unimport/color.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import argparse
import sys
from typing import Tuple

__all__ = [
"BLACK",
"BLUE",
"BOLD_WHITE",
"COLOR_CHOICES",
"CYAN",
"GREEN",
"MAGENTA",
Expand All @@ -13,8 +15,10 @@
"TERMINAL_SUPPORT_COLOR",
"WHITE",
"YELLOW",
"add_color_option",
"difference",
"paint",
"use_color",
]

if sys.platform == "win32": # pragma: no cover (windows)
Expand Down Expand Up @@ -81,16 +85,37 @@ def bool_errcheck(result, func, args):
WHITE = "\033[97m"
BOLD_WHITE = "\033[1;37m"

COLOR_CHOICES = ("auto", "always", "never")

def paint(content: str, color: str) -> str:
if TERMINAL_SUPPORT_COLOR:
return color + content + RESET

def paint(text: str, color: str, use_color_setting: bool = True) -> str:
if use_color_setting:
return color + text + RESET
else:
return content
return text


def use_color(setting: str) -> bool:
if setting not in COLOR_CHOICES:
raise ValueError(setting)

return setting == "always" or (
setting == "auto" and sys.stderr.isatty() and TERMINAL_SUPPORT_COLOR
)


def add_color_option(parser: argparse.ArgumentParser) -> None:
parser.add_argument(
"--color",
default="auto",
type=use_color,
metavar="{" + ",".join(COLOR_CHOICES) + "}",
help="Select whether to use color in the output. Defaults to `%(default)s`.",
)


def difference(content: Tuple[str, ...]) -> str: # pragma: no cover
lines = list(content)
def difference(text: Tuple[str, ...]) -> str: # pragma: no cover
lines = list(text)
for i, line in enumerate(lines):
if line.startswith("+++") or line.startswith("---"):
lines[i] = paint(line, BOLD_WHITE)
Expand Down
15 changes: 15 additions & 0 deletions unimport/commands/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
from unimport.commands.check import check, requirements_check
from unimport.commands.diff import diff, requirements_diff
from unimport.commands.permission import permission, requirements_permission
from unimport.commands.remove import remove, requirements_remove

__all__ = [
"check",
"diff",
"permission",
"remove",
"requirements_check",
"requirements_diff",
"requirements_permission",
"requirements_remove",
]
43 changes: 43 additions & 0 deletions unimport/commands/check.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
from pathlib import Path
from typing import List, Union

from unimport.color import CYAN, GREEN, RED, YELLOW, paint
from unimport.statement import Import, ImportFrom

__all__ = ["check", "requirements_check"]


def check(
path: Path,
unused_imports: List[Union[Import, ImportFrom]],
use_color_setting: bool,
) -> None:
for imp in unused_imports:
if isinstance(imp, ImportFrom) and imp.star and imp.suggestions:
context = (
paint(f"from {imp.name} import *", RED, use_color_setting)
+ " -> "
+ paint(
f"from {imp.name} import {', '.join(imp.suggestions)}",
GREEN,
use_color_setting,
)
)
else:
context = paint(imp.name, YELLOW, use_color_setting)
print(
context
+ " at "
+ paint(path.as_posix(), GREEN, use_color_setting)
+ ":"
+ paint(str(imp.lineno), GREEN, use_color_setting)
)


def requirements_check(
path: Path, index: int, requirement: str, use_color_setting: bool
) -> None:
print(
f"{paint(requirement, CYAN), use_color_setting} at "
f"{paint(path.as_posix(), CYAN, use_color_setting)}:{paint(str(index + 1), CYAN, use_color_setting)}"
)
34 changes: 34 additions & 0 deletions unimport/commands/diff.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
from pathlib import Path

from unimport import utils
from unimport.color import difference as color_difference

__all__ = ["diff", "requirements_diff"]


def diff(
path: Path,
source: str,
refactor_result: str,
) -> bool:
diff = utils.diff(
source=source, refactor_result=refactor_result, fromfile=path
)
exists_diff = bool(diff)
if exists_diff:
print(color_difference(diff))

return exists_diff


def requirements_diff(path: Path, source: str, refactor_result: str) -> bool:
diff = utils.diff(
source=source,
refactor_result=refactor_result,
fromfile=path,
)
exists_diff = bool(diff)
if exists_diff:
print(color_difference(diff))

return exists_diff
Loading

0 comments on commit 04f6a9f

Please sign in to comment.