mformat 0.2__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- mformat/__init__.py +6 -0
- mformat/factory.py +185 -0
- mformat/mformat.py +719 -0
- mformat/mformat_html.py +245 -0
- mformat/mformat_lists_impl.py +368 -0
- mformat/mformat_md.py +384 -0
- mformat/mformat_state.py +39 -0
- mformat/mformat_textbased.py +69 -0
- mformat/py.typed +0 -0
- mformat/reg_pkg_formats.py +23 -0
- mformat-0.2.dist-info/METADATA +130 -0
- mformat-0.2.dist-info/RECORD +14 -0
- mformat-0.2.dist-info/WHEEL +5 -0
- mformat-0.2.dist-info/top_level.txt +1 -0
mformat/__init__.py
ADDED
mformat/factory.py
ADDED
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
#! /usr/local/bin/python3
|
|
2
|
+
"""Factory class for creating MultiFormat instances."""
|
|
3
|
+
|
|
4
|
+
# Copyright (c) 2025 - 2026 Tom Björkholm
|
|
5
|
+
# MIT License
|
|
6
|
+
#
|
|
7
|
+
|
|
8
|
+
from typing import Optional, TypedDict, Callable
|
|
9
|
+
from mformat.mformat import MultiFormat, FormatterDescriptor
|
|
10
|
+
from mformat.reg_pkg_formats import register_formats_in_pkg
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
_the_factory: Optional['MultiFormatFactory'] = None # pylint: disable=invalid-name # noqa: E501
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class OptArgsDict(TypedDict, total=False):
|
|
17
|
+
"""Optional arguments for the MultiFormat constructor."""
|
|
18
|
+
|
|
19
|
+
file_exists_callback: Optional[Callable[[str], None]]
|
|
20
|
+
lang: Optional[str]
|
|
21
|
+
title: Optional[str]
|
|
22
|
+
css_file: Optional[str]
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
type OptArgs = Optional[OptArgsDict]
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class MultiFormatFactory:
|
|
29
|
+
"""Factory class for creating instances of MultiFormat subclasses."""
|
|
30
|
+
|
|
31
|
+
def __init__(self) -> None:
|
|
32
|
+
"""Initialize the factory with an empty registry."""
|
|
33
|
+
self._registry: dict[str, type[MultiFormat]] = {}
|
|
34
|
+
self._usage: dict[str, FormatterDescriptor] = {}
|
|
35
|
+
formats: list[type[MultiFormat]] = register_formats_in_pkg()
|
|
36
|
+
for format_class in formats:
|
|
37
|
+
self.i_register(format_class)
|
|
38
|
+
|
|
39
|
+
@staticmethod
|
|
40
|
+
def i_get_factory() -> 'MultiFormatFactory':
|
|
41
|
+
"""Internally get the factory instance."""
|
|
42
|
+
global _the_factory # pylint: disable=global-statement # noqa: E501
|
|
43
|
+
if _the_factory is None:
|
|
44
|
+
_the_factory = MultiFormatFactory()
|
|
45
|
+
return _the_factory
|
|
46
|
+
|
|
47
|
+
@staticmethod
|
|
48
|
+
def register(format_class: type[MultiFormat]) -> None:
|
|
49
|
+
"""Register a MultiFormat subclass with the factory."""
|
|
50
|
+
factory = MultiFormatFactory.i_get_factory()
|
|
51
|
+
factory.i_register(format_class=format_class)
|
|
52
|
+
|
|
53
|
+
def i_register(self, format_class: type[MultiFormat]) -> None:
|
|
54
|
+
"""Internally register a MultiFormat subclass with the factory."""
|
|
55
|
+
if not issubclass(format_class, MultiFormat):
|
|
56
|
+
err = f'{format_class.__name__} must be a subclass of MultiFormat'
|
|
57
|
+
raise ValueError(err)
|
|
58
|
+
desc: FormatterDescriptor = format_class.get_arg_desciption()
|
|
59
|
+
self._registry[desc.name] = format_class
|
|
60
|
+
self._usage[desc.name] = desc
|
|
61
|
+
|
|
62
|
+
@staticmethod
|
|
63
|
+
def create(format_name: str, file_name: str,
|
|
64
|
+
url_as_text: bool = False,
|
|
65
|
+
args: OptArgs = None) -> MultiFormat:
|
|
66
|
+
"""Create an instance of a registered MultiFormat subclass.
|
|
67
|
+
|
|
68
|
+
Args:
|
|
69
|
+
format_name: The name identifier of the format class to create.
|
|
70
|
+
file_name: The file path to pass to the MultiFormat constructor.
|
|
71
|
+
url_as_text: Format URLs as text not clickable URLs.
|
|
72
|
+
args: additional arguments to pass to the MultiFormat constructor.
|
|
73
|
+
Returns:
|
|
74
|
+
An instance of the requested MultiFormat subclass.
|
|
75
|
+
Intended to be used as context manager, using a with statement.
|
|
76
|
+
Raises:
|
|
77
|
+
ValueError: If the format_name is not registered.
|
|
78
|
+
"""
|
|
79
|
+
factory = MultiFormatFactory.i_get_factory()
|
|
80
|
+
return factory.i_create(format_name=format_name,
|
|
81
|
+
file_name=file_name,
|
|
82
|
+
url_as_text=url_as_text,
|
|
83
|
+
args=args)
|
|
84
|
+
|
|
85
|
+
def i_create(self, format_name: str, file_name: str,
|
|
86
|
+
url_as_text: bool = False,
|
|
87
|
+
args: OptArgs = None) -> MultiFormat:
|
|
88
|
+
"""Internally create an instance of a registered subclass."""
|
|
89
|
+
if format_name not in self._registry:
|
|
90
|
+
raise ValueError(
|
|
91
|
+
f'Format "{format_name}" is not registered. ' +
|
|
92
|
+
'Available formats: ' +
|
|
93
|
+
f'{", ".join(sorted(list(self._registry.keys())))}'
|
|
94
|
+
)
|
|
95
|
+
format_class = self._registry[format_name]
|
|
96
|
+
if args is None:
|
|
97
|
+
return format_class(file_name=file_name, url_as_text=url_as_text)
|
|
98
|
+
assert args is not None
|
|
99
|
+
return format_class(file_name=file_name, # type: ignore[misc]
|
|
100
|
+
url_as_text=url_as_text, **args)
|
|
101
|
+
# mypy cannot see which MultiFormat subclass is being created, so it
|
|
102
|
+
# cannot know which arguments are valid.
|
|
103
|
+
|
|
104
|
+
@staticmethod
|
|
105
|
+
def get_registered_formats() -> list[str]:
|
|
106
|
+
"""Get a list of all registered format names.
|
|
107
|
+
|
|
108
|
+
Returns:
|
|
109
|
+
A list of registered format name strings.
|
|
110
|
+
"""
|
|
111
|
+
factory = MultiFormatFactory.i_get_factory()
|
|
112
|
+
return factory.i_get_registered_formats()
|
|
113
|
+
|
|
114
|
+
def i_get_registered_formats(self) -> list[str]:
|
|
115
|
+
"""Internally get a list of registered format names."""
|
|
116
|
+
return sorted(list(self._registry.keys()))
|
|
117
|
+
|
|
118
|
+
@staticmethod
|
|
119
|
+
def get_usage(format_name: str) -> FormatterDescriptor:
|
|
120
|
+
"""Get the usage information for a registered format."""
|
|
121
|
+
factory = MultiFormatFactory.i_get_factory()
|
|
122
|
+
return factory.i_get_usage(format_name=format_name)
|
|
123
|
+
|
|
124
|
+
def i_get_usage(self, format_name: str) -> FormatterDescriptor:
|
|
125
|
+
"""Internally get the usage information for a registered format."""
|
|
126
|
+
if format_name not in self._usage:
|
|
127
|
+
raise ValueError(
|
|
128
|
+
f'Format "{format_name}" is not registered. ' +
|
|
129
|
+
'Available formats: ' +
|
|
130
|
+
f'{", ".join(sorted(list(self._registry.keys())))}'
|
|
131
|
+
)
|
|
132
|
+
return self._usage[format_name]
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def create_mf(format_name: str, file_name: str,
|
|
136
|
+
url_as_text: bool = False,
|
|
137
|
+
args: OptArgs = None) -> MultiFormat:
|
|
138
|
+
"""Create an instance of a registered MultiFormat subclass.
|
|
139
|
+
|
|
140
|
+
Intended to be used as context manager, using a with statement.
|
|
141
|
+
This is a shortcut for MultiFormatFactory.create().
|
|
142
|
+
Args:
|
|
143
|
+
format_name: The name identifier of the format class to create.
|
|
144
|
+
file_name: The file path to pass to the MultiFormat constructor.
|
|
145
|
+
url_as_text: Format URLs as text not clickable URLs.
|
|
146
|
+
args: additional arguments to pass to the MultiFormat constructor.
|
|
147
|
+
Returns:
|
|
148
|
+
An instance of the requested MultiFormat subclass.
|
|
149
|
+
Raises:
|
|
150
|
+
ValueError: If the format_name is not registered.
|
|
151
|
+
"""
|
|
152
|
+
return MultiFormatFactory.create(format_name=format_name,
|
|
153
|
+
file_name=file_name,
|
|
154
|
+
url_as_text=url_as_text,
|
|
155
|
+
args=args)
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
def list_registered_mf() -> list[str]:
|
|
159
|
+
"""Get a list of all registered format names.
|
|
160
|
+
|
|
161
|
+
This is a shortcut for MultiFormatFactory.get_registered_formats().
|
|
162
|
+
Returns:
|
|
163
|
+
A list of registered format name strings.
|
|
164
|
+
"""
|
|
165
|
+
return MultiFormatFactory.get_registered_formats()
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def usage_mf(format_name: str) -> FormatterDescriptor:
|
|
169
|
+
"""Get the usage information for a registered format.
|
|
170
|
+
|
|
171
|
+
This is a shortcut for MultiFormatFactory.get_usage().
|
|
172
|
+
"""
|
|
173
|
+
return MultiFormatFactory.get_usage(format_name=format_name)
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def register_mf(format_class: type[MultiFormat]) -> None:
|
|
177
|
+
"""Register a MultiFormat subclass with the factory.
|
|
178
|
+
|
|
179
|
+
This is a shortcut for MultiFormatFactory.register().
|
|
180
|
+
Args:
|
|
181
|
+
format_class: The MultiFormat subclass to register.
|
|
182
|
+
Raises:
|
|
183
|
+
ValueError: If the format_class is not a subclass of MultiFormat.
|
|
184
|
+
"""
|
|
185
|
+
MultiFormatFactory.register(format_class=format_class)
|