all2md.utils.inputs
Utilities for uniform input handling across all2md modules.
This module provides standardized input validation and conversion functions that are used across all conversion modules in all2md. This ensures consistent behavior when handling different input types (paths, file-like objects, bytes, etc.) and provides clear error messages for unsupported inputs.
The functions in this module handle the conversion between different input types and perform validation to ensure that inputs are suitable for the requested conversion operations.
Functions
validate_and_convert_input: Main input validation and conversion function
is_path_like: Check if input is path-like (string or Path object)
is_file_like: Check if input is a file-like object
escape_markdown_special: Escape special Markdown characters in text
format_special_text: Format special text (underline, superscript, subscript)
format_markdown_heading: Format headings in hash or underline style
- all2md.utils.inputs.is_path_like(obj: Any) bool
Check if an object is path-like (string or pathlib.Path).
- Parameters:
obj (Any) – Object to check
- Returns:
True if object is path-like, False otherwise
- Return type:
bool
Examples
>>> is_path_like("document.pdf") True >>> is_path_like(Path("document.pdf")) True >>> is_path_like(BytesIO(b"data")) False
- all2md.utils.inputs.is_file_like(obj: Any) bool
Check if an object is file-like (has read method).
- Parameters:
obj (Any) – Object to check
- Returns:
True if object appears to be file-like, False otherwise
- Return type:
bool
Examples
>>> from io import BytesIO >>> is_file_like(BytesIO(b"data")) True >>> is_file_like("not_file_like") False
- all2md.utils.inputs.MAX_PROBEABLE_PATH_LENGTH = 260
Longest
strthat is probed against the filesystem. Most platforms cap a single path component at 255 bytes, and on some of themPath.is_file()raisesOSErrorrather than returningFalsefor an over-long name.
- all2md.utils.inputs.looks_like_path_attempt(value: str) bool
Return whether a
strwas plausibly meant as a file path.Text parsers accept a
strthat may be either a path or the document content itself, and nothing in the signature distinguishes them. When the filesystem probe misses, this decides between two failure modes: raising (the caller meant a path and mistyped it) or parsing the string as content (the caller meant content all along).The predicate is deliberately narrow, because a false positive breaks working code that parses short strings while a false negative only leaves today’s behaviour in place. All four conditions must hold:
short enough and single-line to be a plausible filename,
no whitespace, so prose such as
"read config.json"stays content,no
://, so a bare URL such as"https://example.com/a.md"stays content,ends with a file extension all2md actually knows.
- Parameters:
value (str) – The ambiguous string.
- Returns:
True if the string should be read as a mistyped path rather than as document content.
- Return type:
bool
Examples
>>> looks_like_path_attempt("does_not_exist.md") True >>> looks_like_path_attempt("read config.json") False >>> looks_like_path_attempt("Visit https://example.com") False
- all2md.utils.inputs.resolve_str_input(value: str) Path | None
Classify an ambiguous
stras a filesystem path or as document content.Every text parser takes a
strthat may be either. This is the single place that decides, so the 18 parsers agree and the rule can be changed once.- Parameters:
value (str) – The ambiguous string.
- Returns:
The resolved path when the string names an existing file, otherwise
None, meaning treatvalueitself as the document content.- Return type:
Path or None
- Raises:
all2md.exceptions.FileNotFoundError – If the string looks like a path (see
looks_like_path_attempt()) but no such file exists. Without this a mistyped filename parsed into a one-line document containing the filename, which reads downstream as a successful conversion of the wrong thing.
Examples
>>> resolve_str_input("# A heading") is None True
- all2md.utils.inputs.validate_page_range(pages: list[int] | str | None, max_pages: int | None = None) list[int] | None
Validate and normalize a page range specification.
All page numbers are 1-based (like “page 1”, “page 2”) and converted to 0-based internally.
- Parameters:
pages (list[int], str, or None) – Page specification (1-based): - list[int]: List of page numbers [1, 2, 3] - str: Page range string, e.g. “1-3,5,10-” - None: Use all pages
max_pages (int or None, optional) – Maximum number of pages available for validation
- Returns:
Validated list of 0-based page indices, or None if input was None
- Return type:
list[int] or None
- Raises:
PageRangeError – If page numbers are invalid or out of range
Examples
>>> validate_page_range([1, 2, 3], max_pages=5) [0, 1, 2] >>> validate_page_range("1-3,5", max_pages=10) [0, 1, 2, 4] >>> validate_page_range("10-", max_pages=12) [9, 10, 11] >>> validate_page_range([0], max_pages=5) Traceback (most recent call last): ... all2md.exceptions.PageRangeError: Invalid page number: 0. Pages must be >= 1.
- all2md.utils.inputs.validate_and_convert_input(input_data: str | Path | BinaryIO | BytesIO | StringIO | bytes | Any, supported_types: list[str] | None = None, require_binary: bool = False) tuple[Any, str]
Validate input and convert to appropriate format for processing.
This function handles the common pattern of accepting different input types (paths, file-like objects, bytes, document objects) and converting them to a format suitable for the conversion functions.
- Parameters:
input_data (InputType) – Input data to validate and convert
supported_types (list[str] or None, optional) – List of supported input type names for error messages
require_binary (bool, default False) – Whether the input must be in binary mode (vs text mode)
- Returns:
Tuple of (converted_input, input_type_description) where input_type_description is one of: “path”, “file”, “bytes”, “object”
- Return type:
tuple[Any, str]
- Raises:
FileNotFoundError – If the file does not exist
ValidationError – If the input type is not supported
Examples
>>> # Path input >>> data, type_desc = validate_and_convert_input("document.pdf") >>> print(type_desc) path
>>> # File-like input >>> from io import BytesIO >>> data, type_desc = validate_and_convert_input(BytesIO(b"content")) >>> print(type_desc) file
- all2md.utils.inputs.escape_markdown_special(text: str, escape_chars: str | None = None) str
Escape special Markdown characters in text to prevent formatting.
- Parameters:
text (str) – Text containing potential Markdown special characters
escape_chars (str or None, optional) – Characters to escape. If None, uses default set from constants
- Returns:
Text with special characters escaped with backslashes
- Return type:
str
Examples
>>> escape_markdown_special("This *should* not be italic") 'This \\*should\\* not be italic' >>> escape_markdown_special("Link: [text](url)") 'Link: \\[text\\]\\(url\\)'
- all2md.utils.inputs.format_special_text(text: str, format_type: str, mode: str = 'html') str
Format special text (underline, superscript, subscript) according to mode.
- Parameters:
text (str) – Text content to format
format_type ({"underline", "superscript", "subscript"}) – Type of special formatting to apply
mode ({"html", "markdown", "ignore"}, default "html") – Output format mode: - “html”: Use HTML tags (<u>, <sup>, <sub>) - “markdown”: Use Markdown-style syntax (__, ^, ~) - “ignore”: Return plain text without formatting
- Returns:
Formatted text according to the specified mode
- Return type:
str
Examples
>>> format_special_text("underlined", "underline", "html") '<u>underlined</u>' >>> format_special_text("superscript", "superscript", "markdown") '^superscript^' >>> format_special_text("subscript", "subscript", "ignore") 'subscript'
- all2md.utils.inputs.format_markdown_heading(text: str, level: int, use_hash: bool = True) str
Format a heading in Markdown using either hash or underline style.
This function formats only the heading itself, without adding trailing blank lines. Block-level spacing should be handled by the caller (e.g., document renderers that add spacing between block elements).
- Parameters:
text (str) – The heading text content
level (int) – The heading level (1-6 for hash style, 1-2 for underline style)
use_hash (bool, default True) – Whether to use hash-style headings (# Heading) or underline style (Headingn=======)
- Returns:
Formatted heading string ending with a single newline
- Return type:
str
Examples
>>> format_markdown_heading("Main Title", 1, use_hash=True) '# Main Title\n' >>> format_markdown_heading("Main Title", 1, use_hash=False) 'Main Title\n==========\n' >>> format_markdown_heading("Subtitle", 2, use_hash=False) 'Subtitle\n--------\n'
Notes
For underline style: - Level 1 headings use ‘=’ characters for underline - Level 2+ headings use ‘-’ characters for underline - Only levels 1-2 are supported for underline style; higher levels fall back to hash style
For hash style: - Supports levels 1-6 (# to ######) - Levels beyond 6 are clamped to 6
Spacing between blocks should be handled externally. This function does NOT add trailing blank lines to avoid conflicts with document-level spacing logic.
- all2md.utils.inputs.parse_page_ranges(page_spec: str, total_pages: int) list[int]
Parse page range specification into list of 0-based page indices.
Supports various formats: - “1-3” → [0, 1, 2] - “5” → [4] - “10-” → [9, 10, …, total_pages-1] - “1-3,5,10-” → combined ranges - “5-3” → [2, 3, 4] (automatically swaps to “3-5”)
Reversed ranges (where start > end) are automatically corrected by swapping the values. For example, “10-5” is treated as “5-10”.
- Parameters:
page_spec (str) – Page range specification (1-based page numbers)
total_pages (int) – Total number of pages in document
- Returns:
Sorted list of 0-based page indices
- Return type:
list of int
Examples
>>> parse_page_ranges("1-3,5", 10) [0, 1, 2, 4] >>> parse_page_ranges("8-", 10) [7, 8, 9] >>> parse_page_ranges("10-5", 10) [4, 5, 6, 7, 8, 9]