Skip to content

Conversation

@cadenmyers13
Copy link
Contributor

The other deprecator didnt match the API for python 3.13. This fixes that. I tested this in python 3.12 and 3.13 envs and it worked as expected (using debugging print statements).

@cadenmyers13
Copy link
Contributor Author

@sbillinge ready for review

@codecov
Copy link

codecov bot commented Dec 9, 2025

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (1033143) to head (3b7b13b).
⚠️ Report is 9 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff            @@
##              main      #366   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files            9         9           
  Lines          520       520           
=========================================
  Hits           520       520           
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@sbillinge sbillinge merged commit 0db2da1 into diffpy:main Dec 10, 2025
7 checks passed
@cadenmyers13 cadenmyers13 deleted the deprecated_update branch December 11, 2025 15:34
@cadenmyers13
Copy link
Contributor Author

cadenmyers13 commented Dec 11, 2025

Directions for deprecating functions

This is step-by-step directions for deprecating an old method or class using the @deprecated decorator.

We are doing this because lots of methods/functions using camel case (ie. myFunction()) which is not the standard. The standard is to use snake case (ie. my_function()).

Additionally, we are refactoring diffpy.srfit to break out its modules into separate packages, meaning where a class is imported from will change (ie from diffpy.srfit.pdf import BasePDFGenerator will change to from diffpy.cmipdf import BasePDFGenerator. The new package created here is diffpy.cmipdf). This will break peoples scripts, therefore we want to deprecate these things.

Here are instructions to do so!!

Deprecating a function/method

First, you will have to locate method or function that needs to be deprecated. For this example, I will be using the setProfile() method from diffpy.srfit. This method is a part of the FitContribution class located under src/diffpy/srfit/fitbase/fitcontribution.py.

1) Write a private function for the deprecation message

To make the deprecation methods consistent, its helpful to write a little function that prints the same message. We want the message to let the user know what is going to be broken in the future, what version it will be broken in, and what its new name will be.

Because these changes are API-breaking, The removal will be made on a version bump of the first number (ie. going from version 3.2.0 to version 4.0.0 or something like that).

This is what that function will look like for our current example:

removal_version = "4.0.0"
def _dep_msg_fitcontrib(old_name, new_name, version=removal_version):
    msg = (
        f"'diffpy.srfit.fitbase.fitcontribution.FitContribution.{old_name}' "
        f"is deprecated and will be removed in version {version}. Please use "
        f"'diffpy.srfit.fitbase.fitcontribution.FitContribution.{new_name}' "
        "instead."
    )
    return msg

Now i can reuse this throughout this file very easily!

2) import the deprecator

As mentioned, I will be deprecating the setProfile() method and changing it to set_profile(). In Python 3.13, there is a built-in deprecation decorator. Since our packages support Python 3.12 still, we have to import a custom deprecator from diffpy.utils. Once we drop support for Python 3.12 (likely next year), we won't have to do this import. The API of the custom deprecator and the built-in one are identical so this will work for any python version. To import it, type

from diffpy.utils._deprecator import deprecated

3) Change name of deprecated function

Once imported, go to the method that needs to be deprecated, for me, this method looks like this currently,

    def setProfile(self, profile, xname=None, yname=None, dyname=None):
        """Assign the Profile for this FitContribution.
        """
        # Enforce type of the profile argument
        if not isinstance(profile, Profile):
            emsg = "Argument must be an instance of the Profile class."
            raise TypeError(emsg)

        # Set the Profile and add its parameters to this organizer.
        self.profile = profile

        if xname is None:
            xname = self.profile.xpar.name
        if yname is None:
            yname = self.profile.ypar.name
        if dyname is None:
            dyname = self.profile.dypar.name

        self._xname = xname
        self._yname = yname
        self._dyname = dyname

        xpar = ParameterProxy(xname, self.profile.xpar)
        ypar = ParameterProxy(yname, self.profile.ypar)
        dypar = ParameterProxy(dyname, self.profile.dypar)
        self.addParameter(xpar, check=False)
        self.addParameter(ypar, check=False)
        self.addParameter(dypar, check=False)

        # If we have ProfileGenerators, set their Profiles.
        for gen in self._generators.values():
            gen.setProfile(profile)

        # If we have _eq, but not _reseq, set the residual
        if self._eq is not None and self._reseq is None:
            self.setResidualEquation("chiv")

        return

To deprecate it, I will change this functions name from setProfile to set_profile. At the same time, I will add a new function directly above it with the old name that points to the new function name. This will look like,

    def setProfile(self, profile, xname=None, yname=None, dyname=None):
        """Deprecated.

        Use set_profile instead.
        """
        return self.set_profile(
            profile, xname=xname, yname=yname, dyname=dyname
        )

    def set_profile(self, profile, xname=None, yname=None, dyname=None):
        """Assign the Profile for this FitContribution.

        ....

4) Use the @deprecated decorator to mark as deprecated

Lastly, to finally do the deprecation, we will use the @deprecated decorator, this will throw the warning message when a user tries to use the deprecated function. To add it, add the following directly above the function with the old name. We will use our helper function right here,

    @deprecated(_dep_msg_fitcontrib("setProfile", "set_profile"))
    def setProfile(self, profile, xname=None, yname=None, dyname=None):
        """Deprecated.

        Use set_profile instead.
        """
        return self.set_profile(
            profile, xname=xname, yname=yname, dyname=dyname
        )

5) global search to replace the old function name with the new one

Lastly, to a case-sensitive global search of the old function name and replace all occurrences with the new name EXCEPT THE FUNCTION THAT WILL THROW THE DEPRECATION WARNING. Pre-commit should catch this redefinition though if you accidentally replace it.

This is very important because if you don't do that, the user will see the deprecation warnings every time the function is called by the code, even if its not called by them.

final outcome

Now, when the user tries to use the setProfile method in a script, it will print the following deprecation message, letting users know what will be removed in the future.

$ python fitBulkNi.py
/diffpy.cmi/docs/examples/pdf/ch03NiModelling/solutions/diffpy-cmi/fitBulkNi.py:151: DeprecationWarning: 'diffpy.srfit.fitbase.fitcontribution.FitContribution.setProfile' is deprecated and will be removed in version 4.0.0. Please use 'diffpy.srfit.fitbase.fitcontribution.FitContribution.set_profile' instead.

@cadenmyers13
Copy link
Contributor Author

@sbillinge I wrote instructions (see above) on how to deprecate things. I tested this and it works too. I think we can begin deprecating and assigning people to deprecation tasks. We can link them to here or put this information in a different/better spot.

@sbillinge
Copy link
Contributor

Let's move this to mission-control. I will create a new repo there called operation-deprecate-everything

This could be a good job for new students at UCSB. Get them learning forking workflow and group standards.....

@cadenmyers13
Copy link
Contributor Author

@sbillinge Perfect, thats exactly what I was thinking. Let me know when its live.

@sbillinge
Copy link
Contributor

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants