Repository: scipy/scipy
File: scipy/stats/_distribution_infrastructure.py (Python)

Code before:

    support_etc = _combine_docs(CustomDistribution, include_examples=False).lstrip()
    docs = [
        f"This class represents `scipy.stats.{dist.name}` as a subclass of "
        "`ContinuousDistribution`.",
        f"The `repr`/`str` of class instances is `{repr_str}`.",
        f"The PDF of the distribution is defined {support_etc}"
    ]
    CustomDistribution.__doc__ = ("\n".join(docs))

    return CustomDistribution


def _get_domain_info(info):
    support_info = {"endpoints": info} if isinstance(info, tuple) else info
    typical = info.pop("typical", None)
    return support_info, typical


def _make_distribution_custom(dist):
    parameters = []

    for name, info in dist.parameters.items():
        support_info, typical = _get_domain_info(info)
        domain = _RealDomain(**support_info)
        param = _RealParameter(name, domain=domain, typical=typical)
        parameters.append(param)

    support_info, _ = _get_domain_info(dist.support)
    _x_support = _RealDomain(**support_info)
    _x_param = _RealParameter('x', domain=_x_support)
    repr_str = dist.__class__.__name__

    class CustomDistribution(ContinuousDistribution):
        _parameterizations = ([_Parameterization(*parameters)] if parameters
                              else [])
        _variable = _x_param

        def __repr__(self):
            s = super().__repr__()
            return s.replace('CustomDistribution', repr_str)

        def __str__(self):
            s = super().__str__()
            return s.replace('CustomDistribution', repr_str)

    methods = {'sample', 'logentropy', 'entropy',
               'median', 'mode', 'logpdf', 'pdf',
               'logcdf2', 'logcdf', 'cdf2', 'cdf',
               'logccdf2', 'logccdf', 'ccdf2', 'ccdf',
               'ilogcdf', 'icdf', 'ilogccdf', 'iccdf'}

Review comment (suggestion):
This highlights that the public interface for `parameters` and `support` combine the ideas of a domain and a parameter. I definitely think that's good for the public interface for the sake of simplicity. Do we agree that it's still good to make the distinction privately? If you think it's fine to leave the distinction, let's do that; just thought I'd ask.

Code after:

    for name, info in dist.parameters.items():
        domain_info, typical = _get_domain_info(info)
        domain = _RealDomain(**domain_info)
        param = _RealParameter(name, domain=domain, typical=typical)
        parameters.append(param)

    domain_info, _ = _get_domain_info(dist.support)
    _x_support = _RealDomain(**domain_info)
    _x_param = _RealParameter('x', domain=_x_support)
    repr_str = dist.__class__.__name__

    class CustomDistribution(ContinuousDistribution):
        _parameterizations = ([_Parameterization(*parameters)] if parameters
                              else [])
        _variable = _x_param

        def __repr__(self):
            s = super().__repr__()
            return s.replace('CustomDistribution', repr_str)

        def __str__(self):
            s = super().__str__()
            return s.replace('CustomDistribution', repr_str)

    methods = {'sample', 'logentropy', 'entropy',
               'median', 'mode', 'logpdf', 'pdf',
               'logcdf2', 'logcdf', 'cdf2', 'cdf',
               'logccdf2', 'logccdf', 'ccdf2', 'ccdf',
               'ilogcdf', 'icdf', 'ilogccdf', 'iccdf'}

    for method in methods:
        if hasattr(dist, method):
            # Make it an attribute of the new object with the new name
            new_method = f"_{method}_formula"
            setattr(CustomDistribution, new_method, getattr(dist, method))

    if hasattr(dist, 'moment'):
        def _moment_raw_formula(self, order, **kwargs):
            return dist.moment(order, kind='raw', **kwargs)

        def _moment_central_formula(self, order, **kwargs):
            return dist.moment(order, kind='central', **kwargs)

        def _moment_standardized_formula(self, order, **kwargs):
            return dist.moment(order, kind='standardized', **kwargs)

        CustomDistribution._moment_raw_formula = _moment_raw_formula
        CustomDistribution._moment_central_formula = _moment_central_formula
        CustomDistribution._moment_standardized_formula = _moment_standardized_formula