Skip to content

Commit e0e5e41

Browse files
authored
Merge release v1.9.6 into main
Merge version 1.9.6 of 'xulbux' into the main branch. For more info on this update, see the change log: https://github.com/xulbux/python-lib-xulbux/blob/main/CHANGELOG.md#v1-9-6
2 parents ac13512 + 5549f5c commit e0e5e41

33 files changed

Lines changed: 1623 additions & 1031 deletions

.github/workflows/test-and-lint.yml

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,13 +40,17 @@ jobs:
4040
run: |
4141
python -m pip install --upgrade pip
4242
pip install -e .[dev]
43-
pip install flake8 flake8-pyproject pytest
43+
pip install flake8 flake8-pyproject pyright pytest
4444
4545
- name: Lint with flake8
4646
run: |
4747
python -m flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics
4848
python -m flake8 . --exit-zero --max-complexity=12 --statistics
4949
50+
- name: Type check with pyright
51+
run: |
52+
pyright
53+
5054
- name: Test with pytest
5155
run: |
5256
python -m pytest --verbose

.gitignore

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ __pycache__/
33
__pypackages__/
44
.mypy_cache/
55
.pytest_cache/
6-
*.py[cod]
6+
*.py[cdio]
77
*$py.class
88

99
# BUILD ARTIFACTS

CHANGELOG.md

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,28 @@
1515
# <br><b>Changelog</b><br>
1616

1717

18+
<span id="v1-9-6" />
19+
20+
## 13.04.2026 `v1.9.6`
21+
22+
* The compiled version of the library now includes the type stub files (`.pyi`), so type checkers can properly check types.
23+
* Made all type hints in the whole library way more strict and accurate.
24+
* Removed leftover unnecessary runtime type-checks in several methods throughout the whole library.
25+
26+
**BREAKING CHANGES:**
27+
* All methods that should use positional-only and/or keyword-only params, now actually enforce that by using the `/` and `*` syntax in the method definitions.
28+
* Renamed the `Spinner` class from the `console` module to `Throbber`, since that name is closer to what it's actually used for.
29+
* Changed the name of the TypeAlias `DataStructure` to `DataObj` because that name is shorter and more general.
30+
* Changed both names `DataStructureTypes` and `IndexIterableTypes` to `DataObjTT` and `IndexIterableTT` respectively (`TT` *stands for type-tuple*).
31+
* Made the return value of `String.single_char_repeats()` always be *`int`* and not <code>*int* | *bool*</code>.
32+
33+
1834
<span id="v1-9-5" />
1935

2036
## 25.01.2026 `v1.9.5`
2137

22-
* Add new class property `Console.encoding`, which returns the encoding used by the console (*e.g.* `utf-8`*,* `cp1252`*, …*).
23-
* Add multiple new class properties to the `System` class:
38+
* Added a new class property `Console.encoding`, which returns the encoding used by the console (*e.g.* `utf-8`*,* `cp1252`*, …*).
39+
* Added multiple new class properties to the `System` class:
2440
- `is_linux` Whether the current OS is Linux or not.
2541
- `is_mac` Whether the current OS is macOS or not.
2642
- `is_unix` Whether the current OS is a Unix-like OS (Linux, macOS, BSD, …) or not.
@@ -482,10 +498,10 @@
482498
## 21.12.2024 `v1.5.9`
483499

484500
* Fixed bugs in method `to_ansi()` in module `xx_format_codes`:<br>
485-
1. The method always returned an empty string, because the color validation was broken, and it would identify all colors as invalid.<br>
486-
Now the validation `Color.is_valid_rgba()` and `Color.is_valid_hexa()` are fixed and now, if a color is identified as invalid, the method returns the original string instead of an empty string.
487-
2. Previously the method `to_ansi()` couldn't handle formats inside `[]` because everything inside the brackets was recognized as an invalid format.<br>
488-
Now you are able to use formats inside `[]` (*e.g.* `"[[red](Red text [b](inside) square brackets!)]"`).
501+
1. The method always returned an empty string, because the color validation was broken, and it would identify all colors as invalid.<br>
502+
Now the validation `Color.is_valid_rgba()` and `Color.is_valid_hexa()` are fixed and now, if a color is identified as invalid, the method returns the original string instead of an empty string.
503+
2. Previously the method `to_ansi()` couldn't handle formats inside `[]` because everything inside the brackets was recognized as an invalid format.<br>
504+
Now you are able to use formats inside `[]` (*e.g.* `"[[red](Red text [b](inside) square brackets!)]"`).
489505
* Introduced a new test for the `xx_format_codes` module.
490506
* Fixed a small bug in the help client-command:<br>
491507
Added back the default text color.
@@ -849,8 +865,8 @@
849865
## 15.10.2024 `v1.0.1``v1.0.2``v1.0.3``v1.0.4``v1.0.5`
850866

851867
* Fixed `f-string` issues for Python 3.10:
852-
1. Not making use of same quotes inside f-strings any more.
853-
2. No backslash escaping in f-strings.
868+
1. Not making use of same quotes inside f-strings any more.
869+
2. No backslash escaping in f-strings.
854870

855871

856872
<span id="release" /><span id="v1-0-0" />

pyproject.toml

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ build-backend = "setuptools.build_meta"
1414

1515
[project]
1616
name = "xulbux"
17-
version = "1.9.5"
17+
version = "1.9.6"
1818
description = "A Python library to simplify common programming tasks."
1919
readme = "README.md"
2020
authors = [{ name = "XulbuX", email = "xulbux.real@gmail.com" }]
@@ -30,6 +30,7 @@ dependencies = [
3030
optional-dependencies = { dev = [
3131
"flake8-pyproject>=1.2.3",
3232
"flake8>=6.1.0",
33+
"pyright>=1.1.408",
3334
"pytest>=7.4.2",
3435
"toml>=0.10.2",
3536
] }
@@ -121,15 +122,18 @@ xulbux-help = "xulbux.cli.help:show_help"
121122
max-complexity = 12
122123
max-line-length = 127
123124
select = ["E", "F", "W", "C90"]
124-
extend-ignore = ["E203", "E266", "E502", "W503"]
125-
per-file-ignores = ["__init__.py:F403,F405"]
125+
extend-ignore = ["E124", "E203", "E266", "E502", "W503"]
126+
per-file-ignores = ["__init__.py:F403,F405", "types.py:E302,E305"]
126127

127128
[tool.setuptools]
128129
package-dir = { "" = "src" }
129130

130131
[tool.setuptools.packages.find]
131132
where = ["src"]
132133

134+
[tool.setuptools.package-data]
135+
xulbux = ["py.typed", "*.pyi", "**/*.pyi"]
136+
133137
[tool.pytest.ini_options]
134138
minversion = "7.0"
135139
addopts = "-ra -q"
@@ -150,3 +154,7 @@ testpaths = [
150154
"tests/test_string.py",
151155
"tests/test_system.py",
152156
]
157+
158+
[tool.pyright]
159+
include = ["src", "tests"]
160+
typeCheckingMode = "strict"

setup.py

Lines changed: 62 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
from setuptools import setup
22
from pathlib import Path
3+
import subprocess
4+
import shutil
5+
import sys
36
import os
47

58

@@ -10,14 +13,71 @@ def find_python_files(directory: str) -> list[str]:
1013
return python_files
1114

1215

13-
# OPTIONALLY USE MYPYC COMPILATION
16+
def generate_stubs_for_package():
17+
print("\nGenerating stub files with stubgen...\n")
18+
19+
try:
20+
skip_stubgen = {
21+
Path("src/xulbux/base/types.py"), # COMPLEX TYPE DEFINITIONS
22+
Path("src/xulbux/__init__.py"), # PRESERVE PACKAGE METADATA CONSTANTS
23+
}
24+
25+
src_dir = Path("src/xulbux")
26+
generated_count = 0
27+
skipped_count = 0
28+
29+
for py_file in src_dir.rglob("*.py"):
30+
pyi_file = py_file.with_suffix(".pyi")
31+
rel_path = py_file.relative_to(src_dir.parent)
32+
33+
if py_file in skip_stubgen:
34+
pyi_file.write_text(py_file.read_text(encoding="utf-8"), encoding="utf-8")
35+
print(f" copied {rel_path.with_suffix('.pyi')} (preserving type definitions)")
36+
skipped_count += 1
37+
continue
38+
39+
stubgen_exe = (
40+
shutil.which("stubgen")
41+
or str(Path(sys.executable).parent / ("stubgen.exe" if sys.platform == "win32" else "stubgen"))
42+
)
43+
result = subprocess.run(
44+
[stubgen_exe,
45+
str(py_file),
46+
"-o", "src",
47+
"--include-private",
48+
"--export-less"],
49+
capture_output=True,
50+
text=True
51+
)
52+
53+
if result.returncode == 0:
54+
print(f" generated {rel_path.with_suffix('.pyi')}")
55+
generated_count += 1
56+
else:
57+
print(f" failed {rel_path}")
58+
if result.stderr:
59+
print(f" {result.stderr.strip()}")
60+
61+
print(f"\nStub generation complete. ({generated_count} generated, {skipped_count} copied)\n")
62+
63+
except Exception as e:
64+
fmt_error = "\n ".join(str(e).splitlines())
65+
print(f"[WARNING] Could not generate stubs:\n {fmt_error}\n")
66+
67+
1468
ext_modules = []
69+
70+
# OPTIONALLY USE MYPYC COMPILATION
1571
if os.environ.get("XULBUX_USE_MYPYC", "1") == "1":
1672
try:
1773
from mypyc.build import mypycify
74+
1875
print("\nCompiling with mypyc...\n")
1976
source_files = find_python_files("src/xulbux")
20-
ext_modules = mypycify(source_files)
77+
ext_modules = mypycify(source_files, opt_level="3")
78+
print("\nMypyc compilation complete.\n")
79+
80+
generate_stubs_for_package()
2181

2282
except (ImportError, Exception) as e:
2383
fmt_error = "\n ".join(str(e).splitlines())

src/xulbux/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
__package_name__ = "xulbux"
2-
__version__ = "1.9.5"
2+
__version__ = "1.9.6"
33
__description__ = "A Python library to simplify common programming tasks."
44
__status__ = "Production/Stable"
55

src/xulbux/base/consts.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -88,7 +88,7 @@ class ANSI:
8888
"""End of an ANSI escape sequence."""
8989

9090
@classmethod
91-
def seq(cls, placeholders: int = 1) -> FormattableString:
91+
def seq(cls, placeholders: int = 1, /) -> FormattableString:
9292
"""Generates an ANSI escape sequence with the specified number of placeholders."""
9393
return cls.CHAR + cls.START + cls.SEP.join(["{}" for _ in range(placeholders)]) + cls.END
9494

src/xulbux/base/exceptions.py

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,8 @@
44

55
from .decorators import mypyc_attr
66

7-
#
8-
################################################## FILE ##################################################
97

8+
################################################## FILE ##################################################
109

1110
@mypyc_attr(native_class=False)
1211
class SameContentFileExistsError(FileExistsError):
@@ -16,7 +15,6 @@ class SameContentFileExistsError(FileExistsError):
1615

1716
################################################## PATH ##################################################
1817

19-
2018
@mypyc_attr(native_class=False)
2119
class PathNotFoundError(FileNotFoundError):
2220
"""Raised when a file system path does not exist or cannot be accessed."""

0 commit comments

Comments
 (0)