From 909e1a99ed8487d995224703dfd541760080ac14 Mon Sep 17 00:00:00 2001 From: zhizhongpu Date: Thu, 11 Jun 2026 11:02:01 -0600 Subject: [PATCH 01/12] #155 bd improve autofill --- source/analysis/top_gdp/topgdp_value.py | 10 +-- source/lib/JMSLab/autofill.py | 89 ++++++++++-------------- source/lib/JMSLab/tests/test_autofill.py | 81 +++++++++++---------- 3 files changed, 83 insertions(+), 97 deletions(-) diff --git a/source/analysis/top_gdp/topgdp_value.py b/source/analysis/top_gdp/topgdp_value.py index c3600d1c..f73d93ad 100644 --- a/source/analysis/top_gdp/topgdp_value.py +++ b/source/analysis/top_gdp/topgdp_value.py @@ -1,19 +1,15 @@ from pathlib import Path import pandas as pd -from source.lib.JMSLab.autofill import GenerateAutofillMacros +from source.lib.JMSLab.autofill import AutoFill def Main(): instub = Path("output/derived/wb_clean") outstub = Path("output/analysis/top_gdp") df = pd.read_csv(instub / "gdp_education.csv") - TopGDPValue = df['GDP_2010'].sort_values(ascending=False).iloc[0] + top_gdp_value = df['GDP_2010'].sort_values(ascending=False).iloc[0] - GenerateAutofillMacros( - ["TopGDPValue"], - "{:,.0f}", - outstub / "top_gdp.tex" - ) + AutoFill({"TopGDPValue": top_gdp_value}, "{:,.0f}", outstub / "top_gdp.tex") if __name__ == '__main__': diff --git a/source/lib/JMSLab/autofill.py b/source/lib/JMSLab/autofill.py index b3cdf5b1..cca2a872 100644 --- a/source/lib/JMSLab/autofill.py +++ b/source/lib/JMSLab/autofill.py @@ -1,56 +1,41 @@ +from __future__ import annotations import inspect +from pathlib import Path +from typing import Any + + +def AutoFill( + macros: dict[str, Any] | list[str], + format: str | None = "{:.2f}", + outfile: str | Path = "autofill.tex", + mode: str = "math", + append: bool = False, +) -> None: + if isinstance(macros, dict): + resolved = macros + elif isinstance(macros, list): + caller_frame = inspect.currentframe().f_back + resolved = {} + for var in macros: + frame = caller_frame + while frame is not None and var not in frame.f_locals: + frame = frame.f_back + if frame is None: + raise Exception(f"AutoFill: Variable '{var}' not found") + resolved[var] = frame.f_locals[var] + else: + raise Exception("Argument 'macros' must be a dict or list") -def Autofill(var, format = "{}", namespace = None): - newcommand = f"\\newcommand{{{{\\{{}}}}}}{{{{{format}}}}}\n" - - # Look for var in parent frames - if namespace is None: - parent = inspect.currentframe().f_back - while var not in parent.f_locals.keys() and parent.f_back: - parent = parent.f_back - - if var not in parent.f_locals.keys() or parent is None: - raise Exception(f"Autofill: Variable '{var}' not found") - - namespace = parent.f_locals - - commandname, content = var, namespace[var] - - return newcommand.format(commandname, content) - -def GenerateAutofillMacros(autofill_lists, autofill_formats = "{:.2f}", autofill_outfile = "autofill.tex"): - ''' - - Parameters - ---------- - autofill_lists : TYPE - list - autofill_formats : TYPE - str or list - autofill_outfile : TYPE - str - - Returns - ------- - .tex file - - ''' - if type(autofill_lists) != list: - raise Exception("Argument 'autofill_lists' must be list") - - nested_list = any(isinstance(i, list) for i in autofill_lists) - - if ((nested_list and type(autofill_formats) is str) - or (not nested_list and type(autofill_formats) is list)): - raise Exception("Arguments 'autofill_lists' and 'autofill_formats' are incompatible") - - autofill_file = open(autofill_outfile, 'w') + output = "".join( + _FormatMacro(name, value, format, mode) for name, value in resolved.items() + ) + open_mode = "a" if append else "w" + with open(outfile, open_mode) as f: + f.write(output) - if type(autofill_formats) == str: - output_macros = ''.join(Autofill(autofill_var, autofill_formats) for autofill_var in autofill_lists) - else: - autofill_macros = [] - for autofill_list, autofill_format in zip(autofill_lists, autofill_formats): - autofill_macros.append(''.join(Autofill(autofill_var, autofill_format) for autofill_var in autofill_list)) - output_macros = ''.join(autofill_macros) - - autofill_file.write(output_macros) - autofill_file.close() +def _FormatMacro(name: str, value: Any, format: str | None, mode: str) -> str: + formatted = format.format(value) if format is not None else str(value) + if mode == "text": + return f"\\newcommand{{\\{name}}}{{\\textnormal{{{formatted}}}}}\n" + return f"\\newcommand{{\\{name}}}{{{formatted}}}\n" diff --git a/source/lib/JMSLab/tests/test_autofill.py b/source/lib/JMSLab/tests/test_autofill.py index 33559b6f..a8041737 100644 --- a/source/lib/JMSLab/tests/test_autofill.py +++ b/source/lib/JMSLab/tests/test_autofill.py @@ -1,15 +1,15 @@ from unittest import main, TestCase from os.path import exists -import tempfile, shutil +import tempfile, shutil -from ..autofill import GenerateAutofillMacros +from ..autofill import AutoFill class Test(TestCase): def setUp(self): - self.tempdir = tempfile.mkdtemp() - self.outfile = self.tempdir + r"output_macros.tex" + self.tempdir = tempfile.mkdtemp() + self.outfile = self.tempdir + r"\output_macros.tex" return None def tearDown(self): @@ -17,53 +17,58 @@ def tearDown(self): return def test_file_exists(self): - Epsilon = -1.19 - - GenerateAutofillMacros(["Epsilon"], autofill_outfile = self.outfile) + AutoFill({"Epsilon": -1.19}, outfile=self.outfile) self.assertTrue(exists(self.outfile)) return None - def test_exception(self): - with self.assertRaises(Exception) as context: - GenerateAutofillMacros(["Epsilon"], autofill_outfile = self.outfile) - - self.assertTrue("Autofill: Variable 'Epsilon' not found" in str(context.exception)) + def test_dict_output(self): + AutoFill({"Epsilon": -1.19, "MarginalCost": 2.59}, "{:.2f}", self.outfile) + with open(self.outfile) as f: + content = f.read() + self.assertEqual(content, "\\newcommand{\\Epsilon}{-1.19}\n\\newcommand{\\MarginalCost}{2.59}\n") return None - def test_output(self): - Epsilon = - 1.19 - MarginalCost = 2.59 - - GenerateAutofillMacros([["Epsilon"], ["MarginalCost"]], - autofill_formats = ["{:.2f}", "\\textnormal{{{:.2f}}}"], - autofill_outfile = self.outfile) - tex_file = open(self.outfile, 'r') - content = tex_file.read() - self.assertEqual(content, "\\newcommand{\\Epsilon}{-1.19}\n\\newcommand{\\MarginalCost}{\\textnormal{2.59}}\n") - tex_file.close() + def test_list_output(self): + Epsilon = -1.19 + AutoFill(["Epsilon"], "{:.2f}", self.outfile) + with open(self.outfile) as f: + content = f.read() + self.assertEqual(content, "\\newcommand{\\Epsilon}{-1.19}\n") return None - def test_list_format(self): - Epsilon = - 1.19 - MarginalCost = 2.59 - + def test_list_variable_not_found(self): with self.assertRaises(Exception) as context: - GenerateAutofillMacros("Epsilon", autofill_outfile = self.outfile) - - self.assertTrue("Argument 'autofill_lists' must be list" in str(context.exception)) + AutoFill(["Epsilon"], outfile=self.outfile) + self.assertIn("AutoFill: Variable 'Epsilon' not found", str(context.exception)) + return None + def test_invalid_macros_type(self): with self.assertRaises(Exception) as context: - GenerateAutofillMacros(["Epsilon"], autofill_formats = ["{:.2f}", "\\textnormal{{{:.2f}}}"], - autofill_outfile = self.outfile) + AutoFill("Epsilon", outfile=self.outfile) + self.assertIn("Argument 'macros' must be a dict or list", str(context.exception)) + return None - self.assertTrue("Arguments 'autofill_lists' and 'autofill_formats' are incompatible" in str(context.exception)) + def test_text_mode(self): + AutoFill({"MarginalCost": 2.59}, "{:.2f}", self.outfile, mode="text") + with open(self.outfile) as f: + content = f.read() + self.assertEqual(content, "\\newcommand{\\MarginalCost}{\\textnormal{2.59}}\n") + return None - with self.assertRaises(Exception) as context: - GenerateAutofillMacros([["Epsilon"], ["MarginalCost"]], autofill_outfile = self.outfile) + def test_none_format(self): + AutoFill({"SampleStart": "January 2010"}, None, self.outfile) + with open(self.outfile) as f: + content = f.read() + self.assertEqual(content, "\\newcommand{\\SampleStart}{January 2010}\n") + return None - self.assertTrue("Arguments 'autofill_lists' and 'autofill_formats' are incompatible" in str(context.exception)) + def test_append(self): + AutoFill({"Epsilon": -1.19}, "{:.2f}", self.outfile) + AutoFill({"MarginalCost": 2.59}, "{:.2f}", self.outfile, append=True) + with open(self.outfile) as f: + content = f.read() + self.assertEqual(content, "\\newcommand{\\Epsilon}{-1.19}\n\\newcommand{\\MarginalCost}{2.59}\n") return None - + if __name__ == '__main__': main() - From 3407fbd93d535ae146ca2ee86d0d551646bc5d3b Mon Sep 17 00:00:00 2001 From: zhizhongpu Date: Fri, 12 Jun 2026 08:54:13 -0600 Subject: [PATCH 02/12] #155 bd test for Path --- source/lib/JMSLab/tests/test_autofill.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/source/lib/JMSLab/tests/test_autofill.py b/source/lib/JMSLab/tests/test_autofill.py index a8041737..4e407000 100644 --- a/source/lib/JMSLab/tests/test_autofill.py +++ b/source/lib/JMSLab/tests/test_autofill.py @@ -1,5 +1,6 @@ from unittest import main, TestCase from os.path import exists +from pathlib import Path import tempfile, shutil @@ -17,8 +18,9 @@ def tearDown(self): return def test_file_exists(self): - AutoFill({"Epsilon": -1.19}, outfile=self.outfile) - self.assertTrue(exists(self.outfile)) + for outfile in [self.outfile, Path(self.outfile)]: + AutoFill({"Epsilon": -1.19}, outfile=outfile) + self.assertTrue(exists(outfile)) return None def test_dict_output(self): From b77a4ba60ef1eb2a0cb1bd367bdc461c25aace40 Mon Sep 17 00:00:00 2001 From: zhizhongpu Date: Fri, 12 Jun 2026 08:59:13 -0600 Subject: [PATCH 03/12] #155 bd literal type --- source/lib/JMSLab/autofill.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/source/lib/JMSLab/autofill.py b/source/lib/JMSLab/autofill.py index cca2a872..5cbcb717 100644 --- a/source/lib/JMSLab/autofill.py +++ b/source/lib/JMSLab/autofill.py @@ -1,14 +1,14 @@ from __future__ import annotations import inspect from pathlib import Path -from typing import Any +from typing import Any, Literal def AutoFill( macros: dict[str, Any] | list[str], format: str | None = "{:.2f}", outfile: str | Path = "autofill.tex", - mode: str = "math", + mode: Literal["math", "text"] = "math", append: bool = False, ) -> None: if isinstance(macros, dict): From a9d0405929eb0599e905882574f3303818e08c90 Mon Sep 17 00:00:00 2001 From: zhizhongpu Date: Fri, 12 Jun 2026 09:06:03 -0600 Subject: [PATCH 04/12] #155 cl typechecker bypass MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit typechecker fails without it inspect.currentframe() is typed as returning FrameType | None — it can theoretically return None in implementations that don't support stack frames (e.g., some embedded Python runtimes). So ty complains that you're calling .f_back on something that could be None. In practice, CPython always returns a frame, so this will never actually be None in normal usage. This is just a quick suppress. --- source/lib/JMSLab/autofill.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/lib/JMSLab/autofill.py b/source/lib/JMSLab/autofill.py index 5cbcb717..3e76d8c4 100644 --- a/source/lib/JMSLab/autofill.py +++ b/source/lib/JMSLab/autofill.py @@ -14,7 +14,7 @@ def AutoFill( if isinstance(macros, dict): resolved = macros elif isinstance(macros, list): - caller_frame = inspect.currentframe().f_back + caller_frame = inspect.currentframe().f_back # type: ignore resolved = {} for var in macros: frame = caller_frame From d862d55338035ff605347f257ca710a129ad9fbc Mon Sep 17 00:00:00 2001 From: zhizhongpu <84325421+zhizhongpu@users.noreply.github.com> Date: Fri, 12 Jun 2026 09:56:35 -0600 Subject: [PATCH 05/12] #155 bd allow format to be a list --- source/lib/JMSLab/autofill.py | 16 ++++++++++++---- source/lib/JMSLab/tests/test_autofill.py | 12 ++++++------ 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/source/lib/JMSLab/autofill.py b/source/lib/JMSLab/autofill.py index 3e76d8c4..58414777 100644 --- a/source/lib/JMSLab/autofill.py +++ b/source/lib/JMSLab/autofill.py @@ -6,10 +6,10 @@ def AutoFill( macros: dict[str, Any] | list[str], - format: str | None = "{:.2f}", - outfile: str | Path = "autofill.tex", - mode: Literal["math", "text"] = "math", + outfile: str | Path, + format: str | list[str | None] | None = None, append: bool = False, + mode: Literal["math", "text"] = "math", ) -> None: if isinstance(macros, dict): resolved = macros @@ -26,8 +26,16 @@ def AutoFill( else: raise Exception("Argument 'macros' must be a dict or list") + if isinstance(format, list): + if len(format) != len(resolved): + raise Exception("AutoFill: 'format' list length must match number of macros") + formats = format + else: + formats = [format] * len(resolved) + output = "".join( - _FormatMacro(name, value, format, mode) for name, value in resolved.items() + _FormatMacro(name, value, fmt, mode) + for (name, value), fmt in zip(resolved.items(), formats) ) open_mode = "a" if append else "w" with open(outfile, open_mode) as f: diff --git a/source/lib/JMSLab/tests/test_autofill.py b/source/lib/JMSLab/tests/test_autofill.py index 4e407000..9faa7548 100644 --- a/source/lib/JMSLab/tests/test_autofill.py +++ b/source/lib/JMSLab/tests/test_autofill.py @@ -24,7 +24,7 @@ def test_file_exists(self): return None def test_dict_output(self): - AutoFill({"Epsilon": -1.19, "MarginalCost": 2.59}, "{:.2f}", self.outfile) + AutoFill({"Epsilon": -1.19, "MarginalCost": 2.59}, self.outfile, "{:.2f}") with open(self.outfile) as f: content = f.read() self.assertEqual(content, "\\newcommand{\\Epsilon}{-1.19}\n\\newcommand{\\MarginalCost}{2.59}\n") @@ -32,7 +32,7 @@ def test_dict_output(self): def test_list_output(self): Epsilon = -1.19 - AutoFill(["Epsilon"], "{:.2f}", self.outfile) + AutoFill(["Epsilon"], self.outfile, "{:.2f}") with open(self.outfile) as f: content = f.read() self.assertEqual(content, "\\newcommand{\\Epsilon}{-1.19}\n") @@ -51,22 +51,22 @@ def test_invalid_macros_type(self): return None def test_text_mode(self): - AutoFill({"MarginalCost": 2.59}, "{:.2f}", self.outfile, mode="text") + AutoFill({"MarginalCost": 2.59}, self.outfile, "{:.2f}", mode="text") with open(self.outfile) as f: content = f.read() self.assertEqual(content, "\\newcommand{\\MarginalCost}{\\textnormal{2.59}}\n") return None def test_none_format(self): - AutoFill({"SampleStart": "January 2010"}, None, self.outfile) + AutoFill({"SampleStart": "January 2010"}, self.outfile) with open(self.outfile) as f: content = f.read() self.assertEqual(content, "\\newcommand{\\SampleStart}{January 2010}\n") return None def test_append(self): - AutoFill({"Epsilon": -1.19}, "{:.2f}", self.outfile) - AutoFill({"MarginalCost": 2.59}, "{:.2f}", self.outfile, append=True) + AutoFill({"Epsilon": -1.19}, self.outfile, "{:.2f}") + AutoFill({"MarginalCost": 2.59}, self.outfile, "{:.2f}", append=True) with open(self.outfile) as f: content = f.read() self.assertEqual(content, "\\newcommand{\\Epsilon}{-1.19}\n\\newcommand{\\MarginalCost}{2.59}\n") From 7b37cd38eee2fd91d3d297c33c601018e9d52170 Mon Sep 17 00:00:00 2001 From: zhizhongpu <84325421+zhizhongpu@users.noreply.github.com> Date: Fri, 12 Jun 2026 09:57:14 -0600 Subject: [PATCH 06/12] #155 fx changing position --- source/analysis/top_gdp/topgdp_value.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/source/analysis/top_gdp/topgdp_value.py b/source/analysis/top_gdp/topgdp_value.py index f73d93ad..32dbf32f 100644 --- a/source/analysis/top_gdp/topgdp_value.py +++ b/source/analysis/top_gdp/topgdp_value.py @@ -9,7 +9,7 @@ def Main(): df = pd.read_csv(instub / "gdp_education.csv") top_gdp_value = df['GDP_2010'].sort_values(ascending=False).iloc[0] - AutoFill({"TopGDPValue": top_gdp_value}, "{:,.0f}", outstub / "top_gdp.tex") + AutoFill({"TopGDPValue": top_gdp_value}, outstub / "top_gdp.tex", "{:,.0f}") if __name__ == '__main__': From 26d8ea0545f627262871f5f09c84fe51ac14896a Mon Sep 17 00:00:00 2001 From: zhizhongpu <84325421+zhizhongpu@users.noreply.github.com> Date: Fri, 12 Jun 2026 10:10:56 -0600 Subject: [PATCH 07/12] #155 bd test appendmode --- source/lib/JMSLab/tests/test_autofill.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/source/lib/JMSLab/tests/test_autofill.py b/source/lib/JMSLab/tests/test_autofill.py index 9faa7548..aff0ad38 100644 --- a/source/lib/JMSLab/tests/test_autofill.py +++ b/source/lib/JMSLab/tests/test_autofill.py @@ -72,5 +72,24 @@ def test_append(self): self.assertEqual(content, "\\newcommand{\\Epsilon}{-1.19}\n\\newcommand{\\MarginalCost}{2.59}\n") return None + def test_append_multiple_calls(self): + AutoFill({"Alpha": 1.0}, self.outfile, "{:.1f}") + AutoFill({"Beta": 2.0}, self.outfile, "{:.1f}", append=True) + with open(self.outfile) as f: + content = f.read() + self.assertEqual( + content, + "\\newcommand{\\Alpha}{1.0}\n\\newcommand{\\Beta}{2.0}\n" + ) + return None + + def test_overwrite_without_append(self): + AutoFill({"Epsilon": -1.19}, self.outfile, "{:.2f}") + AutoFill({"MarginalCost": 2.59}, self.outfile, "{:.2f}") + with open(self.outfile) as f: + content = f.read() + self.assertEqual(content, "\\newcommand{\\MarginalCost}{2.59}\n") + return None + if __name__ == '__main__': main() From 71f796b2cc19384620e4a38bc6ccbfb01ac9947d Mon Sep 17 00:00:00 2001 From: zhizhongpu <84325421+zhizhongpu@users.noreply.github.com> Date: Thu, 25 Jun 2026 18:49:54 -0400 Subject: [PATCH 08/12] #155 cl legacy tools --- source/lib/JMSLab/autofill.py | 1 - 1 file changed, 1 deletion(-) diff --git a/source/lib/JMSLab/autofill.py b/source/lib/JMSLab/autofill.py index 58414777..5d850b06 100644 --- a/source/lib/JMSLab/autofill.py +++ b/source/lib/JMSLab/autofill.py @@ -1,4 +1,3 @@ -from __future__ import annotations import inspect from pathlib import Path from typing import Any, Literal From bfdc7b8eea95ee96686027effbef16c341a02922 Mon Sep 17 00:00:00 2001 From: zhizhongpu <84325421+zhizhongpu@users.noreply.github.com> Date: Thu, 25 Jun 2026 18:57:40 -0400 Subject: [PATCH 09/12] #155 cl refactor --- source/lib/JMSLab/autofill.py | 28 ++++++++++++++++++++-------- 1 file changed, 20 insertions(+), 8 deletions(-) diff --git a/source/lib/JMSLab/autofill.py b/source/lib/JMSLab/autofill.py index 5d850b06..1530fe2b 100644 --- a/source/lib/JMSLab/autofill.py +++ b/source/lib/JMSLab/autofill.py @@ -11,36 +11,48 @@ def AutoFill( mode: Literal["math", "text"] = "math", ) -> None: if isinstance(macros, dict): - resolved = macros + macro_values = macros elif isinstance(macros, list): caller_frame = inspect.currentframe().f_back # type: ignore - resolved = {} + macro_values = {} for var in macros: frame = caller_frame - while frame is not None and var not in frame.f_locals: + found = False + while frame is not None: + found, value = _LookupVar(var, frame) + if found: + break frame = frame.f_back - if frame is None: + if not found: raise Exception(f"AutoFill: Variable '{var}' not found") - resolved[var] = frame.f_locals[var] + macro_values[var] = value else: raise Exception("Argument 'macros' must be a dict or list") if isinstance(format, list): - if len(format) != len(resolved): + if len(format) != len(macro_values): raise Exception("AutoFill: 'format' list length must match number of macros") formats = format else: - formats = [format] * len(resolved) + formats = [format] * len(macro_values) output = "".join( _FormatMacro(name, value, fmt, mode) - for (name, value), fmt in zip(resolved.items(), formats) + for (name, value), fmt in zip(macro_values.items(), formats) ) open_mode = "a" if append else "w" with open(outfile, open_mode) as f: f.write(output) +def _LookupVar(name: str, frame) -> tuple[bool, Any]: + if name in frame.f_locals: + return True, frame.f_locals[name] + if name in frame.f_globals: + return True, frame.f_globals[name] + return False, None + + def _FormatMacro(name: str, value: Any, format: str | None, mode: str) -> str: formatted = format.format(value) if format is not None else str(value) if mode == "text": From a19e431b6ec608b80abbfc888fde93dddd5fc4d8 Mon Sep 17 00:00:00 2001 From: zhizhongpu <84325421+zhizhongpu@users.noreply.github.com> Date: Fri, 26 Jun 2026 12:13:14 -0400 Subject: [PATCH 10/12] #155 bd tests for autofill --- source/lib/JMSLab/tests/test_autofill.py | 107 +++++++++++++---------- 1 file changed, 60 insertions(+), 47 deletions(-) diff --git a/source/lib/JMSLab/tests/test_autofill.py b/source/lib/JMSLab/tests/test_autofill.py index aff0ad38..631ced22 100644 --- a/source/lib/JMSLab/tests/test_autofill.py +++ b/source/lib/JMSLab/tests/test_autofill.py @@ -1,68 +1,73 @@ from unittest import main, TestCase -from os.path import exists +import os +import tempfile +import shutil from pathlib import Path -import tempfile, shutil - from ..autofill import AutoFill -class Test(TestCase): + +class TestOutputContent(TestCase): def setUp(self): self.tempdir = tempfile.mkdtemp() - self.outfile = self.tempdir + r"\output_macros.tex" - return None + self.outfile = os.path.join(self.tempdir, "output_macros.tex") def tearDown(self): shutil.rmtree(self.tempdir) - return - def test_file_exists(self): + def test_dict_output(self): for outfile in [self.outfile, Path(self.outfile)]: - AutoFill({"Epsilon": -1.19}, outfile=outfile) - self.assertTrue(exists(outfile)) - return None + AutoFill({"Epsilon": -1.19, "MarginalCost": 2.59}, outfile, "{:.2f}") + with open(outfile) as f: + content = f.read() + self.assertEqual(content, "\\newcommand{\\Epsilon}{-1.19}\n\\newcommand{\\MarginalCost}{2.59}\n") - def test_dict_output(self): - AutoFill({"Epsilon": -1.19, "MarginalCost": 2.59}, self.outfile, "{:.2f}") + def test_list_output(self): + Epsilon = -1.19 + MarginalCost = 2.59 + AutoFill(["Epsilon", "MarginalCost"], self.outfile, "{:.2f}") with open(self.outfile) as f: content = f.read() self.assertEqual(content, "\\newcommand{\\Epsilon}{-1.19}\n\\newcommand{\\MarginalCost}{2.59}\n") - return None - def test_list_output(self): - Epsilon = -1.19 - AutoFill(["Epsilon"], self.outfile, "{:.2f}") + def test_none_format(self): + AutoFill({"SampleStart": "January 2010"}, self.outfile) with open(self.outfile) as f: content = f.read() - self.assertEqual(content, "\\newcommand{\\Epsilon}{-1.19}\n") - return None + self.assertEqual(content, "\\newcommand{\\SampleStart}{January 2010}\n") - def test_list_variable_not_found(self): - with self.assertRaises(Exception) as context: - AutoFill(["Epsilon"], outfile=self.outfile) - self.assertIn("AutoFill: Variable 'Epsilon' not found", str(context.exception)) - return None - def test_invalid_macros_type(self): - with self.assertRaises(Exception) as context: - AutoFill("Epsilon", outfile=self.outfile) - self.assertIn("Argument 'macros' must be a dict or list", str(context.exception)) - return None +class TestModeAndFormat(TestCase): + + def setUp(self): + self.tempdir = tempfile.mkdtemp() + self.outfile = os.path.join(self.tempdir, "output_macros.tex") + + def tearDown(self): + shutil.rmtree(self.tempdir) def test_text_mode(self): AutoFill({"MarginalCost": 2.59}, self.outfile, "{:.2f}", mode="text") with open(self.outfile) as f: content = f.read() self.assertEqual(content, "\\newcommand{\\MarginalCost}{\\textnormal{2.59}}\n") - return None - def test_none_format(self): - AutoFill({"SampleStart": "January 2010"}, self.outfile) + def test_format_list(self): + AutoFill({"Epsilon": -1.19, "MarginalCost": 2.59}, self.outfile, ["{:.2f}", "{:.1f}"]) with open(self.outfile) as f: content = f.read() - self.assertEqual(content, "\\newcommand{\\SampleStart}{January 2010}\n") - return None + self.assertEqual(content, "\\newcommand{\\Epsilon}{-1.19}\n\\newcommand{\\MarginalCost}{2.6}\n") + + +class TestFileBehavior(TestCase): + + def setUp(self): + self.tempdir = tempfile.mkdtemp() + self.outfile = os.path.join(self.tempdir, "output_macros.tex") + + def tearDown(self): + shutil.rmtree(self.tempdir) def test_append(self): AutoFill({"Epsilon": -1.19}, self.outfile, "{:.2f}") @@ -70,18 +75,6 @@ def test_append(self): with open(self.outfile) as f: content = f.read() self.assertEqual(content, "\\newcommand{\\Epsilon}{-1.19}\n\\newcommand{\\MarginalCost}{2.59}\n") - return None - - def test_append_multiple_calls(self): - AutoFill({"Alpha": 1.0}, self.outfile, "{:.1f}") - AutoFill({"Beta": 2.0}, self.outfile, "{:.1f}", append=True) - with open(self.outfile) as f: - content = f.read() - self.assertEqual( - content, - "\\newcommand{\\Alpha}{1.0}\n\\newcommand{\\Beta}{2.0}\n" - ) - return None def test_overwrite_without_append(self): AutoFill({"Epsilon": -1.19}, self.outfile, "{:.2f}") @@ -89,7 +82,27 @@ def test_overwrite_without_append(self): with open(self.outfile) as f: content = f.read() self.assertEqual(content, "\\newcommand{\\MarginalCost}{2.59}\n") - return None + + +class TestErrors(TestCase): + + def setUp(self): + self.tempdir = tempfile.mkdtemp() + self.outfile = os.path.join(self.tempdir, "output_macros.tex") + + def tearDown(self): + shutil.rmtree(self.tempdir) + + def test_list_variable_not_found(self): + with self.assertRaises(Exception) as context: + AutoFill(["Epsilon"], outfile=self.outfile) + self.assertIn("AutoFill: Variable 'Epsilon' not found", str(context.exception)) + + def test_invalid_macros_type(self): + with self.assertRaises(Exception) as context: + AutoFill("Epsilon", outfile=self.outfile) + self.assertIn("Argument 'macros' must be a dict or list", str(context.exception)) + if __name__ == '__main__': main() From eb49d8557ae19508427b9fc8aae811c4ab3c73c3 Mon Sep 17 00:00:00 2001 From: zhizhongpu <84325421+zhizhongpu@users.noreply.github.com> Date: Fri, 26 Jun 2026 12:28:24 -0400 Subject: [PATCH 11/12] #155 cl rename objects --- source/lib/JMSLab/tests/test_autofill.py | 46 +++++++++++++----------- 1 file changed, 26 insertions(+), 20 deletions(-) diff --git a/source/lib/JMSLab/tests/test_autofill.py b/source/lib/JMSLab/tests/test_autofill.py index 631ced22..48f1dcee 100644 --- a/source/lib/JMSLab/tests/test_autofill.py +++ b/source/lib/JMSLab/tests/test_autofill.py @@ -18,20 +18,26 @@ def tearDown(self): def test_dict_output(self): for outfile in [self.outfile, Path(self.outfile)]: - AutoFill({"Epsilon": -1.19, "MarginalCost": 2.59}, outfile, "{:.2f}") + AutoFill({"NegativePi": -3.1415, "Pi": 3.1415}, outfile, "{:.2f}") with open(outfile) as f: content = f.read() - self.assertEqual(content, "\\newcommand{\\Epsilon}{-1.19}\n\\newcommand{\\MarginalCost}{2.59}\n") + self.assertEqual(content, "\\newcommand{\\NegativePi}{-3.14}\n\\newcommand{\\Pi}{3.14}\n") def test_list_output(self): - Epsilon = -1.19 - MarginalCost = 2.59 - AutoFill(["Epsilon", "MarginalCost"], self.outfile, "{:.2f}") + NegativePi = -3.1415 + Pi = 3.1415 + AutoFill(["NegativePi", "Pi"], self.outfile, "{:.2f}") with open(self.outfile) as f: content = f.read() - self.assertEqual(content, "\\newcommand{\\Epsilon}{-1.19}\n\\newcommand{\\MarginalCost}{2.59}\n") + self.assertEqual(content, "\\newcommand{\\NegativePi}{-3.14}\n\\newcommand{\\Pi}{3.1415}\n") - def test_none_format(self): + def test_none_format_numeric(self): + AutoFill({"NegativePi": -3.1415, "Pi": 3.1415}, self.outfile) + with open(self.outfile) as f: + content = f.read() + self.assertEqual(content, "\\newcommand{\\NegativePi}{-3.1415}\n\\newcommand{\\Pi}{3.1415}\n") + + def test_none_format_text(self): AutoFill({"SampleStart": "January 2010"}, self.outfile) with open(self.outfile) as f: content = f.read() @@ -48,16 +54,16 @@ def tearDown(self): shutil.rmtree(self.tempdir) def test_text_mode(self): - AutoFill({"MarginalCost": 2.59}, self.outfile, "{:.2f}", mode="text") + AutoFill({"Pi": 3.1415}, self.outfile, "{:.2f}", mode="text") with open(self.outfile) as f: content = f.read() - self.assertEqual(content, "\\newcommand{\\MarginalCost}{\\textnormal{2.59}}\n") + self.assertEqual(content, "\\newcommand{\\Pi}{\\textnormal{3.14}}\n") def test_format_list(self): - AutoFill({"Epsilon": -1.19, "MarginalCost": 2.59}, self.outfile, ["{:.2f}", "{:.1f}"]) + AutoFill({"NegativePi": -3.1415, "IntegerPi": 3.1415}, self.outfile, ["{:.2f}", "{:.0f}"]) with open(self.outfile) as f: content = f.read() - self.assertEqual(content, "\\newcommand{\\Epsilon}{-1.19}\n\\newcommand{\\MarginalCost}{2.6}\n") + self.assertEqual(content, "\\newcommand{\\NegativePi}{-3.14}\n\\newcommand{\\IntegerPi}{3}\n") class TestFileBehavior(TestCase): @@ -70,18 +76,18 @@ def tearDown(self): shutil.rmtree(self.tempdir) def test_append(self): - AutoFill({"Epsilon": -1.19}, self.outfile, "{:.2f}") - AutoFill({"MarginalCost": 2.59}, self.outfile, "{:.2f}", append=True) + AutoFill({"NegativePi": -3.1415}, self.outfile, "{:.2f}") + AutoFill({"Pi": 3.1415}, self.outfile, "{:.2f}", append=True) with open(self.outfile) as f: content = f.read() - self.assertEqual(content, "\\newcommand{\\Epsilon}{-1.19}\n\\newcommand{\\MarginalCost}{2.59}\n") + self.assertEqual(content, "\\newcommand{\\NegativePi}{-3.14}\n\\newcommand{\\Pi}{3.14}\n") def test_overwrite_without_append(self): - AutoFill({"Epsilon": -1.19}, self.outfile, "{:.2f}") - AutoFill({"MarginalCost": 2.59}, self.outfile, "{:.2f}") + AutoFill({"NegativePi": -3.1415}, self.outfile, "{:.2f}") + AutoFill({"Pi": 3.1415}, self.outfile, "{:.2f}") with open(self.outfile) as f: content = f.read() - self.assertEqual(content, "\\newcommand{\\MarginalCost}{2.59}\n") + self.assertEqual(content, "\\newcommand{\\Pi}{3.14}\n") class TestErrors(TestCase): @@ -95,12 +101,12 @@ def tearDown(self): def test_list_variable_not_found(self): with self.assertRaises(Exception) as context: - AutoFill(["Epsilon"], outfile=self.outfile) - self.assertIn("AutoFill: Variable 'Epsilon' not found", str(context.exception)) + AutoFill(["NegativePi"], outfile=self.outfile) + self.assertIn("AutoFill: Variable 'NegativePi' not found", str(context.exception)) def test_invalid_macros_type(self): with self.assertRaises(Exception) as context: - AutoFill("Epsilon", outfile=self.outfile) + AutoFill("NegativePi", outfile=self.outfile) self.assertIn("Argument 'macros' must be a dict or list", str(context.exception)) From 46391227decdd502a128fde69c50da8d9bbe43e5 Mon Sep 17 00:00:00 2001 From: zhizhongpu <84325421+zhizhongpu@users.noreply.github.com> Date: Fri, 26 Jun 2026 12:32:16 -0400 Subject: [PATCH 12/12] #155 fx bug --- source/lib/JMSLab/tests/test_autofill.py | 26 ++++++++---------------- 1 file changed, 9 insertions(+), 17 deletions(-) diff --git a/source/lib/JMSLab/tests/test_autofill.py b/source/lib/JMSLab/tests/test_autofill.py index 48f1dcee..fe3879de 100644 --- a/source/lib/JMSLab/tests/test_autofill.py +++ b/source/lib/JMSLab/tests/test_autofill.py @@ -19,28 +19,24 @@ def tearDown(self): def test_dict_output(self): for outfile in [self.outfile, Path(self.outfile)]: AutoFill({"NegativePi": -3.1415, "Pi": 3.1415}, outfile, "{:.2f}") - with open(outfile) as f: - content = f.read() + content = Path(outfile).read_text() self.assertEqual(content, "\\newcommand{\\NegativePi}{-3.14}\n\\newcommand{\\Pi}{3.14}\n") def test_list_output(self): NegativePi = -3.1415 Pi = 3.1415 AutoFill(["NegativePi", "Pi"], self.outfile, "{:.2f}") - with open(self.outfile) as f: - content = f.read() - self.assertEqual(content, "\\newcommand{\\NegativePi}{-3.14}\n\\newcommand{\\Pi}{3.1415}\n") + content = Path(self.outfile).read_text() + self.assertEqual(content, "\\newcommand{\\NegativePi}{-3.14}\n\\newcommand{\\Pi}{3.14}\n") def test_none_format_numeric(self): AutoFill({"NegativePi": -3.1415, "Pi": 3.1415}, self.outfile) - with open(self.outfile) as f: - content = f.read() + content = Path(self.outfile).read_text() self.assertEqual(content, "\\newcommand{\\NegativePi}{-3.1415}\n\\newcommand{\\Pi}{3.1415}\n") def test_none_format_text(self): AutoFill({"SampleStart": "January 2010"}, self.outfile) - with open(self.outfile) as f: - content = f.read() + content = Path(self.outfile).read_text() self.assertEqual(content, "\\newcommand{\\SampleStart}{January 2010}\n") @@ -55,14 +51,12 @@ def tearDown(self): def test_text_mode(self): AutoFill({"Pi": 3.1415}, self.outfile, "{:.2f}", mode="text") - with open(self.outfile) as f: - content = f.read() + content = Path(self.outfile).read_text() self.assertEqual(content, "\\newcommand{\\Pi}{\\textnormal{3.14}}\n") def test_format_list(self): AutoFill({"NegativePi": -3.1415, "IntegerPi": 3.1415}, self.outfile, ["{:.2f}", "{:.0f}"]) - with open(self.outfile) as f: - content = f.read() + content = Path(self.outfile).read_text() self.assertEqual(content, "\\newcommand{\\NegativePi}{-3.14}\n\\newcommand{\\IntegerPi}{3}\n") @@ -78,15 +72,13 @@ def tearDown(self): def test_append(self): AutoFill({"NegativePi": -3.1415}, self.outfile, "{:.2f}") AutoFill({"Pi": 3.1415}, self.outfile, "{:.2f}", append=True) - with open(self.outfile) as f: - content = f.read() + content = Path(self.outfile).read_text() self.assertEqual(content, "\\newcommand{\\NegativePi}{-3.14}\n\\newcommand{\\Pi}{3.14}\n") def test_overwrite_without_append(self): AutoFill({"NegativePi": -3.1415}, self.outfile, "{:.2f}") AutoFill({"Pi": 3.1415}, self.outfile, "{:.2f}") - with open(self.outfile) as f: - content = f.read() + content = Path(self.outfile).read_text() self.assertEqual(content, "\\newcommand{\\Pi}{3.14}\n")