1# Copyright 2018 The TensorFlow Authors. All Rights Reserved. 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14# ============================================================================== 15"""Utilities for API compatibility between TensorFlow release versions. 16 17See [Version 18Compatibility](https://tensorflow.org/guide/version_compat#backward_forward) 19""" 20 21from __future__ import absolute_import 22from __future__ import division 23from __future__ import print_function 24 25import datetime 26import os 27 28from tensorflow.python.platform import tf_logging as logging 29from tensorflow.python.util import tf_contextlib 30from tensorflow.python.util.tf_export import tf_export 31 32 33# This value changes every day with an automatic CL. It can be modified in code 34# via `forward_compatibility_horizon()` or with the environment variable 35# TF_FORWARD_COMPATIBILITY_DELTA_DAYS, which is added to the compatibility date. 36_FORWARD_COMPATIBILITY_HORIZON = datetime.date(2021, 2, 18) 37_FORWARD_COMPATIBILITY_DELTA_DAYS_VAR_NAME = "TF_FORWARD_COMPATIBILITY_DELTA_DAYS" 38_FORWARD_COMPATIBILITY_DATE_NUMBER = None 39 40 41def _date_to_date_number(year, month, day): 42 return (year << 9) | (month << 5) | day 43 44 45def _update_forward_compatibility_date_number(date_to_override=None): 46 """Update the base date to compare in forward_compatible function.""" 47 48 global _FORWARD_COMPATIBILITY_DATE_NUMBER 49 50 if date_to_override: 51 date = date_to_override 52 else: 53 date = _FORWARD_COMPATIBILITY_HORIZON 54 delta_days = os.getenv(_FORWARD_COMPATIBILITY_DELTA_DAYS_VAR_NAME) 55 if delta_days: 56 date += datetime.timedelta(days=int(delta_days)) 57 58 if date < _FORWARD_COMPATIBILITY_HORIZON: 59 logging.warning("Trying to set the forward compatibility date to the past" 60 " date %s. This will be ignored by TensorFlow." % (date)) 61 return 62 _FORWARD_COMPATIBILITY_DATE_NUMBER = _date_to_date_number( 63 date.year, date.month, date.day) 64 65 66_update_forward_compatibility_date_number() 67 68 69@tf_export("compat.forward_compatible") 70def forward_compatible(year, month, day): 71 """Return true if the forward compatibility window has expired. 72 73 See [Version 74 compatibility](https://tensorflow.org/guide/version_compat#backward_forward). 75 76 Forward-compatibility refers to scenarios where the producer of a TensorFlow 77 model (a GraphDef or SavedModel) is compiled against a version of the 78 TensorFlow library newer than what the consumer was compiled against. The 79 "producer" is typically a Python program that constructs and trains a model 80 while the "consumer" is typically another program that loads and serves the 81 model. 82 83 TensorFlow has been supporting a 3 week forward-compatibility window for 84 programs compiled from source at HEAD. 85 86 For example, consider the case where a new operation `MyNewAwesomeAdd` is 87 created with the intent of replacing the implementation of an existing Python 88 wrapper - `tf.add`. The Python wrapper implementation should change from 89 something like: 90 91 ```python 92 def add(inputs, name=None): 93 return gen_math_ops.add(inputs, name) 94 ``` 95 96 to: 97 98 ```python 99 from tensorflow.python.compat import compat 100 101 def add(inputs, name=None): 102 if compat.forward_compatible(year, month, day): 103 # Can use the awesome new implementation. 104 return gen_math_ops.my_new_awesome_add(inputs, name) 105 # To maintain forward compatibility, use the old implementation. 106 return gen_math_ops.add(inputs, name) 107 ``` 108 109 Where `year`, `month`, and `day` specify the date beyond which binaries 110 that consume a model are expected to have been updated to include the 111 new operations. This date is typically at least 3 weeks beyond the date 112 the code that adds the new operation is committed. 113 114 Args: 115 year: A year (e.g., 2018). Must be an `int`. 116 month: A month (1 <= month <= 12) in year. Must be an `int`. 117 day: A day (1 <= day <= 31, or 30, or 29, or 28) in month. Must be an 118 `int`. 119 120 Returns: 121 True if the caller can expect that serialized TensorFlow graphs produced 122 can be consumed by programs that are compiled with the TensorFlow library 123 source code after (year, month, day). 124 """ 125 return _FORWARD_COMPATIBILITY_DATE_NUMBER > _date_to_date_number( 126 year, month, day) 127 128 129@tf_export("compat.forward_compatibility_horizon") 130@tf_contextlib.contextmanager 131def forward_compatibility_horizon(year, month, day): 132 """Context manager for testing forward compatibility of generated graphs. 133 134 See [Version 135 compatibility](https://tensorflow.org/guide/version_compat#backward_forward). 136 137 To ensure forward compatibility of generated graphs (see `forward_compatible`) 138 with older binaries, new features can be gated with: 139 140 ```python 141 if compat.forward_compatible(year=2018, month=08, date=01): 142 generate_graph_with_new_features() 143 else: 144 generate_graph_so_older_binaries_can_consume_it() 145 ``` 146 147 However, when adding new features, one may want to unittest it before 148 the forward compatibility window expires. This context manager enables 149 such tests. For example: 150 151 ```python 152 from tensorflow.python.compat import compat 153 154 def testMyNewFeature(self): 155 with compat.forward_compatibility_horizon(2018, 08, 02): 156 # Test that generate_graph_with_new_features() has an effect 157 ``` 158 159 Args: 160 year: A year (e.g., 2018). Must be an `int`. 161 month: A month (1 <= month <= 12) in year. Must be an `int`. 162 day: A day (1 <= day <= 31, or 30, or 29, or 28) in month. Must be an 163 `int`. 164 165 Yields: 166 Nothing. 167 """ 168 try: 169 _update_forward_compatibility_date_number(datetime.date(year, month, day)) 170 yield 171 finally: 172 _update_forward_compatibility_date_number() 173