Skip to content

Commit 5a402df

Browse files
committed
remove unnecessary # type: ignore[...]'s and add missing type checking
1 parent d4dec59 commit 5a402df

7 files changed

Lines changed: 82 additions & 47 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,10 @@
2525

2626
**BREAKING CHANGES:**
2727
* Made the value input into the params `bar_format` and `limited_bar_format` of `ProgressBar` be a list/tuple of strings instead of a single string, so the user can define multiple formats for different console widths.
28-
* Added a new param <code>sep: str = " "</code> to the `ProgressBar` class, which is used to join multiple bar-format strings.
28+
* Added a new param <code>sep: *str* = " "</code> to the `ProgressBar` class, which is used to join multiple bar-format strings.
2929
* Renamed the class property `Console.wh` to `Console.size`, since it describes the property better.
3030
* Renamed the class property `Console.usr` to `Console.user`, since it describes the property better.
31+
* Added missing type checking to methods in the `path` module.
3132

3233
<span id="v1-9-0" />
3334

src/xulbux/console.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -184,13 +184,13 @@ def items(self) -> Generator[tuple[str, ArgResultRegular | ArgResultPositional],
184184
class Console:
185185
"""This class provides methods for logging and other actions within the console."""
186186

187-
w: int = _ConsoleWidth() # type: ignore[assignment]
187+
w: int = cast(int, _ConsoleWidth())
188188
"""The width of the console in characters."""
189-
h: int = _ConsoleHeight() # type: ignore[assignment]
189+
h: int = cast(int, _ConsoleHeight())
190190
"""The height of the console in lines."""
191-
size: tuple[int, int] = _ConsoleSize() # type: ignore[assignment]
191+
size: tuple[int, int] = cast(tuple[int, int], _ConsoleSize())
192192
"""A tuple with the width and height of the console in characters and lines."""
193-
user: str = _ConsoleUser() # type: ignore[assignment]
193+
user: str = cast(str, _ConsoleUser())
194194
"""The name of the current user."""
195195

196196
@staticmethod
@@ -1054,7 +1054,7 @@ def input(
10541054
allow_paste: bool = True,
10551055
validator: Optional[Callable[[str], Optional[str]]] = None,
10561056
default_val: Optional[T] = None,
1057-
output_type: type[T] = str, # type: ignore[assignment]
1057+
output_type: type[T] = str,
10581058
) -> T:
10591059
"""Acts like a standard Python `input()` a bunch of cool extra features.\n
10601060
------------------------------------------------------------------------------------

src/xulbux/path.py

Lines changed: 62 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44

55
from .base.exceptions import PathNotFoundError
66

7-
from typing import Optional
7+
from typing import Optional, cast
88
import tempfile as _tempfile
99
import difflib as _difflib
1010
import shutil as _shutil
@@ -37,9 +37,9 @@ def __get__(self, obj, owner=None):
3737
class Path:
3838
"""This class provides methods to work with file and directory paths."""
3939

40-
cwd: str = _Cwd() # type: ignore[assignment]
40+
cwd: str = cast(str, _Cwd())
4141
"""The path to the current working directory."""
42-
script_dir: str = _ScriptDir() # type: ignore[assignment]
42+
script_dir: str = cast(str, _ScriptDir())
4343
"""The path to the directory of the current script."""
4444

4545
@staticmethod
@@ -63,68 +63,82 @@ def extend(
6363
it will be searched in the `search_in` directory/s.<br>
6464
If the `rel_path` is still not found, it returns `None` or
6565
raises a `PathNotFoundError` if `raise_error` is true."""
66-
if rel_path in {"", None}:
66+
search_dirs: list[str] = []
67+
68+
if not isinstance(rel_path, str):
69+
raise TypeError(f"The 'rel_path' parameter must be a string, got {type(rel_path)}")
70+
if search_in is not None:
71+
if isinstance(search_in, str):
72+
search_dirs.extend([search_in])
73+
elif isinstance(search_in, list):
74+
if not all(isinstance(item, str) for item in search_in):
75+
raise TypeError("All items in the 'search_in' list must be strings.")
76+
search_dirs.extend(search_in)
77+
else:
78+
raise TypeError(f"The 'search_in' parameter must be a string or a list of strings, got {type(search_in)}")
79+
if not isinstance(raise_error, bool):
80+
raise TypeError(f"The 'raise_error' parameter must be a boolean, got {type(raise_error)}")
81+
if not isinstance(use_closest_match, bool):
82+
raise TypeError(f"The 'use_closest_match' parameter must be a boolean, got {type(use_closest_match)}")
83+
84+
if rel_path == "":
6785
if raise_error:
6886
raise PathNotFoundError("Path is empty.")
69-
return None
87+
else:
88+
return None
7089
elif _os.path.isabs(rel_path):
7190
return rel_path
7291

7392
def get_closest_match(dir: str, part: str) -> Optional[str]:
7493
try:
75-
files_and_dirs = _os.listdir(dir)
76-
matches = _difflib.get_close_matches(part, files_and_dirs, n=1, cutoff=0.6)
94+
matches = _difflib.get_close_matches(part, _os.listdir(dir), n=1, cutoff=0.6)
7795
return matches[0] if matches else None
7896
except Exception:
7997
return None
8098

8199
def find_path(start: str, parts: list[str]) -> Optional[str]:
82100
current = start
101+
83102
for part in parts:
84103
if _os.path.isfile(current):
85104
return current
86105
closest_match = get_closest_match(current, part) if use_closest_match else part
87106
current = _os.path.join(current, closest_match) if closest_match else None
88107
if current is None:
89108
return None
109+
90110
return current if _os.path.exists(current) and current != start else None
91111

92112
def expand_env_path(p: str) -> str:
93113
if "%" not in p:
94114
return p
95-
parts = p.split("%")
96-
for i in range(1, len(parts), 2):
115+
116+
for i in range(1, len(parts := p.split("%")), 2):
97117
if parts[i].upper() in _os.environ:
98118
parts[i] = _os.environ[parts[i].upper()]
119+
99120
return "".join(parts)
100121

101122
rel_path = _os.path.normpath(expand_env_path(rel_path))
123+
102124
if _os.path.isabs(rel_path):
103125
drive, rel_path = _os.path.splitdrive(rel_path)
104126
rel_path = rel_path.lstrip(_os.sep)
105-
search_dirs = [(drive + _os.sep) if drive else _os.sep]
127+
search_dirs.extend([(drive + _os.sep) if drive else _os.sep])
106128
else:
107129
rel_path = rel_path.lstrip(_os.sep)
108-
base_dir = Path.script_dir
109-
search_dirs = [
110-
_os.getcwd(),
111-
base_dir,
112-
_os.path.expanduser("~"),
113-
_tempfile.gettempdir(),
114-
]
115-
if search_in:
116-
search_dirs.extend([search_in] if isinstance(search_in, str) else search_in)
117-
path_parts = rel_path.split(_os.sep)
130+
search_dirs.extend([_os.getcwd(), Path.script_dir, _os.path.expanduser("~"), _tempfile.gettempdir()])
131+
118132
for search_dir in search_dirs:
119-
full_path = _os.path.join(search_dir, rel_path)
120-
if _os.path.exists(full_path):
133+
if _os.path.exists(full_path := _os.path.join(search_dir, rel_path)):
121134
return full_path
122-
match = find_path(search_dir, path_parts) if use_closest_match else None
123-
if match:
135+
if (match := find_path(search_dir, rel_path.split(_os.sep)) if use_closest_match else None):
124136
return match
137+
125138
if raise_error:
126139
raise PathNotFoundError(f"Path '{rel_path}' not found in specified directories.")
127-
return None
140+
else:
141+
return None
128142

129143
@staticmethod
130144
def extend_or_make(
@@ -151,12 +165,24 @@ def extend_or_make(
151165
even though the `rel_path` doesn't exist there.<br>
152166
If `prefer_script_dir` is false, it will instead make a path
153167
that points to where the `rel_path` would be in the CWD."""
168+
# THE 'rel_path' PARAM IS CHECKED IN 'Path.extend()'
169+
# THE 'search_in' PARAM IS CHECKED IN 'Path.extend()'
170+
if not isinstance(prefer_script_dir, bool):
171+
raise TypeError(f"The 'prefer_script_dir' parameter must be a boolean, got {type(prefer_script_dir)}")
172+
# THE 'use_closest_match' PARAM IS CHECKED IN 'Path.extend()'
173+
154174
try:
155-
return str(Path.extend(rel_path, search_in, raise_error=True, use_closest_match=use_closest_match))
175+
return str(Path.extend( \
176+
rel_path=rel_path,
177+
search_in=search_in,
178+
raise_error=True,
179+
use_closest_match=use_closest_match,
180+
))
156181
except PathNotFoundError:
157-
normalized_rel_path = _os.path.normpath(rel_path)
158-
base = Path.script_dir if prefer_script_dir else _os.getcwd()
159-
return _os.path.join(base, normalized_rel_path)
182+
return _os.path.join(
183+
Path.script_dir if prefer_script_dir else _os.getcwd(),
184+
_os.path.normpath(rel_path),
185+
)
160186

161187
@staticmethod
162188
def remove(path: str, only_content: bool = False) -> None:
@@ -165,13 +191,20 @@ def remove(path: str, only_content: bool = False) -> None:
165191
- `path` -⠀the path to the directory or file to remove
166192
- `only_content` -⠀if true, only the content of the directory is removed
167193
and the directory itself is kept"""
194+
if not isinstance(path, str):
195+
raise TypeError(f"The 'path' parameter must be a string, got {type(path)}")
196+
if not isinstance(only_content, bool):
197+
raise TypeError(f"The 'only_content' parameter must be a boolean, got {type(only_content)}")
198+
168199
if not _os.path.exists(path):
169200
return None
201+
170202
if not only_content:
171203
if _os.path.isfile(path) or _os.path.islink(path):
172204
_os.unlink(path)
173205
elif _os.path.isdir(path):
174206
_shutil.rmtree(path)
207+
175208
elif _os.path.isdir(path):
176209
for filename in _os.listdir(path):
177210
file_path = _os.path.join(path, filename)

src/xulbux/system.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
from .format_codes import FormatCodes
99
from .console import Console
1010

11-
from typing import Optional
11+
from typing import Optional, cast
1212
import subprocess as _subprocess
1313
import platform as _platform
1414
import ctypes as _ctypes
@@ -33,7 +33,7 @@ def __get__(self, obj, owner=None):
3333
class System:
3434
"""This class provides methods to interact with the underlying operating system."""
3535

36-
is_elevated: bool = _IsElevated() # type: ignore[assignment]
36+
is_elevated: bool = cast(bool, _IsElevated())
3737
"""Is `True` if the current process has elevated privileges and `False` otherwise."""
3838

3939
@staticmethod

tests/test_color.py

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ def test_is_valid_rgba():
2727
assert Color.is_valid_rgba({"r": 255, "g": 0, "b": 0, "a": 0.5}) is True
2828
assert Color.is_valid_rgba(rgba(255, 0, 0)) is True
2929
assert Color.is_valid_rgba((300, 0, 0)) is False
30-
assert Color.is_valid_rgba((255, 0)) is False # type: ignore[assignment]
30+
assert Color.is_valid_rgba((255, 0)) is False
3131
assert Color.is_valid_rgba((255, 0, 0, 2)) is False
3232
assert Color.is_valid_rgba("not a color") is False
3333
assert Color.is_valid_rgba((255, 0, 0), allow_alpha=False) is True
@@ -45,7 +45,7 @@ def test_is_valid_hsla():
4545
assert Color.is_valid_hsla((370, 100, 50)) is False
4646
assert Color.is_valid_hsla((0, 101, 50)) is False
4747
assert Color.is_valid_hsla((0, 100, 101)) is False
48-
assert Color.is_valid_hsla((0, 100)) is False # type: ignore[assignment]
48+
assert Color.is_valid_hsla((0, 100)) is False
4949
assert Color.is_valid_hsla("not a color") is False
5050
assert Color.is_valid_hsla((0, 100, 50), allow_alpha=False) is True
5151
assert Color.is_valid_hsla((0, 100, 50, 0.5), allow_alpha=False) is False
@@ -117,8 +117,8 @@ def test_str_to_rgba():
117117
assert color.values() == (255, 0, 0, 0.5)
118118
colors = Color.str_to_rgba("first color: rgb(255, 0, 0) | second color: rgba(0,255,0,.5)")
119119
assert len(colors) == 2 # type: ignore[assignment]
120-
assert colors[0].values() == (255, 0, 0, None) # type: ignore
121-
assert colors[1].values() == (0, 255, 0, 0.5) # type: ignore
120+
assert colors[0].values() == (255, 0, 0, None) # type: ignore[not-subscriptable]
121+
assert colors[1].values() == (0, 255, 0, 0.5) # type: ignore[not-subscriptable]
122122
assert Color.str_to_rgba("No colors here") is None
123123

124124

@@ -159,7 +159,7 @@ def test_adjust_lightness():
159159

160160
def test_adjust_saturation():
161161
color = rgba(128, 80, 80)
162-
saturated = Color.adjust_saturation(color, 0.25) # type: ignore[assignment]
162+
saturated = Color.adjust_saturation(color, 0.25)
163163
assert isinstance(saturated, rgba)
164164
assert saturated.to_hsla().s > color.to_hsla().s
165165
assert saturated == rgba(155, 54, 54)

tests/test_console.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -220,12 +220,12 @@ def test_get_args_no_spaces(monkeypatch, argv, find_args, expected_args_dict):
220220
for key, expected in expected_args_dict.items():
221221
assert (key in args_result) is True
222222
assert isinstance(args_result[key], ArgResult)
223-
assert args_result[key].exists == expected["exists"] # type: ignore[access]
223+
assert args_result[key].exists == expected["exists"] # type: ignore[cannot-access-attr]
224224
# CHECK IF THIS IS A POSITIONAL ARG (HAS 'values') OR REGULAR ARG (HAS 'value')
225225
if "values" in expected:
226-
assert args_result[key].values == expected["values"] # type: ignore[access]
226+
assert args_result[key].values == expected["values"] # type: ignore[cannot-access-attr]
227227
else:
228-
assert args_result[key].value == expected["value"] # type: ignore[access]
228+
assert args_result[key].value == expected["value"] # type: ignore[cannot-access-attr]
229229
assert bool(args_result[key]) == expected["exists"]
230230
assert list(args_result.keys()) == list(expected_args_dict.keys())
231231
assert [v.exists for v in args_result.values()] == [d["exists"] for d in expected_args_dict.values()]
@@ -388,7 +388,7 @@ def test_get_args_invalid_config():
388388
Console.get_args({"bad_flags": {"flags": ["--flag"]}}) # type: ignore[assignment]
389389

390390
with pytest.raises(ValueError, match="Invalid 'flags' for alias 'bad_flags'. Must be a set of strings."):
391-
Console.get_args({"bad_flags": {"flags": "not-a-list", "default": "value"}}) # type: ignore[assignment]
391+
Console.get_args({"bad_flags": {"flags": "not-a-set", "default": "value"}}) # type: ignore[assignment]
392392

393393

394394
def test_get_args_duplicate_flag():

tests/test_path.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,10 @@ def test_extend(setup_test_environment):
6060

6161
# EMPTY PATH
6262
assert Path.extend("") is None
63-
assert Path.extend(None) is None # type: ignore[assignment]
6463
with pytest.raises(PathNotFoundError, match="Path is empty."):
6564
Path.extend("", raise_error=True)
65+
with pytest.raises(TypeError, match="parameter must be a string"):
66+
Path.extend(None, raise_error=True) # type: ignore[assignment]
6667

6768
# FOUND IN STANDARD LOCATIONS
6869
assert Path.extend("file_in_cwd.txt") == str(env["cwd"] / "file_in_cwd.txt")

0 commit comments

Comments
 (0)