pyact-cli 0.5.0__tar.gz → 0.6.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.
- {pyact_cli-0.5.0 → pyact_cli-0.6.0}/PKG-INFO +4 -1
- {pyact_cli-0.5.0 → pyact_cli-0.6.0}/pamd_helpers/__init__.py +59 -1
- pyact_cli-0.6.0/pamd_helpers/conversions.py +126 -0
- pyact_cli-0.6.0/pamd_helpers/unit.py +43 -0
- pyact_cli-0.6.0/pyact/__init__.py +1 -0
- {pyact_cli-0.5.0 → pyact_cli-0.6.0}/pyact/cli.py +113 -75
- pyact_cli-0.6.0/pyact/linter.py +85 -0
- {pyact_cli-0.5.0 → pyact_cli-0.6.0}/pyact_cli.egg-info/PKG-INFO +4 -1
- {pyact_cli-0.5.0 → pyact_cli-0.6.0}/pyact_cli.egg-info/SOURCES.txt +4 -0
- pyact_cli-0.6.0/pyact_cli.egg-info/requires.txt +2 -0
- {pyact_cli-0.5.0 → pyact_cli-0.6.0}/setup.py +5 -1
- pyact_cli-0.5.0/pyact/__init__.py +0 -1
- {pyact_cli-0.5.0 → pyact_cli-0.6.0}/README.md +0 -0
- {pyact_cli-0.5.0 → pyact_cli-0.6.0}/pyact/core.py +0 -0
- {pyact_cli-0.5.0 → pyact_cli-0.6.0}/pyact/css2json.py +0 -0
- {pyact_cli-0.5.0 → pyact_cli-0.6.0}/pyact/mdTOword.py +0 -0
- {pyact_cli-0.5.0 → pyact_cli-0.6.0}/pyact/py2tex.py +0 -0
- {pyact_cli-0.5.0 → pyact_cli-0.6.0}/pyact_cli.egg-info/dependency_links.txt +0 -0
- {pyact_cli-0.5.0 → pyact_cli-0.6.0}/pyact_cli.egg-info/entry_points.txt +0 -0
- {pyact_cli-0.5.0 → pyact_cli-0.6.0}/pyact_cli.egg-info/top_level.txt +0 -0
- {pyact_cli-0.5.0 → pyact_cli-0.6.0}/setup.cfg +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyact-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.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
|
|
@@ -10,11 +10,14 @@ Classifier: License :: OSI Approved :: MIT License
|
|
|
10
10
|
Classifier: Operating System :: OS Independent
|
|
11
11
|
Requires-Python: >=3.8
|
|
12
12
|
Description-Content-Type: text/markdown
|
|
13
|
+
Requires-Dist: python-docx
|
|
14
|
+
Requires-Dist: ezdxf
|
|
13
15
|
Dynamic: author
|
|
14
16
|
Dynamic: classifier
|
|
15
17
|
Dynamic: description
|
|
16
18
|
Dynamic: description-content-type
|
|
17
19
|
Dynamic: home-page
|
|
20
|
+
Dynamic: requires-dist
|
|
18
21
|
Dynamic: requires-python
|
|
19
22
|
Dynamic: summary
|
|
20
23
|
|
|
@@ -182,4 +182,62 @@ class Footnote:
|
|
|
182
182
|
"""
|
|
183
183
|
Outputs (str): The short inline reference marker to place in the paragraph text.
|
|
184
184
|
"""
|
|
185
|
-
return f"[^{self.number}]\n"
|
|
185
|
+
return f"[^{self.number}]\n"
|
|
186
|
+
|
|
187
|
+
def table_to_dxf(headers: List[str], values: List[List[Any]], filename: str, cell_width: float = 50.0, cell_height: float = 10.0) -> str:
|
|
188
|
+
"""
|
|
189
|
+
Exports a table (headers + values) to a 2D CAD DXF file.
|
|
190
|
+
|
|
191
|
+
Why it is needed:
|
|
192
|
+
Engineers and designers often need to embed tabulated data (e.g. Bill of Materials,
|
|
193
|
+
coordinate tables, parts lists) directly into AutoCAD drawings. This helper converts
|
|
194
|
+
a Python table directly into DXF geometry (lines and text) that can be opened in any CAD software.
|
|
195
|
+
|
|
196
|
+
Inputs:
|
|
197
|
+
headers (List[str]): List of column header names.
|
|
198
|
+
values (List[List[Any]]): A 2D list containing the row data.
|
|
199
|
+
filename (str): The output filename (e.g., 'table.dxf').
|
|
200
|
+
cell_width (float): The width of each cell in drawing units.
|
|
201
|
+
cell_height (float): The height of each cell in drawing units.
|
|
202
|
+
|
|
203
|
+
Outputs:
|
|
204
|
+
str: A string confirming the export, formatted as a Markdown link.
|
|
205
|
+
"""
|
|
206
|
+
try:
|
|
207
|
+
import ezdxf
|
|
208
|
+
except ImportError:
|
|
209
|
+
return f"*(Error: ezdxf library is not installed. Cannot generate {filename})*"
|
|
210
|
+
|
|
211
|
+
doc = ezdxf.new('R2010')
|
|
212
|
+
msp = doc.modelspace()
|
|
213
|
+
|
|
214
|
+
# Construct full grid data
|
|
215
|
+
rows = [headers] + values
|
|
216
|
+
num_rows = len(rows)
|
|
217
|
+
num_cols = len(headers)
|
|
218
|
+
|
|
219
|
+
# Draw horizontal grid lines
|
|
220
|
+
for i in range(num_rows + 1):
|
|
221
|
+
y = -i * cell_height
|
|
222
|
+
msp.add_line((0, y), (num_cols * cell_width, y))
|
|
223
|
+
|
|
224
|
+
# Draw vertical grid lines
|
|
225
|
+
for j in range(num_cols + 1):
|
|
226
|
+
x = j * cell_width
|
|
227
|
+
msp.add_line((x, 0), (x, -num_rows * cell_height))
|
|
228
|
+
|
|
229
|
+
# Draw text centered in each cell
|
|
230
|
+
for i, row in enumerate(rows):
|
|
231
|
+
y = -i * cell_height - (cell_height / 2)
|
|
232
|
+
for j, cell_val in enumerate(row):
|
|
233
|
+
if j >= num_cols: continue # Safety bounds
|
|
234
|
+
x = j * cell_width + (cell_width / 2)
|
|
235
|
+
msp.add_text(
|
|
236
|
+
str(cell_val),
|
|
237
|
+
dxfattribs={
|
|
238
|
+
'height': cell_height * 0.4,
|
|
239
|
+
}
|
|
240
|
+
).set_placement((x, y), align=ezdxf.enums.TextEntityAlignment.MIDDLE_CENTER)
|
|
241
|
+
|
|
242
|
+
doc.saveas(filename)
|
|
243
|
+
return f"*[Successfully exported table to {filename}]({filename})*"
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Engineering Unit Conversions for PyAct
|
|
3
|
+
|
|
4
|
+
A collection of helper functions to seamlessly convert values between
|
|
5
|
+
Metric (SI) and Imperial / US Customary systems.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
# ==========================================
|
|
9
|
+
# LENGTH CONVERSIONS
|
|
10
|
+
# ==========================================
|
|
11
|
+
|
|
12
|
+
def mm_to_inch(mm: float) -> float:
|
|
13
|
+
"""Converts millimeters [mm] to inches [in]"""
|
|
14
|
+
return mm / 25.4
|
|
15
|
+
|
|
16
|
+
def inch_to_mm(inch: float) -> float:
|
|
17
|
+
"""Converts inches [in] to millimeters [mm]"""
|
|
18
|
+
return inch * 25.4
|
|
19
|
+
|
|
20
|
+
def m_to_ft(m: float) -> float:
|
|
21
|
+
"""Converts meters [m] to feet [ft]"""
|
|
22
|
+
return m * 3.280839895
|
|
23
|
+
|
|
24
|
+
def ft_to_m(ft: float) -> float:
|
|
25
|
+
"""Converts feet [ft] to meters [m]"""
|
|
26
|
+
return ft / 3.280839895
|
|
27
|
+
|
|
28
|
+
# ==========================================
|
|
29
|
+
# AREA & VOLUME CONVERSIONS
|
|
30
|
+
# ==========================================
|
|
31
|
+
|
|
32
|
+
def mm2_to_in2(mm2: float) -> float:
|
|
33
|
+
"""Converts square millimeters [mm^2] to square inches [in^2]"""
|
|
34
|
+
return mm2 / 645.16
|
|
35
|
+
|
|
36
|
+
def in2_to_mm2(in2: float) -> float:
|
|
37
|
+
"""Converts square inches [in^2] to square millimeters [mm^2]"""
|
|
38
|
+
return in2 * 645.16
|
|
39
|
+
|
|
40
|
+
def m3_to_ft3(m3: float) -> float:
|
|
41
|
+
"""Converts cubic meters [m^3] to cubic feet [ft^3]"""
|
|
42
|
+
return m3 * 35.3146667
|
|
43
|
+
|
|
44
|
+
def ft3_to_m3(ft3: float) -> float:
|
|
45
|
+
"""Converts cubic feet [ft^3] to cubic meters [m^3]"""
|
|
46
|
+
return ft3 / 35.3146667
|
|
47
|
+
|
|
48
|
+
# ==========================================
|
|
49
|
+
# MASS & FORCE CONVERSIONS
|
|
50
|
+
# ==========================================
|
|
51
|
+
|
|
52
|
+
def kg_to_lbs(kg: float) -> float:
|
|
53
|
+
"""Converts kilograms [kg] to pounds-mass [lbs]"""
|
|
54
|
+
return kg * 2.20462262
|
|
55
|
+
|
|
56
|
+
def lbs_to_kg(lbs: float) -> float:
|
|
57
|
+
"""Converts pounds-mass [lbs] to kilograms [kg]"""
|
|
58
|
+
return lbs / 2.20462262
|
|
59
|
+
|
|
60
|
+
def kn_to_kips(kn: float) -> float:
|
|
61
|
+
"""Converts kilonewtons [kN] to kilopounds-force [kips]"""
|
|
62
|
+
return kn * 0.224808943
|
|
63
|
+
|
|
64
|
+
def kips_to_kn(kips: float) -> float:
|
|
65
|
+
"""Converts kilopounds-force [kips] to kilonewtons [kN]"""
|
|
66
|
+
return kips / 0.224808943
|
|
67
|
+
|
|
68
|
+
def n_to_lbf(n: float) -> float:
|
|
69
|
+
"""Converts newtons [N] to pounds-force [lbf]"""
|
|
70
|
+
return n * 0.224808943
|
|
71
|
+
|
|
72
|
+
def lbf_to_n(lbf: float) -> float:
|
|
73
|
+
"""Converts pounds-force [lbf] to newtons [N]"""
|
|
74
|
+
return lbf / 0.224808943
|
|
75
|
+
|
|
76
|
+
# ==========================================
|
|
77
|
+
# PRESSURE & STRESS CONVERSIONS
|
|
78
|
+
# ==========================================
|
|
79
|
+
|
|
80
|
+
def mpa_to_psi(mpa: float) -> float:
|
|
81
|
+
"""Converts Megapascals [MPa] (or N/mm^2) to pounds per square inch [psi]"""
|
|
82
|
+
return mpa * 145.037738
|
|
83
|
+
|
|
84
|
+
def psi_to_mpa(psi: float) -> float:
|
|
85
|
+
"""Converts pounds per square inch [psi] to Megapascals [MPa]"""
|
|
86
|
+
return psi / 145.037738
|
|
87
|
+
|
|
88
|
+
def mpa_to_ksi(mpa: float) -> float:
|
|
89
|
+
"""Converts Megapascals [MPa] to kilopounds per square inch [ksi]"""
|
|
90
|
+
return mpa * 0.145037738
|
|
91
|
+
|
|
92
|
+
def ksi_to_mpa(ksi: float) -> float:
|
|
93
|
+
"""Converts kilopounds per square inch [ksi] to Megapascals [MPa]"""
|
|
94
|
+
return ksi / 0.145037738
|
|
95
|
+
|
|
96
|
+
# ==========================================
|
|
97
|
+
# MOMENT & TORQUE CONVERSIONS
|
|
98
|
+
# ==========================================
|
|
99
|
+
|
|
100
|
+
def knm_to_kipft(knm: float) -> float:
|
|
101
|
+
"""Converts kilonewton-meters [kNm] to kilopound-feet [kip-ft]"""
|
|
102
|
+
return knm * 0.737562149
|
|
103
|
+
|
|
104
|
+
def kipft_to_knm(kipft: float) -> float:
|
|
105
|
+
"""Converts kilopound-feet [kip-ft] to kilonewton-meters [kNm]"""
|
|
106
|
+
return kipft / 0.737562149
|
|
107
|
+
|
|
108
|
+
def knm_to_lbfft(knm: float) -> float:
|
|
109
|
+
"""Converts kilonewton-meters [kNm] to pound-force feet [lbf-ft]"""
|
|
110
|
+
return knm * 737.562149
|
|
111
|
+
|
|
112
|
+
def lbfft_to_knm(lbfft: float) -> float:
|
|
113
|
+
"""Converts pound-force feet [lbf-ft] to kilonewton-meters [kNm]"""
|
|
114
|
+
return lbfft / 737.562149
|
|
115
|
+
|
|
116
|
+
# ==========================================
|
|
117
|
+
# TEMPERATURE CONVERSIONS
|
|
118
|
+
# ==========================================
|
|
119
|
+
|
|
120
|
+
def c_to_f(celsius: float) -> float:
|
|
121
|
+
"""Converts degrees Celsius [°C] to degrees Fahrenheit [°F]"""
|
|
122
|
+
return (celsius * 9/5) + 32
|
|
123
|
+
|
|
124
|
+
def f_to_c(fahrenheit: float) -> float:
|
|
125
|
+
"""Converts degrees Fahrenheit [°F] to degrees Celsius [°C]"""
|
|
126
|
+
return (fahrenheit - 32) * 5/9
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
class Unit:
|
|
2
|
+
def __init__(self, value: float, unit: str):
|
|
3
|
+
self.value: float = value
|
|
4
|
+
self.unit: str = unit
|
|
5
|
+
|
|
6
|
+
def __repr__(self) -> str:
|
|
7
|
+
return f"{self.value} {self.unit}"
|
|
8
|
+
|
|
9
|
+
def _extract(self, other) -> float:
|
|
10
|
+
return float(other.value) if isinstance(other, Unit) else float(other)
|
|
11
|
+
|
|
12
|
+
def __add__(self, other):
|
|
13
|
+
return float(self.value) + self._extract(other)
|
|
14
|
+
|
|
15
|
+
def __radd__(self, other):
|
|
16
|
+
return self._extract(other) + float(self.value)
|
|
17
|
+
|
|
18
|
+
def __sub__(self, other):
|
|
19
|
+
return float(self.value) - self._extract(other)
|
|
20
|
+
|
|
21
|
+
def __rsub__(self, other):
|
|
22
|
+
return self._extract(other) - float(self.value)
|
|
23
|
+
|
|
24
|
+
def __mul__(self, other):
|
|
25
|
+
return float(self.value) * self._extract(other)
|
|
26
|
+
|
|
27
|
+
def __rmul__(self, other):
|
|
28
|
+
return self._extract(other) * float(self.value)
|
|
29
|
+
|
|
30
|
+
def __truediv__(self, other):
|
|
31
|
+
return float(self.value) / self._extract(other)
|
|
32
|
+
|
|
33
|
+
def __rtruediv__(self, other):
|
|
34
|
+
return self._extract(other) / float(self.value)
|
|
35
|
+
|
|
36
|
+
def __pow__(self, power):
|
|
37
|
+
return float(self.value) ** self._extract(power)
|
|
38
|
+
|
|
39
|
+
def __neg__(self):
|
|
40
|
+
return -float(self.value)
|
|
41
|
+
|
|
42
|
+
def __abs__(self):
|
|
43
|
+
return abs(float(self.value))
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "0.6.0"
|
|
@@ -1,75 +1,113 @@
|
|
|
1
|
-
import argparse
|
|
2
|
-
import sys
|
|
3
|
-
import os
|
|
4
|
-
from .core import map_content, process_content
|
|
5
|
-
|
|
6
|
-
def
|
|
7
|
-
"""
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
1
|
+
import argparse
|
|
2
|
+
import sys
|
|
3
|
+
import os
|
|
4
|
+
from .core import map_content, process_content
|
|
5
|
+
|
|
6
|
+
def is_cache_valid(build_tree, cache_mtime):
|
|
7
|
+
file_path = os.path.join(build_tree.get("path", ""), build_tree.get("name", "") + ".pamd")
|
|
8
|
+
if os.path.exists(file_path) and os.path.getmtime(file_path) > cache_mtime:
|
|
9
|
+
return False
|
|
10
|
+
for t in build_tree.get("templates", []):
|
|
11
|
+
if not is_cache_valid(t, cache_mtime):
|
|
12
|
+
return False
|
|
13
|
+
return True
|
|
14
|
+
|
|
15
|
+
def main():
|
|
16
|
+
"""
|
|
17
|
+
Main entry point for the PyAct Command Line Interface.
|
|
18
|
+
|
|
19
|
+
Why it is needed:
|
|
20
|
+
This acts as the bridge between the terminal and the PyAct compilation engine.
|
|
21
|
+
It parses command-line arguments, triggers the document mapping/compilation process,
|
|
22
|
+
and handles file I/O for outputting the compiled Markdown and DOCX files.
|
|
23
|
+
|
|
24
|
+
Inputs (via sys.argv):
|
|
25
|
+
input (str): Positional argument for the target `.pamd` file path.
|
|
26
|
+
--output (str): Optional path to save the generated `.md` file.
|
|
27
|
+
--docx (str): Optional path to save the generated `.docx` file.
|
|
28
|
+
--css (str): Optional path to a `.css` file for styling the DOCX output.
|
|
29
|
+
--lint (bool): Run the linter to check for unused files and variables.
|
|
30
|
+
|
|
31
|
+
Outputs:
|
|
32
|
+
Writes the generated `.md` and `.docx` files to the filesystem and prints
|
|
33
|
+
status messages to stdout/stderr.
|
|
34
|
+
"""
|
|
35
|
+
parser = argparse.ArgumentParser(description="PyAct CLI - PAMD to Markdown compiler")
|
|
36
|
+
parser.add_argument("input", help="Path to the main .pamd file")
|
|
37
|
+
parser.add_argument("-o", "--output", help="Output file path (default prints to stdout)")
|
|
38
|
+
parser.add_argument("--docx", help="Also generate a DOCX file at this path")
|
|
39
|
+
parser.add_argument("--css", help="Optional CSS file path to style the DOCX")
|
|
40
|
+
parser.add_argument("--lint", action="store_true", help="Run the linter on the project to check for unused variables and files")
|
|
41
|
+
|
|
42
|
+
args = parser.parse_args()
|
|
43
|
+
|
|
44
|
+
input_path = os.path.abspath(args.input)
|
|
45
|
+
directory = os.path.dirname(input_path)
|
|
46
|
+
filename = os.path.basename(input_path)
|
|
47
|
+
|
|
48
|
+
if filename.endswith(".pamd"):
|
|
49
|
+
filename = filename[:-5]
|
|
50
|
+
|
|
51
|
+
if args.lint:
|
|
52
|
+
from .linter import run_linter
|
|
53
|
+
run_linter(filename, directory)
|
|
54
|
+
return
|
|
55
|
+
|
|
56
|
+
try:
|
|
57
|
+
build_tree = map_content(filename, directory)
|
|
58
|
+
|
|
59
|
+
cache_dir = os.path.join(directory, ".pamd-cache")
|
|
60
|
+
cache_file = os.path.join(cache_dir, f"{filename}.cache.md")
|
|
61
|
+
use_cache = False
|
|
62
|
+
|
|
63
|
+
if os.path.exists(cache_file):
|
|
64
|
+
cache_mtime = os.path.getmtime(cache_file)
|
|
65
|
+
if is_cache_valid(build_tree, cache_mtime):
|
|
66
|
+
use_cache = True
|
|
67
|
+
|
|
68
|
+
if use_cache:
|
|
69
|
+
print("pamd-cache: Using cached markdown...")
|
|
70
|
+
with open(cache_file, "r", encoding="utf-8") as f:
|
|
71
|
+
content = f.read()
|
|
72
|
+
else:
|
|
73
|
+
print("pamd-cache: Cache invalid or missing. Rebuilding...")
|
|
74
|
+
content = process_content(build_tree)
|
|
75
|
+
# Write cache
|
|
76
|
+
if not os.path.exists(cache_dir):
|
|
77
|
+
os.makedirs(cache_dir)
|
|
78
|
+
with open(cache_file, "w", encoding="utf-8") as f:
|
|
79
|
+
f.write(content)
|
|
80
|
+
|
|
81
|
+
if args.output:
|
|
82
|
+
with open(args.output, "w", encoding="utf-8") as f:
|
|
83
|
+
f.write(content)
|
|
84
|
+
print(f"Successfully compiled to {args.output}")
|
|
85
|
+
else:
|
|
86
|
+
if not args.docx:
|
|
87
|
+
print(content)
|
|
88
|
+
|
|
89
|
+
if args.docx:
|
|
90
|
+
from .mdTOword import style_parser, markdown_parser, docx_writer
|
|
91
|
+
styles = {}
|
|
92
|
+
if args.css:
|
|
93
|
+
styles = style_parser(args.css)
|
|
94
|
+
else:
|
|
95
|
+
# Try to look for style.css in the active directory
|
|
96
|
+
local_css = os.path.join(directory, "style.css")
|
|
97
|
+
if os.path.exists(local_css):
|
|
98
|
+
styles = style_parser(local_css)
|
|
99
|
+
else:
|
|
100
|
+
# Fallback to the default one packaged in pyact
|
|
101
|
+
default_css = os.path.join(os.path.dirname(__file__), "style.css")
|
|
102
|
+
styles = style_parser(default_css)
|
|
103
|
+
|
|
104
|
+
blocks = markdown_parser(content)
|
|
105
|
+
docx_writer(blocks, styles, args.docx)
|
|
106
|
+
print(f"Successfully compiled DOCX to {args.docx}")
|
|
107
|
+
|
|
108
|
+
except Exception as e:
|
|
109
|
+
print(f"Error: {e}", file=sys.stderr)
|
|
110
|
+
sys.exit(1)
|
|
111
|
+
|
|
112
|
+
if __name__ == "__main__":
|
|
113
|
+
main()
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import os
|
|
2
|
+
from .core import read_pamd_cells, get_imports, find_imports, map_content
|
|
3
|
+
|
|
4
|
+
def run_linter(main_filename: str, directory: str):
|
|
5
|
+
"""
|
|
6
|
+
Runs the linter on a PyAct project.
|
|
7
|
+
|
|
8
|
+
Checks for:
|
|
9
|
+
1. Unused .pamd files in the directory.
|
|
10
|
+
2. Context keys exported but not used in the markdown.
|
|
11
|
+
3. Context tags requested in the markdown but missing from the context dictionary.
|
|
12
|
+
"""
|
|
13
|
+
print(f"--- PyAct Linter ---")
|
|
14
|
+
print(f"Analyzing project starting from: {main_filename}.pamd\n")
|
|
15
|
+
|
|
16
|
+
try:
|
|
17
|
+
build_tree = map_content(main_filename, directory)
|
|
18
|
+
except Exception as e:
|
|
19
|
+
print(f"Error mapping project: {e}")
|
|
20
|
+
return
|
|
21
|
+
|
|
22
|
+
used_files = set()
|
|
23
|
+
|
|
24
|
+
def collect_used_files(tree):
|
|
25
|
+
file_path = os.path.join(tree['path'], tree['name'])
|
|
26
|
+
used_files.add(os.path.abspath(file_path + ".pamd"))
|
|
27
|
+
for child in tree.get("templates", []):
|
|
28
|
+
collect_used_files(child)
|
|
29
|
+
|
|
30
|
+
collect_used_files(build_tree)
|
|
31
|
+
|
|
32
|
+
# Check for unused .pamd files in the base directory
|
|
33
|
+
all_pamd_files = set()
|
|
34
|
+
for root, _, files in os.walk(directory):
|
|
35
|
+
for f in files:
|
|
36
|
+
if f.endswith(".pamd"):
|
|
37
|
+
all_pamd_files.add(os.path.abspath(os.path.join(root, f)))
|
|
38
|
+
|
|
39
|
+
unused_files = all_pamd_files - used_files
|
|
40
|
+
if unused_files:
|
|
41
|
+
print(f"[WARNING] Unused .pamd files found in project directory:")
|
|
42
|
+
for uf in unused_files:
|
|
43
|
+
print(f" - {os.path.relpath(uf, directory)}")
|
|
44
|
+
else:
|
|
45
|
+
print("[OK] No unused .pamd files found.")
|
|
46
|
+
|
|
47
|
+
print("\n--- Context Check ---")
|
|
48
|
+
|
|
49
|
+
# Check context in each used file
|
|
50
|
+
for used_file in used_files:
|
|
51
|
+
rel_path = os.path.relpath(used_file, directory)
|
|
52
|
+
try:
|
|
53
|
+
code_text, md_text = read_pamd_cells(used_file)
|
|
54
|
+
except Exception:
|
|
55
|
+
continue
|
|
56
|
+
|
|
57
|
+
try:
|
|
58
|
+
# Need to pass base name for error reporting in core.py
|
|
59
|
+
base_name = os.path.splitext(os.path.basename(used_file))[0]
|
|
60
|
+
context_dict = get_imports(base_name, code_text)
|
|
61
|
+
exported_keys = set(context_dict.keys())
|
|
62
|
+
except Exception:
|
|
63
|
+
# If a file has no code cell or no context(), it returns an empty dict effectively,
|
|
64
|
+
# or throws ValueError in get_imports. Let's just treat as empty if error.
|
|
65
|
+
exported_keys = set()
|
|
66
|
+
|
|
67
|
+
requested_keys = set(find_imports(md_text))
|
|
68
|
+
|
|
69
|
+
unused_exports = exported_keys - requested_keys
|
|
70
|
+
missing_exports = requested_keys - exported_keys
|
|
71
|
+
|
|
72
|
+
if unused_exports or missing_exports:
|
|
73
|
+
print(f"\nFile: {rel_path}")
|
|
74
|
+
if unused_exports:
|
|
75
|
+
print(f" [WARNING] Unused context exports (defined in python, not used in markdown):")
|
|
76
|
+
for k in unused_exports:
|
|
77
|
+
print(f" - {k}")
|
|
78
|
+
if missing_exports:
|
|
79
|
+
print(f" [ERROR] Missing context exports (used in markdown, missing in python):")
|
|
80
|
+
for k in missing_exports:
|
|
81
|
+
print(f" - {k}")
|
|
82
|
+
else:
|
|
83
|
+
pass
|
|
84
|
+
|
|
85
|
+
print("\nLinting complete!")
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pyact-cli
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.6.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
|
|
@@ -10,11 +10,14 @@ Classifier: License :: OSI Approved :: MIT License
|
|
|
10
10
|
Classifier: Operating System :: OS Independent
|
|
11
11
|
Requires-Python: >=3.8
|
|
12
12
|
Description-Content-Type: text/markdown
|
|
13
|
+
Requires-Dist: python-docx
|
|
14
|
+
Requires-Dist: ezdxf
|
|
13
15
|
Dynamic: author
|
|
14
16
|
Dynamic: classifier
|
|
15
17
|
Dynamic: description
|
|
16
18
|
Dynamic: description-content-type
|
|
17
19
|
Dynamic: home-page
|
|
20
|
+
Dynamic: requires-dist
|
|
18
21
|
Dynamic: requires-python
|
|
19
22
|
Dynamic: summary
|
|
20
23
|
|
|
@@ -1,14 +1,18 @@
|
|
|
1
1
|
README.md
|
|
2
2
|
setup.py
|
|
3
3
|
pamd_helpers/__init__.py
|
|
4
|
+
pamd_helpers/conversions.py
|
|
5
|
+
pamd_helpers/unit.py
|
|
4
6
|
pyact/__init__.py
|
|
5
7
|
pyact/cli.py
|
|
6
8
|
pyact/core.py
|
|
7
9
|
pyact/css2json.py
|
|
10
|
+
pyact/linter.py
|
|
8
11
|
pyact/mdTOword.py
|
|
9
12
|
pyact/py2tex.py
|
|
10
13
|
pyact_cli.egg-info/PKG-INFO
|
|
11
14
|
pyact_cli.egg-info/SOURCES.txt
|
|
12
15
|
pyact_cli.egg-info/dependency_links.txt
|
|
13
16
|
pyact_cli.egg-info/entry_points.txt
|
|
17
|
+
pyact_cli.egg-info/requires.txt
|
|
14
18
|
pyact_cli.egg-info/top_level.txt
|
|
@@ -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.
|
|
8
|
+
version="0.6.0",
|
|
9
9
|
author="Abstergo2003",
|
|
10
10
|
author_email="",
|
|
11
11
|
description="A CLI tool to compile .pamd files to Markdown.",
|
|
@@ -19,6 +19,10 @@ setup(
|
|
|
19
19
|
"Operating System :: OS Independent",
|
|
20
20
|
],
|
|
21
21
|
python_requires=">=3.8",
|
|
22
|
+
install_requires=[
|
|
23
|
+
"python-docx",
|
|
24
|
+
"ezdxf"
|
|
25
|
+
],
|
|
22
26
|
entry_points={
|
|
23
27
|
"console_scripts": [
|
|
24
28
|
"pyact=pyact.cli:main",
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "0.5.0"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|