Skip to content

Conversation

@jokasimr
Copy link
Contributor

Fixes #3466

@github-project-automation github-project-automation bot moved this to In progress in Development Board Aug 18, 2025
@jokasimr jokasimr moved this from In progress to Selected in Development Board Aug 18, 2025
dtype=obj.dtype,
with_variances=obj.variances is not None,
)
new_values = zeros(**(default | kwargs))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There is a subtlety with dims and shape. When you create zeros etc.., you can either use dims/shape or you can use sizes which is a dict mapping dim to shape.
Your implementation here would cause a clash of arguments if you do zeros_like(a, sizes={'x': 10, 'y': 5})

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch!

Create an object with uninitialized elements.
"""
new_values = ones(
default = dict( # noqa: C408
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the noqa because the linter is asking you to not use the dict() constructor but the {dims: obj.dims,...} syntax instead?
If so, why not keep the linter happy instead?

Copy link
Contributor Author

@jokasimr jokasimr Aug 18, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Because I think it's clearer like this, it makes the diff smaller, but if you prefer I can change it to be a dict literal 🤷

Comment on lines 87 to 94
default = {
'unit': obj.unit,
'dtype': obj.dtype,
'with_variances': obj.variances is not None,
}
if 'sizes' not in kwargs:
default.setdefault('dims', obj.dims)
default.setdefault('shape', obj.shape)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is the code here now common to all functions?
If so, how about moving all this to small helper and calling that inside each function?


@pytest.mark.parametrize(('dims', 'shape'), [(['x'], (1,)), (['x', 'y'], (2, 3))])
def test_full_like_with_dims_shape(dims, shape):
to_copy = sc.zeros(dims=["x", "y"], shape=(2, 2))
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can you add a unit and/or dtype here, and check that this is propagated to the output of full_like?
Cause I think in the full_like below, we are sort of just making an entirely new array -- i.e. it is not obvious that any properties from to_copy are propagated to the result?

Copy link
Member

@nvaytet nvaytet left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good, just a final suggestion to make the docstrings link to the related functions.

Comment on lines +18 to +20
if 'sizes' not in kwargs:
default.setdefault('dims', obj.dims)
default.setdefault('shape', obj.shape)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What if someone passes, e.g., sizes and dims? Shouldn't we raise?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If someone passes sizes and dims the scipp.ones method will raise because it received both sizes and dims, right?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I guess one could rely on that.

@SimonHeybrock
Copy link
Member

While the approach of using kwargs reduces the code duplication, I wonder if we should do the legwork and write this explicitly. That would (1) allow IDEs to auto-complete, (2) give tools the ability to show errors, and (3) make the docstrings readable without clicking on a link (which again simplies scipp usage in an IDE or terminal when using help or the like.

@jokasimr
Copy link
Contributor Author

While the approach of using kwargs reduces the code duplication, I wonder if we should do the legwork and write this explicitly. That would (1) allow IDEs to auto-complete, (2) give tools the ability to show errors, and (3) make the docstrings readable without clicking on a link (which again simplies scipp usage in an IDE or terminal when using help or the like.

Makes sense. I'll change it to use explicit argument names instead

jokasimr and others added 5 commits August 19, 2025 10:00
Co-authored-by: Neil Vaytet <39047984+nvaytet@users.noreply.github.com>
Co-authored-by: Neil Vaytet <39047984+nvaytet@users.noreply.github.com>
Co-authored-by: Neil Vaytet <39047984+nvaytet@users.noreply.github.com>
Co-authored-by: Neil Vaytet <39047984+nvaytet@users.noreply.github.com>
sizes: dict[str, int] | None | _NoArgProvided = _no_arg_provided,
unit: Unit | str | DefaultUnit | _NoArgProvided = _no_arg_provided,
dtype: DTypeLike | _NoArgProvided = _no_arg_provided,
with_variances: bool | _NoArgProvided = _no_arg_provided,
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The whole _NoArgProvided thing looks strange but it's done that way to distinguish the case when no argument was provided by the user from the case when the user provided the default argument value.

We need to distinguish the cases because if the user explicitly provided the default argument value we should use that. But if the argument was not provided then we will determine it from obj.

Comment on lines +25 to +31
class _NoArgProvided:
'''Dummy type to indicate that no argument was passed to the function.'''

pass


_no_arg_provided = _NoArgProvided()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
class _NoArgProvided:
'''Dummy type to indicate that no argument was passed to the function.'''
pass
_no_arg_provided = _NoArgProvided()
_no_arg_provided = object()

Should have the same effect but simpler?

Copy link
Contributor Author

@jokasimr jokasimr Aug 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah I agree it's simpler, but how do you type annotate the arguments if you do that?

One option is to keep the type annotations from the original functions (sc.zeros etc) and then ignore the type errors. 🤔 Maybe that's better actually, because changing the type annotations from the original functions might be confusing.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hmm, you are right, I think we want it in the type annotation?

@jokasimr
Copy link
Contributor Author

@SimonHeybrock is there anything blocking you from approving this PR?

@jokasimr jokasimr enabled auto-merge (squash) August 22, 2025 09:06
@jokasimr jokasimr merged commit 644ba86 into main Aug 22, 2025
4 checks passed
@jokasimr jokasimr deleted the kwargs-in-like branch August 22, 2025 09:42
@github-project-automation github-project-automation bot moved this from Selected to Done in Development Board Aug 22, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

zeros_like doesn't take any unit and dtype arguments

4 participants