Skip to content

Commit 9c70003

Browse files
authored
2.1.0rc1 (#59)
1 parent b4eeb4f commit 9c70003

File tree

16 files changed

+480
-456
lines changed

16 files changed

+480
-456
lines changed

.github/workflows/github-pages.yml

Lines changed: 26 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,9 @@ on:
55
branches:
66
- master
77
- develop
8-
pull_request:
9-
branches:
10-
- master
11-
- develop
8+
release:
9+
types:
10+
- published
1211

1312
permissions:
1413
contents: read
@@ -20,26 +19,26 @@ jobs:
2019
runs-on: ubuntu-22.04
2120
environment: github-pages
2221
steps:
23-
- name: Checkout code
24-
uses: actions/checkout@v4
25-
- name: Set up Python
26-
uses: actions/setup-python@v5
27-
with:
28-
python-version: '3.10'
29-
- name: Install dependencies
30-
run: |
31-
python -m pip install --upgrade pip
32-
pip install -r docs/source/requirements.txt
33-
- name: Build documentation
34-
run: |
35-
cd docs
36-
make html
37-
- name: Disable Jekyll
38-
run: |
39-
touch docs/build/html/.nojekyll
40-
- name: Upload pages artifact
41-
uses: actions/upload-pages-artifact@v3
42-
with:
43-
path: docs/build/html
44-
- name: Deploy to GitHub Pages
45-
uses: actions/deploy-pages@v4
22+
- name: Checkout code
23+
uses: actions/checkout@v4
24+
- name: Set up Python
25+
uses: actions/setup-python@v5
26+
with:
27+
python-version: '3.11'
28+
- name: Install dependencies
29+
run: |
30+
python -m pip install --upgrade pip
31+
pip install -r docs/source/requirements.txt
32+
- name: Build documentation
33+
run: |
34+
cd docs
35+
make html
36+
- name: Disable Jekyll
37+
run: |
38+
touch docs/build/html/.nojekyll
39+
- name: Upload pages artifact
40+
uses: actions/upload-pages-artifact@v3
41+
with:
42+
path: docs/build/html
43+
- name: Deploy to GitHub Pages
44+
uses: actions/deploy-pages@v4

README.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
11
# PyHelpers
22

3-
[![PyPI](https://img.shields.io/pypi/v/pyhelpers)](https://pypi.org/project/pyhelpers/)
4-
[![Python Version](https://img.shields.io/pypi/pyversions/pyhelpers)](https://docs.python.org/3/)
5-
[![Documentation Status](https://readthedocs.org/projects/pyhelpers/badge/?version=latest)](https://pyhelpers.readthedocs.io/en/latest/?badge=latest)
6-
[![License](https://img.shields.io/github/license/mikeqfu/pyhelpers)
7-
](https://github.com/mikeqfu/pyhelpers/blob/master/LICENSE)
8-
[![Codacy Badge](https://app.codacy.com/project/badge/Grade/c3ed8571c494450da12cb0c4d3c8c7e9)](https://app.codacy.com/gh/mikeqfu/pyhelpers/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)
3+
[![PyPI - Version](https://img.shields.io/pypi/v/pyhelpers)](https://pypi.org/project/pyhelpers/)
4+
[![PyPI - Python Version](https://img.shields.io/pypi/pyversions/pyhelpers)](https://docs.python.org/3/)
5+
[![License](https://img.shields.io/github/license/mikeqfu/pyhelpers)](https://github.com/mikeqfu/pyhelpers/blob/master/LICENSE)
6+
[![Read the Docs - Documentation](https://img.shields.io/readthedocs/pyhelpers?logo=readthedocs)](https://pyhelpers.readthedocs.io/en/latest/?badge=latest)
7+
[![GitHub Pages - Documentation](https://img.shields.io/github/actions/workflow/status/mikeqfu/pyhelpers/github-pages.yml?branch=master&logo=github&label=docs)](https://mikeqfu.github.io/pyhelpers/)
8+
[![Codacy - Code Quality](https://app.codacy.com/project/badge/Grade/c3ed8571c494450da12cb0c4d3c8c7e9)](https://app.codacy.com/gh/mikeqfu/pyhelpers/dashboard?utm_source=gh&utm_medium=referral&utm_content=&utm_campaign=Badge_grade)
99
[![Zenodo](https://zenodo.org/badge/173177909.svg)](https://zenodo.org/badge/latestdoi/173177909)
1010

1111
PyHelpers is an open-source Python package designed to streamline data (pre-)processing and manipulation tasks. It accommodates a wide range of functions and classes grounded in practical applications, making common data operations more accessible and efficient. This toolkit is particularly useful for Python learners, researchers and data scientists seeking to enhance their workflows.
@@ -26,15 +26,15 @@ For more information, see the [Installation](https://pyhelpers.readthedocs.io/en
2626

2727
## Quick Start
2828

29-
For a concise guide on how to use PyHelpers, check out the [Quick Start](https://pyhelpers.readthedocs.io/en/latest/quick-start.html) tutorial, which includes illustrative examples for each of the [Modules](https://pyhelpers.readthedocs.io/en/latest/modules.html).
29+
For a concise guide on how to use PyHelpers, check out the [Quick Start](https://pyhelpers.readthedocs.io/en/latest/quick-start.html) tutorial, which includes illustrative examples for each of the [Modules](https://pyhelpers.readthedocs.io/en/latest/modules.html).
3030

3131
These examples briefly demonstrate the capabilities of PyHelpers in facilitating data manipulation tasks and streamlining work processes.
3232

3333
## Documentation
3434

3535
The complete PyHelpers Documentation is available in [HTML](https://pyhelpers.readthedocs.io/en/latest/) and [PDF](https://pyhelpers.readthedocs.io/_/downloads/en/latest/pdf/) formats.
3636

37-
It is hosted on [ReadTheDocs](https://readthedocs.org/projects/pyhelpers/) and includes detailed examples.
37+
It is hosted on [Read the Docs](https://readthedocs.org/projects/pyhelpers/), and the HTML version is also accessible via [GitHub Pages](https://mikeqfu.github.io/pyhelpers/). The documentation includes detailed examples, tutorials and comprehensive references to help users get the most out of PyHelpers.
3838

3939
## Cite as
4040

docs/source/dirs.rst

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,8 +27,7 @@ Directory validation
2727
:toctree: _generated/
2828
:template: function.rst
2929

30-
path2linux
31-
uniform_pathname
30+
normalize_pathname
3231
is_dir
3332
validate_dir
3433
validate_filename

docs/source/ops.rst

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,6 @@ Basic data manipulation
6464
detect_nan_for_str_column
6565
create_rotation_matrix
6666
dict_to_dataframe
67-
parse_csr_matrix
6867
swap_cols
6968
swap_rows
7069
np_shift

docs/source/store.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ Loading data
4040
load_json
4141
load_joblib
4242
load_feather
43+
load_csr_matrix
4344
load_data
4445

4546
Transforming data files

pyhelpers/data/metadata

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55
"Author": "Qian Fu",
66
"Affiliation": "School of Engineering, University of Birmingham",
77
"Email": "q.fu@bham.ac.uk",
8-
"Version": "2.0.1",
8+
"Version": "2.1.0",
99
"License": "MIT License",
1010
"First release": "September 2019"
1111
}

pyhelpers/dirs/val.py

Lines changed: 80 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -11,61 +11,40 @@
1111
from .._cache import _check_relative_pathname
1212

1313

14-
def path2linux(path):
14+
def normalize_pathname(pathname, sep="/", **kwargs):
1515
"""
16-
Converts a path to a standardized Linux file path format for cross-platform compatibility.
16+
Converts a pathname to a consistent file path format for cross-platform compatibility.
1717
18-
This function:
18+
This function formats a file (or an OS-specific) path to ensure compatibility across
19+
Windows, Linux and macOS.
1920
20-
- Formats the file path to ensure compatibility across Windows, Linux and macOS.
21-
- Converts an OS-specific path to a standard Linux path.
22-
23-
:param path: Absolute or relative pathname.
24-
:type path: str | bytes | os.PathLike
25-
:return: Standard Linux pathname.
21+
:param pathname: A pathname.
22+
:type pathname: str | bytes | pathlib.Path | os.PathLike
23+
:param sep: File path separator used by the operating system;
24+
defaults to ``"/"`` (forward slash) for Linux and macOS pathname.
25+
:type sep: str
26+
:return: Pathname of a consistent file path format.
2627
:rtype: str
2728
2829
**Examples**::
2930
30-
>>> from pyhelpers.dirs import path2linux
31+
>>> from pyhelpers.dirs import normalize_pathname
32+
>>> import os
3133
>>> import pathlib
32-
>>> path2linux("tests\\data\\dat.csv")
34+
>>> normalize_pathname("tests\\data\\dat.csv")
3335
'tests/data/dat.csv'
34-
>>> path2linux(pathlib.Path("tests\\data\\dat.csv"))
36+
>>> normalize_pathname("tests//data/dat.csv")
3537
'tests/data/dat.csv'
38+
>>> normalize_pathname(pathlib.Path("tests\\data/dat.csv"), sep=os.path.sep) # On Windows
39+
'tests\\data\\dat.csv'
3640
"""
3741

38-
# noinspection PyBroadException
39-
try:
40-
return path.replace("\\", "/")
41-
except Exception:
42-
return str(path).replace("\\", "/")
43-
44-
45-
def uniform_pathname(pathname):
46-
"""
47-
Converts a pathname to a standard Linux file path format.
48-
49-
This function serves as an alternative to :func:`~pyhelpers.dirs.path2linux`.
50-
51-
:param pathname: Absolute or relative pathname.
52-
:type pathname: str | pathlib.Path
53-
:return: Standard Linux pathname.
54-
:rtype: str
55-
56-
**Examples**::
57-
58-
>>> from pyhelpers.dirs import uniform_pathname
59-
>>> import pathlib
60-
>>> uniform_pathname("tests\\data\\dat.csv")
61-
'tests/data/dat.csv'
62-
>>> uniform_pathname("tests//data/dat.csv")
63-
'tests/data/dat.csv'
64-
>>> uniform_pathname(pathlib.Path("tests\\data/dat.csv"))
65-
'tests/data/dat.csv'
66-
"""
42+
if isinstance(pathname, bytes):
43+
pathname_ = pathname.decode(**kwargs)
44+
else:
45+
pathname_ = str(pathname)
6746

68-
pathname_ = re.sub(r"[\\/]+", "/", str(pathname))
47+
pathname_ = re.sub(r"[\\/]+", re.escape(sep), pathname_)
6948

7049
return pathname_
7150

@@ -198,9 +177,9 @@ def validate_filename(file_pathname, suffix_num=1):
198177
>>> from pyhelpers.dirs import validate_filename
199178
>>> import os
200179
>>> test_file_pathname = "tests/data/test.txt"
201-
>>> # When the file does not exist, return the same file name
202180
>>> os.path.exists(test_file_pathname)
203181
False
182+
>>> # If the file does not exist, the function returns the same filename
204183
>>> file_pathname_0 = validate_filename(test_file_pathname)
205184
>>> os.path.relpath(file_pathname_0)
206185
'tests\\data\\test.txt'
@@ -224,26 +203,26 @@ def validate_filename(file_pathname, suffix_num=1):
224203
... os.remove(x)
225204
"""
226205

227-
# convert the path to standard linux path
228-
filename_abspath = path2linux(os.path.abspath(file_pathname))
206+
# Convert the path to standard linux path
207+
filename_abspath = os.path.normpath(file_pathname)
229208

230-
# get the file suffix
209+
# Get the file suffix
231210
file_suffix = filename_abspath.split(".")[-1]
232211
file_without_suffix = filename_abspath[:-len(file_suffix) - 1]
233212

234-
# remove the suffix if the file name contains "("
213+
# Remove the suffix if the file name contains "("
235214
if "(" in file_without_suffix:
236215
file_without_suffix = file_without_suffix.split("(")[0]
237216

238-
# if the file does not exist, return the same file name
217+
# If the file does not exist, return the same file name
239218
if os.path.exists(filename_abspath):
240219
filename_update = f"{file_without_suffix}({suffix_num}).{file_suffix}"
241220
return validate_filename(filename_update, suffix_num + 1)
242221

243222
return filename_abspath
244223

245224

246-
def get_file_pathnames(path_to_dir, file_ext=None, incl_subdir=False):
225+
def get_file_pathnames(path_to_dir, file_ext=None, incl_subdir=False, abs_path=False):
247226
"""
248227
Gets paths of files in a directory matching the specified file extension.
249228
@@ -260,52 +239,69 @@ def get_file_pathnames(path_to_dir, file_ext=None, incl_subdir=False):
260239
when ``incl_subdir=True``, it includes files from all subdirectories recursively;
261240
defaults to `False`.
262241
:type incl_subdir: bool
242+
:param abs_path: Whether to return absolute pathname(s).
243+
:type abs_path: bool
263244
:return: List of file paths matching the criteria.
264245
:rtype: list
265246
266247
**Examples**::
267248
268-
>>> from pyhelpers.dirs import get_file_pathnames
249+
>>> from pyhelpers.dirs import get_file_pathnames, delete_dir
250+
>>> from pyhelpers.store import unzip
251+
>>> import os
269252
>>> test_dir_name = "tests/data"
270-
>>> # Get all files in the folder (without sub-folders)
253+
>>> # Get all files in the directory (without subdirectories) on Windows
271254
>>> get_file_pathnames(test_dir_name)
272-
['tests/data/csr_mat.npz',
273-
'tests/data/dat.csv',
274-
'tests/data/dat.feather',
275-
'tests/data/dat.joblib',
276-
'tests/data/dat.json',
277-
'tests/data/dat.pickle',
278-
'tests/data/dat.txt',
279-
'tests/data/dat.xlsx',
280-
'tests/data/zipped',
281-
'tests/data/zipped.7z',
282-
'tests/data/zipped.txt',
283-
'tests/data/zipped.zip']
255+
['tests\\data\\csr_mat.npz',
256+
'tests\\data\\dat.csv',
257+
'tests\\data\\dat.feather',
258+
'tests\\data\\dat.joblib',
259+
'tests\\data\\dat.json',
260+
'tests\\data\\dat.ods',
261+
'tests\\data\\dat.pickle',
262+
'tests\\data\\dat.pickle.bz2',
263+
'tests\\data\\dat.pickle.gz',
264+
'tests\\data\\dat.pickle.xz',
265+
'tests\\data\\dat.txt',
266+
'tests\\data\\dat.xlsx',
267+
'tests\\data\\zipped.7z',
268+
'tests\\data\\zipped.txt',
269+
'tests\\data\\zipped.zip']
284270
>>> get_file_pathnames(test_dir_name, file_ext=".txt")
285-
['tests/data/dat.txt', 'tests/data/zipped.txt']
271+
['tests\\data\\dat.txt', 'tests\\data\\zipped.txt']
272+
>>> output_dir = unzip('tests\\data\\zipped.zip', ret_output_dir=True)
273+
>>> os.listdir(output_dir)
274+
['zipped.txt']
286275
>>> # Get absolute pathnames of all files contained in the folder (incl. all subdirectories)
287-
>>> get_file_pathnames(test_dir_name, file_ext="txt", incl_subdir=True)
288-
['tests/data/dat.txt', 'tests/data/zipped.txt', 'tests/data/zipped/zipped.txt']
276+
>>> get_file_pathnames(test_dir_name, file_ext="txt", incl_subdir=True, abs_path=True)
277+
['<Parent directories>\\tests\\data\\dat.txt',
278+
'<Parent directories>\\tests\\data\\zipped.txt',
279+
'<Parent directories>\\tests\\data\\zipped\\zipped.txt']
280+
>>> delete_dir(output_dir, confirmation_required=False)
289281
"""
290282

291283
if incl_subdir:
292-
files_list = []
293-
for root, _, files in os.walk(path_to_dir):
294-
files_list.extend([os.path.join(root, file) for file in files])
284+
file_pathnames = [
285+
os.path.normpath(os.path.join(root, file))
286+
for root, _, files in os.walk(path_to_dir)
287+
for file in files
288+
]
295289

296-
if file_ext in {None, "*", "all"}:
297-
return [path2linux(file) for file in files_list]
298-
299-
return [path2linux(file) for file in files_list if file.endswith(file_ext)]
290+
else:
291+
file_pathnames = [
292+
os.path.normpath(os.path.join(path_to_dir, file))
293+
for file in os.listdir(path_to_dir)
294+
if os.path.isfile(os.path.join(path_to_dir, file))
295+
]
300296

301-
# Files in the first layer of the folder
302297
if file_ext in {None, "*", "all"}:
303-
return [path2linux(os.path.join(path_to_dir, file)) for file in os.listdir(path_to_dir)]
298+
file_pathnames = [
299+
os.path.abspath(p) if abs_path else p for p in file_pathnames]
300+
elif file_ext:
301+
file_pathnames = [
302+
os.path.abspath(p) if abs_path else p for p in file_pathnames if p.endswith(file_ext)]
304303

305-
# noinspection PyTypeChecker
306-
return [
307-
path2linux(os.path.join(path_to_dir, file)) for file in os.listdir(path_to_dir)
308-
if file.endswith(file_ext)]
304+
return file_pathnames
309305

310306

311307
def check_files_exist(filenames, path_to_dir, verbose=False):
@@ -326,21 +322,21 @@ def check_files_exist(filenames, path_to_dir, verbose=False):
326322
>>> from pyhelpers.dirs import check_files_exist
327323
>>> test_dir_name = "tests/data"
328324
>>> # Check if all required files exist in the directory
329-
>>> check_files_exist(["dat.csv", "dat.txt"], test_dir_name)
325+
>>> check_files_exist(["dat.csv", "dat.txt"], path_to_dir=test_dir_name)
330326
True
331327
>>> # If not all required files exist, print the missing files
332-
>>> check_files_exist(("dat.csv", "dat.txt", "dat_0.txt"), test_dir_name)
328+
>>> check_files_exist(("dat.csv", "dat.txt", "dat_0.txt"), test_dir_name, verbose=True)
333329
Error: Required files are not satisfied, missing files are: ['dat_0.txt']
334330
False
335331
"""
336332

337333
dir_files = get_file_pathnames(path_to_dir, file_ext="*")
338334

339335
# Format the required file name to standard linux path
340-
filenames = [path2linux(os.path.abspath(filename)) for filename in filenames]
336+
file_or_pathnames = [os.path.abspath(filename) for filename in filenames]
341337

342-
required_files_short = [filename.split("/")[-1] for filename in filenames]
343-
dir_files_short = [filename.split("/")[-1] for filename in dir_files]
338+
required_files_short = [filename.split(os.path.sep)[-1] for filename in file_or_pathnames]
339+
dir_files_short = [filename.split(os.path.sep)[-1] for filename in dir_files]
344340

345341
# `mask` have the same length as `filenames`
346342
mask = [file in dir_files_short for file in required_files_short]

0 commit comments

Comments
 (0)