pyact-cli 0.6.0__tar.gz → 0.7.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.6.0
3
+ Version: 0.7.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
@@ -68,7 +68,19 @@ You can also include other pamd files as templates!
68
68
  <tmp>path/to/another_file</tmp>
69
69
  ```
70
70
 
71
- ### 3. Compile to Markdown
71
+ ### 3. Use Global Variables `<glb>`
72
+ If you have data you want to access everywhere across multiple nested files (like an author name or project ID), simply create a `global.py` file in your root folder:
73
+
74
+ ```python
75
+ def global_context():
76
+ return {"author": "Jane Doe"}
77
+ ```
78
+ You can then inject this data into any Markdown cell using the `<glb>` tag!
79
+ ```markdown
80
+ Written by <glb>author</glb>
81
+ ```
82
+
83
+ ### 4. Compile to Markdown
72
84
  Run the compiler from your terminal to generate your final Markdown document:
73
85
  ```bash
74
86
  pyact path/to/your_file.pamd -o output.md
@@ -45,7 +45,19 @@ You can also include other pamd files as templates!
45
45
  <tmp>path/to/another_file</tmp>
46
46
  ```
47
47
 
48
- ### 3. Compile to Markdown
48
+ ### 3. Use Global Variables `<glb>`
49
+ If you have data you want to access everywhere across multiple nested files (like an author name or project ID), simply create a `global.py` file in your root folder:
50
+
51
+ ```python
52
+ def global_context():
53
+ return {"author": "Jane Doe"}
54
+ ```
55
+ You can then inject this data into any Markdown cell using the `<glb>` tag!
56
+ ```markdown
57
+ Written by <glb>author</glb>
58
+ ```
59
+
60
+ ### 4. Compile to Markdown
49
61
  Run the compiler from your terminal to generate your final Markdown document:
50
62
  ```bash
51
63
  pyact path/to/your_file.pamd -o output.md
@@ -0,0 +1 @@
1
+ __version__ = "0.6.1"
@@ -1,9 +1,15 @@
1
1
  import argparse
2
2
  import sys
3
3
  import os
4
- from .core import map_content, process_content
4
+ import time
5
+ from .core import map_content, process_content, build_logs
6
+
7
+ def is_cache_valid(build_tree, cache_mtime, root_dir=None):
8
+ if root_dir is not None:
9
+ global_file = os.path.join(root_dir, "global.py")
10
+ if os.path.exists(global_file) and os.path.getmtime(global_file) > cache_mtime:
11
+ return False
5
12
 
6
- def is_cache_valid(build_tree, cache_mtime):
7
13
  file_path = os.path.join(build_tree.get("path", ""), build_tree.get("name", "") + ".pamd")
8
14
  if os.path.exists(file_path) and os.path.getmtime(file_path) > cache_mtime:
9
15
  return False
@@ -62,7 +68,7 @@ def main():
62
68
 
63
69
  if os.path.exists(cache_file):
64
70
  cache_mtime = os.path.getmtime(cache_file)
65
- if is_cache_valid(build_tree, cache_mtime):
71
+ if is_cache_valid(build_tree, cache_mtime, directory):
66
72
  use_cache = True
67
73
 
68
74
  if use_cache:
@@ -71,12 +77,28 @@ def main():
71
77
  content = f.read()
72
78
  else:
73
79
  print("pamd-cache: Cache invalid or missing. Rebuilding...")
80
+ build_logs.clear()
81
+
82
+ total_start = time.perf_counter()
74
83
  content = process_content(build_tree)
84
+ total_end = time.perf_counter()
85
+ total_ms = (total_end - total_start) * 1000
86
+
75
87
  # Write cache
76
88
  if not os.path.exists(cache_dir):
77
89
  os.makedirs(cache_dir)
78
90
  with open(cache_file, "w", encoding="utf-8") as f:
79
91
  f.write(content)
92
+
93
+ # Write build log
94
+ log_file = os.path.join(cache_dir, "build.log")
95
+ with open(log_file, "w", encoding="utf-8") as f:
96
+ f.write("PyAct Build Log\n")
97
+ f.write("================\n\n")
98
+ f.write("File Execution Times:\n")
99
+ for log in build_logs:
100
+ f.write(f"- {log['file']}: {log['time_ms']:.2f} ms\n")
101
+ f.write(f"\nTotal Build Time: {total_ms:.2f} ms\n")
80
102
 
81
103
  if args.output:
82
104
  with open(args.output, "w", encoding="utf-8") as f:
@@ -1,5 +1,9 @@
1
1
  import re
2
2
  import json
3
+ import time
4
+ import os
5
+
6
+ build_logs = []
3
7
 
4
8
  def get_directory(path: str) -> list:
5
9
  """
@@ -94,6 +98,34 @@ def get_imports(file: str, code_text: str):
94
98
 
95
99
  return result_dict
96
100
 
101
+ def get_globals(directory: str):
102
+ """
103
+ Reads global.py and executes its global_context() function to get global variables.
104
+ """
105
+ global_path = os.path.join(directory, "global.py")
106
+ if not os.path.exists(global_path):
107
+ return {}
108
+
109
+ with open(global_path, "r", encoding="utf-8") as f:
110
+ code_text = f.read()
111
+
112
+ file_namespace = {"__file__": global_path}
113
+ try:
114
+ exec(code_text, file_namespace)
115
+ except Exception as e:
116
+ raise RuntimeError(f"Error executing global.py: {e}")
117
+
118
+ if "global_context" in file_namespace:
119
+ func = file_namespace["global_context"]
120
+ else:
121
+ raise ValueError("The global.py file must define a 'global_context()' function. (Note: 'global' is a reserved Python keyword!)")
122
+
123
+ res = func()
124
+ if not isinstance(res, dict):
125
+ raise TypeError("'global_context()' in global.py must return a dictionary.")
126
+
127
+ return res
128
+
97
129
  def find_imports(text: str):
98
130
  """
99
131
  Finds all `<ctx>var_name</ctx>` tags in a markdown string.
@@ -111,6 +143,13 @@ def find_imports(text: str):
111
143
  return re.findall(pattern, text)
112
144
 
113
145
 
146
+ def find_globals(text: str):
147
+ """
148
+ Finds all `<glb>var_name</glb>` tags in a markdown string.
149
+ """
150
+ pattern = r"<glb>(.*?)</glb>"
151
+ return re.findall(pattern, text)
152
+
114
153
  def find_templates(text: str):
115
154
  """
116
155
  Finds all `<tmp>file_name</tmp>` tags in a markdown string.
@@ -155,6 +194,7 @@ def map_content(file: str, path: str):
155
194
  code_text, main_file = read_pamd_cells(f"{full_path}.pamd")
156
195
  templates = find_templates(main_file)
157
196
  imports = find_imports(main_file)
197
+ globals_req = find_globals(main_file)
158
198
  meta = get_directory(full_path)
159
199
 
160
200
  ready_t = []
@@ -166,24 +206,19 @@ def map_content(file: str, path: str):
166
206
  "name": meta[1],
167
207
  "path": meta[0],
168
208
  "imports": imports,
209
+ "globals": globals_req,
169
210
  "templates": ready_t
170
211
  }
171
212
 
172
- def process_content(build_tree: dict):
213
+ def process_content(build_tree: dict, globals_dict: dict = None):
173
214
  """
174
215
  Compiles a mapped build tree into a final flat Markdown string.
216
+ """
217
+ start_time = time.perf_counter()
175
218
 
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()`.
219
+ if globals_dict is None:
220
+ globals_dict = get_globals(build_tree.get("path", ""))
183
221
 
184
- Outputs:
185
- str: The final, fully compiled Markdown text string ready for export.
186
- """
187
222
  file_path_base = build_tree.get("path", "") + build_tree.get("name", "")
188
223
  code_text, file_text = read_pamd_cells(file_path_base + ".pamd")
189
224
 
@@ -192,7 +227,23 @@ def process_content(build_tree: dict):
192
227
  imports = get_imports(file_path_base, code_text)
193
228
  for i in needed_imports:
194
229
  file_text = file_text.replace(f"<ctx>{i}</ctx>", str(imports.get(i, '')))
230
+
231
+ needed_globals = build_tree.get("globals", [])
232
+ for g in needed_globals:
233
+ if g in globals_dict:
234
+ file_text = file_text.replace(f"<glb>{g}</glb>", str(globals_dict[g]))
235
+ else:
236
+ raise ValueError(f"Global variable '{g}' requested via <glb> but not found in global.py's global_context().")
195
237
 
196
238
  for i in build_tree.get("templates", []):
197
- file_text = file_text.replace(f"<tmp>{i.get('tag_name')}</tmp>", process_content(i))
239
+ file_text = file_text.replace(f"<tmp>{i.get('tag_name')}</tmp>", process_content(i, globals_dict))
240
+
241
+ end_time = time.perf_counter()
242
+ elapsed_ms = (end_time - start_time) * 1000
243
+
244
+ build_logs.append({
245
+ "file": f"{build_tree.get('name', '')}.pamd",
246
+ "time_ms": elapsed_ms
247
+ })
248
+
198
249
  return file_text
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pyact-cli
3
- Version: 0.6.0
3
+ Version: 0.7.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
@@ -68,7 +68,19 @@ You can also include other pamd files as templates!
68
68
  <tmp>path/to/another_file</tmp>
69
69
  ```
70
70
 
71
- ### 3. Compile to Markdown
71
+ ### 3. Use Global Variables `<glb>`
72
+ If you have data you want to access everywhere across multiple nested files (like an author name or project ID), simply create a `global.py` file in your root folder:
73
+
74
+ ```python
75
+ def global_context():
76
+ return {"author": "Jane Doe"}
77
+ ```
78
+ You can then inject this data into any Markdown cell using the `<glb>` tag!
79
+ ```markdown
80
+ Written by <glb>author</glb>
81
+ ```
82
+
83
+ ### 4. Compile to Markdown
72
84
  Run the compiler from your terminal to generate your final Markdown document:
73
85
  ```bash
74
86
  pyact path/to/your_file.pamd -o output.md
@@ -1,8 +1,6 @@
1
1
  README.md
2
2
  setup.py
3
3
  pamd_helpers/__init__.py
4
- pamd_helpers/conversions.py
5
- pamd_helpers/unit.py
6
4
  pyact/__init__.py
7
5
  pyact/cli.py
8
6
  pyact/core.py
@@ -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.6.0",
8
+ version="0.7.0",
9
9
  author="Abstergo2003",
10
10
  author_email="",
11
11
  description="A CLI tool to compile .pamd files to Markdown.",
@@ -1,126 +0,0 @@
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
@@ -1,43 +0,0 @@
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))
@@ -1 +0,0 @@
1
- __version__ = "0.6.0"
File without changes
File without changes
File without changes
File without changes
File without changes