diff --git a/.github/workflows/matrix-and-codecov-on-merge-to-main.yml b/.github/workflows/matrix-and-codecov-on-merge-to-main.yml index c1be0beb..ee58ec41 100644 --- a/.github/workflows/matrix-and-codecov-on-merge-to-main.yml +++ b/.github/workflows/matrix-and-codecov-on-merge-to-main.yml @@ -15,7 +15,7 @@ jobs: uses: Billingegroup/release-scripts/.github/workflows/_matrix-and-codecov-on-merge-to-main.yml@v0 with: project: diffpy.pdffit2 - python_versions: "3.11, 3.12" + python_versions: "3.11, 3.12, 3.13" c_extension: true headless: false secrets: diff --git a/.github/workflows/publish-docs-on-release.yml b/.github/workflows/publish-docs-on-release.yml index be60ce19..a9b26da9 100644 --- a/.github/workflows/publish-docs-on-release.yml +++ b/.github/workflows/publish-docs-on-release.yml @@ -14,4 +14,4 @@ jobs: project: diffpy.pdffit2 c_extension: true headless: false - python_version: 3.12 + python_version: 3.13 diff --git a/.github/workflows/tests-on-pr.yml b/.github/workflows/tests-on-pr.yml index 6d008cdc..198624a4 100644 --- a/.github/workflows/tests-on-pr.yml +++ b/.github/workflows/tests-on-pr.yml @@ -14,6 +14,6 @@ jobs: project: diffpy.pdffi2 c_extension: true headless: false - python_version: 3.12 + python_version: 3.13 secrets: CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} diff --git a/README.rst b/README.rst index fa2c6cc1..35d810db 100644 --- a/README.rst +++ b/README.rst @@ -72,7 +72,7 @@ If you use diffpy.pdffit2 in a scientific publication, we would like you to cite Installation ------------ -diffpy.pdffit2 supports Python 3.11 and 3.12. +diffpy.pdffit2 supports Python 3.11, 3.12, and 3.13. Windows, macOS (non-Arm64), Linux ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -102,7 +102,7 @@ macOS (Arm64) Create a new conda environment ``diffpy.pdffit2_env``: :: - conda create -n diffpy.pdffit2_env python=3.12 + conda create -n diffpy.pdffit2_env python=3.13 Activate the environment: :: @@ -121,7 +121,7 @@ Build from source For advanced users, obtain the source archive, and in the ``diffpy.pdffit2`` directory, run :: - conda create -n diffpy.pdffit2_env python=3.12 \ + conda create -n diffpy.pdffit2_env python=3.13 \ --file requirements/test.txt \ --file requirements/conda.txt \ --file requirements/build.txt diff --git a/news/win_memory.rst b/news/win_memory.rst new file mode 100644 index 00000000..4fc4fdb4 --- /dev/null +++ b/news/win_memory.rst @@ -0,0 +1,24 @@ +**Added:** + +* Added `restore_stdout` function and wrapper. +* Added Python 3.13 support. + +**Changed:** + +* Changed `pytest` `capture_output` fixture. Now automatically restores `sys.stdout`. + +**Deprecated:** + +* + +**Removed:** + +* + +**Fixed:** + +* Fixed `SystemError` when running `pytest` on Windows with Python 3.13. + +**Security:** + +* diff --git a/pyproject.toml b/pyproject.toml index 7df539b9..8a1b15c5 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -14,7 +14,7 @@ maintainers = [ description = "PDFfit2 - real space structure refinement program." keywords = ["PDF", "structure refinement"] readme = "README.rst" -requires-python = ">=3.11, <3.13" +requires-python = ">=3.11, <3.14" classifiers = [ 'Development Status :: 5 - Production/Stable', 'Environment :: Console', @@ -27,6 +27,7 @@ classifiers = [ 'Operating System :: Unix', 'Programming Language :: Python :: 3.11', 'Programming Language :: Python :: 3.12', + 'Programming Language :: Python :: 3.13', 'Topic :: Scientific/Engineering :: Physics', 'Topic :: Scientific/Engineering :: Chemistry', ] diff --git a/src/diffpy/pdffit2/output.py b/src/diffpy/pdffit2/output.py index f24c28e9..9fc55eea 100644 --- a/src/diffpy/pdffit2/output.py +++ b/src/diffpy/pdffit2/output.py @@ -39,4 +39,13 @@ def redirect_stdout(dst): return +def restore_stdout(): + """Restore the standard output.""" + from diffpy.pdffit2.pdffit2 import restore_stdout + + restore_stdout() + global stdout + return + + # End of file diff --git a/src/extensions/pdffit2module/bindings.cc b/src/extensions/pdffit2module/bindings.cc index 568e1dd1..3a542aed 100644 --- a/src/extensions/pdffit2module/bindings.cc +++ b/src/extensions/pdffit2module/bindings.cc @@ -330,6 +330,10 @@ struct PyMethodDef pypdffit2_methods[] = { {pypdffit2_redirect_stdout__name__, pypdffit2_redirect_stdout, METH_VARARGS, pypdffit2_redirect_stdout__doc__}, + //restore_stdout + {pypdffit2_restore_stdout__name__, pypdffit2_restore_stdout, + METH_VARARGS, pypdffit2_restore_stdout__doc__}, + //is_element {pypdffit2_is_element__name__, pypdffit2_is_element, METH_VARARGS, pypdffit2_is_element__doc__}, diff --git a/src/extensions/pdffit2module/misc.cc b/src/extensions/pdffit2module/misc.cc index 17e0833d..20547a23 100644 --- a/src/extensions/pdffit2module/misc.cc +++ b/src/extensions/pdffit2module/misc.cc @@ -2201,6 +2201,36 @@ PyObject * pypdffit2_redirect_stdout(PyObject *, PyObject *args) return Py_None; } +// restore_stdout +char pypdffit2_restore_stdout__doc__[] = + "Restore engine output to the default stream (std::cout)."; +char pypdffit2_restore_stdout__name__[] = + "restore_stdout"; + +PyObject * pypdffit2_restore_stdout(PyObject *, PyObject *args) +{ + // no arguments. + if (!PyArg_ParseTuple(args, "")) + return 0; + + // If the global output stream pointer is not std::cout, then delete the custom stream. + if (NS_PDFFIT2::pout != &std::cout) + { + delete NS_PDFFIT2::pout; + NS_PDFFIT2::pout = &std::cout; + } + + // Clean up the custom stream buffer + if (py_stdout_streambuf) + { + delete py_stdout_streambuf; + py_stdout_streambuf = nullptr; + } + + Py_INCREF(Py_None); + return Py_None; +} + // is_element char pypdffit2_is_element__doc__[] = "Check if element or isotope is defined in the built-in periodic table."; char pypdffit2_is_element__name__[] = "is_element"; diff --git a/src/extensions/pdffit2module/misc.h b/src/extensions/pdffit2module/misc.h index f8782307..2ccec0fa 100644 --- a/src/extensions/pdffit2module/misc.h +++ b/src/extensions/pdffit2module/misc.h @@ -477,6 +477,12 @@ extern char pypdffit2_redirect_stdout__name__[]; extern "C" PyObject * pypdffit2_redirect_stdout(PyObject *, PyObject *); +// restore_stdout +extern char pypdffit2_restore_stdout__doc__[]; +extern char pypdffit2_restore_stdout__name__[]; +extern "C" +PyObject * pypdffit2_restore_stdout(PyObject *, PyObject *); + // is_element extern char pypdffit2_is_element__doc__[]; extern char pypdffit2_is_element__name__[]; diff --git a/tests/conftest.py b/tests/conftest.py index 6a142536..2192452a 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -45,6 +45,7 @@ def _capture(f, *args, **kwargs): f(*args, **kwargs) finally: diffpy.pdffit2.redirect_stdout(savestdout) + diffpy.pdffit2.output.restore_stdout() return fp.getvalue() return _capture