1
1
from __future__ import annotations
2
2
from typing import NamedTuple , TypedDict
3
+ from functools import total_ordering
3
4
4
5
6
+ @total_ordering
5
7
class APIVersion (NamedTuple ):
6
8
major : int
7
9
minor : int
10
+ experimental : bool = False
8
11
9
12
@classmethod
10
13
def from_string (cls , inp : str ) -> APIVersion :
14
+ if inp == "experimental" :
15
+ return cls .as_experimental ()
11
16
parts = inp .split ("." )
12
17
if len (parts ) != 2 :
13
18
raise ValueError (inp )
14
19
intparts = [int (p ) for p in parts ]
15
20
16
- return cls (major = intparts [0 ], minor = intparts [1 ])
21
+ return cls (major = intparts [0 ], minor = intparts [1 ], experimental = False )
22
+
23
+ @classmethod
24
+ def as_experimental (cls ) -> APIVersion :
25
+ return cls (
26
+ major = MAX_SUPPORTED_VERSION .major ,
27
+ minor = MAX_SUPPORTED_VERSION .minor ,
28
+ experimental = True ,
29
+ )
17
30
18
31
def __str__ (self ) -> str :
19
- return f"{ self .major } .{ self .minor } "
32
+ return f"{ self .major } .{ self .minor } " if not self .experimental else "experimental"
33
+
34
+ def __le__ (self , other : tuple [int , int , bool ] | tuple [int , int ]) -> bool : # type: ignore[override]
35
+ # this __le__ supports calling it against a version raw tuple or an API version;
36
+ # the type ignore happens because NamedTuple says its parent is tuple[int...] in this
37
+ # case, not tuple[int, int, bool]
38
+ b_maj = other [0 ]
39
+ b_min = other [1 ]
40
+ b_exp = other [2 ] if len (other ) > 2 else False
41
+ # when you do a <= b, interpreter calls a.__le__(b)
42
+ if self .experimental and b_exp :
43
+ # both a and b are experimental: same version
44
+ return True
45
+ elif self .experimental :
46
+ # a is experimental, and b is not: a > b, a !<= b
47
+ return False
48
+ elif b_exp :
49
+ # a is not experimental, and b is: a < b, a <= b
50
+ return True
51
+ # both are not experimental, standard version compare
52
+ return (self .major , self .minor ) <= (b_maj , b_min )
20
53
21
54
22
55
class ThermocyclerStepBase (TypedDict ):
@@ -30,3 +63,21 @@ class ThermocyclerStep(ThermocyclerStepBase, total=False):
30
63
31
64
hold_time_seconds : float
32
65
hold_time_minutes : float
66
+
67
+
68
+ MAX_SUPPORTED_VERSION = APIVersion (2 , 23 )
69
+ """The maximum supported protocol API version in this release."""
70
+
71
+ MIN_SUPPORTED_VERSION = APIVersion (2 , 0 )
72
+ """The minimum supported protocol API version in this release, across all robot types."""
73
+
74
+ MIN_SUPPORTED_VERSION_FOR_FLEX = APIVersion (2 , 15 )
75
+ """The minimum protocol API version supported by the Opentrons Flex.
76
+
77
+ It's an infrastructural requirement for this to be at least newer than 2.14. Before then,
78
+ the protocol API is backed by the legacy non-Protocol-engine backend, which is not prepared to
79
+ handle anything but OT-2s.
80
+
81
+ The additional bump to 2.15 is because that's what we tested on, and because it adds all the
82
+ Flex-specific features.
83
+ """
0 commit comments