In [0]:
from __future__ import division
from __future__ import print_function
import random
import gin
# When using Gin interactively, reregistering a function is not an error.
gin.enter_interactive_mode()
In [0]:
gin.clear_config()
@gin.configurable
def say_hello(name="world"):
print("Hello %s!" % name)
# Decorated functions or classes preserve their default behavior.
say_hello()
# Bindings are usually specified in a file, e.g., "config.gin". For simplicity
# here we pass a string directly to `gin.parse_config`.
gin.parse_config("""
say_hello.name = "Gin"
""")
# With the above config, the "name" parameter now defaults to "Gin".
say_hello()
# But the caller can always override it.
say_hello("world")
Any Python literal is an acceptable "value" in a Gin binding. Classes can be made configurable too.
In [0]:
gin.clear_config()
@gin.configurable
class Picker(object):
# Bindings affect the constructor of the class.
def __init__(self, items):
self._items = items
def pick(self):
print(random.choice(self._items))
Picker(['item']).pick()
gin.parse_config("""
Picker.items = ['one', 2, ((), (), ()),]
""")
Picker().pick()
When calling a function where arguments are expected to be supplied by Gin, passing the value gin.REQUIRED
clearly documents this expectation in the code.
In [0]:
gin.clear_config()
# Calling Picker with a missing argument yields an informative but somewhat
# verbose error message.
try:
Picker()
except TypeError as e:
print('Error message (missing arg):', e)
# We can use gin.REQUIRED to indicate we expect the value to be supplied by Gin.
# This improves the readability of the code and clarifies the error message.
try:
Picker(gin.REQUIRED)
except RuntimeError as e:
print('Error message (gin.REQUIRED):', e)
# gin.REQUIRED can also be used for keyword arguments.
try:
say_hello(name=gin.REQUIRED)
except RuntimeError as e:
print('Error message (gin.REQUIRED):', e)
In [0]:
gin.clear_config()
@gin.configurable
def return_a_value():
return 'fn1_return'
@gin.configurable
def print_arg(arg):
print('arg = %r' % arg)
gin.parse_config("""
print_arg.arg = @return_a_value
""")
print_arg(gin.REQUIRED)
References can be "evaluated", using the syntax @fn_or_class()
.
In [0]:
gin.parse_config("""
print_arg.arg = @return_a_value()
""")
print_arg(gin.REQUIRED)
Evaluated references are re-evaluated on every call.
In [0]:
@gin.configurable
def randint():
return random.randint(0, 10)
gin.parse_config("""
print_arg.arg = @randint()
""")
print_arg(gin.REQUIRED)
print_arg(gin.REQUIRED)
print_arg(gin.REQUIRED)
print_arg(gin.REQUIRED)
Bindings can be "scoped", using the scope/fn_or_class.parameter = value
syntax. Such bindings are only in effect when the function is called with that scope active. References can be marked with a scope (@scope/fn_or_class
) to force the associated function or class to be called within the specified scope.
In [0]:
@gin.configurable
def return_arg(arg):
return arg
@gin.configurable
def call_fns(fn1, fn2):
print('fn1() = %r' % fn1())
print('fn2() = %r' % fn2())
gin.parse_config("""
call_fns.fn1 = @scope_a/return_arg
scope_a/return_arg.arg = 'scope_a'
call_fns.fn2 = @scope_b/return_arg
scope_b/return_arg.arg = 'scope_b'
""")
call_fns(gin.REQUIRED, gin.REQUIRED)