From eae357eaf83c94fea2a1fa903b0b167a80a33820 Mon Sep 17 00:00:00 2001 From: stevenhua0320 Date: Thu, 20 Nov 2025 15:26:17 -0500 Subject: [PATCH 01/11] skpkg: change version build file. --- src/diffpy/__init__.py | 10 ------ src/diffpy/pdffit2/__init__.py | 20 ++---------- src/diffpy/pdffit2/version.py | 57 ++++++---------------------------- 3 files changed, 12 insertions(+), 75 deletions(-) diff --git a/src/diffpy/__init__.py b/src/diffpy/__init__.py index 2efe939..04d8ace 100644 --- a/src/diffpy/__init__.py +++ b/src/diffpy/__init__.py @@ -14,13 +14,3 @@ # See LICENSE.rst for license information. # ############################################################################## -"""diffpy - tools for structure analysis by diffraction. - -Blank namespace package for module diffpy.""" - - -from pkgutil import extend_path - -__path__ = extend_path(__path__, __name__) - -# End of file diff --git a/src/diffpy/pdffit2/__init__.py b/src/diffpy/pdffit2/__init__.py index 527477a..fceee94 100644 --- a/src/diffpy/pdffit2/__init__.py +++ b/src/diffpy/pdffit2/__init__.py @@ -16,24 +16,10 @@ ############################################################################## """PDFfit2 - real space structure refinement program.""" -# WARNING: Do NOT remove the isort: off/on comments in this file. -# These tags are used to prevent isort from reordering imports in this file. -# __version__ must be initialized before importing C++ extensions. +# package version +from diffpy.pdffit2.version import __version__ # noqa -# isort: off -# Import the package version before C++ extensions are loaded. -from diffpy.pdffit2.output import redirect_stdout -from diffpy.pdffit2.version import __date__, __version__ - -# Import C++ related modules since the __version__ attribute is used. -from diffpy.pdffit2.pdffit import PdfFit -from diffpy.pdffit2.pdffit2 import is_element - -# isort: on - -# Ensure all necessary components are imported and initialized +# silence the pyflakes syntax checker assert __version__ or True -assert __date__ or True -assert all((PdfFit, redirect_stdout, is_element)) # End of file diff --git a/src/diffpy/pdffit2/version.py b/src/diffpy/pdffit2/version.py index 8615928..0ade395 100644 --- a/src/diffpy/pdffit2/version.py +++ b/src/diffpy/pdffit2/version.py @@ -7,59 +7,20 @@ # File coded by: Billinge Group members and community contributors. # # See GitHub contributions for a more detailed list of contributors. -# https://github.com/diffpy/diffpy.pdffit2/graphs/contributors +# https://github.com/diffpy/diffpy.pdffit2/graphs/contributors # noqa: E501 # # See LICENSE.rst for license information. # ############################################################################## """Definition of __version__.""" -import datetime -import json -import urllib.request -from importlib.metadata import version -from pathlib import Path +# We do not use the other three variables, but can be added back if needed. +# __all__ = ["__date__", "__git_commit__", "__timestamp__", "__version__"] +# obtain version information +from importlib.metadata import PackageNotFoundError, version -def get_pypi_release_date(package_name, timeout=5): - package_file = Path(__file__).resolve() - - try: - with open(package_file, "r", encoding="utf-8") as f: - lines = f.readlines() - for line in reversed(lines): - if line.startswith("# Release date:"): - return line.split(":", 1)[1].strip() - - url = f"https://pypi.org/pypi/{package_name}/json" - with urllib.request.urlopen(url, timeout=timeout) as response: - data = json.loads(response.read().decode("utf-8")) - - installed_version = version(package_name) - release_data = data["releases"].get(installed_version, []) - if not release_data: - raise ValueError( - f"No release data found for version {installed_version}" - ) - - release_date_str = release_data[-1]["upload_time"] - release_date = datetime.datetime.fromisoformat(release_date_str).date() - - with open(package_file, "a", encoding="utf-8") as f: - f.write(f"\n# Release date: {release_date}") - - except (ValueError, OSError) as e: - print(f"Warning: Could not fetch release date from PyPI: {e}") - release_date = datetime.datetime.fromtimestamp( - package_file.stat().st_ctime - ).isoformat() - - return str(release_date) - - -__version__ = version("diffpy.pdffit2") -__date__ = get_pypi_release_date("diffpy.pdffit2") - -# End of file - -# Release date: 2025-02-07 +try: + __version__ = version("diffpy.pdffit2") +except PackageNotFoundError: + __version__ = "unknown" From 8b93472f93ce0f75a24a1f1539acae2eb3e8b23d Mon Sep 17 00:00:00 2001 From: stevenhua0320 Date: Thu, 20 Nov 2025 20:26:12 -0500 Subject: [PATCH 02/11] skpkg: package update command with no manual edits. --- setup.py | 197 +++------------------------------------------- tests/conftest.py | 31 -------- 2 files changed, 12 insertions(+), 216 deletions(-) diff --git a/setup.py b/setup.py index 9a334a8..9c1e69c 100644 --- a/setup.py +++ b/setup.py @@ -1,199 +1,27 @@ #!/usr/bin/env python # Extensions script for diffpy.pdffit2 -"""PDFfit2 - real space structure refinement engine - -Packages: diffpy.pdffit2 -Scripts: pdffit2 -""" import glob -import os -import re -import shutil -import sys -import warnings -from pathlib import Path +import os # noqa +import re # noqa +import sys # noqa from setuptools import Extension, setup -from setuptools.command.build_ext import build_ext - -# Use this version when git data are not available, like in git zip archive. -# Update when tagging a new release. -FALLBACK_VERSION = "1.4.3" - -MYDIR = str(Path(__file__).parent.resolve()) - -# Helper functions ----------------------------------------------------------- - - -def get_compiler_type(): - """Return the compiler type used during the build.""" - cc_arg = [a for a in sys.argv if a.startswith("--compiler=")] - if cc_arg: - return cc_arg[-1].split("=", 1)[1] - from distutils.ccompiler import new_compiler - - return new_compiler().compiler_type - - -def get_gsl_config(): - """ - Determine the GSL include and library directories by trying in order: - 1) CONDA_PREFIX, - 2) GSL_PATH, - 3) gsl-config (for Unix-like systems). - Raises EnvironmentError if none are found. - """ - rv = {"include_dirs": [], "library_dirs": []} - - # 1. Check using CONDA_PREFIX. - conda_prefix = os.environ.get("CONDA_PREFIX", "") - if conda_prefix: - if os.name == "nt": - inc = Path(conda_prefix) / "Library" / "include" - lib = Path(conda_prefix) / "Library" / "lib" - else: - inc = Path(conda_prefix) / "include" - lib = Path(conda_prefix) / "lib" - if inc.is_dir() and lib.is_dir(): - rv["include_dirs"].append(str(inc)) - rv["library_dirs"].append(str(lib)) - return rv - else: - warnings.warn( - f"CONDA_PREFIX is set to {conda_prefix}, " - "but GSL not found at those paths. Proceeding..." - ) - # 2. Check using GSL_PATH. - gsl_path = os.environ.get("GSL_PATH", "") - if gsl_path: - inc = Path(gsl_path) / "include" - lib = Path(gsl_path) / "lib" - if inc.is_dir() and lib.is_dir(): - rv["include_dirs"].append(str(inc)) - rv["library_dirs"].append(str(lib)) - return rv - else: - raise EnvironmentError( - f"GSL_PATH={gsl_path} is set, but {inc} or {lib} not found. " - "Please verify your GSL_PATH." - ) - - # 3. Try using the gsl-config executable (only on Unix-like systems). - if os.name != "nt": - path_dirs = os.environ.get("PATH", "").split(os.pathsep) - gslcfg_paths = [Path(p) / "gsl-config" for p in path_dirs if p] - gslcfg_paths = [p for p in gslcfg_paths if p.is_file()] - if gslcfg_paths: - gslcfg = gslcfg_paths[0] - txt = gslcfg.read_text() - prefix_match = re.search(r"(?m)^prefix=(.+)", txt) - include_match = re.search(r"(?m)^[^#]*\s-I(\S+)", txt) - lib_match = re.search(r"(?m)^[^#]*\s-L(\S+)", txt) - if prefix_match: - prefix_path = Path(prefix_match.group(1)) - inc_dir = ( - include_match.group(1) - if include_match - else (prefix_path / "include") - ) - lib_dir = ( - lib_match.group(1) if lib_match else (prefix_path / "lib") - ) - rv["include_dirs"].append(str(inc_dir)) - rv["library_dirs"].append(str(lib_dir)) - return rv - else: - raise RuntimeError(f"Cannot parse 'prefix=' from {gslcfg}.") - else: - warnings.warn( - "No gsl-config found in PATH. GSL may not be installed or not in PATH. " - "Proceeding without GSL configuration." - ) - - # 4. Nothing found: raise error. - raise EnvironmentError( - "Unable to locate GSL:\n" - "1) CONDA_PREFIX not set or no GSL there\n" - "2) GSL_PATH not set or invalid\n" - "3) gsl-config not available\n" - "Please set GSL_PATH or use a conda environment with GSL." - ) - - -class CustomBuildExt(build_ext): - def run(self): - # Retrieve the GSL library directories and append them to each extension. - gsl_cfg = get_gsl_config() - lib_dirs = gsl_cfg.get("library_dirs", []) - for ext in self.extensions: - # Add gsl lib for linking. - ext.library_dirs.extend(lib_dirs) - # Embed RPATH flags, runtime linking without LD_LIBRARY_PATH. - ext.extra_link_args = ext.extra_link_args or [] - for lib in lib_dirs: - ext.extra_link_args.append(f"-Wl,-rpath,{lib}") - super().run() - # Avoid dll error - gsl_path = ( - Path(os.environ.get("GSL_PATH")) - if os.environ.get("GSL_PATH") - else Path(os.environ.get("CONDA_PREFIX", "")) / "Library" - ) - bin_path = gsl_path / "bin" - dest_path = Path(self.build_lib) / "diffpy" / "pdffit2" - dest_path.mkdir(parents=True, exist_ok=True) - for dll_file in bin_path.glob("gsl*.dll"): - shutil.copy(str(dll_file), str(dest_path)) +# Define extension arguments here +ext_kws = { + "libraries": [], + "extra_compile_args": [], + "extra_link_args": [], + "include_dirs": [], +} def create_extensions(): - """Create the list of Extension objects for the build.""" - # lazy evaluation prevents build sdist failure - try: - gcfg = get_gsl_config() - except EnvironmentError: - return [] - - libraries = ["gsl"] - - include_dirs = [MYDIR] + gcfg["include_dirs"] - library_dirs = gcfg["library_dirs"] - define_macros = [] - extra_objects = [] - extra_compile_args = [] - extra_link_args = [] - - compiler_type = get_compiler_type() - if compiler_type in ("unix", "cygwin", "mingw32"): - extra_compile_args = [ - "-std=c++11", - "-Wall", - "-Wno-write-strings", - "-O3", - "-funroll-loops", - "-ffast-math", - ] - elif compiler_type == "msvc": - define_macros += [("_USE_MATH_DEFINES", None)] - extra_compile_args = ["/EHs"] - - # Extension keyword arguments. - ext_kws = { - "include_dirs": include_dirs, - "libraries": libraries, - "library_dirs": library_dirs, - "define_macros": define_macros, - "extra_compile_args": extra_compile_args, - "extra_link_args": extra_link_args, - "extra_objects": extra_objects, - } + "Initialize Extension objects for the setup function." ext = Extension( - "diffpy.pdffit2.pdffit2", - glob.glob("src/extensions/**/*.cc"), - **ext_kws, + "diffpy.pdffit2.pdffit2", glob.glob("src/extensions/*.cpp"), **ext_kws ) return [ext] @@ -201,7 +29,6 @@ def create_extensions(): # Extensions not included in pyproject.toml setup_args = dict( ext_modules=[], - cmdclass={"build_ext": CustomBuildExt}, ) diff --git a/tests/conftest.py b/tests/conftest.py index 6a14253..e3b6313 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,12 +1,8 @@ -import io import json from pathlib import Path import pytest -import diffpy.pdffit2 -import diffpy.pdffit2.output # assuming this is the correct import path - @pytest.fixture def user_filesystem(tmp_path): @@ -21,30 +17,3 @@ def user_filesystem(tmp_path): json.dump(home_config_data, f) yield tmp_path - - -@pytest.fixture -def datafile(): - """Fixture to dynamically load any test file.""" - - def _load(filename): - return "tests/testdata/" + filename - - return _load - - -@pytest.fixture -def capture_output(): - """Capture output from pdffit2 engine produced in function call.""" - - def _capture(f, *args, **kwargs): - savestdout = diffpy.pdffit2.output.stdout - fp = io.StringIO() - diffpy.pdffit2.redirect_stdout(fp) - try: - f(*args, **kwargs) - finally: - diffpy.pdffit2.redirect_stdout(savestdout) - return fp.getvalue() - - return _capture From f7bc156cc7997be7f311034162ccd6328deb1cd4 Mon Sep 17 00:00:00 2001 From: stevenhua0320 Date: Fri, 21 Nov 2025 13:12:09 -0500 Subject: [PATCH 03/11] fix: revert setup.py and conftest.py to original --- setup.py | 192 +++++++++++++++++++++++++++++++++++++++++++--- tests/conftest.py | 31 ++++++++ 2 files changed, 211 insertions(+), 12 deletions(-) diff --git a/setup.py b/setup.py index 9c1e69c..d808441 100644 --- a/setup.py +++ b/setup.py @@ -3,25 +3,192 @@ # Extensions script for diffpy.pdffit2 import glob -import os # noqa -import re # noqa -import sys # noqa +import os +import re +import shutil +import sys +import warnings +from pathlib import Path from setuptools import Extension, setup +from setuptools.command.build_ext import build_ext -# Define extension arguments here -ext_kws = { - "libraries": [], - "extra_compile_args": [], - "extra_link_args": [], - "include_dirs": [], -} +# Use this version when git data are not available, like in git zip archive. +# Update when tagging a new release. +FALLBACK_VERSION = "1.4.3" + +MYDIR = str(Path(__file__).parent.resolve()) + +# Helper functions ----------------------------------------------------------- + + +def get_compiler_type(): + """Return the compiler type used during the build.""" + cc_arg = [a for a in sys.argv if a.startswith("--compiler=")] + if cc_arg: + return cc_arg[-1].split("=", 1)[1] + from distutils.ccompiler import new_compiler + + return new_compiler().compiler_type + + +def get_gsl_config(): + """ + Determine the GSL include and library directories by trying in order: + 1) CONDA_PREFIX, + 2) GSL_PATH, + 3) gsl-config (for Unix-like systems). + Raises EnvironmentError if none are found. + """ + rv = {"include_dirs": [], "library_dirs": []} + + # 1. Check using CONDA_PREFIX. + conda_prefix = os.environ.get("CONDA_PREFIX", "") + if conda_prefix: + if os.name == "nt": + inc = Path(conda_prefix) / "Library" / "include" + lib = Path(conda_prefix) / "Library" / "lib" + else: + inc = Path(conda_prefix) / "include" + lib = Path(conda_prefix) / "lib" + if inc.is_dir() and lib.is_dir(): + rv["include_dirs"].append(str(inc)) + rv["library_dirs"].append(str(lib)) + return rv + else: + warnings.warn( + f"CONDA_PREFIX is set to {conda_prefix}, " + "but GSL not found at those paths. Proceeding..." + ) + + # 2. Check using GSL_PATH. + gsl_path = os.environ.get("GSL_PATH", "") + if gsl_path: + inc = Path(gsl_path) / "include" + lib = Path(gsl_path) / "lib" + if inc.is_dir() and lib.is_dir(): + rv["include_dirs"].append(str(inc)) + rv["library_dirs"].append(str(lib)) + return rv + else: + raise EnvironmentError( + f"GSL_PATH={gsl_path} is set, but {inc} or {lib} not found. " + "Please verify your GSL_PATH." + ) + + # 3. Try using the gsl-config executable (only on Unix-like systems). + if os.name != "nt": + path_dirs = os.environ.get("PATH", "").split(os.pathsep) + gslcfg_paths = [Path(p) / "gsl-config" for p in path_dirs if p] + gslcfg_paths = [p for p in gslcfg_paths if p.is_file()] + if gslcfg_paths: + gslcfg = gslcfg_paths[0] + txt = gslcfg.read_text() + prefix_match = re.search(r"(?m)^prefix=(.+)", txt) + include_match = re.search(r"(?m)^[^#]*\s-I(\S+)", txt) + lib_match = re.search(r"(?m)^[^#]*\s-L(\S+)", txt) + if prefix_match: + prefix_path = Path(prefix_match.group(1)) + inc_dir = ( + include_match.group(1) + if include_match + else (prefix_path / "include") + ) + lib_dir = ( + lib_match.group(1) if lib_match else (prefix_path / "lib") + ) + rv["include_dirs"].append(str(inc_dir)) + rv["library_dirs"].append(str(lib_dir)) + return rv + else: + raise RuntimeError(f"Cannot parse 'prefix=' from {gslcfg}.") + else: + warnings.warn( + "No gsl-config found in PATH. GSL may not be installed or not in PATH. " + "Proceeding without GSL configuration." + ) + + # 4. Nothing found: raise error. + raise EnvironmentError( + "Unable to locate GSL:\n" + "1) CONDA_PREFIX not set or no GSL there\n" + "2) GSL_PATH not set or invalid\n" + "3) gsl-config not available\n" + "Please set GSL_PATH or use a conda environment with GSL." + ) + + +class CustomBuildExt(build_ext): + def run(self): + # Retrieve the GSL library directories and append them to each extension. + gsl_cfg = get_gsl_config() + lib_dirs = gsl_cfg.get("library_dirs", []) + for ext in self.extensions: + # Add gsl lib for linking. + ext.library_dirs.extend(lib_dirs) + # Embed RPATH flags, runtime linking without LD_LIBRARY_PATH. + ext.extra_link_args = ext.extra_link_args or [] + for lib in lib_dirs: + ext.extra_link_args.append(f"-Wl,-rpath,{lib}") + super().run() + # Avoid dll error + gsl_path = ( + Path(os.environ.get("GSL_PATH")) + if os.environ.get("GSL_PATH") + else Path(os.environ.get("CONDA_PREFIX", "")) / "Library" + ) + bin_path = gsl_path / "bin" + dest_path = Path(self.build_lib) / "diffpy" / "pdffit2" + dest_path.mkdir(parents=True, exist_ok=True) + for dll_file in bin_path.glob("gsl*.dll"): + shutil.copy(str(dll_file), str(dest_path)) def create_extensions(): - "Initialize Extension objects for the setup function." + """Create the list of Extension objects for the build.""" + # lazy evaluation prevents build sdist failure + try: + gcfg = get_gsl_config() + except EnvironmentError: + return [] + + libraries = ["gsl"] + + include_dirs = [MYDIR] + gcfg["include_dirs"] + library_dirs = gcfg["library_dirs"] + define_macros = [] + extra_objects = [] + extra_compile_args = [] + extra_link_args = [] + + compiler_type = get_compiler_type() + if compiler_type in ("unix", "cygwin", "mingw32"): + extra_compile_args = [ + "-std=c++11", + "-Wall", + "-Wno-write-strings", + "-O3", + "-funroll-loops", + "-ffast-math", + ] + elif compiler_type == "msvc": + define_macros += [("_USE_MATH_DEFINES", None)] + extra_compile_args = ["/EHs"] + + # Extension keyword arguments. + ext_kws = { + "include_dirs": include_dirs, + "libraries": libraries, + "library_dirs": library_dirs, + "define_macros": define_macros, + "extra_compile_args": extra_compile_args, + "extra_link_args": extra_link_args, + "extra_objects": extra_objects, + } ext = Extension( - "diffpy.pdffit2.pdffit2", glob.glob("src/extensions/*.cpp"), **ext_kws + "diffpy.pdffit2.pdffit2", + glob.glob("src/extensions/**/*.cc"), + **ext_kws, ) return [ext] @@ -29,6 +196,7 @@ def create_extensions(): # Extensions not included in pyproject.toml setup_args = dict( ext_modules=[], + cmdclass={"build_ext": CustomBuildExt}, ) diff --git a/tests/conftest.py b/tests/conftest.py index e3b6313..6a14253 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -1,8 +1,12 @@ +import io import json from pathlib import Path import pytest +import diffpy.pdffit2 +import diffpy.pdffit2.output # assuming this is the correct import path + @pytest.fixture def user_filesystem(tmp_path): @@ -17,3 +21,30 @@ def user_filesystem(tmp_path): json.dump(home_config_data, f) yield tmp_path + + +@pytest.fixture +def datafile(): + """Fixture to dynamically load any test file.""" + + def _load(filename): + return "tests/testdata/" + filename + + return _load + + +@pytest.fixture +def capture_output(): + """Capture output from pdffit2 engine produced in function call.""" + + def _capture(f, *args, **kwargs): + savestdout = diffpy.pdffit2.output.stdout + fp = io.StringIO() + diffpy.pdffit2.redirect_stdout(fp) + try: + f(*args, **kwargs) + finally: + diffpy.pdffit2.redirect_stdout(savestdout) + return fp.getvalue() + + return _capture From a8689bf450fcc6343a3958b30601c10c291bdc82 Mon Sep 17 00:00:00 2001 From: stevenhua0320 Date: Fri, 21 Nov 2025 13:52:20 -0500 Subject: [PATCH 04/11] chore: add docstring back to setup.py --- setup.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/setup.py b/setup.py index d808441..9a334a8 100644 --- a/setup.py +++ b/setup.py @@ -1,6 +1,11 @@ #!/usr/bin/env python # Extensions script for diffpy.pdffit2 +"""PDFfit2 - real space structure refinement engine + +Packages: diffpy.pdffit2 +Scripts: pdffit2 +""" import glob import os From 7293c68d63c908fbc550f12700a18535f85c91a6 Mon Sep 17 00:00:00 2001 From: stevenhua0320 Date: Fri, 21 Nov 2025 16:04:35 -0500 Subject: [PATCH 05/11] fix: change import directory --- tests/test_exceptions.py | 3 ++- tests/test_pdffit.py | 3 ++- tests/test_phase_fractions.py | 2 +- tests/test_shape_factors.py | 3 ++- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/tests/test_exceptions.py b/tests/test_exceptions.py index 83f2b37..07a4f4e 100644 --- a/tests/test_exceptions.py +++ b/tests/test_exceptions.py @@ -18,7 +18,8 @@ import pytest -from diffpy.pdffit2 import PdfFit, pdffit2 +from diffpy.pdffit2 import pdffit2 +from diffpy.pdffit2.pdffit import PdfFit class read_structExceptions(unittest.TestCase): diff --git a/tests/test_pdffit.py b/tests/test_pdffit.py index 7336f0e..54b1a1c 100644 --- a/tests/test_pdffit.py +++ b/tests/test_pdffit.py @@ -6,7 +6,8 @@ import pytest -from diffpy.pdffit2 import PdfFit, pdffit2 +from diffpy.pdffit2 import pdffit2 +from diffpy.pdffit2.pdffit import PdfFit from diffpy.structure import loadStructure # ---------------------------------------------------------------------------- diff --git a/tests/test_phase_fractions.py b/tests/test_phase_fractions.py index ac33ec6..c86961e 100644 --- a/tests/test_phase_fractions.py +++ b/tests/test_phase_fractions.py @@ -6,7 +6,7 @@ import pytest -from diffpy.pdffit2 import PdfFit +from diffpy.pdffit2.pdffit import PdfFit ############################################################################## diff --git a/tests/test_shape_factors.py b/tests/test_shape_factors.py index 110ce55..70424e5 100644 --- a/tests/test_shape_factors.py +++ b/tests/test_shape_factors.py @@ -8,7 +8,8 @@ import numpy import pytest -from diffpy.pdffit2 import PdfFit, pdffit2 +from diffpy.pdffit2 import pdffit2 +from diffpy.pdffit2.pdffit import PdfFit def spherefactor(r, d): From 3acf21ea80f8cb4d8ab450f4d4743ef42431c1da Mon Sep 17 00:00:00 2001 From: stevenhua0320 Date: Fri, 21 Nov 2025 17:34:12 -0500 Subject: [PATCH 06/11] fix: revert the get release date function. --- src/diffpy/pdffit2/__init__.py | 2 +- src/diffpy/pdffit2/version.py | 44 ++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+), 1 deletion(-) diff --git a/src/diffpy/pdffit2/__init__.py b/src/diffpy/pdffit2/__init__.py index fceee94..2e143ec 100644 --- a/src/diffpy/pdffit2/__init__.py +++ b/src/diffpy/pdffit2/__init__.py @@ -17,7 +17,7 @@ """PDFfit2 - real space structure refinement program.""" # package version -from diffpy.pdffit2.version import __version__ # noqa +from diffpy.pdffit2.version import __date__, __version__ # noqa # silence the pyflakes syntax checker assert __version__ or True diff --git a/src/diffpy/pdffit2/version.py b/src/diffpy/pdffit2/version.py index 0ade395..7a1eaaa 100644 --- a/src/diffpy/pdffit2/version.py +++ b/src/diffpy/pdffit2/version.py @@ -17,8 +17,52 @@ # We do not use the other three variables, but can be added back if needed. # __all__ = ["__date__", "__git_commit__", "__timestamp__", "__version__"] +import datetime +import json +import urllib.request + # obtain version information from importlib.metadata import PackageNotFoundError, version +from pathlib import Path + + +def get_pypi_release_date(package_name, timeout=5): + package_file = Path(__file__).resolve() + + try: + with open(package_file, "r", encoding="utf-8") as f: + lines = f.readlines() + for line in reversed(lines): + if line.startswith("# Release date:"): + return line.split(":", 1)[1].strip() + + url = f"https://pypi.org/pypi/{package_name}/json" + with urllib.request.urlopen(url, timeout=timeout) as response: + data = json.loads(response.read().decode("utf-8")) + + installed_version = version(package_name) + release_data = data["releases"].get(installed_version, []) + if not release_data: + raise ValueError( + f"No release data found for version {installed_version}" + ) + + release_date_str = release_data[-1]["upload_time"] + release_date = datetime.datetime.fromisoformat(release_date_str).date() + + with open(package_file, "a", encoding="utf-8") as f: + f.write(f"\n# Release date: {release_date}") + + except (ValueError, OSError) as e: + print(f"Warning: Could not fetch release date from PyPI: {e}") + release_date = datetime.datetime.fromtimestamp( + package_file.stat().st_ctime + ).isoformat() + + return str(release_date) + + +__date__ = get_pypi_release_date("diffpy.pdffit2") try: __version__ = version("diffpy.pdffit2") From 3ec420e41009ed61fd8147c533d15ddce13b24d7 Mon Sep 17 00:00:00 2001 From: stevenhua0320 Date: Fri, 21 Nov 2025 17:43:04 -0500 Subject: [PATCH 07/11] revert the import of redirect_stdout --- src/diffpy/pdffit2/__init__.py | 1 + 1 file changed, 1 insertion(+) diff --git a/src/diffpy/pdffit2/__init__.py b/src/diffpy/pdffit2/__init__.py index 2e143ec..048588e 100644 --- a/src/diffpy/pdffit2/__init__.py +++ b/src/diffpy/pdffit2/__init__.py @@ -18,6 +18,7 @@ # package version from diffpy.pdffit2.version import __date__, __version__ # noqa +from diffpy.pdffit2.output import redirect_stdout # silence the pyflakes syntax checker assert __version__ or True From 07635d0d1581c4e207de965f46ea1e92b17ae973 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 21 Nov 2025 22:43:22 +0000 Subject: [PATCH 08/11] [pre-commit.ci] auto fixes from pre-commit hooks --- src/diffpy/pdffit2/__init__.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/diffpy/pdffit2/__init__.py b/src/diffpy/pdffit2/__init__.py index 048588e..f6f617f 100644 --- a/src/diffpy/pdffit2/__init__.py +++ b/src/diffpy/pdffit2/__init__.py @@ -16,9 +16,10 @@ ############################################################################## """PDFfit2 - real space structure refinement program.""" +from diffpy.pdffit2.output import redirect_stdout + # package version from diffpy.pdffit2.version import __date__, __version__ # noqa -from diffpy.pdffit2.output import redirect_stdout # silence the pyflakes syntax checker assert __version__ or True From ce191940c74e614ff0d58b91fef23bc16c7cdcfd Mon Sep 17 00:00:00 2001 From: stevenhua0320 Date: Fri, 21 Nov 2025 17:47:38 -0500 Subject: [PATCH 09/11] chore: suppress flake8 since it is used in C++ --- src/diffpy/pdffit2/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/diffpy/pdffit2/__init__.py b/src/diffpy/pdffit2/__init__.py index f6f617f..1b81823 100644 --- a/src/diffpy/pdffit2/__init__.py +++ b/src/diffpy/pdffit2/__init__.py @@ -16,7 +16,7 @@ ############################################################################## """PDFfit2 - real space structure refinement program.""" -from diffpy.pdffit2.output import redirect_stdout +from diffpy.pdffit2.output import redirect_stdout # noqa # package version from diffpy.pdffit2.version import __date__, __version__ # noqa From 29005904ac9793d53ddf78284f1ea0e56d2af9a2 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Fri, 21 Nov 2025 22:47:57 +0000 Subject: [PATCH 10/11] [pre-commit.ci] auto fixes from pre-commit hooks --- src/diffpy/pdffit2/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/diffpy/pdffit2/__init__.py b/src/diffpy/pdffit2/__init__.py index 1b81823..f22d9cb 100644 --- a/src/diffpy/pdffit2/__init__.py +++ b/src/diffpy/pdffit2/__init__.py @@ -16,7 +16,7 @@ ############################################################################## """PDFfit2 - real space structure refinement program.""" -from diffpy.pdffit2.output import redirect_stdout # noqa +from diffpy.pdffit2.output import redirect_stdout # noqa # package version from diffpy.pdffit2.version import __date__, __version__ # noqa From d352c0964b621f964d50568099396a081acac24a Mon Sep 17 00:00:00 2001 From: stevenhua0320 Date: Fri, 21 Nov 2025 23:56:02 -0500 Subject: [PATCH 11/11] fix: revert original code to import --- src/diffpy/pdffit2/__init__.py | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/src/diffpy/pdffit2/__init__.py b/src/diffpy/pdffit2/__init__.py index f22d9cb..527477a 100644 --- a/src/diffpy/pdffit2/__init__.py +++ b/src/diffpy/pdffit2/__init__.py @@ -16,12 +16,24 @@ ############################################################################## """PDFfit2 - real space structure refinement program.""" -from diffpy.pdffit2.output import redirect_stdout # noqa +# WARNING: Do NOT remove the isort: off/on comments in this file. +# These tags are used to prevent isort from reordering imports in this file. +# __version__ must be initialized before importing C++ extensions. -# package version -from diffpy.pdffit2.version import __date__, __version__ # noqa +# isort: off +# Import the package version before C++ extensions are loaded. +from diffpy.pdffit2.output import redirect_stdout +from diffpy.pdffit2.version import __date__, __version__ -# silence the pyflakes syntax checker +# Import C++ related modules since the __version__ attribute is used. +from diffpy.pdffit2.pdffit import PdfFit +from diffpy.pdffit2.pdffit2 import is_element + +# isort: on + +# Ensure all necessary components are imported and initialized assert __version__ or True +assert __date__ or True +assert all((PdfFit, redirect_stdout, is_element)) # End of file