Skip to content

Commit 92898fa

Browse files
Add parameter deprecation guide (#563)
* Add deprecation guide * Update docs/source/developer_guide/tutorial.rst Co-authored-by: Jacob Wilkins <46597752+oerc0122@users.noreply.github.com> --------- Co-authored-by: Jacob Wilkins <46597752+oerc0122@users.noreply.github.com>
1 parent 1df3df1 commit 92898fa

File tree

1 file changed

+237
-0
lines changed

1 file changed

+237
-0
lines changed

docs/source/developer_guide/tutorial.rst

Lines changed: 237 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -377,3 +377,240 @@ For comparison the :class:`janus_core.processing.observables.Velocity` built-in'
377377
378378
def __call__(self, atoms: Atoms) -> list[float]:
379379
return atoms.get_velocities()[self.atoms_slice, :][:, self._indices].flatten()
380+
381+
382+
Deprecating a parameter
383+
=======================
384+
385+
When deprecating a parameter, we aim to include a warning for at least one release cycle
386+
before removing the original parameter, to minimise the impact on users.
387+
388+
This deprecation period will be extended following the release of v1.
389+
390+
Deprecation should be handled for both the CLI and Python interfaces,
391+
as described below for the requirements in renaming ``model_path`` to ``model``,
392+
as well as renaming ``filter_func`` to ``filter_class`` when these requirements differ.
393+
394+
395+
Python interface
396+
----------------
397+
398+
1. Update the parameters.
399+
400+
In addition to adding the new parameter, the old parameter default should be changed to ``None``:
401+
402+
.. code-block:: python
403+
404+
def __init__(
405+
self,
406+
...
407+
model: PathLike | None = None,
408+
model_path: PathLike | None = None,
409+
...
410+
filter_func: Callable | str | None = None,
411+
filter_kwargs: dict[str, Any] | None = None,
412+
)
413+
414+
All references to the old paramter must be updated, which may include cases where separate calculations interact:
415+
416+
.. code-block:: python
417+
418+
self.minimize_kwargs.setdefault("filter_class", None)
419+
420+
421+
2. Handle the duplicated parameters.
422+
423+
If both are specifed, we should raise an error if possible:
424+
425+
.. code-block:: python
426+
427+
if model_path:
428+
# `model` is a new parameter, so there is no reason to be using both
429+
if model:
430+
raise ValueError(
431+
"`model` has replaced `model_path`. Please only use `model`"
432+
)
433+
self.model = model_path
434+
435+
436+
If the new parameter's default is not ``None``, it's usually acceptable to allow both,
437+
prefering the old option, as this is only not ``None`` if it has been set explicitly:
438+
439+
.. code-block:: python
440+
441+
if filter_func:
442+
filter_class = filter_func
443+
444+
445+
3. Raise a ``FutureWarning`` if the old parameter is set.
446+
447+
This usually needs to be later than the duplicate parameters are handled,
448+
to ensure logging has been set up:
449+
450+
.. code-block:: python
451+
452+
if model_path:
453+
warn(
454+
"`model_path` has been deprecated. Please use `model`.",
455+
FutureWarning,
456+
stacklevel=2,
457+
)
458+
459+
460+
4. Update the parameter docstrings:
461+
462+
.. code-block:: python
463+
464+
""""
465+
Parameters
466+
----------
467+
...
468+
model
469+
MLIP model label, path to model, or loaded model. Default is `None`.
470+
model_path
471+
Deprecated. Please use `model`.
472+
...
473+
""""
474+
475+
476+
5. Test that the old option still correctly sets the new parameter.
477+
478+
This should also check that a ``FutureWarning`` is raised:
479+
480+
.. code-block:: python
481+
482+
def test_deprecation_model_path():
483+
"""Test FutureWarning raised for model_path."""
484+
skip_extras("mace")
485+
486+
with pytest.warns(FutureWarning, match="`model_path` has been deprecated"):
487+
sp = SinglePoint(
488+
arch="mace_mp",
489+
model_path=MACE_PATH,
490+
struct=DATA_PATH / "NaCl.cif",
491+
)
492+
493+
assert sp.struct.calc.parameters["model"] == str(MACE_PATH.as_posix())
494+
495+
496+
6. Replace the old option in tests and documentation, including:
497+
498+
- README
499+
- Python user guide
500+
- Python tutorial notebooks
501+
- Python tests
502+
503+
504+
Command line
505+
------------
506+
507+
1. Add the new parameter:
508+
509+
.. code-block:: python
510+
511+
Model = Annotated[
512+
str | None,
513+
Option(
514+
help="MLIP model name, or path to model.", rich_help_panel="MLIP calculator"
515+
),
516+
]
517+
518+
2. Add the ``deprecated_option`` callback to the old parameter,
519+
which prints a warning if it is used, and hide the option:
520+
521+
.. code-block:: python
522+
523+
from janus_core.cli.utils import deprecated_option
524+
525+
ModelPath = Annotated[
526+
str | None,
527+
Option(
528+
help="Deprecated. Please use --model",
529+
rich_help_panel="MLIP calculator",
530+
callback=deprecated_option,
531+
hidden=True,
532+
),
533+
]
534+
535+
3. Update the docstrings, with a deprecation note for consistency:
536+
537+
.. code-block:: python
538+
539+
"""
540+
Parameters
541+
----------
542+
...
543+
model
544+
Path to MLIP model or name of model. Default is `None`.
545+
model_path
546+
Deprecated. Please use `model`.
547+
...
548+
""""
549+
550+
551+
4. Handle the duplicate values.
552+
553+
If the parameter is passed directly to the Python interface, and no other parameters depend on it,
554+
it may be sufficient to pass both through and handle it within the Python interface, as is the case for ``model_path``:
555+
556+
.. code-block:: python
557+
558+
singlepoint_kwargs = {
559+
...
560+
"model": model,
561+
"model_path": model_path,
562+
...
563+
}
564+
565+
566+
However, it may be necessary to ensure only one value has been specified before the Python interface is reached,
567+
as is the case for ``filter_func`` in ``geomopt``:
568+
569+
.. code-block:: python
570+
571+
if filter_func and filter_class:
572+
raise ValueError("--filter-func is deprecated, please only use --filter")
573+
574+
575+
5. Test that the old option still correctly sets the new parameter.
576+
577+
Ideally, this would also check that a ``FutureWarning`` is raised,
578+
but capture of this is inconsistent during tests, so we do not currently test against this:
579+
580+
.. code-block:: python
581+
582+
def test_model_path_deprecated(tmp_path):
583+
"""Test model_path sets model."""
584+
file_prefix = tmp_path / "NaCl"
585+
results_path = tmp_path / "NaCl-results.extxyz"
586+
log_path = tmp_path / "test.log"
587+
588+
result = runner.invoke(
589+
app,
590+
[
591+
"singlepoint",
592+
"--struct",
593+
DATA_PATH / "NaCl.cif",
594+
"--arch",
595+
"mace_mp",
596+
"--model-path",
597+
MACE_PATH,
598+
"--log",
599+
log_path,
600+
"--file-prefix",
601+
file_prefix,
602+
"--no-tracker",
603+
],
604+
)
605+
assert result.exit_code == 0
606+
607+
atoms = read(results_path)
608+
assert "model" in atoms.info
609+
assert atoms.info["model"] == str(MACE_PATH.as_posix())
610+
611+
6. Replace the old CLI option in tests and documentation, including:
612+
613+
- README
614+
- CLI user guide
615+
- CLI tutorial notebooks
616+
- CLI tests

0 commit comments

Comments
 (0)