This rule raises an issue when a `tensorflow.function` depends on a global or free Python variable.

== Why is this an issue?

When calling a `tensorflow.function` behind the scenes a `ConcreteFunction` is created everytime a new value is passed as argument.
This is not the case with Python global variables, closure or nonlocal variables.

This means the state and the result of the `tensorflow.function` may not be what is expected.

[source,python]
----
import tensorflow as tf

@tf.function
def addition():
  return 1 + foo

foo = 4
addition() # tf.Tensor(5, shape=(), dtype=int32): on this first step we obtain the expected result

foo = 10
addition() # tf.Tensor(5, shape=(), dtype=int32): unexpected result of 5 instead of 11
----

As we can see in the example above the second time `addition` is called, we obtain the same result as the first call.
This is due to the fact that between the 2 calls of `addition` the value of the argument passed to the function did not change.
This result in the creation of a single `ConcreteFunction` during the first call of `addition`, with the value of foo set to 4.

This is why it is a good practice to not use and mutate global variables or nonlocal variables inside of a `tensorflow.function`.

=== Exceptions

This rule will not raise an issue if the global or nonlocal variable is a `tensorflow.Variable`. 

[source,python]
----
import tensorflow as tf

@tf.function
def addition():
  return 1 + foo

foo = tf.Variable(4)
addition()

foo.assign(10)
addition() 
----

In this case the `ConcreteFunction` will be created properly each call if the value of the variable changes.

== How to fix it

To fix this issue refactor the Python global or nonlocal variable to be an argument of the `tensorflow.function`.

=== Code examples

==== Noncompliant code example

[source,python,diff-id=1,diff-type=noncompliant]
----
import tensorflow as tf

@tf.function
def addition():
  return 1 + foo # Noncompliant the usage of the nonlocal variable may not behave as expected.

foo = 4
addition()

foo = 10
addition()
----

==== Compliant solution

[source,python,diff-id=1,diff-type=compliant]
----
import tensorflow as tf

@tf.function
def addition(foo):
  return 1 + foo # Compliant

foo = 4
addition(foo)

foo = 10
addition(foo)
----

== Resources
=== Documentation

* TensorFlow Documentation - https://www.tensorflow.org/guide/function#depending_on_python_global_and_free_variables[Depending on Python global and free variables]
* TensorFlow Documentation - https://www.tensorflow.org/jvm/api_docs/java/org/tensorflow/ConcreteFunction?hl=en[ConcreteFunction reference]

