mdforge 0.2.0__tar.gz → 0.2.2__tar.gz
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.
- {mdforge-0.2.0 → mdforge-0.2.2}/PKG-INFO +24 -11
- {mdforge-0.2.0 → mdforge-0.2.2}/README.md +14 -0
- {mdforge-0.2.0 → mdforge-0.2.2}/mdforge/container.py +5 -2
- mdforge-0.2.2/mdforge/document.py +59 -0
- {mdforge-0.2.0 → mdforge-0.2.2}/mdforge/element.py +29 -7
- {mdforge-0.2.0 → mdforge-0.2.2}/mdforge/elements/block/basic.py +1 -1
- {mdforge-0.2.0 → mdforge-0.2.2}/mdforge/elements/block/lists/definition.py +2 -2
- {mdforge-0.2.0 → mdforge-0.2.2}/mdforge/elements/block/lists/item.py +2 -2
- {mdforge-0.2.0 → mdforge-0.2.2}/mdforge/elements/block/section.py +1 -1
- mdforge-0.2.2/mdforge/elements/block/table/_context.py +35 -0
- mdforge-0.2.2/mdforge/elements/block/table/_flavors/flavors.py +64 -0
- mdforge-0.2.2/mdforge/elements/block/table/_flavors/frame/context.py +816 -0
- mdforge-0.2.2/mdforge/elements/block/table/_flavors/frame/variant.py +140 -0
- {mdforge-0.2.0 → mdforge-0.2.2}/mdforge/elements/block/table/_flavors/frame/variants.py +1 -1
- mdforge-0.2.2/mdforge/elements/block/table/_variant.py +56 -0
- {mdforge-0.2.0 → mdforge-0.2.2}/mdforge/elements/block/table/_vcell.py +3 -3
- {mdforge-0.2.0 → mdforge-0.2.2}/mdforge/elements/block/table/cell.py +2 -2
- {mdforge-0.2.0 → mdforge-0.2.2}/mdforge/elements/block/table/table.py +6 -11
- {mdforge-0.2.0 → mdforge-0.2.2}/mdforge/elements/inline/basic.py +1 -1
- {mdforge-0.2.0 → mdforge-0.2.2}/mdforge/elements/inline/text.py +1 -1
- {mdforge-0.2.0 → mdforge-0.2.2}/pyproject.toml +36 -31
- mdforge-0.2.0/mdforge/document.py +0 -79
- mdforge-0.2.0/mdforge/elements/block/table/_context.py +0 -427
- mdforge-0.2.0/mdforge/elements/block/table/_flavors/flavor.py +0 -87
- mdforge-0.2.0/mdforge/elements/block/table/_flavors/flavors.py +0 -31
- mdforge-0.2.0/mdforge/elements/block/table/_flavors/frame/frame.py +0 -492
- {mdforge-0.2.0 → mdforge-0.2.2}/LICENSE +0 -0
- {mdforge-0.2.0 → mdforge-0.2.2}/mdforge/__init__.py +0 -0
- {mdforge-0.2.0 → mdforge-0.2.2}/mdforge/_norm.py +0 -0
- {mdforge-0.2.0 → mdforge-0.2.2}/mdforge/_utils.py +0 -0
- {mdforge-0.2.0 → mdforge-0.2.2}/mdforge/elements/__init__.py +0 -0
- {mdforge-0.2.0 → mdforge-0.2.2}/mdforge/elements/_image.py +0 -0
- {mdforge-0.2.0 → mdforge-0.2.2}/mdforge/elements/block/__init__.py +0 -0
- {mdforge-0.2.0 → mdforge-0.2.2}/mdforge/elements/block/lists/__init__.py +0 -0
- {mdforge-0.2.0 → mdforge-0.2.2}/mdforge/elements/block/table/__init__.py +0 -0
- {mdforge-0.2.0 → mdforge-0.2.2}/mdforge/elements/block/table/_flavors/frame/__init__.py +0 -0
- {mdforge-0.2.0 → mdforge-0.2.2}/mdforge/elements/block/table/_flavors/list/__init__.py +0 -0
- {mdforge-0.2.0 → mdforge-0.2.2}/mdforge/elements/block/table/_params.py +0 -0
- {mdforge-0.2.0 → mdforge-0.2.2}/mdforge/elements/block/text.py +0 -0
- {mdforge-0.2.0 → mdforge-0.2.2}/mdforge/elements/inline/__init__.py +0 -0
- {mdforge-0.2.0 → mdforge-0.2.2}/mdforge/exceptions.py +0 -0
- {mdforge-0.2.0 → mdforge-0.2.2}/mdforge/types.py +0 -0
|
@@ -1,24 +1,23 @@
|
|
|
1
|
-
Metadata-Version: 2.
|
|
1
|
+
Metadata-Version: 2.3
|
|
2
2
|
Name: mdforge
|
|
3
|
-
Version: 0.2.
|
|
4
|
-
Summary: Pythonic Markdown generator
|
|
5
|
-
Home-page: https://github.com/mm21/mdforge
|
|
3
|
+
Version: 0.2.2
|
|
4
|
+
Summary: Pythonic multi-flavor Markdown generator
|
|
6
5
|
Author: mm21
|
|
7
6
|
Author-email: mm21.dev@gmail.com
|
|
8
7
|
Requires-Python: >=3.12,<4.0
|
|
9
|
-
Classifier:
|
|
10
|
-
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
-
Classifier: Natural Language :: English
|
|
8
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
12
9
|
Classifier: Operating System :: OS Independent
|
|
10
|
+
Classifier: Typing :: Typed
|
|
11
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Natural Language :: English
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
13
15
|
Classifier: Programming Language :: Python :: 3
|
|
14
16
|
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
-
Classifier: Programming Language :: Python :: 3.11
|
|
16
17
|
Classifier: Programming Language :: Python :: 3.13
|
|
17
|
-
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
18
|
-
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
19
|
-
Classifier: Typing :: Typed
|
|
20
18
|
Requires-Dist: pyrollup (>=0.1.0,<0.2.0)
|
|
21
19
|
Requires-Dist: pyyaml (>=6.0.2,<7.0.0)
|
|
20
|
+
Project-URL: Homepage, https://github.com/mm21/mdforge
|
|
22
21
|
Description-Content-Type: text/markdown
|
|
23
22
|
|
|
24
23
|
# MDForge
|
|
@@ -39,6 +38,7 @@ Pythonic multi-flavor Markdown generator
|
|
|
39
38
|
- [Lists](#lists)
|
|
40
39
|
- [Tables](#tables)
|
|
41
40
|
- [Images](#images)
|
|
41
|
+
- [Rendering standalone elements](#rendering-standalone-elements)
|
|
42
42
|
- [Advanced features](#advanced-features)
|
|
43
43
|
- [HTML attributes](#html-attributes)
|
|
44
44
|
- [Pandoc extensions](#pandoc-extensions)
|
|
@@ -131,6 +131,7 @@ This is rendered as:
|
|
|
131
131
|
title: My Document
|
|
132
132
|
author: Me
|
|
133
133
|
---
|
|
134
|
+
|
|
134
135
|
# Introduction
|
|
135
136
|
|
|
136
137
|
This is an introduction paragraph.
|
|
@@ -539,6 +540,18 @@ Note the following observations:
|
|
|
539
540
|
|
|
540
541
|
- Support for caption and alignment depends on flavor
|
|
541
542
|
|
|
543
|
+
### Rendering standalone elements
|
|
544
|
+
|
|
545
|
+
Any element class can be rendered by itself, even if not placed in a document. This is useful for generating document snippets which may be embedded in a document, e.g. via a templating engine.
|
|
546
|
+
|
|
547
|
+
```python
|
|
548
|
+
from mdforge import InlineContainer, Strong
|
|
549
|
+
|
|
550
|
+
element = InlineContainer("This is a ", Strong("test element"))
|
|
551
|
+
|
|
552
|
+
assert element.render(flavor="pandoc") == "This is a **test element**"
|
|
553
|
+
```
|
|
554
|
+
|
|
542
555
|
## Advanced features
|
|
543
556
|
|
|
544
557
|
### HTML attributes
|
|
@@ -16,6 +16,7 @@ Pythonic multi-flavor Markdown generator
|
|
|
16
16
|
- [Lists](#lists)
|
|
17
17
|
- [Tables](#tables)
|
|
18
18
|
- [Images](#images)
|
|
19
|
+
- [Rendering standalone elements](#rendering-standalone-elements)
|
|
19
20
|
- [Advanced features](#advanced-features)
|
|
20
21
|
- [HTML attributes](#html-attributes)
|
|
21
22
|
- [Pandoc extensions](#pandoc-extensions)
|
|
@@ -108,6 +109,7 @@ This is rendered as:
|
|
|
108
109
|
title: My Document
|
|
109
110
|
author: Me
|
|
110
111
|
---
|
|
112
|
+
|
|
111
113
|
# Introduction
|
|
112
114
|
|
|
113
115
|
This is an introduction paragraph.
|
|
@@ -516,6 +518,18 @@ Note the following observations:
|
|
|
516
518
|
|
|
517
519
|
- Support for caption and alignment depends on flavor
|
|
518
520
|
|
|
521
|
+
### Rendering standalone elements
|
|
522
|
+
|
|
523
|
+
Any element class can be rendered by itself, even if not placed in a document. This is useful for generating document snippets which may be embedded in a document, e.g. via a templating engine.
|
|
524
|
+
|
|
525
|
+
```python
|
|
526
|
+
from mdforge import InlineContainer, Strong
|
|
527
|
+
|
|
528
|
+
element = InlineContainer("This is a ", Strong("test element"))
|
|
529
|
+
|
|
530
|
+
assert element.render(flavor="pandoc") == "This is a **test element**"
|
|
531
|
+
```
|
|
532
|
+
|
|
519
533
|
## Advanced features
|
|
520
534
|
|
|
521
535
|
### HTML attributes
|
|
@@ -31,7 +31,7 @@ class InlineContainerMixin(BaseElement):
|
|
|
31
31
|
__auto_space: bool
|
|
32
32
|
|
|
33
33
|
def __init__(
|
|
34
|
-
self, *elements:
|
|
34
|
+
self, *elements: BaseInlineElement | str, auto_space: bool = False
|
|
35
35
|
):
|
|
36
36
|
def coerce(obj: str) -> BaseInlineElement:
|
|
37
37
|
return coerce_inline_text(
|
|
@@ -95,7 +95,10 @@ class BaseBlockContainer(BaseBlockElement):
|
|
|
95
95
|
|
|
96
96
|
def _render_block(self, flavor: FlavorType) -> Generator[str, None, None]:
|
|
97
97
|
yield "\n\n".join(
|
|
98
|
-
[
|
|
98
|
+
[
|
|
99
|
+
"\n".join(element._render_block(flavor))
|
|
100
|
+
for element in self.__elements
|
|
101
|
+
]
|
|
99
102
|
)
|
|
100
103
|
|
|
101
104
|
def _get_pandoc_extensions(self) -> set[str]:
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Interface for Markdown document generation.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from typing import Any, Generator, Iterable
|
|
8
|
+
|
|
9
|
+
import yaml
|
|
10
|
+
|
|
11
|
+
from .container import BaseLevelBlockContainer
|
|
12
|
+
from .element import BaseElement
|
|
13
|
+
from .types import FlavorType
|
|
14
|
+
|
|
15
|
+
__all__ = [
|
|
16
|
+
"Document",
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
ROOT_LEVEL: int = 1
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
class Document(BaseLevelBlockContainer):
|
|
23
|
+
"""
|
|
24
|
+
Encapsulates a Markdown document. Add elements using the `+=` operator.
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
_level_inc: int = 0
|
|
28
|
+
"""
|
|
29
|
+
Treat all nested containers as top-level sections.
|
|
30
|
+
"""
|
|
31
|
+
|
|
32
|
+
__frontmatter: dict[str, Any] | None
|
|
33
|
+
|
|
34
|
+
def __init__(
|
|
35
|
+
self,
|
|
36
|
+
*,
|
|
37
|
+
frontmatter: dict[str, Any] | None = None,
|
|
38
|
+
elements: BaseElement | str | Iterable[BaseElement | str] | None = None,
|
|
39
|
+
):
|
|
40
|
+
super().__init__()
|
|
41
|
+
self.__frontmatter = frontmatter
|
|
42
|
+
self._level = ROOT_LEVEL
|
|
43
|
+
|
|
44
|
+
if elements:
|
|
45
|
+
self += elements
|
|
46
|
+
|
|
47
|
+
def _render_block(self, flavor: FlavorType) -> Generator[str, None, None]:
|
|
48
|
+
yield from self.__render_frontmatter()
|
|
49
|
+
yield from super()._render_block(flavor)
|
|
50
|
+
yield ""
|
|
51
|
+
|
|
52
|
+
def __render_frontmatter(self) -> Generator[str, None, None]:
|
|
53
|
+
if self.__frontmatter is None:
|
|
54
|
+
return None
|
|
55
|
+
|
|
56
|
+
content = yaml.dump(
|
|
57
|
+
self.__frontmatter, default_flow_style=False, sort_keys=False
|
|
58
|
+
)
|
|
59
|
+
yield from ["---", f"{content}---", ""]
|
|
@@ -6,6 +6,7 @@ from __future__ import annotations
|
|
|
6
6
|
|
|
7
7
|
from abc import ABC, abstractmethod
|
|
8
8
|
from dataclasses import dataclass
|
|
9
|
+
from pathlib import Path
|
|
9
10
|
from typing import TYPE_CHECKING, Generator
|
|
10
11
|
|
|
11
12
|
from ._norm import norm_list
|
|
@@ -23,13 +24,40 @@ __all__ = [
|
|
|
23
24
|
|
|
24
25
|
|
|
25
26
|
class BaseElement(ABC):
|
|
27
|
+
"""
|
|
28
|
+
Base renderable markdown element, which may be a container of other
|
|
29
|
+
elements.
|
|
30
|
+
"""
|
|
26
31
|
|
|
27
32
|
__container: BaseLevelBlockContainer | None = None
|
|
28
33
|
"""
|
|
29
34
|
Container to which this element belongs. Must be added to a container in
|
|
30
|
-
order to be rendered in
|
|
35
|
+
order to be rendered in a document, but otherwise can still be rendered
|
|
36
|
+
standalone.
|
|
31
37
|
"""
|
|
32
38
|
|
|
39
|
+
def render(self, *, flavor: FlavorType) -> str:
|
|
40
|
+
"""
|
|
41
|
+
Return element as a string using the provided flavor.
|
|
42
|
+
"""
|
|
43
|
+
return "\n".join(self._render_element(flavor))
|
|
44
|
+
|
|
45
|
+
def render_file(self, path: Path | str, *, flavor: FlavorType):
|
|
46
|
+
"""
|
|
47
|
+
Write Markdown document using the provided flavor to the provided file.
|
|
48
|
+
"""
|
|
49
|
+
path_norm = path if isinstance(path, Path) else Path(path)
|
|
50
|
+
with path_norm.open("w") as fh:
|
|
51
|
+
fh.write(self.render(flavor=flavor))
|
|
52
|
+
|
|
53
|
+
def get_pandoc_extensions(self) -> list[str]:
|
|
54
|
+
"""
|
|
55
|
+
Get a list of extensions required to convert the resulting
|
|
56
|
+
markdown document in pandoc, assuming pandoc flavor is used for
|
|
57
|
+
rendering.
|
|
58
|
+
"""
|
|
59
|
+
return sorted(self._get_pandoc_extensions())
|
|
60
|
+
|
|
33
61
|
@abstractmethod
|
|
34
62
|
def _render_element(self, flavor: FlavorType) -> Generator[str, None, None]:
|
|
35
63
|
"""
|
|
@@ -98,12 +126,6 @@ class BaseBlockElement(BaseElement):
|
|
|
98
126
|
def _render_element(self, flavor: FlavorType) -> Generator[str, None, None]:
|
|
99
127
|
yield from self._render_block(flavor)
|
|
100
128
|
|
|
101
|
-
def _render_block_lines(self, flavor: FlavorType) -> str:
|
|
102
|
-
"""
|
|
103
|
-
Render as multi-line string.
|
|
104
|
-
"""
|
|
105
|
-
return "\n".join(self._render_block(flavor))
|
|
106
|
-
|
|
107
129
|
|
|
108
130
|
@dataclass(kw_only=True)
|
|
109
131
|
class Attributes:
|
|
@@ -74,7 +74,7 @@ class BlockImage(BaseBlockElement, ImageMixin):
|
|
|
74
74
|
def __init__(
|
|
75
75
|
self,
|
|
76
76
|
path: str,
|
|
77
|
-
caption:
|
|
77
|
+
caption: BaseInlineElement | str | None = None,
|
|
78
78
|
align: AlignType | None = None,
|
|
79
79
|
*,
|
|
80
80
|
attributes: Attributes | None = None,
|
|
@@ -31,8 +31,8 @@ class DefinitionItem:
|
|
|
31
31
|
|
|
32
32
|
def __init__(
|
|
33
33
|
self,
|
|
34
|
-
term:
|
|
35
|
-
definitions:
|
|
34
|
+
term: BaseInlineElement | str,
|
|
35
|
+
definitions: BaseElement | str | list[BaseElement | str],
|
|
36
36
|
):
|
|
37
37
|
"""
|
|
38
38
|
:param term: Term as a string or inline element
|
|
@@ -22,7 +22,7 @@ __all__ = [
|
|
|
22
22
|
]
|
|
23
23
|
|
|
24
24
|
|
|
25
|
-
type ListItemType =
|
|
25
|
+
type ListItemType = BaseElement | ListItem | str
|
|
26
26
|
|
|
27
27
|
|
|
28
28
|
class ListItem:
|
|
@@ -32,7 +32,7 @@ class ListItem:
|
|
|
32
32
|
|
|
33
33
|
def __init__(
|
|
34
34
|
self,
|
|
35
|
-
content:
|
|
35
|
+
content: BaseElement | str,
|
|
36
36
|
sub_items: list[ListItemType] | BaseItemList | None = None,
|
|
37
37
|
):
|
|
38
38
|
self.__element = norm_obj(
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Encapsulates context for rendering tables.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from abc import ABC, abstractmethod
|
|
6
|
+
from typing import Generator
|
|
7
|
+
|
|
8
|
+
from ....types import FlavorType
|
|
9
|
+
from ._params import TableParams
|
|
10
|
+
from ._variant import BaseTableVariant
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class BaseRenderContext[VariantT: BaseTableVariant](ABC):
|
|
14
|
+
"""
|
|
15
|
+
Encapsulates all state needed to render a table: immutable params and
|
|
16
|
+
variant info.
|
|
17
|
+
"""
|
|
18
|
+
|
|
19
|
+
flavor: FlavorType
|
|
20
|
+
params: TableParams
|
|
21
|
+
variant: VariantT
|
|
22
|
+
|
|
23
|
+
def __init__(
|
|
24
|
+
self, flavor: FlavorType, params: TableParams, variant: VariantT
|
|
25
|
+
):
|
|
26
|
+
self.flavor = flavor
|
|
27
|
+
self.params = params
|
|
28
|
+
self.variant = variant
|
|
29
|
+
|
|
30
|
+
@abstractmethod
|
|
31
|
+
def render(self) -> Generator[str, None, None]:
|
|
32
|
+
"""
|
|
33
|
+
Render table.
|
|
34
|
+
"""
|
|
35
|
+
...
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Listing of table flavors.
|
|
3
|
+
"""
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
from dataclasses import dataclass
|
|
8
|
+
|
|
9
|
+
from .....types import FlavorType
|
|
10
|
+
from .._context import BaseRenderContext
|
|
11
|
+
from .._params import TableParams
|
|
12
|
+
from .._variant import VariantConfig, VariantT
|
|
13
|
+
from .frame.context import FrameRenderContext
|
|
14
|
+
from .frame.variants import GRID_VARIANT, MULTILINE_VARIANT
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@dataclass(frozen=True, kw_only=True)
|
|
18
|
+
class TableFlavorSpec:
|
|
19
|
+
"""
|
|
20
|
+
Specification of markdown table flavor.
|
|
21
|
+
"""
|
|
22
|
+
|
|
23
|
+
variant_config: VariantConfig[VariantT]
|
|
24
|
+
render_context_cls: type[BaseRenderContext[VariantT]]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
FLAVOR_MAP: dict[FlavorType, TableFlavorSpec] = {
|
|
28
|
+
"pandoc": TableFlavorSpec(
|
|
29
|
+
variant_config=VariantConfig(
|
|
30
|
+
inline=MULTILINE_VARIANT, block=GRID_VARIANT
|
|
31
|
+
),
|
|
32
|
+
render_context_cls=FrameRenderContext,
|
|
33
|
+
)
|
|
34
|
+
}
|
|
35
|
+
"""
|
|
36
|
+
Mapping of markdown flavor to table config.
|
|
37
|
+
"""
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
def create_render_context(
|
|
41
|
+
flavor: FlavorType, params: TableParams, block: bool
|
|
42
|
+
) -> BaseRenderContext:
|
|
43
|
+
"""
|
|
44
|
+
Create a render context for the given markdown flavor and capabilities.
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
err = (
|
|
48
|
+
f"Tables for flavor {flavor} with block={block} not currently supported"
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
flavor_spec = FLAVOR_MAP.get(flavor)
|
|
52
|
+
assert flavor_spec, err
|
|
53
|
+
|
|
54
|
+
variant = (
|
|
55
|
+
flavor_spec.variant_config.block
|
|
56
|
+
if block
|
|
57
|
+
else flavor_spec.variant_config.inline
|
|
58
|
+
)
|
|
59
|
+
assert variant, err
|
|
60
|
+
|
|
61
|
+
variant.validate_params(params)
|
|
62
|
+
|
|
63
|
+
render_context = flavor_spec.render_context_cls(flavor, params, variant)
|
|
64
|
+
return render_context
|