Add model init params, deprecate params arguments#7789
Add model init params, deprecate params arguments#7789matthew-brett merged 23 commits intoscikit-image:mainfrom
params arguments#7789Conversation
1050ab9 to
cc1768e
Compare
I like your proposals @matthew-brett. |
|
Discussed on call with @lagru and @matthew-brett: will make |
In general, ask for input parameters to models such as Circle, etc. Deprecate use of no params to these models. This is sensible because we use `from_estimate` for the most sensible no-params case.
cc1768e to
91e99e8
Compare
Deprecate use of `.params`. Use `center`, `radius` etc for shape model classes.
|
Ok - I deprecated This one is now ready for review. |
ed906ad to
8c009e0
Compare
* origin/main: Update import convention in certain gallery examples (scikit-image#7764) Refactor fundamental matrix scaling (scikit-image#7767)
|
I also added notes on edits to expire the deprecations. |
skimage/measure/fit.py
Outdated
| @deprecate_func( | ||
| deprecated_version=_PARAMS_DEP_START, | ||
| removed_version=_PARAMS_DEP_STOP, | ||
| hint='`params` attribute deprecated; use object attributes directly', |
There was a problem hiding this comment.
I don't think the hint adds anything that the default message is not already including?
| hint='`params` attribute deprecated; use object attributes directly', |
There was a problem hiding this comment.
Except, the suggestion is to use e.g. center, rather than params. Can you think of a better way of phrasing this?
There was a problem hiding this comment.
I'm guessing that's because this decorator is supposed to work for all models? If we added independent deprecation machinery for each of the models, it would be a lot easier to use a specific hint here, saying "use origin/center/radius".
Could also remove some complexity. Because each implementation only has to work for one model instead of all?
There was a problem hiding this comment.
I've pasted and edited the params property into all the model classes. Yes, maybe that is easier to follow.
skimage/measure/fit.py
Outdated
| return tf if msg is None else FailedEstimation(f'{cls.__name__}: {msg}') | ||
|
|
||
|
|
||
| class _ParamsBaseModel(BaseModel): |
There was a problem hiding this comment.
Is there reason for not adding the methods of this class directly to the BaseModel? As far as I can see, BaseModel isn't used anywhere besides as a subclass for this one.
There was a problem hiding this comment.
I noticed it popped up a couple of times in grep of the code-base, and it is in the public API - so I was insecure about messing with it. Should I investigate further?
There was a problem hiding this comment.
I checked usage with my local IDE and it seemed like merging the two classes should have no negative implications for unrelated usages. But I or IDE could be wrong.
There was a problem hiding this comment.
I guess it doesn't matter that BaseModel gets a few more methods - I've fused _ParamsBaseModel into BaseModel.
| # from the ``_estimate`` methods, to the respective ``from_estimate`` | ||
| # class methods. | ||
| with catch_warnings(action='ignore'): | ||
| tf = cls() |
There was a problem hiding this comment.
In retrospect this might have been easier if we used the classmethod from_estimate to implement estimate instead of the other way around were we have to awkwardly call create an instance to call a method in a classmethod. 😅
But not worth changing now I think.
skimage/measure/fit.py
Outdated
| # * Delete `_params2init_values` methods. | ||
| # * Delete `params` property of LineModelND. | ||
| # * Rename `_arg_init` methods to `__init__`. | ||
| # * Clean `params` parsing from `predict_*` methods. |
There was a problem hiding this comment.
After starting to review this – even with this comment – completing the deprecation seems like a lot. I suspect, anybody trying to complete this deprecation 1-2 years from now will have a hard time completing this cleanly.
And since we are potentially planning to update the coordinate conventions too, there is potentially be another complicating factor that will come on top of this.
I guess what I want to say is, that my "spider sense" is tingling a lot when looking at all the back and forth between base and subclasses and decorators.
Do you think we could simplify this? I'd be totally on board with a lot more duplication if that can help with that endeavor. E.g. a straightforward __init__ for each of the models, that does what's required instead of _deprecate_no_args.
In my mind, an ideal deprecation mostly implements the new API as straightforward as possible. An then there is an added layer on top – ideally a decorator – that handles the backward compatibility. To complete it, then mostly only deleting the decorator is required.
The current implementation seems like something in between, mixing the old and the new implementation of the API as well as the deprecation machinery.
That said, you are more familiar here, so if you say this level of complexity is generally necessary, than I'll go with it. 😅
There was a problem hiding this comment.
I hesitate to say this is the best way to do it - but I did think quite hard about it, as well as expiring the deprecation, as you see. I don't myself think it would be all that hard - most of it is deleting stuff.
The problem here is that I want the init signature to show only the new signature, and not *args, **kwargs, as it must if we don't do this kind of dance. This is to prepare the user best for the new API. And the rest of the complexity it having to deal simultaneously with the old params args, and the deprecation of the no-argument instantiation calls.
Fuse BaseModel and _ParamsBaseModel. From @lagru review.
* origin/main: Setup stub creation with docstub in CI (scikit-image#7802)
stefanv
left a comment
There was a problem hiding this comment.
Approved, pending an update to TODO.txt.
Thanks @matthew-brett!
From review by @stefanv. Co-authored-by: Stefan van der Walt <[email protected]>
Add TODO on deprecation procedure.
2cb15bf to
bc591d7
Compare
Co-authored-by: Stefan van der Walt <[email protected]>
|
pre-commit.ci autofix |
|
I'll have a look at the docstub failure. |
Just use `~.DEPRECATED` for the deprecated parameters. This has the added benefit of type checkers flagging the usuage of the parameter.
Must have accidentally skipped that somehow during the most recent merge.
From clarification by @lagru
| class BaseModel: | ||
| def __init__(self): | ||
| self.params = None | ||
| def __init_subclass__(self): |
There was a problem hiding this comment.
Off-topic: I guess this works to discourage subclassing BaseModel.
We could also flat out refactor to _fit.py and deprecate any access to skimage.measure.fit. But I'm thinking that's something we could do a bit later during the transition to skimage2. Maybe with a some meta-programming shenanigans that warns for all access to modules that are not exposed in our HTML documentation (synonymous with the tree defined by __all__).
| @_deprecate_no_args | ||
| class LineModelND(_BaseModel): |
There was a problem hiding this comment.
I'm wondering how the interaction between these three parts affects users that subclass LineModelND. E.g. will they get a useful warning and do we need to worry about this?
There was a problem hiding this comment.
Some local testing implies that this seems to work, e.g. when called with super().estimate(...). So probably fine. 🤞
|
I'm planning to merge this soon - @lagru - anything you see as a blocker as of now? |
|
@matthew-brett, still reviewing this. I'll merge once ready and if I don't find anything critical. Hope that's okay. |
|
Let me know when you're happy, I'll write a suitable merge message. |
and fix typo: "by passing not input arguments to constructor" -> "by passing no input arguments to constructor"
lagru
left a comment
There was a problem hiding this comment.
Thanks @matthew-brett. Feel welcome to merge.
|
In addition to the merge summary we should also update the release notes in the PR description. I'd like to split them into distinct items like in this example here https://github.com/scientific-python/changelist?tab=readme-ov-file#writing-pull-request-summaries. I can do that after the merge, if you haven't done so already then. |
|
Thanks @lagru - merged - please do update the release notes - sorry I did not do that. |
|
I tried to make the release notes a bit more end user friendly in the context of our release notes. Will be updated in #7967. |
As discussed over at #7771 (comment) - the PR:
LineModelND;.paramsattributes of models. Instead set model-specific attributes:origin, directionforLineModelND;center, radiusforCircleModel,center, ax_lens, thetaforEllipseModel.params) inpredict*calls.There are a couple of changes for consistency and clarity in error type from
params=Nonepassed to thepredictetc methods - see release note below.For discussion
All three models have a
paramsattribute, with differing interpretation of the elements:LineModelND: sequence of two length N 1D arrays, elementsoriginanddirection.CircleModel: length 3 vector with elementscenter_x,center_y,radius.EllipseModel: length 5 vector with elementscenter_x,center_y,first_ax_len,second_ax_len,theta(angle of first axis).I wonder whether it would be better to record these input parameters directly on the instance, rather than munging them into
paramsas in:LineModelND: attributesoriginanddirection.CircleModel: attributescenter(length 2 array),radius.EllipseModel: attributescenter(length 2 array),ax_lens(length 2 array),theta(scalar).I say this because at the moment (in this PR) we ask for input parameters according to the attributes above, but do not record them as attributes in the same way, but rather in the concatenated
paramsform, which might be confusing.Release note
For maintainers and optionally contributors, please refer to the instructions on how to document this PR for the release notes.