From a1b2fa2877fd0c608ed94e25be996e5b3dd88074 Mon Sep 17 00:00:00 2001 From: YigitElma Date: Mon, 30 Mar 2026 23:55:09 -0400 Subject: [PATCH 1/7] write the desc input file to a temporary directory when vmec input file is used in from_input_file method --- desc/input_reader.py | 5 ++++- tests/test_input_output.py | 24 ++++++++++++++++++------ 2 files changed, 22 insertions(+), 7 deletions(-) diff --git a/desc/input_reader.py b/desc/input_reader.py index 09f06b9eaf..faa35de242 100644 --- a/desc/input_reader.py +++ b/desc/input_reader.py @@ -5,6 +5,7 @@ import os import pathlib import re +import tempfile import warnings from datetime import datetime @@ -215,7 +216,9 @@ def parse_inputs(self, fname=None): # noqa: C901 isVMEC = re.search(r"&INDATA", line, re.IGNORECASE) if isVMEC: print("Converting VMEC input to DESC input") - path = self.input_path + "_desc" + basename = os.path.basename(self.input_path) + "_desc" + tmpdir = tempfile.mkdtemp() + path = os.path.join(tmpdir, basename) InputReader.vmec_to_desc_input(self.input_path, path) print("Generated DESC input file {}:".format(path)) return self.parse_inputs(path) diff --git a/tests/test_input_output.py b/tests/test_input_output.py index ab4f362994..212c04ccd4 100644 --- a/tests/test_input_output.py +++ b/tests/test_input_output.py @@ -243,15 +243,27 @@ def test_near_axis_input_files(): @pytest.mark.unit -def test_from_input_file_equilibrium_desc_vmec_DSHAPE(): - """Test that from_input_file works for DESC input files.""" - vmec_path = ".//tests//inputs//input.DSHAPE" +def test_from_input_file_equilibrium_desc_vmec_DSHAPE(tmp_path): + """Test that from_input_file works for DESC and VMEC input files.""" desc_path = ".//tests//inputs//input.DSHAPE_desc" kwargs = {"spectral_indexing": "fringe"} with pytest.warns(UserWarning, match="Left handed"): eq = Equilibrium.from_input_file(desc_path, **kwargs) - with pytest.warns(UserWarning): - eq_VMEC = Equilibrium.from_input_file(vmec_path, **kwargs) + + # load VMEC input from a read-only directory to ensure temp file handling works + # Related to issue #2139 + vmec_src = ".//tests//inputs//input.DSHAPE" + locked_dir = tmp_path / "locked" + locked_dir.mkdir() + shutil.copy(vmec_src, locked_dir / "input.DSHAPE") + os.chmod(locked_dir, 0o555) + try: + with pytest.warns(UserWarning): + eq_VMEC = Equilibrium.from_input_file( + str(locked_dir / "input.DSHAPE"), **kwargs + ) + finally: + os.chmod(locked_dir, 0o755) # make sure the loaded eqs are equivalent np.testing.assert_allclose(eq.R_lmn, eq_VMEC.R_lmn) @@ -814,7 +826,7 @@ def test_io_OmnigenousField(tmpdir_factory): @pytest.mark.unit def test_io_file_like_object(tmpdir_factory): - """Test loading an equilibrium from a file-like object (BytesIO)""" + """Test loading an equilibrium from a file-like object (BytesIO).""" file_path = "./tests/inputs/iotest_HELIOTRON.h5" with open(file_path, "rb") as f: file_like_obj = BytesIO(f.read()) From cb504c248c2e0cce55063d01c66d63dff6c91830 Mon Sep 17 00:00:00 2001 From: YigitElma Date: Tue, 31 Mar 2026 00:00:59 -0400 Subject: [PATCH 2/7] update changelog --- CHANGELOG.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 6eaea9cae6..a47371b9ac 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,17 @@ Changelog Bug Fixes +- Fixes the possible permission error in `Equilibrium.from_input_file` method when used with a VMEC input file. + +v0.17.1 +------- + +This patch release updates package dependencies to resolve critical incompatibilities between jax-related libraries. DESC versions 0.16.0 and 0.17.0 contain a known bug when used alongside jax-finufft v1.3.0, and we recommend that users upgrade to DESC version 0.17.1 to ensure environment stability. + +If an immediate upgrade is not feasible, the issue can be mitigated by manually downgrading jax-finufft to version 1.2.0. + +Bug Fixes + - Fixes incorrect units in the documentation of some curvature variables. - Fixes SyntaxError thrown when loading hdf5 data from file-like objects. From a396cd04d17f77a60818cae948d6f65152aaeda7 Mon Sep 17 00:00:00 2001 From: YigitElma Date: Tue, 31 Mar 2026 00:03:49 -0400 Subject: [PATCH 3/7] update changelog --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a47371b9ac..0716ee0f3a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ Changelog Bug Fixes -- Fixes the possible permission error in `Equilibrium.from_input_file` method when used with a VMEC input file. +- Fixes the possible permission error in `Equilibrium.from_input_file` method when used with a VMEC input file. Now the automaticallyt generated DESC input file is written to a temporary file under `./tmp/`. v0.17.1 ------- From 38797a3d991e55e14af99f5b4d3c2343b8a17153 Mon Sep 17 00:00:00 2001 From: YigitElma Date: Tue, 31 Mar 2026 00:07:55 -0400 Subject: [PATCH 4/7] remove redundant file deletions --- tests/test_input_output.py | 5 ----- 1 file changed, 5 deletions(-) diff --git a/tests/test_input_output.py b/tests/test_input_output.py index 212c04ccd4..593bf9a700 100644 --- a/tests/test_input_output.py +++ b/tests/test_input_output.py @@ -238,8 +238,6 @@ def test_near_axis_input_files(): np.testing.assert_allclose( inputs_desc[arg], inputs_vmec[arg], rtol=1e-6, atol=1e-8 ) - if os.path.exists(".//tests//inputs//input.QSC_r2_5.5_vmec_desc"): - os.remove(".//tests//inputs//input.QSC_r2_5.5_vmec_desc") @pytest.mark.unit @@ -316,9 +314,6 @@ def test_from_input_file_equilibrium_desc_vmec(): assert eq_VMEC.iota is None assert eq.sym == eq_VMEC.sym - if os.path.exists(".//tests//inputs//input.QSC_r2_5.5_vmec_desc"): - os.remove(".//tests//inputs//input.QSC_r2_5.5_vmec_desc") - @pytest.mark.unit def test_vmec_input_surface_threshold(): From a1b19a1388a8b3bfa7e929bd1a49c8d1934d8834 Mon Sep 17 00:00:00 2001 From: YigitElma Date: Tue, 31 Mar 2026 00:13:17 -0400 Subject: [PATCH 5/7] update changelog to state other related methods --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0716ee0f3a..b24b7a9524 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,7 +3,7 @@ Changelog Bug Fixes -- Fixes the possible permission error in `Equilibrium.from_input_file` method when used with a VMEC input file. Now the automaticallyt generated DESC input file is written to a temporary file under `./tmp/`. +- Fixes the possible permission error in `from_input_file` method of `Equilibrium`, `FourierRZToroidalSurface` and `FourierRZCurve` classes when used with a VMEC input file. Now the automatically generated DESC input file is written to a temporary file under `./tmp/`. v0.17.1 ------- From ddccfa7c9f85421e053954508169cafeb01968a7 Mon Sep 17 00:00:00 2001 From: YigitElma Date: Tue, 31 Mar 2026 14:21:17 -0400 Subject: [PATCH 6/7] fix test --- tests/test_input_output.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/test_input_output.py b/tests/test_input_output.py index 593bf9a700..4263dbd09a 100644 --- a/tests/test_input_output.py +++ b/tests/test_input_output.py @@ -45,8 +45,6 @@ def test_vmec_input(tmpdir_factory): with pytest.warns(UserWarning): ir = InputReader(cl_args=[str(tmp_path)]) vmec_inputs = ir.inputs - # ir makes a VMEC file automatically - path_converted_file = tmpdir.join("input.DSHAPE_desc") # also test making a DESC file from the ir.inputs manually path = tmpdir.join("desc_from_vmec") ir.write_desc_input(path, ir.inputs) @@ -60,6 +58,12 @@ def test_vmec_input(tmpdir_factory): correct_file_path = ".//tests//inputs//input.DSHAPE_desc" # check DESC input file matches known correct one line-by-line + # above InputReader(cl_args=[str(tmp_path)]) generates a DESC input file + # inside unknown tmp folder, we re create the input file here at a specific + # location, so that we can compare + path_converted_file = tmpdir.join("input.DSHAPE_converted") + with pytest.warns(UserWarning): + InputReader.vmec_to_desc_input(input_path, str(path_converted_file)) with open(correct_file_path) as f: lines_correct = f.readlines() with open(path) as f: From b0a94feaa2fe763cf8bf08203e5711be830bdfdb Mon Sep 17 00:00:00 2001 From: Dario Panici Date: Thu, 9 Jul 2026 12:09:14 -0400 Subject: [PATCH 7/7] use in-memory buffer instead of writing to file when we convert a VMEC to DESC input --- desc/input_reader.py | 22 ++++++++++++++-------- tests/test_input_output.py | 7 ++++--- 2 files changed, 18 insertions(+), 11 deletions(-) diff --git a/desc/input_reader.py b/desc/input_reader.py index faa35de242..9859e1d391 100644 --- a/desc/input_reader.py +++ b/desc/input_reader.py @@ -5,7 +5,6 @@ import os import pathlib import re -import tempfile import warnings from datetime import datetime @@ -216,12 +215,13 @@ def parse_inputs(self, fname=None): # noqa: C901 isVMEC = re.search(r"&INDATA", line, re.IGNORECASE) if isVMEC: print("Converting VMEC input to DESC input") - basename = os.path.basename(self.input_path) + "_desc" - tmpdir = tempfile.mkdtemp() - path = os.path.join(tmpdir, basename) - InputReader.vmec_to_desc_input(self.input_path, path) - print("Generated DESC input file {}:".format(path)) - return self.parse_inputs(path) + # use a buffer here to avoid having to read/write a file + # unnecessarily + buffer = io.StringIO() + InputReader.vmec_to_desc_input(self.input_path, buffer) + buffer.seek(0) + desc_file = buffer + return self.parse_inputs(desc_file) # extract numbers & words match = re.search(r"[!#]", line) @@ -683,9 +683,11 @@ def write_desc_input(filename, inputs, header=""): # noqa: C901 - fxn too compl """ # open the file, unless its already open + opened_here = False if not isinstance(filename, io.IOBase): filename = os.path.expanduser(filename) f = open(filename, "w+") + opened_here = True else: f = filename f.seek(0) @@ -786,7 +788,11 @@ def write_desc_input(filename, inputs, header=""): # noqa: C901 - fxn too compl for n, R0, Z0 in inputs[0]["axis"]: f.write("n: {:3d}\tR0 = {:16.8E}\tZ0 = {:16.8E}\n".format(int(n), R0, Z0)) - f.close() + # only close the file if we opened it here, as we also + # can use this function to write to an already open file + # or to an in-memory buffer + if opened_here: + f.close() @staticmethod def desc_output_to_input( # noqa: C901 - fxn too complex diff --git a/tests/test_input_output.py b/tests/test_input_output.py index 4263dbd09a..66a4602e21 100644 --- a/tests/test_input_output.py +++ b/tests/test_input_output.py @@ -58,8 +58,9 @@ def test_vmec_input(tmpdir_factory): correct_file_path = ".//tests//inputs//input.DSHAPE_desc" # check DESC input file matches known correct one line-by-line - # above InputReader(cl_args=[str(tmp_path)]) generates a DESC input file - # inside unknown tmp folder, we re create the input file here at a specific + # above InputReader(cl_args=[str(tmp_path)]) uses a temp buffer + # instead of the old behavior of writing an intermediate file (#2148) + # so we re-create the input file here at a specific # location, so that we can compare path_converted_file = tmpdir.join("input.DSHAPE_converted") with pytest.warns(UserWarning): @@ -252,7 +253,7 @@ def test_from_input_file_equilibrium_desc_vmec_DSHAPE(tmp_path): with pytest.warns(UserWarning, match="Left handed"): eq = Equilibrium.from_input_file(desc_path, **kwargs) - # load VMEC input from a read-only directory to ensure temp file handling works + # load VMEC input from a read-only directory to ensure temp buffer works # Related to issue #2139 vmec_src = ".//tests//inputs//input.DSHAPE" locked_dir = tmp_path / "locked"