pyact-cli 0.4.0__tar.gz → 0.5.0__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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyact-cli
3
- Version: 0.4.0
3
+ Version: 0.5.0
4
4
  Summary: A CLI tool to compile .pamd files to Markdown.
5
5
  Home-page: https://github.com/Abstergo2003/PyAct
6
6
  Author: Abstergo2003
@@ -65,10 +65,17 @@ You can also include other pamd files as templates!
65
65
  <tmp>path/to/another_file</tmp>
66
66
  ```
67
67
 
68
- ### 3. Compile!
68
+ ### 3. Compile to Markdown
69
69
  Run the compiler from your terminal to generate your final Markdown document:
70
70
  ```bash
71
71
  pyact path/to/your_file.pamd -o output.md
72
72
  ```
73
73
 
74
- That's it! You now have a standard Markdown file ready to be shared, published, or converted to PDF.
74
+ ### 4. Compile to Word (DOCX)
75
+ You can also compile straight to a native Microsoft Word Document, complete with CSS styling!
76
+ ```bash
77
+ pyact path/to/your_file.pamd -o output.md --docx output.docx
78
+ ```
79
+ *Note: If you have a `style.css` file in the same directory, PyAct will automatically load it to style your Word Document. You can also specify one manually using the `--css path/to/style.css` flag!*
80
+
81
+ That's it! You now have documents ready to be shared, published, or distributed.
@@ -45,10 +45,17 @@ You can also include other pamd files as templates!
45
45
  <tmp>path/to/another_file</tmp>
46
46
  ```
47
47
 
48
- ### 3. Compile!
48
+ ### 3. Compile to Markdown
49
49
  Run the compiler from your terminal to generate your final Markdown document:
50
50
  ```bash
51
51
  pyact path/to/your_file.pamd -o output.md
52
52
  ```
53
53
 
54
- That's it! You now have a standard Markdown file ready to be shared, published, or converted to PDF.
54
+ ### 4. Compile to Word (DOCX)
55
+ You can also compile straight to a native Microsoft Word Document, complete with CSS styling!
56
+ ```bash
57
+ pyact path/to/your_file.pamd -o output.md --docx output.docx
58
+ ```
59
+ *Note: If you have a `style.css` file in the same directory, PyAct will automatically load it to style your Word Document. You can also specify one manually using the `--css path/to/style.css` flag!*
60
+
61
+ That's it! You now have documents ready to be shared, published, or distributed.
@@ -0,0 +1,185 @@
1
+ import inspect
2
+ from typing import Callable, List, Any
3
+ from pyact.py2tex import py2tex
4
+
5
+ def equation(func: Callable, values: List[Any]) -> str:
6
+ """
7
+ Converts a Python lambda or simple function into a LaTeX math equation string.
8
+
9
+ Why it is needed:
10
+ To allow users to define equations in Python syntax and automatically render them
11
+ as mathematical formulas in the final document, without needing to hand-write LaTeX.
12
+
13
+ Inputs:
14
+ func (Callable): A simple Python function (e.g., `lambda x: x**2`).
15
+ values (List[Any]): The arguments to pass to the function to compute the result.
16
+
17
+ Outputs:
18
+ str: A Markdown math string formatted as `$$ Name = Equation = Value $$`.
19
+ """
20
+ source = inspect.getsource(func)
21
+ value = func(*values)
22
+ name, sep, tail = source.rpartition("=")
23
+ head, sep, equ = tail.rpartition(":")
24
+ return f"$$ {name} = {py2tex(equ.replace(chr(10), ''))} = {value} $$"
25
+
26
+ def image(link: str, caption: str) -> str:
27
+ """
28
+ Generates a Markdown image string with an embedded HTML caption.
29
+
30
+ Why it is needed:
31
+ Standard Markdown images `![caption](url)` don't display visible captions below the image.
32
+ This helper injects a styled HTML `<span>` below the image to force a visual caption.
33
+
34
+ Inputs:
35
+ link (str): The URL or local path to the image.
36
+ caption (str): The text to display as the caption.
37
+
38
+ Outputs:
39
+ str: A formatted string containing the Markdown image and HTML span caption.
40
+ """
41
+ image_link = f"![{caption}]({link})"
42
+ caption_html = f"<span style='text-align: center; display: block; font-style: italic;'>{caption}</span>"
43
+ return f"{image_link} \n {caption_html}"
44
+
45
+ def table(headers: List[str], values: List[List[Any]], caption: str, add_no: bool = False, starting_number: int = 1) -> str:
46
+ """
47
+ Generates a Markdown table from Python lists, including a caption.
48
+
49
+ Why it is needed:
50
+ Writing Markdown tables by hand is tedious. This function allows users to programmatically
51
+ build tables from 2D data arrays, inject automatic numbering, and append a styled caption.
52
+
53
+ Inputs:
54
+ headers (List[str]): List of column header names.
55
+ values (List[List[Any]]): A 2D list containing the row data.
56
+ caption (str): The text to display above the table.
57
+ add_no (bool): If True, adds an auto-incrementing "No." column to the left.
58
+ starting_number (int): The starting index for the "No." column.
59
+
60
+ Outputs:
61
+ str: A formatted Markdown string containing the caption and the table.
62
+ """
63
+ if add_no:
64
+ headers_string = f"|No.|"
65
+ breaker_line = "|---|"
66
+ else:
67
+ headers_string = f"|"
68
+ breaker_line = "|"
69
+
70
+ for i in headers:
71
+ headers_string += f"{i}|"
72
+ breaker_line += "-"*len(i)+"|"
73
+
74
+ table_content = ""
75
+ for i, row in enumerate(values):
76
+ if add_no:
77
+ table_content += f"|{i+starting_number}|"
78
+ else:
79
+ table_content += "|"
80
+ for value in row:
81
+ table_content += f"{value}|"
82
+ table_content += "\n"
83
+
84
+ caption_html = f"<span style='text-align: center; display: block; font-style: italic;'>{caption}</span>"
85
+ return f"{caption_html} \n {headers_string}\n{breaker_line}\n{table_content}"
86
+
87
+ def hyperlink(caption: str, link: str) -> str:
88
+ """
89
+ Generates a simple Markdown hyperlink.
90
+
91
+ Why it is needed:
92
+ Provides a standardized helper method in the PyAct ecosystem for injecting links.
93
+
94
+ Inputs:
95
+ caption (str): The clickable text to display.
96
+ link (str): The URL destination.
97
+
98
+ Outputs:
99
+ str: The Markdown hyperlink string `[caption](link)`.
100
+ """
101
+ return f"[{caption}]({link})"
102
+
103
+ def unordered_list(items: List[str]) -> str:
104
+ """
105
+ Generates a Markdown unordered (bullet) list from a Python list.
106
+
107
+ Why it is needed:
108
+ Allows programmatic generation of bullet points from array data.
109
+
110
+ Inputs:
111
+ items (List[str]): A list of string items.
112
+
113
+ Outputs:
114
+ str: A Markdown string where each item is prefixed with `* `.
115
+ """
116
+ list_string = ""
117
+ for i in items:
118
+ list_string += f"* {i}\n"
119
+ return list_string
120
+
121
+ def ordered_list(items: List[str]) -> str:
122
+ """
123
+ Generates a Markdown ordered (numbered) list from a Python list.
124
+
125
+ Why it is needed:
126
+ Allows programmatic generation of numbered lists from array data.
127
+
128
+ Inputs:
129
+ items (List[str]): A list of string items.
130
+
131
+ Outputs:
132
+ str: A Markdown string where each item is numbered sequentially (1. 2. 3.).
133
+ """
134
+ list_string = ""
135
+ for i, item in enumerate(items):
136
+ list_string += f"{i+1}. {item}\n"
137
+ return list_string
138
+
139
+ def checklist(items: List[str]) -> str:
140
+ """
141
+ Generates a Markdown task checklist from a Python list.
142
+
143
+ Why it is needed:
144
+ Allows programmatic generation of checkable task items.
145
+
146
+ Inputs:
147
+ items (List[str]): A list of string items.
148
+
149
+ Outputs:
150
+ str: A Markdown string where each item is prefixed with `- [ ] `.
151
+ """
152
+ list_string = ""
153
+ for i in items:
154
+ list_string += f"- [ ] {i}\n"
155
+ return list_string
156
+
157
+ class Footnote:
158
+ """
159
+ A helper class for managing Markdown footnotes.
160
+
161
+ Why it is needed:
162
+ Footnotes require two separate parts in Markdown: the annotation mark in the text `[^1]`,
163
+ and the definition at the bottom of the document `[^1]: The text`. This class links
164
+ the two together to prevent numbering mismatches.
165
+ """
166
+ def __init__(self, number: int, text: str):
167
+ """
168
+ Inputs:
169
+ number (int): The footnote reference number.
170
+ text (str): The expanded text definition of the footnote.
171
+ """
172
+ self.number = number
173
+ self.text = text
174
+
175
+ def define_string(self) -> str:
176
+ """
177
+ Outputs (str): The full footnote definition block to place at the bottom of the document.
178
+ """
179
+ return f"[^{self.number}]: {self.text}"
180
+
181
+ def adnotation(self) -> str:
182
+ """
183
+ Outputs (str): The short inline reference marker to place in the paragraph text.
184
+ """
185
+ return f"[^{self.number}]\n"
@@ -0,0 +1 @@
1
+ __version__ = "0.5.0"
@@ -4,6 +4,24 @@ import os
4
4
  from .core import map_content, process_content
5
5
 
6
6
  def main():
7
+ """
8
+ Main entry point for the PyAct Command Line Interface.
9
+
10
+ Why it is needed:
11
+ This acts as the bridge between the terminal and the PyAct compilation engine.
12
+ It parses command-line arguments, triggers the document mapping/compilation process,
13
+ and handles file I/O for outputting the compiled Markdown and DOCX files.
14
+
15
+ Inputs (via sys.argv):
16
+ input (str): Positional argument for the target `.pamd` file path.
17
+ --output (str): Optional path to save the generated `.md` file.
18
+ --docx (str): Optional path to save the generated `.docx` file.
19
+ --css (str): Optional path to a `.css` file for styling the DOCX output.
20
+
21
+ Outputs:
22
+ Writes the generated `.md` and `.docx` files to the filesystem and prints
23
+ status messages to stdout/stderr.
24
+ """
7
25
  parser = argparse.ArgumentParser(description="PyAct CLI - PAMD to Markdown compiler")
8
26
  parser.add_argument("input", help="Path to the main .pamd file")
9
27
  parser.add_argument("-o", "--output", help="Output file path (default prints to stdout)")
@@ -0,0 +1,198 @@
1
+ import re
2
+ import json
3
+
4
+ def get_directory(path: str) -> list:
5
+ """
6
+ Splits a full file path into its directory component and its filename component.
7
+
8
+ Why it is needed:
9
+ When resolving nested templates or generating output files, the engine needs to
10
+ know the base directory of the current `.pamd` file so it can correctly locate
11
+ sibling template files.
12
+
13
+ Inputs:
14
+ path (str): The full path to a file (e.g., './main/retro.pamd').
15
+
16
+ Outputs:
17
+ list: A two-element list `[directory_path, filename]`.
18
+ """
19
+ head, sep, tail = path.rpartition('/')
20
+ return [head + sep, tail]
21
+
22
+ def read_pamd_cells(file_path: str):
23
+ """
24
+ Reads a .pamd (JSON Notebook) file and extracts the Markdown and Python code.
25
+
26
+ Why it is needed:
27
+ `.pamd` files are stored exactly like Jupyter Notebooks (a list of cell objects).
28
+ This function parses the JSON structure and concatenates all 'code' cells into a
29
+ single executable Python script, and all 'markdown' cells into a single Markdown document.
30
+
31
+ Inputs:
32
+ file_path (str): Path to the `.pamd` file to read.
33
+
34
+ Outputs:
35
+ tuple (str, str): A tuple containing `(code_content, markdown_content)`.
36
+ """
37
+ with open(file_path, 'r', encoding='utf-8') as f:
38
+ data = json.load(f)
39
+
40
+ code_content = ""
41
+ markdown_content = ""
42
+
43
+ for cell in data.get("cells", []):
44
+ cell_type = cell.get("cell_type", "")
45
+ source = cell.get("source", "")
46
+ if isinstance(source, list):
47
+ source = "".join(source)
48
+
49
+ if cell_type == "code":
50
+ code_content += source + "\n"
51
+ elif cell_type == "markdown":
52
+ markdown_content += source + "\n"
53
+
54
+ return code_content, markdown_content
55
+
56
+ def get_imports(file: str, code_text: str):
57
+ """
58
+ Executes the Python code from a .pamd file and retrieves the context variables.
59
+
60
+ Why it is needed:
61
+ To inject dynamic data into the Markdown document, PyAct executes the user's Python
62
+ script. The user must define a `context()` function that returns a dictionary mapping
63
+ variable names (like 'title') to their computed values. This function safely executes
64
+ that code and extracts the resulting dictionary.
65
+
66
+ Inputs:
67
+ file (str): The name of the file being executed (for error reporting).
68
+ code_text (str): The raw Python code extracted from the `.pamd` file.
69
+
70
+ Outputs:
71
+ dict: The dictionary of variables returned by the user's `context()` function.
72
+ """
73
+ file_namespace = {"__file__": f"{file}.pamd"}
74
+ if not code_text.strip():
75
+ raise ValueError(f"The file {file}.pamd needs a 'context()' function but has no code cell.")
76
+
77
+ try:
78
+ exec(code_text, file_namespace)
79
+ except Exception as e:
80
+ raise RuntimeError(f"Error executing code in {file}.pamd: {e}")
81
+
82
+ if "context" not in file_namespace:
83
+ raise ValueError(f"The file {file}.pamd is missing the required 'context()' function.")
84
+
85
+ context_func = file_namespace["context"]
86
+
87
+ if not callable(context_func):
88
+ raise TypeError(f"In {file}.pamd, 'context' was found but it is not a function!")
89
+
90
+ result_dict = context_func()
91
+
92
+ if not isinstance(result_dict, dict):
93
+ raise TypeError(f"The 'context()' function in {file}.pamd must return a dictionary.")
94
+
95
+ return result_dict
96
+
97
+ def find_imports(text: str):
98
+ """
99
+ Finds all `<ctx>var_name</ctx>` tags in a markdown string.
100
+
101
+ Why it is needed:
102
+ Identifies which variables the Markdown document is requesting from the Python context.
103
+
104
+ Inputs:
105
+ text (str): The raw Markdown text.
106
+
107
+ Outputs:
108
+ list: A list of variable names extracted from the tags.
109
+ """
110
+ pattern = r"<ctx>(.*?)</ctx>"
111
+ return re.findall(pattern, text)
112
+
113
+
114
+ def find_templates(text: str):
115
+ """
116
+ Finds all `<tmp>file_name</tmp>` tags in a markdown string.
117
+
118
+ Why it is needed:
119
+ Identifies nested `.pamd` template files that need to be recursively compiled
120
+ and injected into the current document.
121
+
122
+ Inputs:
123
+ text (str): The raw Markdown text.
124
+
125
+ Outputs:
126
+ list: A list of template file names extracted from the tags.
127
+ """
128
+ pattern = r"<tmp>(.*?)</tmp>"
129
+ return re.findall(pattern, text)
130
+
131
+ def map_content(file: str, path: str):
132
+ """
133
+ Recursively maps the dependency tree of a `.pamd` file.
134
+
135
+ Why it is needed:
136
+ Before compiling, PyAct needs to build an Abstract Syntax Tree (AST) of the document,
137
+ mapping out all required context variables and any nested child templates that need
138
+ to be resolved. This allows for deep, nested project structures.
139
+
140
+ Inputs:
141
+ file (str): The target `.pamd` file name (without extension).
142
+ path (str): The directory where the file is located.
143
+
144
+ Outputs:
145
+ dict: A recursive "build tree" dictionary containing metadata, required imports,
146
+ and the build trees of all nested templates.
147
+ """
148
+ if path and not path.endswith('/'):
149
+ full_path = f"{path}/{file}"
150
+ elif path.endswith('/'):
151
+ full_path = f"{path}{file}"
152
+ else:
153
+ full_path = file
154
+
155
+ code_text, main_file = read_pamd_cells(f"{full_path}.pamd")
156
+ templates = find_templates(main_file)
157
+ imports = find_imports(main_file)
158
+ meta = get_directory(full_path)
159
+
160
+ ready_t = []
161
+ for i in templates:
162
+ ready_t.append(map_content(i, meta[0]))
163
+
164
+ return {
165
+ "tag_name": file,
166
+ "name": meta[1],
167
+ "path": meta[0],
168
+ "imports": imports,
169
+ "templates": ready_t
170
+ }
171
+
172
+ def process_content(build_tree: dict):
173
+ """
174
+ Compiles a mapped build tree into a final flat Markdown string.
175
+
176
+ Why it is needed:
177
+ This is the core renderer. It reads the `.pamd` files, executes their Python code
178
+ to fetch the context dictionary, replaces all `<ctx>` tags with dynamic data,
179
+ and recursively replaces all `<tmp>` tags with their fully compiled child documents.
180
+
181
+ Inputs:
182
+ build_tree (dict): The AST mapping generated by `map_content()`.
183
+
184
+ Outputs:
185
+ str: The final, fully compiled Markdown text string ready for export.
186
+ """
187
+ file_path_base = build_tree.get("path", "") + build_tree.get("name", "")
188
+ code_text, file_text = read_pamd_cells(file_path_base + ".pamd")
189
+
190
+ needed_imports = build_tree.get("imports", [])
191
+ if needed_imports:
192
+ imports = get_imports(file_path_base, code_text)
193
+ for i in needed_imports:
194
+ file_text = file_text.replace(f"<ctx>{i}</ctx>", str(imports.get(i, '')))
195
+
196
+ for i in build_tree.get("templates", []):
197
+ file_text = file_text.replace(f"<tmp>{i.get('tag_name')}</tmp>", process_content(i))
198
+ return file_text
@@ -2,7 +2,21 @@ import re
2
2
  import json
3
3
 
4
4
  def css_to_dict(css_string: str) -> dict:
5
- """Parses a CSS string into a Python dictionary."""
5
+ """
6
+ Parses a raw CSS string into a nested Python dictionary.
7
+
8
+ Why it is needed:
9
+ CSS is text-based and formatted with selectors, curly braces, and semicolons.
10
+ To programmatically apply these styles to python-docx elements, we need to
11
+ convert the syntax into a highly structured, queryable dictionary map.
12
+
13
+ Inputs:
14
+ css_string (str): The raw contents of a CSS file as a string.
15
+
16
+ Outputs:
17
+ dict: A dictionary where keys are CSS selectors (e.g., 'p', 'h1') and
18
+ values are dictionaries of style properties (e.g., {'color': '#000'}).
19
+ """
6
20
  # Remove CSS comments
7
21
  css_string = re.sub(r'/\*[\s\S]*?\*/', '', css_string)
8
22
 
@@ -36,8 +50,19 @@ def css_to_dict(css_string: str) -> dict:
36
50
 
37
51
  def parse_css_file(file_path: str, as_json_string: bool = False):
38
52
  """
39
- Reads a CSS file and converts it to a dictionary map.
40
- If as_json_string is True, returns a formatted JSON string instead.
53
+ Reads a CSS file from disk and parses it into a dictionary or JSON string.
54
+
55
+ Why it is needed:
56
+ Provides a clean interface for reading a physical file from the OS, extracting
57
+ its contents, and piping it into the core `css_to_dict` parser.
58
+
59
+ Inputs:
60
+ file_path (str): The path to the `.css` file on disk.
61
+ as_json_string (bool): If True, returns the output as a serialized JSON string
62
+ instead of a Python dictionary. Defaults to False.
63
+
64
+ Outputs:
65
+ dict or str: The parsed CSS dictionary, or a JSON-formatted string if requested.
41
66
  """
42
67
  with open(file_path, 'r', encoding='utf-8') as f:
43
68
  css_string = f.read()
@@ -7,9 +7,22 @@ from docx.oxml.ns import nsdecls
7
7
  import math2docx
8
8
  from . import css2json
9
9
 
10
- def style_parser(css_path):
10
+ def style_parser(css_path: str) -> dict:
11
11
  """
12
- Reads a CSS file and converts it into a style dictionary using css2json.
12
+ Parses a CSS file and converts it into a Python dictionary containing style maps.
13
+
14
+ Why it is needed:
15
+ To allow users to style their exported Word Documents using standard CSS syntax,
16
+ we must first translate that CSS into a structured Python dictionary that the
17
+ DOCX generation engine can query.
18
+
19
+ Inputs:
20
+ css_path (str): The absolute or relative file path to the target .css file.
21
+
22
+ Outputs:
23
+ dict: A nested dictionary where keys are CSS selectors (e.g., 'h1', 'p')
24
+ and values are dictionaries of property-value pairs (e.g., {'color': '#000', 'font-size': '12pt'}).
25
+ Returns an empty dictionary {} if parsing fails.
13
26
  """
14
27
  try:
15
28
  return css2json.parse_css_file(css_path)
@@ -17,10 +30,26 @@ def style_parser(css_path):
17
30
  print(f"Warning: Could not parse CSS ({e})")
18
31
  return {}
19
32
 
20
- def markdown_parser(md_text):
33
+ def markdown_parser(md_text: str) -> list:
21
34
  """
22
- Parses Markdown text into a list of structured blocks.
23
- Supported blocks: heading, paragraph, list, table, math
35
+ Parses raw Markdown text into a structured list of block tuples.
36
+
37
+ Why it is needed:
38
+ Because python-docx generates documents block-by-block (paragraphs, tables, headings),
39
+ we need to break down the raw string of Markdown text into distinct, classified chunks
40
+ that our DOCX writer can loop through and translate into native Word elements.
41
+
42
+ Inputs:
43
+ md_text (str): The complete raw Markdown text string.
44
+
45
+ Outputs:
46
+ list: A list of tuples where each tuple represents a block element.
47
+ Examples:
48
+ - ('heading', 2, 'Title Text')
49
+ - ('paragraph', 'Some plain text')
50
+ - ('list', 'List item text', 'ordered')
51
+ - ('math', 'x = 2')
52
+ - ('table', ['|Header|', '|---|', '|Data|'])
24
53
  """
25
54
  blocks = []
26
55
  lines = md_text.split('\n')
@@ -355,6 +384,19 @@ def _process_inline(paragraph, text, styles, tag):
355
384
  def docx_writer(blocks: list, styles: dict, output_file: str):
356
385
  """
357
386
  Translates parsed markdown blocks into a Word document and applies CSS styling.
387
+
388
+ Why it is needed:
389
+ This is the core generation engine. It takes the abstract syntax structure generated
390
+ by `markdown_parser()` and the style rules from `style_parser()`, loops through them,
391
+ and issues the corresponding `python-docx` commands to physically build the `.docx` file.
392
+
393
+ Inputs:
394
+ blocks (list): The list of parsed Markdown block tuples (from `markdown_parser`).
395
+ styles (dict): The nested dictionary of CSS rules (from `style_parser`).
396
+ output_file (str): The absolute or relative file path where the resulting .docx should be saved.
397
+
398
+ Outputs:
399
+ None: This function does not return a value, but it writes a binary .docx file to disk.
358
400
  """
359
401
  doc = Document()
360
402
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyact-cli
3
- Version: 0.4.0
3
+ Version: 0.5.0
4
4
  Summary: A CLI tool to compile .pamd files to Markdown.
5
5
  Home-page: https://github.com/Abstergo2003/PyAct
6
6
  Author: Abstergo2003
@@ -65,10 +65,17 @@ You can also include other pamd files as templates!
65
65
  <tmp>path/to/another_file</tmp>
66
66
  ```
67
67
 
68
- ### 3. Compile!
68
+ ### 3. Compile to Markdown
69
69
  Run the compiler from your terminal to generate your final Markdown document:
70
70
  ```bash
71
71
  pyact path/to/your_file.pamd -o output.md
72
72
  ```
73
73
 
74
- That's it! You now have a standard Markdown file ready to be shared, published, or converted to PDF.
74
+ ### 4. Compile to Word (DOCX)
75
+ You can also compile straight to a native Microsoft Word Document, complete with CSS styling!
76
+ ```bash
77
+ pyact path/to/your_file.pamd -o output.md --docx output.docx
78
+ ```
79
+ *Note: If you have a `style.css` file in the same directory, PyAct will automatically load it to style your Word Document. You can also specify one manually using the `--css path/to/style.css` flag!*
80
+
81
+ That's it! You now have documents ready to be shared, published, or distributed.
@@ -5,7 +5,7 @@ with open("README.md", "r", encoding="utf-8") as fh:
5
5
 
6
6
  setup(
7
7
  name="pyact-cli",
8
- version="0.4.0",
8
+ version="0.5.0",
9
9
  author="Abstergo2003",
10
10
  author_email="",
11
11
  description="A CLI tool to compile .pamd files to Markdown.",
@@ -1,76 +0,0 @@
1
- import inspect
2
- from pyact.py2tex import py2tex
3
-
4
- def equation(func: function, values: list) -> str:
5
- source = inspect.getsource(func)
6
- value = func(*values)
7
- name, sep, tail = source.rpartition("=")
8
- head, sep, equ = tail.rpartition(":")
9
- return f"$$ {name} = {py2tex(equ.replace("\n", ""))} = {value} $$"
10
-
11
-
12
- def image(link: str, caption: str) -> str:
13
- image_link = f"![{caption}]({link})"
14
- caption = f"<span style='text-align: center; display: block; font-style: italic;'>{caption}</span>"
15
- return f"{image_link} \n {caption}"
16
-
17
- def table(headers: list, values: list, caption: str, add_no: bool = False, starting_number: int = 1) -> str:
18
- if add_no:
19
- headers_string = f"|No.|"
20
- breaker_line = "|---|"
21
- else:
22
- headers_string = f"|"
23
- breaker_line = "|"
24
-
25
-
26
- for i in headers:
27
- headers_string += f"{i}|"
28
- breaker_line += "-"*len(i)+"|"
29
-
30
- table_content = ""
31
-
32
- for i, row in enumerate(values):
33
- if add_no:
34
- table_content += f"|{i+starting_number}|"
35
- else:
36
- table_content += "|"
37
-
38
- for value in row:
39
- table_content += f"{value}|"
40
- table_content += "\n"
41
-
42
- caption = f"<span style='text-align: center; display: block; font-style: italic;'>{caption}</span>"
43
- return f"{caption} \n {headers_string}\n{breaker_line}\n{table_content}"
44
-
45
-
46
- def hyperlink(caption: str, link: str) -> str:
47
- return f"[{caption}]({link})"
48
-
49
- def unordered_list(items: list) -> str:
50
- list_string = ""
51
- for i in items:
52
- list_string += f"* {i}\n"
53
- return list_string
54
-
55
- def ordered_list(items: list) -> str:
56
- list_string = ""
57
- for i, item in enumerate(items):
58
- list_string += f"{i+1}. {item}\n"
59
- return list_string
60
-
61
- def checklist(items: list) -> str:
62
- list_string = ""
63
- for i in items:
64
- list_string += f"- [ ] {i}\n"
65
- return list_string
66
-
67
- class Footnote:
68
- def __init__(self, number, text):
69
- self.number = number
70
- self.text = text
71
-
72
- def define_string(self) -> str:
73
- return f"[^{self.number}]: {self.text}"
74
-
75
- def adnotation(self) -> str:
76
- return f"[^{self.number}]\n"
@@ -1 +0,0 @@
1
- __version__ = "0.4.0"
@@ -1,103 +0,0 @@
1
- import re
2
- import json
3
-
4
- def get_directory(path: str) -> list:
5
- # Splits path into: ('./main', '/', 'retro.pamd')
6
- head, sep, tail = path.rpartition('/')
7
- # Recombine the first part and the slash
8
- return [head + sep, tail]
9
-
10
- def read_pamd_cells(file_path: str):
11
- with open(file_path, 'r', encoding='utf-8') as f:
12
- data = json.load(f)
13
-
14
- code_content = ""
15
- markdown_content = ""
16
-
17
- for cell in data.get("cells", []):
18
- cell_type = cell.get("cell_type", "")
19
- source = cell.get("source", "")
20
- if isinstance(source, list):
21
- source = "".join(source)
22
-
23
- if cell_type == "code":
24
- code_content += source + "\n"
25
- elif cell_type == "markdown":
26
- markdown_content += source + "\n"
27
-
28
- return code_content, markdown_content
29
-
30
- def get_imports(file: str, code_text: str):
31
- file_namespace = {"__file__": f"{file}.pamd"}
32
- if not code_text.strip():
33
- raise ValueError(f"The file {file}.pamd needs a 'context()' function but has no code cell.")
34
-
35
- try:
36
- exec(code_text, file_namespace)
37
- except Exception as e:
38
- raise RuntimeError(f"Error executing code in {file}.pamd: {e}")
39
-
40
- if "context" not in file_namespace:
41
- raise ValueError(f"The file {file}.pamd is missing the required 'context()' function.")
42
-
43
- context_func = file_namespace["context"]
44
-
45
- if not callable(context_func):
46
- raise TypeError(f"In {file}.pamd, 'context' was found but it is not a function!")
47
-
48
- result_dict = context_func()
49
-
50
- if not isinstance(result_dict, dict):
51
- raise TypeError(f"The 'context()' function in {file}.pamd must return a dictionary.")
52
-
53
- return result_dict
54
-
55
- def find_imports(text: str):
56
- # <ctx></ctx>
57
- pattern = r"<ctx>(.*?)</ctx>"
58
- return re.findall(pattern, text)
59
-
60
-
61
- def find_templates(text: str):
62
- # <tmp></tmp>
63
- pattern = r"<tmp>(.*?)</tmp>"
64
- return re.findall(pattern, text)
65
-
66
- def map_content(file: str, path: str):
67
- if path and not path.endswith('/'):
68
- full_path = f"{path}/{file}"
69
- elif path.endswith('/'):
70
- full_path = f"{path}{file}"
71
- else:
72
- full_path = file
73
-
74
- code_text, main_file = read_pamd_cells(f"{full_path}.pamd")
75
- templates = find_templates(main_file)
76
- imports = find_imports(main_file)
77
- meta = get_directory(full_path)
78
-
79
- ready_t = []
80
- for i in templates:
81
- ready_t.append(map_content(i, meta[0]))
82
-
83
- return {
84
- "tag_name": file,
85
- "name": meta[1],
86
- "path": meta[0],
87
- "imports": imports,
88
- "templates": ready_t
89
- }
90
-
91
- def process_content(build_tree: dict):
92
- file_path_base = build_tree.get("path", "") + build_tree.get("name", "")
93
- code_text, file_text = read_pamd_cells(file_path_base + ".pamd")
94
-
95
- needed_imports = build_tree.get("imports", [])
96
- if needed_imports:
97
- imports = get_imports(file_path_base, code_text)
98
- for i in needed_imports:
99
- file_text = file_text.replace(f"<ctx>{i}</ctx>", str(imports.get(i, '')))
100
-
101
- for i in build_tree.get("templates", []):
102
- file_text = file_text.replace(f"<tmp>{i.get('tag_name')}</tmp>", process_content(i))
103
- return file_text
File without changes
File without changes