Skip to content

Commit

Permalink
Merge pull request #11 from pharmaverse/generic-types
Browse files Browse the repository at this point in the history
Use more generic types
  • Loading branch information
nanxstats authored Jan 12, 2025
2 parents cc45366 + 0690414 commit 2ed2aec
Show file tree
Hide file tree
Showing 5 changed files with 84 additions and 89 deletions.
12 changes: 6 additions & 6 deletions src/pkglite/classify.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,11 +10,11 @@ def is_text_file(path: str, n: int | None = None) -> bool:
in zlib (`doc/txtvsbin.txt`).
Args:
path (str): File path.
n (int, optional): Maximal number of bytes to read. Defaults to file size.
path: File path.
n: Maximal number of bytes to read. Defaults to file size.
Returns:
bool: True if the file is text, False if binary.
True if the file is text, False if binary.
"""
ALLOW: FrozenSet[int] = frozenset([9, 10, 13] + list(range(32, 256)))
BLOCK: FrozenSet[int] = frozenset(list(range(0, 7)) + list(range(14, 32)))
Expand All @@ -33,12 +33,12 @@ def is_text_file(path: str, n: int | None = None) -> bool:

def classify_file(path: str) -> str:
"""
Classify file as 'text' or 'binary'.
Classify file as text or binary.
Args:
path (str): Path to the file to classify.
path: Path to the file to classify.
Returns:
str: 'text' if the file is detected as text, 'binary' otherwise.
`'text'` if the file is detected as text, `'binary'` otherwise.
"""
return "text" if is_text_file(path) else "binary"
6 changes: 3 additions & 3 deletions src/pkglite/main.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
from pathlib import Path
from typing import List, Annotated
from typing import Annotated

import typer

Expand All @@ -19,7 +19,7 @@ def callback():

@app.command()
def pack(
input_dirs: List[Path],
input_dirs: list[Path],
output_file: Annotated[Path, typer.Option("--output-file", "-o")] = Path(
"pkglite.txt"
),
Expand Down Expand Up @@ -55,7 +55,7 @@ def unpack(

@app.command()
def use(
input_dirs: List[Path],
input_dirs: list[Path],
force: Annotated[bool, typer.Option("--force", "-f")] = False,
quiet: Annotated[bool, typer.Option("--quiet", "-q")] = False,
):
Expand Down
64 changes: 33 additions & 31 deletions src/pkglite/pack.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import os
from pathlib import Path
from typing import Callable
from collections.abc import Callable
from typing import Protocol

from pathspec import PathSpec

Expand All @@ -14,16 +15,19 @@
)


def load_ignore_matcher(directory: str) -> Callable[[str], bool]:
class PathMatcher(Protocol):
def __call__(self, path: str) -> bool: ...


def load_ignore_matcher(directory: str) -> PathMatcher:
"""
Load ignore patterns from a `.pkgliteignore` file in the directory.
Args:
directory (str): Path to the directory to pack.
directory: Path to the directory to pack.
Returns:
function: A matcher function that returns True if a path
should be ignored, False otherwise.
A matcher function that returns True if a path should be ignored.
"""
abs_dir = os.path.abspath(os.path.expanduser(directory))
ignore_path = os.path.join(abs_dir, ".pkgliteignore")
Expand All @@ -41,11 +45,11 @@ def matcher(path: str) -> bool:
Check if a path matches any ignore pattern.
Args:
path (str): Path to check against ignore patterns.
path: Path to check against ignore patterns.
Should be relative to the base directory.
Returns:
bool: True if path should be ignored, False otherwise.
True if path should be ignored, False otherwise.
"""
if os.path.isabs(path):
path = os.path.relpath(path, abs_dir)
Expand All @@ -60,10 +64,10 @@ def get_package_name(directory: str) -> str:
Derive the package name from the directory name.
Args:
directory (str): Path to the directory.
directory: Path to the directory.
Returns:
str: The base name of the directory path.
The base name of the directory path.
"""
return os.path.basename(os.path.normpath(directory))

Expand All @@ -73,12 +77,12 @@ def create_file_metadata(package_name: str, relative_path: str, file_type: str)
Create file metadata string.
Args:
package_name (str): Name of the package.
relative_path (str): Relative path of the file within the package.
file_type (str): Type of the file ('text' or 'binary').
package_name: Name of the package.
relative_path: Relative path of the file within the package.
file_type: Type of the file ('text' or 'binary').
Returns:
str: Formatted metadata string.
Formatted metadata string.
"""
return (
f"Package: {package_name}\n"
Expand All @@ -93,10 +97,10 @@ def read_text_content(file_path: str) -> str:
Read text file content and format it.
Args:
file_path (str): Path to the text file to read from.
file_path: Path to the text file to read from.
Returns:
str: Formatted text content.
Formatted text content.
"""
with open(file_path, "r", encoding="utf-8") as f:
return "".join(" " + line for line in f)
Expand All @@ -107,10 +111,10 @@ def read_binary_content(file_path: str) -> str:
Read binary file content and format it in hex format.
Args:
file_path (str): Path to the binary file to read from.
file_path: Path to the binary file to read from.
Returns:
str: Formatted binary content.
Formatted binary content.
"""
with open(file_path, "rb") as f:
content = f.read().hex()
Expand All @@ -124,11 +128,11 @@ def read_file_content(file_path: str, file_type: str) -> str:
Read and format file content based on type.
Args:
file_path (str): Path to the file to read from.
file_type (str): Type of the file ('text' or 'binary').
file_path: Path to the file to read from.
file_type: Type of the file ('text' or 'binary').
Returns:
str: Formatted file content.
Formatted file content.
"""
return (
read_text_content(file_path)
Expand All @@ -147,13 +151,13 @@ def process_single_file(
Process a single file and return its formatted content.
Args:
file_path (str): Path to the file to process.
directory (str): Base directory path.
package_name (str): Name of the package.
ignore_matcher (function): Function to check if file should be ignored.
file_path: Path to the file to process.
directory: Base directory path.
package_name: Name of the package.
ignore_matcher: Function to check if file should be ignored.
Returns:
str | None: Formatted file content if not ignored, None otherwise.
Formatted file content if not ignored, None otherwise.
"""
if ignore_matcher(file_path):
return None
Expand All @@ -173,7 +177,7 @@ def create_header() -> str:
Create the pkglite header string.
Returns:
str: Formatted header string.
Formatted header string.
"""
return (
"# Generated by py-pkglite: do not edit by hand\n"
Expand All @@ -190,11 +194,9 @@ def pack(
Pack files from one or multiple directories into a text file.
Args:
input_dirs (str or Path or list): Path or list of paths to the
directories to pack.
output_file (str or Path): Path to the output file.
Default is 'pkglite.txt'.
quiet (bool): If True, suppress output messages. Default False.
input_dirs: Path or sequence of paths to the directories to pack.
output_file: Path to the output file. Default is 'pkglite.txt'.
quiet: If True, suppress output messages.
"""
dirs = [input_dirs] if isinstance(input_dirs, (str, Path)) else input_dirs
abs_dirs = [os.path.abspath(os.path.expanduser(str(d))) for d in dirs]
Expand Down
63 changes: 31 additions & 32 deletions src/pkglite/unpack.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import os
import binascii
from typing import List, Dict, Set
from collections.abc import Sequence
from pathlib import Path
from dataclasses import dataclass

Expand All @@ -26,28 +26,28 @@ def extract_metadata_field(line: str, tag: str) -> str | None:
Extract a metadata field value from a line with a given tag.
Args:
line (str): The line to extract from.
tag (str): The tag to look for.
line: The line to extract from.
tag: The tag to look for.
Returns:
str | None: The extracted value if found, None otherwise.
The extracted value if found, None otherwise.
"""
return line.split(f"{tag}: ")[1] if line.startswith(f"{tag}: ") else None


def create_file_entry(
package_name: str, content_lines: List[str], file_format: str
) -> Dict[str, str]:
package_name: str, content_lines: list[str], file_format: str
) -> dict[str, str]:
"""
Create a file entry dictionary with the given content.
Args:
package_name (str): Name of the package.
content_lines (list): List of content lines.
file_format (str): Format of the file ('text' or 'binary').
package_name: Name of the package.
content_lines: List of content lines.
file_format: Format of the file ('text' or 'binary').
Returns:
Dict[str, str]: Dictionary containing the file entry data.
Dictionary containing the file entry data.
"""
content = (
"\n".join(content_lines) if file_format == "text" else "".join(content_lines)
Expand All @@ -60,27 +60,27 @@ def process_content_line(line: str) -> str:
Process a content line by removing the leading spaces if present.
Args:
line (str): The line to process.
line: The line to process.
Returns:
str: The processed line with leading spaces removed if present.
The processed line with leading spaces removed if present.
"""
return line[2:] if line.startswith(" ") else ""


def parse_packed_file(input_file: str) -> List[FileData]:
def parse_packed_file(input_file: str) -> Sequence[FileData]:
"""
Parse the packed text file and extract file data.
Args:
input_file (str): Path to the packed file.
input_file: Path to the packed file.
Returns:
List[FileData]: A list of FileData objects containing file information.
A sequence of FileData objects containing file information.
"""

def process_file_entry(
current: Dict[str, str], lines: List[str]
current: dict[str, str], lines: list[str]
) -> FileData | None:
"""
Process a file entry and create a FileData object.
Expand All @@ -90,7 +90,7 @@ def process_file_entry(
lines: List of content lines.
Returns:
FileData | None: FileData object if valid entry, None otherwise.
FileData object if valid entry, None otherwise.
"""
if not (current and "package" in current and "path" in current):
return None
Expand All @@ -104,9 +104,9 @@ def process_file_entry(
content=content["content"],
)

files: List[FileData] = []
current_file: Dict[str, str] = {}
content_lines: List[str] = []
files: list[FileData] = []
current_file: dict[str, str] = {}
content_lines: list[str] = []
in_content = False

with open(input_file, "r", encoding="utf-8") as f:
Expand Down Expand Up @@ -151,8 +151,8 @@ def write_text_file(file_path: Path, content: str) -> None:
Write content to a text file.
Args:
file_path (Path): Path to the file to write.
content (str): Text content to write.
file_path: Path to the file to write.
content: Text content to write.
"""
file_path.parent.mkdir(parents=True, exist_ok=True)
file_path.write_text(content, encoding="utf-8")
Expand All @@ -163,8 +163,8 @@ def write_binary_file(file_path: Path, content: str) -> None:
Write hex content to a binary file.
Args:
file_path (Path): Path to the file to write.
content (str): Hexadecimal string content to write.
file_path: Path to the file to write.
content: Hexadecimal string content to write.
Raises:
ValueError: If the content is not valid hexadecimal.
Expand All @@ -182,8 +182,8 @@ def write_file(file_data: FileData, output_directory: Path) -> None:
Write a file to the specified output directory.
Args:
file_data (FileData): FileData object containing file information
output_directory (Path): Root directory for unpacked files.
file_data: FileData object containing file information
output_directory: Root directory for unpacked files.
"""
file_path = output_directory / file_data.package / file_data.path

Expand All @@ -200,19 +200,18 @@ def unpack(
Unpack files from a text file into the specified directory.
Args:
input_file (str or Path): Path to the packed file.
output_dir (str or Path): Path to the directory to unpack files into.
Default is current directory.
quiet (bool): If True, suppress output messages. Default False.
input_file: Path to the packed file.
output_dir: Path to the directory to unpack files into.
quiet: If True, suppress output messages.
"""
input_path = Path(os.path.expanduser(str(input_file)))
output_path = Path(os.path.expanduser(str(output_dir)))

files = parse_packed_file(str(input_path))
packages: Set[str] = {file_data.package for file_data in files}
packages: set[str] = {file_data.package for file_data in files}

# Group files by package
files_by_package: Dict[str, List[FileData]] = {}
files_by_package: dict[str, list[FileData]] = {}
for file_data in files:
pkg = file_data.package
if pkg not in files_by_package:
Expand Down
Loading

0 comments on commit 2ed2aec

Please sign in to comment.