Skip to content

Commit a34da8c

Browse files
authored
Merge release v1.9.4 into main
Merge version 1.9.4 of 'xulbux' into the main branch. For more info on this update, see the change log: https://github.com/XulbuX/PythonLibraryXulbuX/blob/main/CHANGELOG.md#v1-9-4
2 parents 3d841cc + 3628e7f commit a34da8c

27 files changed

Lines changed: 717 additions & 506 deletions

.github/workflows/build-and-publish.yml

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,14 @@
11
# THIS WORKFLOW WILL BUILD WHEELS FOR ALL MAJOR PLATFORMS AND UPLOAD THEM TO PYPI
22

3+
# TO BUILD AND INSTALL LOCALLY FOR TESTING, RUN THE FOLLOWING COMMAND:
4+
# pip install "/path/to/PythonLibraryXulbuX" --no-deps --no-cache-dir --force-reinstall --no-build-isolation
5+
6+
# TO CREATE A NEW RELEASE, TAG A COMMIT WITH THE FOLLOWING FORMAT:
37
# git tag v1.X.Y
48
# git push origin v1.X.Y
9+
# IF THE TAG v1.X.Y ALREADY EXISTS, RUN THE FOLLOWING COMMANDS FIRST:
10+
# git tag -d v1.X.Y
11+
# git push origin :refs/tags/v1.X.Y
512

613
name: Build and Publish
714
permissions:
@@ -29,6 +36,19 @@ jobs:
2936
env:
3037
CIBW_BUILD: cp310-* cp311-* cp312-* cp313-* cp314-*
3138
CIBW_SKIP: "*-musllinux_*"
39+
CIBW_BEFORE_BUILD: pip install setuptools>=80.0.0 wheel>=0.45.0 mypy>=1.19.0 mypy-extensions>=1.1.0 types-regex types-keyboard prompt_toolkit>=3.0.41
40+
CIBW_BUILD_FRONTEND: "pip; args: --no-build-isolation"
41+
CIBW_ENVIRONMENT: XULBUX_USE_MYPYC=1
42+
43+
- name: Verify wheels were built
44+
run: |
45+
ls -la ./wheelhouse/
46+
if [ -z "$(ls -A ./wheelhouse/)" ]; then
47+
echo "[ERROR] No wheels were built!"
48+
exit 1
49+
fi
50+
echo "[SUCCESS] Built $(ls ./wheelhouse/*.whl | wc -l) wheels."
51+
shell: bash
3252

3353
- uses: actions/upload-artifact@v6
3454
with:
@@ -50,6 +70,7 @@ jobs:
5070
path: dist/*.tar.gz
5171

5272
upload_pypi:
73+
name: Upload to PyPI
5374
needs: [build_wheels, build_sdist]
5475
runs-on: ubuntu-latest
5576
if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags/v')

CHANGELOG.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,25 @@
1515
# <br><b>Changelog</b><br>
1616

1717

18+
<span id="v1-9-4" />
19+
20+
## 06.01.2026 `v1.9.4`
21+
22+
* Added a new base module `base.decorators` which contains custom decorators used throughout the library.
23+
* Made `mypy_extensions` an optional dependency by wrapping all uses of `mypy_extensions.mypyc_attr` in a custom decorator that acts as a no-op if `mypy_extensions` is not installed.
24+
* The methods from the `env_path` module that modify the PATH environment variable, no longer sort all paths alphabetically, but keep the original order, to not mess with the user's intended PATH order.
25+
* Added a new TypeAlias `PathsList` to the `base.types` module, which matches a list of paths as strings or `pathlib.Path` objects.
26+
27+
**BREAKING CHANGES:**
28+
* Renamed the module `path` to `file_sys` and its main class `Path` to `FileSys`, so you can better use it alongside the built-in `pathlib.Path` class without always needing to import one of them under an alias.
29+
* Renamed most `FileSys` methods to better describe their functionality:
30+
- `Path.extend()` is now `FileSys.extend_path()`
31+
- `Path.extend_or_make()` is now `FileSys.extend_or_make_path()`
32+
* Renamed the param `use_closest_match` in `FileSys.extend_path()` and `FileSys.extend_or_make_path()` to `fuzzy_match`, since that name is more commonly used for that functionality.
33+
* Updated all library methods that work with paths to accept `pathlib.Path` objects additionally to strings, as path inputs.
34+
* Also, all library methods that return paths now return `pathlib.Path` objects instead of strings.
35+
36+
1837
<span id="v1-9-3" />
1938

2039
## 01.01.2026 `v1.9.3` Big Update 🚀

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -121,6 +121,10 @@ from xulbux.color import rgba, hsla, hexa
121121
<td><a href="https://github.com/XulbuX/PythonLibraryXulbuX/wiki/env_path"><img src="https://img.shields.io/badge/env__path-B272FC?style=for-the-badge" alt="env_path"></a></td>
122122
<td><code>EnvPath</code> class, which includes methods to work with the PATH environment variable.</td>
123123
</tr>
124+
<tr>
125+
<td><a href="https://github.com/XulbuX/PythonLibraryXulbuX/wiki/path"><img src="https://img.shields.io/badge/file__sys-B272FC?style=for-the-badge" alt="path"></a></td>
126+
<td><code>FileSys</code> class, which includes methods to work with the file system and directories.</td>
127+
</tr>
124128
<tr>
125129
<td><a href="https://github.com/XulbuX/PythonLibraryXulbuX/wiki/file"><img src="https://img.shields.io/badge/file-B272FC?style=for-the-badge" alt="file"></a></td>
126130
<td><code>File</code> class, which includes methods to work with files and file paths.</td>
@@ -135,10 +139,6 @@ from xulbux.color import rgba, hsla, hexa
135139
<td><code>Json</code> class, which includes methods to read, create and update JSON files,<br>
136140
with support for comments inside the JSON data.</td>
137141
</tr>
138-
<tr>
139-
<td><a href="https://github.com/XulbuX/PythonLibraryXulbuX/wiki/path"><img src="https://img.shields.io/badge/path-B272FC?style=for-the-badge" alt="path"></a></td>
140-
<td><code>Path</code> class, which includes methods to work with file and directory paths.</td>
141-
</tr>
142142
<tr>
143143
<td><a href="https://github.com/XulbuX/PythonLibraryXulbuX/wiki/regex"><img src="https://img.shields.io/badge/regex-B272FC?style=for-the-badge" alt="regex"></a></td>
144144
<td><code>Regex</code> class, which includes methods to dynamically generate complex regex patterns<br>

pyproject.toml

Lines changed: 7 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
[build-system]
2+
# SAME BUILD-DEPS ALSO NEED TO BE SPECIFIED IN CIBW_BEFORE_BUILD IN .github/workflows/build-and-publish.yml
23
requires = [
34
"setuptools>=80.0.0",
45
"wheel>=0.45.0",
@@ -13,26 +14,24 @@ build-backend = "setuptools.build_meta"
1314

1415
[project]
1516
name = "xulbux"
16-
version = "1.9.3"
17-
authors = [{ name = "XulbuX", email = "xulbux.real@gmail.com" }]
18-
maintainers = [{ name = "XulbuX", email = "xulbux.real@gmail.com" }]
17+
version = "1.9.4"
1918
description = "A Python library to simplify common programming tasks."
2019
readme = "README.md"
20+
authors = [{ name = "XulbuX", email = "xulbux.real@gmail.com" }]
21+
maintainers = [{ name = "XulbuX", email = "xulbux.real@gmail.com" }]
2122
license = "MIT"
2223
license-files = ["LICENSE"]
2324
requires-python = ">=3.10.0"
2425
dependencies = [
2526
"keyboard>=0.13.5",
26-
"mypy-extensions>=1.1.0",
2727
"prompt_toolkit>=3.0.41",
2828
"regex>=2023.10.3",
2929
]
3030
optional-dependencies = { dev = [
31-
"black>=23.7.0",
3231
"flake8-pyproject>=1.2.3",
3332
"flake8>=6.1.0",
34-
"isort>=5.12.0",
3533
"pytest>=7.4.2",
34+
"toml>=0.10.2",
3635
] }
3736
classifiers = [
3837
"Development Status :: 5 - Production/Stable",
@@ -118,33 +117,6 @@ keywords = [
118117
[project.scripts]
119118
xulbux-help = "xulbux.cli.help:show_help"
120119

121-
[tool.black]
122-
line-length = 127
123-
target-version = ['py310', 'py311', 'py312', 'py313', 'py314']
124-
include = '\.pyi?$'
125-
extend-exclude = '''
126-
/(
127-
# directories
128-
\.eggs
129-
| \.git
130-
| \.hg
131-
| \.mypy_cache
132-
| \.tox
133-
| \.venv
134-
| build
135-
| dist
136-
)/
137-
'''
138-
139-
[tool.isort]
140-
profile = "black"
141-
line_length = 127
142-
multi_line_output = 3
143-
include_trailing_comma = true
144-
force_grid_wrap = 0
145-
use_parentheses = true
146-
ensure_newline_before_comments = true
147-
148120
[tool.flake8]
149121
max-complexity = 12
150122
max-line-length = 127
@@ -169,12 +141,12 @@ testpaths = [
169141
"tests/test_console.py",
170142
"tests/test_data.py",
171143
"tests/test_env_path.py",
144+
"tests/test_file_sys.py",
172145
"tests/test_file.py",
173146
"tests/test_format_codes.py",
174147
"tests/test_json.py",
175-
"tests/test_path.py",
148+
"tests/test_metadata_consistency.py",
176149
"tests/test_regex.py",
177150
"tests/test_string.py",
178151
"tests/test_system.py",
179-
"tests/test_version_consistency.py",
180152
]

setup.py

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,32 @@
1-
from mypyc.build import mypycify
21
from setuptools import setup
2+
from pathlib import Path
33
import os
44

55

66
def find_python_files(directory: str) -> list[str]:
77
python_files: list[str] = []
8-
for root, _, files in os.walk(directory):
9-
for file in files:
10-
if file.endswith(".py"):
11-
python_files.append(os.path.join(root, file))
8+
for file in Path(directory).rglob("*.py"):
9+
python_files.append(str(file))
1210
return python_files
1311

1412

15-
source_files = find_python_files("src/xulbux")
13+
# OPTIONALLY USE MYPYC COMPILATION
14+
ext_modules = []
15+
if os.environ.get("XULBUX_USE_MYPYC", "1") == "1":
16+
try:
17+
from mypyc.build import mypycify
18+
print("\nCompiling with mypyc...\n")
19+
source_files = find_python_files("src/xulbux")
20+
ext_modules = mypycify(source_files)
21+
22+
except (ImportError, Exception) as e:
23+
fmt_error = "\n ".join(str(e).splitlines())
24+
print(
25+
f"\n[WARNING] mypyc compilation disabled (not available or failed):\n {fmt_error}\n"
26+
"\nInstalling as pure Python package...\n"
27+
)
1628

1729
setup(
1830
name="xulbux",
19-
ext_modules=mypycify(source_files),
31+
ext_modules=ext_modules,
2032
)

src/xulbux/__init__.py

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,21 @@
1-
__version__ = "1.9.3"
1+
__package_name__ = "xulbux"
2+
__version__ = "1.9.4"
3+
__description__ = "A Python library to simplify common programming tasks."
4+
__status__ = "Production/Stable"
5+
6+
__url__ = "https://github.com/XulbuX/PythonLibraryXulbuX"
27

38
__author__ = "XulbuX"
49
__email__ = "xulbux.real@gmail.com"
510
__license__ = "MIT"
611
__copyright__ = "Copyright (c) 2024 XulbuX"
7-
__url__ = "https://github.com/XulbuX/PythonLibraryXulbuX"
8-
__description__ = "A Python library to simplify common programming tasks."
12+
13+
__requires_python__ = ">=3.10.0"
14+
__dependencies__ = [
15+
"keyboard>=0.13.5",
16+
"prompt_toolkit>=3.0.41",
17+
"regex>=2023.10.3",
18+
]
919

1020
__all__ = [
1121
"Code",
@@ -14,9 +24,9 @@
1424
"Data",
1525
"EnvPath",
1626
"File",
27+
"FileSys",
1728
"FormatCodes",
1829
"Json",
19-
"Path",
2030
"Regex",
2131
"String",
2232
"System",
@@ -28,9 +38,9 @@
2838
from .data import Data
2939
from .env_path import EnvPath
3040
from .file import File
41+
from .file_sys import FileSys
3142
from .format_codes import FormatCodes
3243
from .json import Json
33-
from .path import Path
3444
from .regex import Regex
3545
from .string import String
3646
from .system import System

src/xulbux/base/decorators.py

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
"""
2+
This module contains custom decorators used throughout the library.
3+
"""
4+
5+
from typing import Callable, TypeVar, Any
6+
7+
8+
T = TypeVar("T")
9+
10+
11+
def _noop_decorator(obj: T) -> T:
12+
"""No-op decorator that returns the object unchanged."""
13+
return obj
14+
15+
16+
def mypyc_attr(**kwargs: Any) -> Callable[[T], T]:
17+
"""A custom decorator that wraps `mypy_extensions.mypyc_attr` when available,<br>
18+
or acts as a no-op decorator when `mypy_extensions` is not installed.\n
19+
This allows the use of `mypyc` compilation hints for compiling without making
20+
`mypy_extensions` a required dependency.\n
21+
-----------------------------------------------------------------------------------------
22+
- `**kwargs` -⠀keyword arguments to pass to `mypy_extensions.mypyc_attr` if available"""
23+
try:
24+
from mypy_extensions import mypyc_attr as _mypyc_attr
25+
return _mypyc_attr(**kwargs)
26+
except ImportError:
27+
# IF 'mypy_extensions' IS NOT INSTALLED, JUST RETURN A NO-OP DECORATOR
28+
return _noop_decorator

src/xulbux/base/exceptions.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
This module contains all custom exception classes used throughout the library.
33
"""
44

5-
from mypy_extensions import mypyc_attr
5+
from .decorators import mypyc_attr
66

77
#
88
################################################## FILE ##################################################

src/xulbux/base/types.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
"""
44

55
from typing import TYPE_CHECKING, Annotated, TypeAlias, TypedDict, Optional, Protocol, Union, Any
6+
from pathlib import Path
67

78
# PREVENT CIRCULAR IMPORTS
89
if TYPE_CHECKING:
@@ -26,6 +27,9 @@
2627
#
2728
################################################## TypeAlias ##################################################
2829

30+
PathsList: TypeAlias = Union[list[Path], list[str], list[Path | str]]
31+
"""Union of all supported list types for a list of paths."""
32+
2933
DataStructure: TypeAlias = Union[list, tuple, set, frozenset, dict]
3034
"""Union of supported data structures used in the `data` module."""
3135
DataStructureTypes = (list, tuple, set, frozenset, dict)

src/xulbux/code.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ def is_js(cls, code: str, funcs: set[str] = {"__", "$t", "$lang"}) -> bool:
104104
return True
105105

106106
js_score = 0.0
107-
funcs_pattern = r"(" + "|".join(_rx.escape(f) for f in funcs) + r")" + Regex.brackets("()")
107+
funcs_pattern = r"(" + "|".join(_rx.escape(func) for func in funcs) + r")" + Regex.brackets("()")
108108
js_indicators: list[tuple[str, float]] = [
109109
(r"\b(var|let|const)\s+[\w_$]+", 2.0), # JS VARIABLE DECLARATIONS
110110
(r"\$[\w_$]+\s*=", 2.0), # jQuery-STYLE VARIABLES

0 commit comments

Comments
 (0)