1"""API for converting notebooks between versions."""
2
3# Copyright (c) IPython Development Team.
4# Distributed under the terms of the Modified BSD License.
5from __future__ import annotations
6
7from . import versions
8from .reader import get_version
9from .validator import ValidationError
10
11
12def convert(nb, to_version):
13 """Convert a notebook node object to a specific version. Assumes that
14 all the versions starting from 1 to the latest major X are implemented.
15 In other words, there should never be a case where v1 v2 v3 v5 exist without
16 a v4. Also assumes that all conversions can be made in one step increments
17 between major versions and ignores minor revisions.
18
19 Parameters
20 ----------
21 nb : NotebookNode
22 to_version : int
23 Major revision to convert the notebook to. Can either be an upgrade or
24 a downgrade.
25
26 Raises
27 ------
28 ValueError
29 Notebook failed to convert.
30 ValueError
31 The version specified is invalid or doesn't exist.
32 ValidationError
33 Conversion failed due to missing expected attributes.
34 """
35
36 # Get input notebook version.
37 (version, version_minor) = get_version(nb)
38
39 # Check if destination is target version, if so return contents
40 if version == to_version:
41 return nb
42
43 # If the version exist, try to convert to it one step at a time.
44 if to_version in versions:
45 # Get the the version that this recursion will convert to as a step
46 # closer to the final revision. Make sure the newer of the conversion
47 # functions is used to perform the conversion.
48 if to_version > version:
49 step_version = version + 1
50 convert_function = versions[step_version].upgrade
51 else:
52 step_version = version - 1
53 convert_function = versions[version].downgrade
54
55 try:
56 # Convert and make sure version changed during conversion.
57 converted = convert_function(nb)
58 if converted.get("nbformat", 1) == version:
59 msg = "Failed to convert notebook from v%d to v%d." % (version, step_version)
60 raise ValueError(msg)
61 except AttributeError as e:
62 msg = f"Notebook could not be converted from version {version} to version {step_version} because it's missing a key: {e}"
63 raise ValidationError(msg) from None
64
65 # Recursively convert until target version is reached.
66 return convert(converted, to_version)
67 raise ValueError(
68 "Cannot convert notebook to v%d because that version doesn't exist" % (to_version)
69 )