Skip to content

Utilize keyword-only arguments, at least in public API #1573

Description

@Sevans711

Proposed new feature or change:

Public API should use "*" in function signatures to enforce "everything after this is a keyword-only argument". This has (at least) three major benefits:

  1. Enforce clarity: no more calls like ux.open_dataset(gridfile, datafile, None, False, True). What do the None, False, and True mean?
  2. Prevent silent bugs from mismatched/misremembered parameters: if I accidentally forgot the order for the 4th and 5th parameters above, I would have accidentally done ux.open_dataset(gridfile, datafile, None, True, False) instead, and uxarray would not give me any warnings about my typo.
  3. Improve code extensibility: I am planning to add a new optional argument soon, something like ux.open_dataset(gridfile, datafile, dirname=path_to_files) (see Improve flexibility of uxarray.open_dataset() #1555). Currently, because we are not using any keyword-only arguments, to support full backwards compatibility I am forced to put dirname last in the signature, after all of the potentially-positional arguments like those taking None, False, and True values above. That's not ideal; conceptually, dirname is very close to gridfile and datafile since they all specify file paths; I also hope it might be a convenient option for many users, so I would prefer to put it earlier to make it easier to find.

Additionally, this would match xarray style. For example the start of the corresponding function's signature there looks like: xarray.open_dataset(filename_or_obj, *, engine=None, ...), meaning filename_or_obj is the only allowed positional argument in xarray's case.

Deprecation cycle

This change would break backwards compatibility, so it would need an associated deprecation cycle. For that, I would propose to utilize something similar to xarray's _deprecate_positional_args wrapper, with slight customizations for uxarray's deprecation cycle.

Example: current version (hiding type annotations here for brevity):

def open_dataset(grid_filename_or_obj, filename_or_obj,
    chunks=None, chunk_grid=True, use_dual=False, grid_kwargs=None, **kwargs):
    ...

New version (during deprecation cycle):

@_deprecate_positional_args('2026.7.0', end='Jan 2027')
def open_dataset(grid_filename_or_obj, filename_or_obj, *,
    chunks=None, chunk_grid=True, use_dual=False, grid_kwargs=None, **kwargs):
    ...

A user passing more than 2 args positionally would receive a warning. The warning would be a FutureWarning (not DeprecationWarning; see pydata/xarray#11112) and would look something like: "f"Passing '{extra_args}' as positional argument(s) to open_dataset was deprecated in version 2026.7.0 and may start to raise an error starting in Jan 2027. Please pass them as keyword arguments instead."

During the deprecation cycle, developers cannot yet rearrange the non-positional arguments, to ensure backwards compatibility.

After the deprecation cycle, developers are free to remove the decorator at any time, and rearrange non-positional arguments to any order. This leads to final version:

def open_dataset(grid_filename_or_obj, filename_or_obj, *,
    chunks=None, chunk_grid=True, use_dual=False, grid_kwargs=None, **kwargs):
    ...

At this point, there will be a crash if a user passes more than 2 args positionally.

Final notes / questions:

  1. When deprecating positional args, it might be smart to open an associated issue which remains open until it is time to close them, and notes what version to close them in, so we remember to remove the @_deprecate_positional_args wrapper eventually.
  2. How long should the deprecation cycle be? I didn't find a standard deprecation cycle duration for uxarray.

Metadata

Metadata

Assignees

No one assigned

    Labels

    developer experienceMakes the codebase easier to read, debug, maintain, or extend.improvementImprovements on existing features or infrastructure

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    Status
    📚 Backlog

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions