New Upstream Release - python-ml-collections

Ready changes

Summary

Merged new upstream version: 0.1.1 (was: 0.1.0).

Resulting package

Built on 2022-03-20T13:30 (took 18m28s)

The resulting binary packages can be installed (if you have the apt repository enabled) by running one of:

apt install -t fresh-releases python3-ml-collections

Lintian Result

Diff

diff --git a/AUTHORS b/AUTHORS
new file mode 100644
index 0000000..243e960
--- /dev/null
+++ b/AUTHORS
@@ -0,0 +1,9 @@
+# This is the list of ML Collections's significant contributors.
+#
+# This does not necessarily list everyone who has contributed code,
+# especially since many employees of one corporation may be contributing.
+# To see the full list of contributors, see the revision history in
+# source control.
+
+DeepMind Technologies Limited
+Google LLC
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..7a4a3ea
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,202 @@
+
+                                 Apache License
+                           Version 2.0, January 2004
+                        http://www.apache.org/licenses/
+
+   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+   1. Definitions.
+
+      "License" shall mean the terms and conditions for use, reproduction,
+      and distribution as defined by Sections 1 through 9 of this document.
+
+      "Licensor" shall mean the copyright owner or entity authorized by
+      the copyright owner that is granting the License.
+
+      "Legal Entity" shall mean the union of the acting entity and all
+      other entities that control, are controlled by, or are under common
+      control with that entity. For the purposes of this definition,
+      "control" means (i) the power, direct or indirect, to cause the
+      direction or management of such entity, whether by contract or
+      otherwise, or (ii) ownership of fifty percent (50%) or more of the
+      outstanding shares, or (iii) beneficial ownership of such entity.
+
+      "You" (or "Your") shall mean an individual or Legal Entity
+      exercising permissions granted by this License.
+
+      "Source" form shall mean the preferred form for making modifications,
+      including but not limited to software source code, documentation
+      source, and configuration files.
+
+      "Object" form shall mean any form resulting from mechanical
+      transformation or translation of a Source form, including but
+      not limited to compiled object code, generated documentation,
+      and conversions to other media types.
+
+      "Work" shall mean the work of authorship, whether in Source or
+      Object form, made available under the License, as indicated by a
+      copyright notice that is included in or attached to the work
+      (an example is provided in the Appendix below).
+
+      "Derivative Works" shall mean any work, whether in Source or Object
+      form, that is based on (or derived from) the Work and for which the
+      editorial revisions, annotations, elaborations, or other modifications
+      represent, as a whole, an original work of authorship. For the purposes
+      of this License, Derivative Works shall not include works that remain
+      separable from, or merely link (or bind by name) to the interfaces of,
+      the Work and Derivative Works thereof.
+
+      "Contribution" shall mean any work of authorship, including
+      the original version of the Work and any modifications or additions
+      to that Work or Derivative Works thereof, that is intentionally
+      submitted to Licensor for inclusion in the Work by the copyright owner
+      or by an individual or Legal Entity authorized to submit on behalf of
+      the copyright owner. For the purposes of this definition, "submitted"
+      means any form of electronic, verbal, or written communication sent
+      to the Licensor or its representatives, including but not limited to
+      communication on electronic mailing lists, source code control systems,
+      and issue tracking systems that are managed by, or on behalf of, the
+      Licensor for the purpose of discussing and improving the Work, but
+      excluding communication that is conspicuously marked or otherwise
+      designated in writing by the copyright owner as "Not a Contribution."
+
+      "Contributor" shall mean Licensor and any individual or Legal Entity
+      on behalf of whom a Contribution has been received by Licensor and
+      subsequently incorporated within the Work.
+
+   2. Grant of Copyright License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      copyright license to reproduce, prepare Derivative Works of,
+      publicly display, publicly perform, sublicense, and distribute the
+      Work and such Derivative Works in Source or Object form.
+
+   3. Grant of Patent License. Subject to the terms and conditions of
+      this License, each Contributor hereby grants to You a perpetual,
+      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+      (except as stated in this section) patent license to make, have made,
+      use, offer to sell, sell, import, and otherwise transfer the Work,
+      where such license applies only to those patent claims licensable
+      by such Contributor that are necessarily infringed by their
+      Contribution(s) alone or by combination of their Contribution(s)
+      with the Work to which such Contribution(s) was submitted. If You
+      institute patent litigation against any entity (including a
+      cross-claim or counterclaim in a lawsuit) alleging that the Work
+      or a Contribution incorporated within the Work constitutes direct
+      or contributory patent infringement, then any patent licenses
+      granted to You under this License for that Work shall terminate
+      as of the date such litigation is filed.
+
+   4. Redistribution. You may reproduce and distribute copies of the
+      Work or Derivative Works thereof in any medium, with or without
+      modifications, and in Source or Object form, provided that You
+      meet the following conditions:
+
+      (a) You must give any other recipients of the Work or
+          Derivative Works a copy of this License; and
+
+      (b) You must cause any modified files to carry prominent notices
+          stating that You changed the files; and
+
+      (c) You must retain, in the Source form of any Derivative Works
+          that You distribute, all copyright, patent, trademark, and
+          attribution notices from the Source form of the Work,
+          excluding those notices that do not pertain to any part of
+          the Derivative Works; and
+
+      (d) If the Work includes a "NOTICE" text file as part of its
+          distribution, then any Derivative Works that You distribute must
+          include a readable copy of the attribution notices contained
+          within such NOTICE file, excluding those notices that do not
+          pertain to any part of the Derivative Works, in at least one
+          of the following places: within a NOTICE text file distributed
+          as part of the Derivative Works; within the Source form or
+          documentation, if provided along with the Derivative Works; or,
+          within a display generated by the Derivative Works, if and
+          wherever such third-party notices normally appear. The contents
+          of the NOTICE file are for informational purposes only and
+          do not modify the License. You may add Your own attribution
+          notices within Derivative Works that You distribute, alongside
+          or as an addendum to the NOTICE text from the Work, provided
+          that such additional attribution notices cannot be construed
+          as modifying the License.
+
+      You may add Your own copyright statement to Your modifications and
+      may provide additional or different license terms and conditions
+      for use, reproduction, or distribution of Your modifications, or
+      for any such Derivative Works as a whole, provided Your use,
+      reproduction, and distribution of the Work otherwise complies with
+      the conditions stated in this License.
+
+   5. Submission of Contributions. Unless You explicitly state otherwise,
+      any Contribution intentionally submitted for inclusion in the Work
+      by You to the Licensor shall be under the terms and conditions of
+      this License, without any additional terms or conditions.
+      Notwithstanding the above, nothing herein shall supersede or modify
+      the terms of any separate license agreement you may have executed
+      with Licensor regarding such Contributions.
+
+   6. Trademarks. This License does not grant permission to use the trade
+      names, trademarks, service marks, or product names of the Licensor,
+      except as required for reasonable and customary use in describing the
+      origin of the Work and reproducing the content of the NOTICE file.
+
+   7. Disclaimer of Warranty. Unless required by applicable law or
+      agreed to in writing, Licensor provides the Work (and each
+      Contributor provides its Contributions) on an "AS IS" BASIS,
+      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+      implied, including, without limitation, any warranties or conditions
+      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+      PARTICULAR PURPOSE. You are solely responsible for determining the
+      appropriateness of using or redistributing the Work and assume any
+      risks associated with Your exercise of permissions under this License.
+
+   8. Limitation of Liability. In no event and under no legal theory,
+      whether in tort (including negligence), contract, or otherwise,
+      unless required by applicable law (such as deliberate and grossly
+      negligent acts) or agreed to in writing, shall any Contributor be
+      liable to You for damages, including any direct, indirect, special,
+      incidental, or consequential damages of any character arising as a
+      result of this License or out of the use or inability to use the
+      Work (including but not limited to damages for loss of goodwill,
+      work stoppage, computer failure or malfunction, or any and all
+      other commercial damages or losses), even if such Contributor
+      has been advised of the possibility of such damages.
+
+   9. Accepting Warranty or Additional Liability. While redistributing
+      the Work or Derivative Works thereof, You may choose to offer,
+      and charge a fee for, acceptance of support, warranty, indemnity,
+      or other liability obligations and/or rights consistent with this
+      License. However, in accepting such obligations, You may act only
+      on Your own behalf and on Your sole responsibility, not on behalf
+      of any other Contributor, and only if You agree to indemnify,
+      defend, and hold each Contributor harmless for any liability
+      incurred by, or claims asserted against, such Contributor by reason
+      of your accepting any such warranty or additional liability.
+
+   END OF TERMS AND CONDITIONS
+
+   APPENDIX: How to apply the Apache License to your work.
+
+      To apply the Apache License to your work, attach the following
+      boilerplate notice, with the fields enclosed by brackets "[]"
+      replaced with your own identifying information. (Don't include
+      the brackets!)  The text should be enclosed in the appropriate
+      comment syntax for the file format. We also recommend that a
+      file or class name and description of purpose be included on the
+      same "printed page" as the copyright notice for easier
+      identification within third-party archives.
+
+   Copyright [yyyy] [name of copyright owner]
+
+   Licensed under the Apache License, Version 2.0 (the "License");
+   you may not use this file except in compliance with the License.
+   You may obtain a copy of the License at
+
+       http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
\ No newline at end of file
diff --git a/MANIFEST.in b/MANIFEST.in
new file mode 100644
index 0000000..c4ef03e
--- /dev/null
+++ b/MANIFEST.in
@@ -0,0 +1,2 @@
+include requirements-test.txt
+include requirements.txt
diff --git a/PKG-INFO b/PKG-INFO
index 0e0b10b..b185bde 100644
--- a/PKG-INFO
+++ b/PKG-INFO
@@ -1,606 +1,11 @@
 Metadata-Version: 2.1
 Name: ml_collections
-Version: 0.1.0
+Version: 0.1.1
 Summary: ML Collections is a library of Python collections designed for ML usecases.
 Home-page: https://github.com/google/ml_collections
 Author: ML Collections Authors
 Author-email: ml-collections@google.com
 License: Apache 2.0
-Description: # ML Collections
-        
-        ML Collections is a library of Python Collections designed for ML use cases.
-        
-        ## ConfigDict
-        
-        The two classes called `ConfigDict` and `FrozenConfigDict` are "dict-like" data
-        structures with dot access to nested elements. Together, they are supposed to be
-        used as a main way of expressing configurations of experiments and models.
-        
-        This document describes example usage of `ConfigDict`, `FrozenConfigDict`,
-        `FieldReference`.
-        
-        ### Features
-        
-        *   Dot-based access to fields.
-        *   Locking mechanism to prevent spelling mistakes.
-        *   Lazy computation.
-        *   FrozenConfigDict() class which is immutable and hashable.
-        *   Type safety.
-        *   "Did you mean" functionality.
-        *   Human readable printing (with valid references and cycles), using valid YAML
-            format.
-        *   Fields can be passed as keyword arguments using the `**` operator.
-        *   There are two exceptions to the strong type-safety of the ConfigDict. `int`
-            values can be passed in to fields of type `float`. In such a case, the value
-            is type-converted to a `float` before being stored. Similarly, all string
-            types (including Unicode strings) can be stored in fields of type `str` or
-            `unicode`.
-        
-        ### Basic Usage
-        
-        ```python
-        import ml_collections
-        
-        cfg = ml_collections.ConfigDict()
-        cfg.float_field = 12.6
-        cfg.integer_field = 123
-        cfg.another_integer_field = 234
-        cfg.nested = ml_collections.ConfigDict()
-        cfg.nested.string_field = 'tom'
-        
-        print(cfg.integer_field)  # Prints 123.
-        print(cfg['integer_field'])  # Prints 123 as well.
-        
-        try:
-          cfg.integer_field = 'tom'  # Raises TypeError as this field is an integer.
-        except TypeError as e:
-          print(e)
-        
-        cfg.float_field = 12  # Works: `Int` types can be assigned to `Float`.
-        cfg.nested.string_field = u'bob'  # `String` fields can store Unicode strings.
-        
-        print(cfg)
-        ```
-        
-        ### FrozenConfigDict
-        
-        A `FrozenConfigDict`is an immutable, hashable type of `ConfigDict`:
-        
-        ```python
-        import ml_collections
-        
-        initial_dictionary = {
-            'int': 1,
-            'list': [1, 2],
-            'tuple': (1, 2, 3),
-            'set': {1, 2, 3, 4},
-            'dict_tuple_list': {'tuple_list': ([1, 2], 3)}
-        }
-        
-        cfg = ml_collections.ConfigDict(initial_dictionary)
-        frozen_dict = ml_collections.FrozenConfigDict(initial_dictionary)
-        
-        print(frozen_dict.tuple)  # Prints tuple (1, 2, 3)
-        print(frozen_dict.list)  # Prints tuple (1, 2)
-        print(frozen_dict.set)  # Prints frozenset {1, 2, 3, 4}
-        print(frozen_dict.dict_tuple_list.tuple_list[0])  # Prints tuple (1, 2)
-        
-        frozen_cfg = ml_collections.FrozenConfigDict(cfg)
-        print(frozen_cfg == frozen_dict)  # True
-        print(hash(frozen_cfg) == hash(frozen_dict))  # True
-        
-        try:
-          frozen_dict.int = 2 # Raises TypeError as FrozenConfigDict is immutable.
-        except AttributeError as e:
-          print(e)
-        
-        # Converting between `FrozenConfigDict` and `ConfigDict`:
-        thawed_frozen_cfg = ml_collections.ConfigDict(frozen_dict)
-        print(thawed_frozen_cfg == cfg)  # True
-        frozen_cfg_to_cfg = frozen_dict.as_configdict()
-        print(frozen_cfg_to_cfg == cfg)  # True
-        ```
-        
-        ### FieldReferences and placeholders
-        
-        A `FieldReference` is useful for having multiple fields use the same value. It
-        can also be used for [lazy computation](#lazy-computation).
-        
-        You can use `placeholder()` as a shortcut to create a `FieldReference` (field)
-        with a `None` default value. This is useful if a program uses optional
-        configuration fields.
-        
-        ```python
-        import ml_collections
-        from ml_collections.config_dict import config_dict
-        
-        placeholder = ml_collections.FieldReference(0)
-        cfg = ml_collections.ConfigDict()
-        cfg.placeholder = placeholder
-        cfg.optional = config_dict.placeholder(int)
-        cfg.nested = ml_collections.ConfigDict()
-        cfg.nested.placeholder = placeholder
-        
-        try:
-          cfg.optional = 'tom'  # Raises Type error as this field is an integer.
-        except TypeError as e:
-          print(e)
-        
-        cfg.optional = 1555  # Works fine.
-        cfg.placeholder = 1  # Changes the value of both placeholder and
-                             # nested.placeholder fields.
-        
-        print(cfg)
-        ```
-        
-        Note that the indirection provided by `FieldReference`s will be lost if accessed
-        through a `ConfigDict`.
-        
-        ```python
-        import ml_collections
-        
-        placeholder = ml_collections.FieldReference(0)
-        cfg.field1 = placeholder
-        cfg.field2 = placeholder  # This field will be tied to cfg.field1.
-        cfg.field3 = cfg.field1  # This will just be an int field initialized to 0.
-        ```
-        
-        ### Lazy computation
-        
-        Using a `FieldReference` in a standard operation (addition, subtraction,
-        multiplication, etc...) will return another `FieldReference` that points to the
-        original's value. You can use `FieldReference.get()` to execute the operations
-        and get the reference's computed value, and `FieldReference.set()` to change the
-        original reference's value.
-        
-        ```python
-        import ml_collections
-        
-        ref = ml_collections.FieldReference(1)
-        print(ref.get())  # Prints 1
-        
-        add_ten = ref.get() + 10  # ref.get() is an integer and so is add_ten
-        add_ten_lazy = ref + 10  # add_ten_lazy is a FieldReference - NOT an integer
-        
-        print(add_ten)  # Prints 11
-        print(add_ten_lazy.get())  # Prints 11 because ref's value is 1
-        
-        # Addition is lazily computed for FieldReferences so changing ref will change
-        # the value that is used to compute add_ten.
-        ref.set(5)
-        print(add_ten)  # Prints 11
-        print(add_ten_lazy.get())  # Prints 15 because ref's value is 5
-        ```
-        
-        If a `FieldReference` has `None` as its original value, or any operation has an
-        argument of `None`, then the lazy computation will evaluate to `None`.
-        
-        We can also use fields in a `ConfigDict` in lazy computation. In this case a
-        field will only be lazily evaluated if `ConfigDict.get_ref()` is used to get it.
-        
-        ```python
-        import ml_collections
-        
-        config = ml_collections.ConfigDict()
-        config.reference_field = ml_collections.FieldReference(1)
-        config.integer_field = 2
-        config.float_field = 2.5
-        
-        # No lazy evaluatuations because we didn't use get_ref()
-        config.no_lazy = config.integer_field * config.float_field
-        
-        # This will lazily evaluate ONLY config.integer_field
-        config.lazy_integer = config.get_ref('integer_field') * config.float_field
-        
-        # This will lazily evaluate ONLY config.float_field
-        config.lazy_float = config.integer_field * config.get_ref('float_field')
-        
-        # This will lazily evaluate BOTH config.integer_field and config.float_Field
-        config.lazy_both = (config.get_ref('integer_field') *
-                            config.get_ref('float_field'))
-        
-        config.integer_field = 3
-        print(config.no_lazy)  # Prints 5.0 - It uses integer_field's original value
-        
-        print(config.lazy_integer)  # Prints 7.5
-        
-        config.float_field = 3.5
-        print(config.lazy_float)  # Prints 7.0
-        print(config.lazy_both)  # Prints 10.5
-        ```
-        
-        #### Changing lazily computed values
-        
-        Lazily computed values in a ConfigDict can be overridden in the same way as
-        regular values. The reference to the `FieldReference` used for the lazy
-        computation will be lost and all computations downstream in the reference graph
-        will use the new value.
-        
-        ```python
-        import ml_collections
-        
-        config = ml_collections.ConfigDict()
-        config.reference = 1
-        config.reference_0 = config.get_ref('reference') + 10
-        config.reference_1 = config.get_ref('reference') + 20
-        config.reference_1_0 = config.get_ref('reference_1') + 100
-        
-        print(config.reference)  # Prints 1.
-        print(config.reference_0)  # Prints 11.
-        print(config.reference_1)  # Prints 21.
-        print(config.reference_1_0)  # Prints 121.
-        
-        config.reference_1 = 30
-        
-        print(config.reference)  # Prints 1 (unchanged).
-        print(config.reference_0)  # Prints 11 (unchanged).
-        print(config.reference_1)  # Prints 30.
-        print(config.reference_1_0)  # Prints 130.
-        ```
-        
-        #### Cycles
-        
-        You cannot create cycles using references. Fortunately
-        [the only way](#changing-lazily-computed-values) to create a cycle is by
-        assigning a computed field to one that *is not* the result of computation. This
-        is forbidden:
-        
-        ```python
-        import ml_collections
-        from ml_collections.config_dict import config_dict
-        
-        config = ml_collections.ConfigDict()
-        config.integer_field = 1
-        config.bigger_integer_field = config.get_ref('integer_field') + 10
-        
-        try:
-          # Raises a MutabilityError because setting config.integer_field would
-          # cause a cycle.
-          config.integer_field = config.get_ref('bigger_integer_field') + 2
-        except config_dict.MutabilityError as e:
-          print(e)
-        ```
-        
-        ### Advanced usage
-        
-        Here are some more advanced examples showing lazy computation with different
-        operators and data types.
-        
-        ```python
-        import ml_collections
-        
-        config = ml_collections.ConfigDict()
-        config.float_field = 12.6
-        config.integer_field = 123
-        config.list_field = [0, 1, 2]
-        
-        config.float_multiply_field = config.get_ref('float_field') * 3
-        print(config.float_multiply_field)  # Prints 37.8
-        
-        config.float_field = 10.0
-        print(config.float_multiply_field)  # Prints 30.0
-        
-        config.longer_list_field = config.get_ref('list_field') + [3, 4, 5]
-        print(config.longer_list_field)  # Prints [0, 1, 2, 3, 4, 5]
-        
-        config.list_field = [-1]
-        print(config.longer_list_field)  # Prints [-1, 3, 4, 5]
-        
-        # Both operands can be references
-        config.ref_subtraction = (
-            config.get_ref('float_field') - config.get_ref('integer_field'))
-        print(config.ref_subtraction)  # Prints -113.0
-        
-        config.integer_field = 10
-        print(config.ref_subtraction)  # Prints 0.0
-        ```
-        
-        ### Equality checking
-        
-        You can use `==` and `.eq_as_configdict()` to check equality among `ConfigDict`
-        and `FrozenConfigDict` objects.
-        
-        ```python
-        import ml_collections
-        
-        dict_1 = {'list': [1, 2]}
-        dict_2 = {'list': (1, 2)}
-        cfg_1 = ml_collections.ConfigDict(dict_1)
-        frozen_cfg_1 = ml_collections.FrozenConfigDict(dict_1)
-        frozen_cfg_2 = ml_collections.FrozenConfigDict(dict_2)
-        
-        # True because FrozenConfigDict converts lists to tuples
-        print(frozen_cfg_1.items() == frozen_cfg_2.items())
-        # False because == distinguishes the underlying difference
-        print(frozen_cfg_1 == frozen_cfg_2)
-        
-        # False because == distinguishes these types
-        print(frozen_cfg_1 == cfg_1)
-        # But eq_as_configdict() treats both as ConfigDict, so these are True:
-        print(frozen_cfg_1.eq_as_configdict(cfg_1))
-        print(cfg_1.eq_as_configdict(frozen_cfg_1))
-        ```
-        
-        ### Equality checking with lazy computation
-        
-        Equality checks see if the computed values are the same. Equality is satisfied
-        if two sets of computations are different as long as they result in the same
-        value.
-        
-        ```python
-        import ml_collections
-        
-        cfg_1 = ml_collections.ConfigDict()
-        cfg_1.a = 1
-        cfg_1.b = cfg_1.get_ref('a') + 2
-        
-        cfg_2 = ml_collections.ConfigDict()
-        cfg_2.a = 1
-        cfg_2.b = cfg_2.get_ref('a') * 3
-        
-        # True because all computed values are the same
-        print(cfg_1 == cfg_2)
-        ```
-        
-        ### Locking and copying
-        
-        Here is an example with `lock()` and `deepcopy()`:
-        
-        ```python
-        import copy
-        import ml_collections
-        
-        cfg = ml_collections.ConfigDict()
-        cfg.integer_field = 123
-        
-        # Locking prohibits the addition and deletion of new fields but allows
-        # modification of existing values.
-        cfg.lock()
-        try:
-          cfg.integer_field = 124  # Raises AttributeError and suggests valid field.
-        except AttributeError as e:
-          print(e)
-        with cfg.unlocked():
-          cfg.integer_field = 1555  # Works fine too.
-        
-        # Get a copy of the config dict.
-        new_cfg = copy.deepcopy(cfg)
-        new_cfg.integer_field = -123  # Works fine.
-        
-        print(cfg)
-        ```
-        
-        ### Dictionary attributes and initialization
-        
-        ```python
-        import ml_collections
-        
-        referenced_dict = {'inner_float': 3.14}
-        d = {
-            'referenced_dict_1': referenced_dict,
-            'referenced_dict_2': referenced_dict,
-            'list_containing_dict': [{'key': 'value'}],
-        }
-        
-        # We can initialize on a dictionary
-        cfg = ml_collections.ConfigDict(d)
-        
-        # Reference structure is preserved
-        print(id(cfg.referenced_dict_1) == id(cfg.referenced_dict_2))  # True
-        
-        # And the dict attributes have been converted to ConfigDict
-        print(type(cfg.referenced_dict_1))  # ConfigDict
-        
-        # However, the initialization does not look inside of lists, so dicts inside
-        # lists are not converted to ConfigDict
-        print(type(cfg.list_containing_dict[0]))  # dict
-        ```
-        
-        ### More Examples
-        
-        TODO(mohitreddy): Add links for examples.
-        
-        For more examples, take a look at these `ml_collections/config_dict/examples/`.
-        
-        For examples and gotchas specifically about initializing a ConfigDict, see
-        `ml_collections/config_dict/examples/config_dict_initialization.py`.
-        
-        ## Config Flags
-        
-        This library adds flag definitions to `absl.flags` to handle config files. It
-        does not wrap `absl.flags` so if using any standard flag definitions alongside
-        config file flags, users must also import `absl.flags`.
-        
-        Currently, this module adds two new flag types, namely `DEFINE_config_file`
-        which accepts a path to a Python file that generates a configuration, and
-        `DEFINE_config_dict` which accepts a configuration directly. Configurations are
-        dict-like structures (see [ConfigDict](#configdict)) whose nested elements
-        can be overridden using special command-line flags. See the examples below
-        for more details.
-        
-        ### Usage
-        
-        Use `ml_collections.config_flags` alongside `absl.flags`. For
-        example:
-        
-        `script.py`:
-        
-        ```python
-        from absl import app
-        from absl import flags
-        
-        from ml_collections.config_flags import config_flags
-        
-        FLAGS = flags.FLAGS
-        config_flags.DEFINE_config_file('my_config')
-        
-        def main(_):
-          print(FLAGS.my_config)
-        
-        if __name__ == '__main__':
-          app.run()
-        ```
-        
-        `config.py`:
-        
-        ```python
-        # Note that this is a valid Python script.
-        # get_config() can return an arbitrary dict-like object. However, it is advised
-        # to use ml_collections.ConfigDict.
-        # See ml_collections/config_dict/examples/config_dict_basic.py
-        
-        import ml_collections
-        
-        def get_config():
-          config = ml_collections.ConfigDict()
-          config.field1 = 1
-          config.field2 = 'tom'
-          config.nested = ml_collections.ConfigDict()
-          config.nested.field = 2.23
-          config.tuple = (1, 2, 3)
-          return config
-        ```
-        
-        Now, after running:
-        
-        ```bash
-        python script.py -- --my_config=config.py \
-                            --my_config.field1=8 \
-                            --my_config.nested.field=2.1 \
-                            --my_config.tuple='(1, 2, (1, 2))'
-        ```
-        
-        we get:
-        
-        ```
-        field1: 8
-        field2: tom
-        nested:
-          field: 2.1
-        tuple: !!python/tuple
-        - 1
-        - 2
-        - !!python/tuple
-          - 1
-          - 2
-        ```
-        
-        Usage of `DEFINE_config_dict` is similar to `DEFINE_config_file`, the main
-        difference is the configuration is defined in `script.py` instead of in a
-        separate file.
-        
-        `script.py`:
-        
-        ```python
-        from absl import app
-        from absl import flags
-        
-        import ml_collections
-        from ml_collections.config_flags import config_flags
-        
-        config = ml_collections.ConfigDict()
-        config.field1 = 1
-        config.field2 = 'tom'
-        config.nested = ml_collections.ConfigDict()
-        config.nested.field = 2.23
-        config.tuple = (1, 2, 3)
-        
-        FLAGS = flags.FLAGS
-        config_flags.DEFINE_config_dict('my_config', config)
-        
-        def main(_):
-          print(FLAGS.my_config)
-        
-        if __name__ == '__main__':
-          app.run()
-        ```
-        
-        `config_file` flags are compatible with the command-line flag syntax. All the
-        following options are supported for non-boolean values in configurations:
-        
-        *   `-(-)config.field=value`
-        *   `-(-)config.field value`
-        
-        Options for boolean values are slightly different:
-        
-        *   `-(-)config.boolean_field`: set boolean value to True.
-        *   `-(-)noconfig.boolean_field`: set boolean value to False.
-        *   `-(-)config.boolean_field=value`: `value` is `true`, `false`, `True` or
-            `False`.
-        
-        Note that `-(-)config.boolean_field value` is not supported.
-        
-        ### Parameterising the get_config() function
-        
-        It's sometimes useful to be able to pass parameters into `get_config`, and
-        change what is returned based on this configuration. One example is if you are
-        grid searching over parameters which have a different hierarchical structure -
-        the flag needs to be present in the resulting ConfigDict. It would be possible
-        to include the union of all possible leaf values in your ConfigDict,
-        but this produces a confusing config result as you have to remember which
-        parameters will actually have an effect and which won't.
-        
-        A better system is to pass some configuration, indicating which structure of
-        ConfigDict should be returned. An example is the following config file:
-        
-        ```python
-        import ml_collections
-        
-        def get_config(config_string):
-          possible_structures = {
-              'linear': ml_collections.ConfigDict({
-                  'model_constructor': 'snt.Linear',
-                  'model_config': ml_collections.ConfigDict({
-                      'output_size': 42,
-                  }),
-              'lstm': ml_collections.ConfigDict({
-                  'model_constructor': 'snt.LSTM',
-                  'model_config': ml_collections.ConfigDict({
-                      'hidden_size': 108,
-                  })
-              })
-          }
-        
-          return possible_structures[config_string]
-        ```
-        
-        The value of `config_string` will be anything that is to the right of the first
-        colon in the config file path, if one exists. If no colon exists, no value is
-        passed to `get_config` (producing a TypeError if `get_config` expects a value.)
-        
-        The above example can be run like:
-        
-        ```bash
-        python script.py -- --config=path_to_config.py:linear \
-                            --config.model_config.output_size=256
-        ```
-        
-        or like:
-        
-        ```bash
-        python script.py -- --config=path_to_config.py:lstm \
-                            --config.model_config.hidden_size=512
-        ```
-        
-        ### Additional features
-        
-        *   Loads any valid python script which defines `get_config()` function
-            returning any python object.
-        *   Automatic locking of the loaded object, if the loaded object defines a
-            callable `.lock()` method.
-        *   Supports command-line overriding of arbitrarily nested values in dict-like
-            objects (with key/attribute based getters/setters) of the following types:
-            *   `types.IntType` (integer)
-            *   `types.FloatType` (float)
-            *   `types.BooleanType` (bool)
-            *   `types.StringType` (string)
-            *   `types.TupleType` (tuple)
-        *   Overriding is type safe.
-        *   Overriding of `TupleType` can be done by passing in the `tuple` as a string
-            (see the example in the [Usage](#usage) section).
-        *   The overriding `tuple` object can be of a different size and have different
-            types than the original. Nested tuples are also supported.
-        
 Platform: UNKNOWN
 Classifier: Development Status :: 4 - Beta
 Classifier: Intended Audience :: Developers
@@ -613,3 +18,611 @@ Classifier: Topic :: Software Development :: Libraries
 Classifier: Topic :: Software Development :: Libraries :: Python Modules
 Requires-Python: >=2.6
 Description-Content-Type: text/markdown
+License-File: LICENSE
+License-File: AUTHORS
+
+# ML Collections
+
+ML Collections is a library of Python Collections designed for ML use cases.
+
+[![Documentation Status](https://readthedocs.org/projects/ml-collections/badge/?version=latest)](https://ml-collections.readthedocs.io/en/latest/?badge=latest)
+[![PyPI version](https://badge.fury.io/py/ml-collections.svg)](https://badge.fury.io/py/ml-collections)
+[![Build Status](https://github.com/google/ml_collections/workflows/Python%20package/badge.svg)](https://github.com/google/ml_collections/actions?query=workflow%3A%22Python+package%22)
+
+## ConfigDict
+
+The two classes called `ConfigDict` and `FrozenConfigDict` are "dict-like" data
+structures with dot access to nested elements. Together, they are supposed to be
+used as a main way of expressing configurations of experiments and models.
+
+This document describes example usage of `ConfigDict`, `FrozenConfigDict`,
+`FieldReference`.
+
+### Features
+
+*   Dot-based access to fields.
+*   Locking mechanism to prevent spelling mistakes.
+*   Lazy computation.
+*   FrozenConfigDict() class which is immutable and hashable.
+*   Type safety.
+*   "Did you mean" functionality.
+*   Human readable printing (with valid references and cycles), using valid YAML
+    format.
+*   Fields can be passed as keyword arguments using the `**` operator.
+*   There are two exceptions to the strong type-safety of the ConfigDict. `int`
+    values can be passed in to fields of type `float`. In such a case, the value
+    is type-converted to a `float` before being stored. Similarly, all string
+    types (including Unicode strings) can be stored in fields of type `str` or
+    `unicode`.
+
+### Basic Usage
+
+```python
+import ml_collections
+
+cfg = ml_collections.ConfigDict()
+cfg.float_field = 12.6
+cfg.integer_field = 123
+cfg.another_integer_field = 234
+cfg.nested = ml_collections.ConfigDict()
+cfg.nested.string_field = 'tom'
+
+print(cfg.integer_field)  # Prints 123.
+print(cfg['integer_field'])  # Prints 123 as well.
+
+try:
+  cfg.integer_field = 'tom'  # Raises TypeError as this field is an integer.
+except TypeError as e:
+  print(e)
+
+cfg.float_field = 12  # Works: `Int` types can be assigned to `Float`.
+cfg.nested.string_field = u'bob'  # `String` fields can store Unicode strings.
+
+print(cfg)
+```
+
+### FrozenConfigDict
+
+A `FrozenConfigDict`is an immutable, hashable type of `ConfigDict`:
+
+```python
+import ml_collections
+
+initial_dictionary = {
+    'int': 1,
+    'list': [1, 2],
+    'tuple': (1, 2, 3),
+    'set': {1, 2, 3, 4},
+    'dict_tuple_list': {'tuple_list': ([1, 2], 3)}
+}
+
+cfg = ml_collections.ConfigDict(initial_dictionary)
+frozen_dict = ml_collections.FrozenConfigDict(initial_dictionary)
+
+print(frozen_dict.tuple)  # Prints tuple (1, 2, 3)
+print(frozen_dict.list)  # Prints tuple (1, 2)
+print(frozen_dict.set)  # Prints frozenset {1, 2, 3, 4}
+print(frozen_dict.dict_tuple_list.tuple_list[0])  # Prints tuple (1, 2)
+
+frozen_cfg = ml_collections.FrozenConfigDict(cfg)
+print(frozen_cfg == frozen_dict)  # True
+print(hash(frozen_cfg) == hash(frozen_dict))  # True
+
+try:
+  frozen_dict.int = 2 # Raises TypeError as FrozenConfigDict is immutable.
+except AttributeError as e:
+  print(e)
+
+# Converting between `FrozenConfigDict` and `ConfigDict`:
+thawed_frozen_cfg = ml_collections.ConfigDict(frozen_dict)
+print(thawed_frozen_cfg == cfg)  # True
+frozen_cfg_to_cfg = frozen_dict.as_configdict()
+print(frozen_cfg_to_cfg == cfg)  # True
+```
+
+### FieldReferences and placeholders
+
+A `FieldReference` is useful for having multiple fields use the same value. It
+can also be used for [lazy computation](#lazy-computation).
+
+You can use `placeholder()` as a shortcut to create a `FieldReference` (field)
+with a `None` default value. This is useful if a program uses optional
+configuration fields.
+
+```python
+import ml_collections
+from ml_collections.config_dict import config_dict
+
+placeholder = ml_collections.FieldReference(0)
+cfg = ml_collections.ConfigDict()
+cfg.placeholder = placeholder
+cfg.optional = config_dict.placeholder(int)
+cfg.nested = ml_collections.ConfigDict()
+cfg.nested.placeholder = placeholder
+
+try:
+  cfg.optional = 'tom'  # Raises Type error as this field is an integer.
+except TypeError as e:
+  print(e)
+
+cfg.optional = 1555  # Works fine.
+cfg.placeholder = 1  # Changes the value of both placeholder and
+                     # nested.placeholder fields.
+
+print(cfg)
+```
+
+Note that the indirection provided by `FieldReference`s will be lost if accessed
+through a `ConfigDict`.
+
+```python
+import ml_collections
+
+placeholder = ml_collections.FieldReference(0)
+cfg.field1 = placeholder
+cfg.field2 = placeholder  # This field will be tied to cfg.field1.
+cfg.field3 = cfg.field1  # This will just be an int field initialized to 0.
+```
+
+### Lazy computation
+
+Using a `FieldReference` in a standard operation (addition, subtraction,
+multiplication, etc...) will return another `FieldReference` that points to the
+original's value. You can use `FieldReference.get()` to execute the operations
+and get the reference's computed value, and `FieldReference.set()` to change the
+original reference's value.
+
+```python
+import ml_collections
+
+ref = ml_collections.FieldReference(1)
+print(ref.get())  # Prints 1
+
+add_ten = ref.get() + 10  # ref.get() is an integer and so is add_ten
+add_ten_lazy = ref + 10  # add_ten_lazy is a FieldReference - NOT an integer
+
+print(add_ten)  # Prints 11
+print(add_ten_lazy.get())  # Prints 11 because ref's value is 1
+
+# Addition is lazily computed for FieldReferences so changing ref will change
+# the value that is used to compute add_ten.
+ref.set(5)
+print(add_ten)  # Prints 11
+print(add_ten_lazy.get())  # Prints 15 because ref's value is 5
+```
+
+If a `FieldReference` has `None` as its original value, or any operation has an
+argument of `None`, then the lazy computation will evaluate to `None`.
+
+We can also use fields in a `ConfigDict` in lazy computation. In this case a
+field will only be lazily evaluated if `ConfigDict.get_ref()` is used to get it.
+
+```python
+import ml_collections
+
+config = ml_collections.ConfigDict()
+config.reference_field = ml_collections.FieldReference(1)
+config.integer_field = 2
+config.float_field = 2.5
+
+# No lazy evaluatuations because we didn't use get_ref()
+config.no_lazy = config.integer_field * config.float_field
+
+# This will lazily evaluate ONLY config.integer_field
+config.lazy_integer = config.get_ref('integer_field') * config.float_field
+
+# This will lazily evaluate ONLY config.float_field
+config.lazy_float = config.integer_field * config.get_ref('float_field')
+
+# This will lazily evaluate BOTH config.integer_field and config.float_Field
+config.lazy_both = (config.get_ref('integer_field') *
+                    config.get_ref('float_field'))
+
+config.integer_field = 3
+print(config.no_lazy)  # Prints 5.0 - It uses integer_field's original value
+
+print(config.lazy_integer)  # Prints 7.5
+
+config.float_field = 3.5
+print(config.lazy_float)  # Prints 7.0
+print(config.lazy_both)  # Prints 10.5
+```
+
+#### Changing lazily computed values
+
+Lazily computed values in a ConfigDict can be overridden in the same way as
+regular values. The reference to the `FieldReference` used for the lazy
+computation will be lost and all computations downstream in the reference graph
+will use the new value.
+
+```python
+import ml_collections
+
+config = ml_collections.ConfigDict()
+config.reference = 1
+config.reference_0 = config.get_ref('reference') + 10
+config.reference_1 = config.get_ref('reference') + 20
+config.reference_1_0 = config.get_ref('reference_1') + 100
+
+print(config.reference)  # Prints 1.
+print(config.reference_0)  # Prints 11.
+print(config.reference_1)  # Prints 21.
+print(config.reference_1_0)  # Prints 121.
+
+config.reference_1 = 30
+
+print(config.reference)  # Prints 1 (unchanged).
+print(config.reference_0)  # Prints 11 (unchanged).
+print(config.reference_1)  # Prints 30.
+print(config.reference_1_0)  # Prints 130.
+```
+
+#### Cycles
+
+You cannot create cycles using references. Fortunately
+[the only way](#changing-lazily-computed-values) to create a cycle is by
+assigning a computed field to one that *is not* the result of computation. This
+is forbidden:
+
+```python
+import ml_collections
+from ml_collections.config_dict import config_dict
+
+config = ml_collections.ConfigDict()
+config.integer_field = 1
+config.bigger_integer_field = config.get_ref('integer_field') + 10
+
+try:
+  # Raises a MutabilityError because setting config.integer_field would
+  # cause a cycle.
+  config.integer_field = config.get_ref('bigger_integer_field') + 2
+except config_dict.MutabilityError as e:
+  print(e)
+```
+
+### Advanced usage
+
+Here are some more advanced examples showing lazy computation with different
+operators and data types.
+
+```python
+import ml_collections
+
+config = ml_collections.ConfigDict()
+config.float_field = 12.6
+config.integer_field = 123
+config.list_field = [0, 1, 2]
+
+config.float_multiply_field = config.get_ref('float_field') * 3
+print(config.float_multiply_field)  # Prints 37.8
+
+config.float_field = 10.0
+print(config.float_multiply_field)  # Prints 30.0
+
+config.longer_list_field = config.get_ref('list_field') + [3, 4, 5]
+print(config.longer_list_field)  # Prints [0, 1, 2, 3, 4, 5]
+
+config.list_field = [-1]
+print(config.longer_list_field)  # Prints [-1, 3, 4, 5]
+
+# Both operands can be references
+config.ref_subtraction = (
+    config.get_ref('float_field') - config.get_ref('integer_field'))
+print(config.ref_subtraction)  # Prints -113.0
+
+config.integer_field = 10
+print(config.ref_subtraction)  # Prints 0.0
+```
+
+### Equality checking
+
+You can use `==` and `.eq_as_configdict()` to check equality among `ConfigDict`
+and `FrozenConfigDict` objects.
+
+```python
+import ml_collections
+
+dict_1 = {'list': [1, 2]}
+dict_2 = {'list': (1, 2)}
+cfg_1 = ml_collections.ConfigDict(dict_1)
+frozen_cfg_1 = ml_collections.FrozenConfigDict(dict_1)
+frozen_cfg_2 = ml_collections.FrozenConfigDict(dict_2)
+
+# True because FrozenConfigDict converts lists to tuples
+print(frozen_cfg_1.items() == frozen_cfg_2.items())
+# False because == distinguishes the underlying difference
+print(frozen_cfg_1 == frozen_cfg_2)
+
+# False because == distinguishes these types
+print(frozen_cfg_1 == cfg_1)
+# But eq_as_configdict() treats both as ConfigDict, so these are True:
+print(frozen_cfg_1.eq_as_configdict(cfg_1))
+print(cfg_1.eq_as_configdict(frozen_cfg_1))
+```
+
+### Equality checking with lazy computation
+
+Equality checks see if the computed values are the same. Equality is satisfied
+if two sets of computations are different as long as they result in the same
+value.
+
+```python
+import ml_collections
+
+cfg_1 = ml_collections.ConfigDict()
+cfg_1.a = 1
+cfg_1.b = cfg_1.get_ref('a') + 2
+
+cfg_2 = ml_collections.ConfigDict()
+cfg_2.a = 1
+cfg_2.b = cfg_2.get_ref('a') * 3
+
+# True because all computed values are the same
+print(cfg_1 == cfg_2)
+```
+
+### Locking and copying
+
+Here is an example with `lock()` and `deepcopy()`:
+
+```python
+import copy
+import ml_collections
+
+cfg = ml_collections.ConfigDict()
+cfg.integer_field = 123
+
+# Locking prohibits the addition and deletion of new fields but allows
+# modification of existing values.
+cfg.lock()
+try:
+  cfg.integer_field = 124  # Raises AttributeError and suggests valid field.
+except AttributeError as e:
+  print(e)
+with cfg.unlocked():
+  cfg.integer_field = 1555  # Works fine too.
+
+# Get a copy of the config dict.
+new_cfg = copy.deepcopy(cfg)
+new_cfg.integer_field = -123  # Works fine.
+
+print(cfg)
+```
+
+### Dictionary attributes and initialization
+
+```python
+import ml_collections
+
+referenced_dict = {'inner_float': 3.14}
+d = {
+    'referenced_dict_1': referenced_dict,
+    'referenced_dict_2': referenced_dict,
+    'list_containing_dict': [{'key': 'value'}],
+}
+
+# We can initialize on a dictionary
+cfg = ml_collections.ConfigDict(d)
+
+# Reference structure is preserved
+print(id(cfg.referenced_dict_1) == id(cfg.referenced_dict_2))  # True
+
+# And the dict attributes have been converted to ConfigDict
+print(type(cfg.referenced_dict_1))  # ConfigDict
+
+# However, the initialization does not look inside of lists, so dicts inside
+# lists are not converted to ConfigDict
+print(type(cfg.list_containing_dict[0]))  # dict
+```
+
+### More Examples
+
+For more examples, take a look at
+[`ml_collections/config_dict/examples/`](https://github.com/google/ml_collections/tree/master/ml_collections/config_dict/examples)
+
+For examples and gotchas specifically about initializing a ConfigDict, see
+[`ml_collections/config_dict/examples/config_dict_initialization.py`](https://github.com/google/ml_collections/blob/master/ml_collections/config_dict/examples/config_dict_initialization.py).
+
+## Config Flags
+
+This library adds flag definitions to `absl.flags` to handle config files. It
+does not wrap `absl.flags` so if using any standard flag definitions alongside
+config file flags, users must also import `absl.flags`.
+
+Currently, this module adds two new flag types, namely `DEFINE_config_file`
+which accepts a path to a Python file that generates a configuration, and
+`DEFINE_config_dict` which accepts a configuration directly. Configurations are
+dict-like structures (see [ConfigDict](#configdict)) whose nested elements
+can be overridden using special command-line flags. See the examples below
+for more details.
+
+### Usage
+
+Use `ml_collections.config_flags` alongside `absl.flags`. For
+example:
+
+`script.py`:
+
+```python
+from absl import app
+from absl import flags
+
+from ml_collections.config_flags import config_flags
+
+FLAGS = flags.FLAGS
+config_flags.DEFINE_config_file('my_config')
+
+def main(_):
+  print(FLAGS.my_config)
+
+if __name__ == '__main__':
+  app.run(main)
+```
+
+`config.py`:
+
+```python
+# Note that this is a valid Python script.
+# get_config() can return an arbitrary dict-like object. However, it is advised
+# to use ml_collections.ConfigDict.
+# See ml_collections/config_dict/examples/config_dict_basic.py
+
+import ml_collections
+
+def get_config():
+  config = ml_collections.ConfigDict()
+  config.field1 = 1
+  config.field2 = 'tom'
+  config.nested = ml_collections.ConfigDict()
+  config.nested.field = 2.23
+  config.tuple = (1, 2, 3)
+  return config
+```
+
+Now, after running:
+
+```bash
+python script.py --my_config=config.py \
+                 --my_config.field1=8 \
+                 --my_config.nested.field=2.1 \
+                 --my_config.tuple='(1, 2, (1, 2))'
+```
+
+we get:
+
+```
+field1: 8
+field2: tom
+nested:
+  field: 2.1
+tuple: !!python/tuple
+- 1
+- 2
+- !!python/tuple
+  - 1
+  - 2
+```
+
+Usage of `DEFINE_config_dict` is similar to `DEFINE_config_file`, the main
+difference is the configuration is defined in `script.py` instead of in a
+separate file.
+
+`script.py`:
+
+```python
+from absl import app
+from absl import flags
+
+import ml_collections
+from ml_collections.config_flags import config_flags
+
+config = ml_collections.ConfigDict()
+config.field1 = 1
+config.field2 = 'tom'
+config.nested = ml_collections.ConfigDict()
+config.nested.field = 2.23
+config.tuple = (1, 2, 3)
+
+FLAGS = flags.FLAGS
+config_flags.DEFINE_config_dict('my_config', config)
+
+def main(_):
+  print(FLAGS.my_config)
+
+if __name__ == '__main__':
+  app.run()
+```
+
+`config_file` flags are compatible with the command-line flag syntax. All the
+following options are supported for non-boolean values in configurations:
+
+*   `-(-)config.field=value`
+*   `-(-)config.field value`
+
+Options for boolean values are slightly different:
+
+*   `-(-)config.boolean_field`: set boolean value to True.
+*   `-(-)noconfig.boolean_field`: set boolean value to False.
+*   `-(-)config.boolean_field=value`: `value` is `true`, `false`, `True` or
+    `False`.
+
+Note that `-(-)config.boolean_field value` is not supported.
+
+### Parameterising the get_config() function
+
+It's sometimes useful to be able to pass parameters into `get_config`, and
+change what is returned based on this configuration. One example is if you are
+grid searching over parameters which have a different hierarchical structure -
+the flag needs to be present in the resulting ConfigDict. It would be possible
+to include the union of all possible leaf values in your ConfigDict,
+but this produces a confusing config result as you have to remember which
+parameters will actually have an effect and which won't.
+
+A better system is to pass some configuration, indicating which structure of
+ConfigDict should be returned. An example is the following config file:
+
+```python
+import ml_collections
+
+def get_config(config_string):
+  possible_structures = {
+      'linear': ml_collections.ConfigDict({
+          'model_constructor': 'snt.Linear',
+          'model_config': ml_collections.ConfigDict({
+              'output_size': 42,
+          }),
+      'lstm': ml_collections.ConfigDict({
+          'model_constructor': 'snt.LSTM',
+          'model_config': ml_collections.ConfigDict({
+              'hidden_size': 108,
+          })
+      })
+  }
+
+  return possible_structures[config_string]
+```
+
+The value of `config_string` will be anything that is to the right of the first
+colon in the config file path, if one exists. If no colon exists, no value is
+passed to `get_config` (producing a TypeError if `get_config` expects a value.)
+
+The above example can be run like:
+
+```bash
+python script.py -- --config=path_to_config.py:linear \
+                    --config.model_config.output_size=256
+```
+
+or like:
+
+```bash
+python script.py -- --config=path_to_config.py:lstm \
+                    --config.model_config.hidden_size=512
+```
+
+### Additional features
+
+*   Loads any valid python script which defines `get_config()` function
+    returning any python object.
+*   Automatic locking of the loaded object, if the loaded object defines a
+    callable `.lock()` method.
+*   Supports command-line overriding of arbitrarily nested values in dict-like
+    objects (with key/attribute based getters/setters) of the following types:
+    *   `types.IntType` (integer)
+    *   `types.FloatType` (float)
+    *   `types.BooleanType` (bool)
+    *   `types.StringType` (string)
+    *   `types.TupleType` (tuple)
+*   Overriding is type safe.
+*   Overriding of `TupleType` can be done by passing in the `tuple` as a string
+    (see the example in the [Usage](#usage) section).
+*   The overriding `tuple` object can be of a different size and have different
+    types than the original. Nested tuples are also supported.
+
+## Authors
+*   Sergio Gómez Colmenarejo - sergomez@google.com
+*   Wojciech Marian Czarnecki - lejlot@google.com
+*   Nicholas Watters
+*   Mohit Reddy - mohitreddy@google.com
+
+
diff --git a/README.md b/README.md
index 18c2a89..e0a6d1f 100644
--- a/README.md
+++ b/README.md
@@ -2,6 +2,10 @@
 
 ML Collections is a library of Python Collections designed for ML use cases.
 
+[![Documentation Status](https://readthedocs.org/projects/ml-collections/badge/?version=latest)](https://ml-collections.readthedocs.io/en/latest/?badge=latest)
+[![PyPI version](https://badge.fury.io/py/ml-collections.svg)](https://badge.fury.io/py/ml-collections)
+[![Build Status](https://github.com/google/ml_collections/workflows/Python%20package/badge.svg)](https://github.com/google/ml_collections/actions?query=workflow%3A%22Python+package%22)
+
 ## ConfigDict
 
 The two classes called `ConfigDict` and `FrozenConfigDict` are "dict-like" data
@@ -390,12 +394,11 @@ print(type(cfg.list_containing_dict[0]))  # dict
 
 ### More Examples
 
-TODO(mohitreddy): Add links for examples.
-
-For more examples, take a look at these `ml_collections/config_dict/examples/`.
+For more examples, take a look at
+[`ml_collections/config_dict/examples/`](https://github.com/google/ml_collections/tree/master/ml_collections/config_dict/examples)
 
 For examples and gotchas specifically about initializing a ConfigDict, see
-`ml_collections/config_dict/examples/config_dict_initialization.py`.
+[`ml_collections/config_dict/examples/config_dict_initialization.py`](https://github.com/google/ml_collections/blob/master/ml_collections/config_dict/examples/config_dict_initialization.py).
 
 ## Config Flags
 
@@ -430,7 +433,7 @@ def main(_):
   print(FLAGS.my_config)
 
 if __name__ == '__main__':
-  app.run()
+  app.run(main)
 ```
 
 `config.py`:
@@ -456,10 +459,10 @@ def get_config():
 Now, after running:
 
 ```bash
-python script.py -- --my_config=config.py \
-                    --my_config.field1=8 \
-                    --my_config.nested.field=2.1 \
-                    --my_config.tuple='(1, 2, (1, 2))'
+python script.py --my_config=config.py \
+                 --my_config.field1=8 \
+                 --my_config.nested.field=2.1 \
+                 --my_config.tuple='(1, 2, (1, 2))'
 ```
 
 we get:
@@ -592,3 +595,9 @@ python script.py -- --config=path_to_config.py:lstm \
     (see the example in the [Usage](#usage) section).
 *   The overriding `tuple` object can be of a different size and have different
     types than the original. Nested tuples are also supported.
+
+## Authors
+*   Sergio Gómez Colmenarejo - sergomez@google.com
+*   Wojciech Marian Czarnecki - lejlot@google.com
+*   Nicholas Watters
+*   Mohit Reddy - mohitreddy@google.com
diff --git a/debian/changelog b/debian/changelog
index 67cf967..f0d6dd1 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,10 @@
+python-ml-collections (0.1.1-1) UNRELEASED; urgency=low
+
+  * New upstream release.
+  * Drop patch fix-test.patch, present upstream.
+
+ -- Debian Janitor <janitor@jelmer.uk>  Sun, 20 Mar 2022 13:12:16 -0000
+
 python-ml-collections (0.1.0-2) unstable; urgency=medium
 
   * Team upload.
diff --git a/debian/patches/fix-test.patch b/debian/patches/fix-test.patch
deleted file mode 100644
index 2535f15..0000000
--- a/debian/patches/fix-test.patch
+++ /dev/null
@@ -1,67 +0,0 @@
-Description: Cherry pick upstream changes to fix tests
-Author: Nilesh Patra <nilesh@debian.org>
-Forwarded: not-needed
-Last-Update: 2021-08-01
---- a/ml_collections/config_dict/tests/config_dict_test.py
-+++ b/ml_collections/config_dict/tests/config_dict_test.py
-@@ -1,4 +1,4 @@
--# Copyright 2020 The ML Collections Authors.
-+# Copyright 2021 The ML Collections Authors.
- #
- # Licensed under the Apache License, Version 2.0 (the "License");
- # you may not use this file except in compliance with the License.
-@@ -16,7 +16,8 @@
- """Tests for ml_collections.ConfigDict."""
- 
- import abc
--import collections as python_collections
-+from collections import abc as collections_abc
-+import functools
- import json
- import pickle
- import sys
-@@ -57,7 +58,7 @@
- _test_object = _TestClass()
- 
- 
--class _TestClassNoStr():  # pylint: disable=old-style-class
-+class _TestClassNoStr():
-   pass
- 
- 
-@@ -589,7 +590,7 @@
-       hash(cfg)
- 
-     # Ensure Python realizes ConfigDict is not hashable.
--    self.assertNotIsInstance(cfg, python_collections.Hashable)
-+    self.assertNotIsInstance(cfg, collections_abc.Hashable)
- 
-   def testDidYouMeanFeature(self):
-     """Tests 'did you mean' suggestions."""
-@@ -675,7 +676,7 @@
-   def testDeYaml(self):
-     """Tests YAML deserialization."""
-     cfg = _get_test_config_dict()
--    deyamled = yaml.load(cfg.to_yaml())
-+    deyamled = yaml.load(cfg.to_yaml(), yaml.UnsafeLoader)
-     self.assertEqual(cfg, deyamled)
- 
-   def testJSONConversion(self):
-@@ -723,7 +724,7 @@
-     cfg_dict.r1 = field
-     cfg_dict.r2 = field
- 
--    cfg_load = yaml.load(repr(cfg_dict))
-+    cfg_load = yaml.load(repr(cfg_dict), yaml.UnsafeLoader)
- 
-     # Test FieldReferences are preserved
-     cfg_load['r1'].set(2)
-@@ -1331,7 +1332,7 @@
-     This checks backward compatibility of deserialisation.
-     """
-     cfg = ml_collections.ConfigDict(dict(a=1))
--    self.assertTrue(yaml.load(cfg.to_yaml())._convert_dict)
-+    self.assertTrue(yaml.load(cfg.to_yaml(), yaml.UnsafeLoader)._convert_dict)
- 
-   def testRecursiveRename(self):
-     """Test recursive_rename.
diff --git a/debian/patches/noRequirements.patch b/debian/patches/noRequirements.patch
index ce17e70..9689f2b 100644
--- a/debian/patches/noRequirements.patch
+++ b/debian/patches/noRequirements.patch
@@ -2,9 +2,11 @@ Description: Do not check requirements, for they will already be available for o
 Author: Steffen Moeller <moeller@debian.org>
 Forwarded: not-needed
 Last-Update: 2021-08-01
---- a/setup.py
-+++ b/setup.py
-@@ -19,9 +19,9 @@
+Index: python-ml-collections/setup.py
+===================================================================
+--- python-ml-collections.orig/setup.py
++++ python-ml-collections/setup.py
+@@ -19,9 +19,9 @@ from setuptools import find_namespace_pa
  from setuptools import setup
  
  
@@ -16,8 +18,8 @@ Last-Update: 2021-08-01
 +#    return fp.read().splitlines()
  
  
- _VERSION = '0.1.0'
-@@ -39,8 +39,8 @@
+ _VERSION = '0.1.1'
+@@ -39,8 +39,8 @@ setup(
      license='Apache 2.0',
      # Contained modules and scripts.
      packages=find_namespace_packages(exclude=['*_test.py']),
diff --git a/debian/patches/series b/debian/patches/series
index 5272e4e..e60c0f7 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -1,2 +1 @@
 noRequirements.patch
-fix-test.patch
diff --git a/docs/conf.py b/docs/conf.py
new file mode 100644
index 0000000..f9630e6
--- /dev/null
+++ b/docs/conf.py
@@ -0,0 +1,83 @@
+# Copyright 2021 The ML Collections Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Configuration file for the Sphinx documentation builder.
+#
+# This file only contains a selection of the most common options. For a full
+# list see the documentation:
+# https://www.sphinx-doc.org/en/master/usage/configuration.html
+
+# -- Path setup --------------------------------------------------------------
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#
+# import os
+# import sys
+# sys.path.insert(0, os.path.abspath('.'))
+
+import os
+import sys
+sys.path.insert(0, os.path.abspath('..'))
+
+# -- Project information -----------------------------------------------------
+
+project = 'ml_collections'
+copyright = '2020, The ML Collection Authors'
+author = 'The ML Collection Authors'
+
+# The full version, including alpha/beta/rc tags
+release = '0.1.0'
+
+
+# -- General configuration ---------------------------------------------------
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
+# ones.
+extensions = [
+    'sphinx.ext.autodoc',
+    'sphinx.ext.autosummary',
+    'sphinx.ext.intersphinx',
+    'sphinx.ext.mathjax',
+    'sphinx.ext.napoleon',
+    'sphinx.ext.viewcode',
+    'nbsphinx',
+    'recommonmark',
+]
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ['_templates']
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+# This pattern also affects html_static_path and html_extra_path.
+exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store']
+
+autosummary_generate = True
+
+master_doc = 'index'
+
+# -- Options for HTML output -------------------------------------------------
+
+# The theme to use for HTML and HTML Help pages.  See the documentation for
+# a list of builtin themes.
+#
+html_theme = 'sphinx_rtd_theme'
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ['_static']
\ No newline at end of file
diff --git a/ml_collections.egg-info/PKG-INFO b/ml_collections.egg-info/PKG-INFO
index 1f16ec9..c7a69aa 100644
--- a/ml_collections.egg-info/PKG-INFO
+++ b/ml_collections.egg-info/PKG-INFO
@@ -1,606 +1,11 @@
 Metadata-Version: 2.1
 Name: ml-collections
-Version: 0.1.0
+Version: 0.1.1
 Summary: ML Collections is a library of Python collections designed for ML usecases.
 Home-page: https://github.com/google/ml_collections
 Author: ML Collections Authors
 Author-email: ml-collections@google.com
 License: Apache 2.0
-Description: # ML Collections
-        
-        ML Collections is a library of Python Collections designed for ML use cases.
-        
-        ## ConfigDict
-        
-        The two classes called `ConfigDict` and `FrozenConfigDict` are "dict-like" data
-        structures with dot access to nested elements. Together, they are supposed to be
-        used as a main way of expressing configurations of experiments and models.
-        
-        This document describes example usage of `ConfigDict`, `FrozenConfigDict`,
-        `FieldReference`.
-        
-        ### Features
-        
-        *   Dot-based access to fields.
-        *   Locking mechanism to prevent spelling mistakes.
-        *   Lazy computation.
-        *   FrozenConfigDict() class which is immutable and hashable.
-        *   Type safety.
-        *   "Did you mean" functionality.
-        *   Human readable printing (with valid references and cycles), using valid YAML
-            format.
-        *   Fields can be passed as keyword arguments using the `**` operator.
-        *   There are two exceptions to the strong type-safety of the ConfigDict. `int`
-            values can be passed in to fields of type `float`. In such a case, the value
-            is type-converted to a `float` before being stored. Similarly, all string
-            types (including Unicode strings) can be stored in fields of type `str` or
-            `unicode`.
-        
-        ### Basic Usage
-        
-        ```python
-        import ml_collections
-        
-        cfg = ml_collections.ConfigDict()
-        cfg.float_field = 12.6
-        cfg.integer_field = 123
-        cfg.another_integer_field = 234
-        cfg.nested = ml_collections.ConfigDict()
-        cfg.nested.string_field = 'tom'
-        
-        print(cfg.integer_field)  # Prints 123.
-        print(cfg['integer_field'])  # Prints 123 as well.
-        
-        try:
-          cfg.integer_field = 'tom'  # Raises TypeError as this field is an integer.
-        except TypeError as e:
-          print(e)
-        
-        cfg.float_field = 12  # Works: `Int` types can be assigned to `Float`.
-        cfg.nested.string_field = u'bob'  # `String` fields can store Unicode strings.
-        
-        print(cfg)
-        ```
-        
-        ### FrozenConfigDict
-        
-        A `FrozenConfigDict`is an immutable, hashable type of `ConfigDict`:
-        
-        ```python
-        import ml_collections
-        
-        initial_dictionary = {
-            'int': 1,
-            'list': [1, 2],
-            'tuple': (1, 2, 3),
-            'set': {1, 2, 3, 4},
-            'dict_tuple_list': {'tuple_list': ([1, 2], 3)}
-        }
-        
-        cfg = ml_collections.ConfigDict(initial_dictionary)
-        frozen_dict = ml_collections.FrozenConfigDict(initial_dictionary)
-        
-        print(frozen_dict.tuple)  # Prints tuple (1, 2, 3)
-        print(frozen_dict.list)  # Prints tuple (1, 2)
-        print(frozen_dict.set)  # Prints frozenset {1, 2, 3, 4}
-        print(frozen_dict.dict_tuple_list.tuple_list[0])  # Prints tuple (1, 2)
-        
-        frozen_cfg = ml_collections.FrozenConfigDict(cfg)
-        print(frozen_cfg == frozen_dict)  # True
-        print(hash(frozen_cfg) == hash(frozen_dict))  # True
-        
-        try:
-          frozen_dict.int = 2 # Raises TypeError as FrozenConfigDict is immutable.
-        except AttributeError as e:
-          print(e)
-        
-        # Converting between `FrozenConfigDict` and `ConfigDict`:
-        thawed_frozen_cfg = ml_collections.ConfigDict(frozen_dict)
-        print(thawed_frozen_cfg == cfg)  # True
-        frozen_cfg_to_cfg = frozen_dict.as_configdict()
-        print(frozen_cfg_to_cfg == cfg)  # True
-        ```
-        
-        ### FieldReferences and placeholders
-        
-        A `FieldReference` is useful for having multiple fields use the same value. It
-        can also be used for [lazy computation](#lazy-computation).
-        
-        You can use `placeholder()` as a shortcut to create a `FieldReference` (field)
-        with a `None` default value. This is useful if a program uses optional
-        configuration fields.
-        
-        ```python
-        import ml_collections
-        from ml_collections.config_dict import config_dict
-        
-        placeholder = ml_collections.FieldReference(0)
-        cfg = ml_collections.ConfigDict()
-        cfg.placeholder = placeholder
-        cfg.optional = config_dict.placeholder(int)
-        cfg.nested = ml_collections.ConfigDict()
-        cfg.nested.placeholder = placeholder
-        
-        try:
-          cfg.optional = 'tom'  # Raises Type error as this field is an integer.
-        except TypeError as e:
-          print(e)
-        
-        cfg.optional = 1555  # Works fine.
-        cfg.placeholder = 1  # Changes the value of both placeholder and
-                             # nested.placeholder fields.
-        
-        print(cfg)
-        ```
-        
-        Note that the indirection provided by `FieldReference`s will be lost if accessed
-        through a `ConfigDict`.
-        
-        ```python
-        import ml_collections
-        
-        placeholder = ml_collections.FieldReference(0)
-        cfg.field1 = placeholder
-        cfg.field2 = placeholder  # This field will be tied to cfg.field1.
-        cfg.field3 = cfg.field1  # This will just be an int field initialized to 0.
-        ```
-        
-        ### Lazy computation
-        
-        Using a `FieldReference` in a standard operation (addition, subtraction,
-        multiplication, etc...) will return another `FieldReference` that points to the
-        original's value. You can use `FieldReference.get()` to execute the operations
-        and get the reference's computed value, and `FieldReference.set()` to change the
-        original reference's value.
-        
-        ```python
-        import ml_collections
-        
-        ref = ml_collections.FieldReference(1)
-        print(ref.get())  # Prints 1
-        
-        add_ten = ref.get() + 10  # ref.get() is an integer and so is add_ten
-        add_ten_lazy = ref + 10  # add_ten_lazy is a FieldReference - NOT an integer
-        
-        print(add_ten)  # Prints 11
-        print(add_ten_lazy.get())  # Prints 11 because ref's value is 1
-        
-        # Addition is lazily computed for FieldReferences so changing ref will change
-        # the value that is used to compute add_ten.
-        ref.set(5)
-        print(add_ten)  # Prints 11
-        print(add_ten_lazy.get())  # Prints 15 because ref's value is 5
-        ```
-        
-        If a `FieldReference` has `None` as its original value, or any operation has an
-        argument of `None`, then the lazy computation will evaluate to `None`.
-        
-        We can also use fields in a `ConfigDict` in lazy computation. In this case a
-        field will only be lazily evaluated if `ConfigDict.get_ref()` is used to get it.
-        
-        ```python
-        import ml_collections
-        
-        config = ml_collections.ConfigDict()
-        config.reference_field = ml_collections.FieldReference(1)
-        config.integer_field = 2
-        config.float_field = 2.5
-        
-        # No lazy evaluatuations because we didn't use get_ref()
-        config.no_lazy = config.integer_field * config.float_field
-        
-        # This will lazily evaluate ONLY config.integer_field
-        config.lazy_integer = config.get_ref('integer_field') * config.float_field
-        
-        # This will lazily evaluate ONLY config.float_field
-        config.lazy_float = config.integer_field * config.get_ref('float_field')
-        
-        # This will lazily evaluate BOTH config.integer_field and config.float_Field
-        config.lazy_both = (config.get_ref('integer_field') *
-                            config.get_ref('float_field'))
-        
-        config.integer_field = 3
-        print(config.no_lazy)  # Prints 5.0 - It uses integer_field's original value
-        
-        print(config.lazy_integer)  # Prints 7.5
-        
-        config.float_field = 3.5
-        print(config.lazy_float)  # Prints 7.0
-        print(config.lazy_both)  # Prints 10.5
-        ```
-        
-        #### Changing lazily computed values
-        
-        Lazily computed values in a ConfigDict can be overridden in the same way as
-        regular values. The reference to the `FieldReference` used for the lazy
-        computation will be lost and all computations downstream in the reference graph
-        will use the new value.
-        
-        ```python
-        import ml_collections
-        
-        config = ml_collections.ConfigDict()
-        config.reference = 1
-        config.reference_0 = config.get_ref('reference') + 10
-        config.reference_1 = config.get_ref('reference') + 20
-        config.reference_1_0 = config.get_ref('reference_1') + 100
-        
-        print(config.reference)  # Prints 1.
-        print(config.reference_0)  # Prints 11.
-        print(config.reference_1)  # Prints 21.
-        print(config.reference_1_0)  # Prints 121.
-        
-        config.reference_1 = 30
-        
-        print(config.reference)  # Prints 1 (unchanged).
-        print(config.reference_0)  # Prints 11 (unchanged).
-        print(config.reference_1)  # Prints 30.
-        print(config.reference_1_0)  # Prints 130.
-        ```
-        
-        #### Cycles
-        
-        You cannot create cycles using references. Fortunately
-        [the only way](#changing-lazily-computed-values) to create a cycle is by
-        assigning a computed field to one that *is not* the result of computation. This
-        is forbidden:
-        
-        ```python
-        import ml_collections
-        from ml_collections.config_dict import config_dict
-        
-        config = ml_collections.ConfigDict()
-        config.integer_field = 1
-        config.bigger_integer_field = config.get_ref('integer_field') + 10
-        
-        try:
-          # Raises a MutabilityError because setting config.integer_field would
-          # cause a cycle.
-          config.integer_field = config.get_ref('bigger_integer_field') + 2
-        except config_dict.MutabilityError as e:
-          print(e)
-        ```
-        
-        ### Advanced usage
-        
-        Here are some more advanced examples showing lazy computation with different
-        operators and data types.
-        
-        ```python
-        import ml_collections
-        
-        config = ml_collections.ConfigDict()
-        config.float_field = 12.6
-        config.integer_field = 123
-        config.list_field = [0, 1, 2]
-        
-        config.float_multiply_field = config.get_ref('float_field') * 3
-        print(config.float_multiply_field)  # Prints 37.8
-        
-        config.float_field = 10.0
-        print(config.float_multiply_field)  # Prints 30.0
-        
-        config.longer_list_field = config.get_ref('list_field') + [3, 4, 5]
-        print(config.longer_list_field)  # Prints [0, 1, 2, 3, 4, 5]
-        
-        config.list_field = [-1]
-        print(config.longer_list_field)  # Prints [-1, 3, 4, 5]
-        
-        # Both operands can be references
-        config.ref_subtraction = (
-            config.get_ref('float_field') - config.get_ref('integer_field'))
-        print(config.ref_subtraction)  # Prints -113.0
-        
-        config.integer_field = 10
-        print(config.ref_subtraction)  # Prints 0.0
-        ```
-        
-        ### Equality checking
-        
-        You can use `==` and `.eq_as_configdict()` to check equality among `ConfigDict`
-        and `FrozenConfigDict` objects.
-        
-        ```python
-        import ml_collections
-        
-        dict_1 = {'list': [1, 2]}
-        dict_2 = {'list': (1, 2)}
-        cfg_1 = ml_collections.ConfigDict(dict_1)
-        frozen_cfg_1 = ml_collections.FrozenConfigDict(dict_1)
-        frozen_cfg_2 = ml_collections.FrozenConfigDict(dict_2)
-        
-        # True because FrozenConfigDict converts lists to tuples
-        print(frozen_cfg_1.items() == frozen_cfg_2.items())
-        # False because == distinguishes the underlying difference
-        print(frozen_cfg_1 == frozen_cfg_2)
-        
-        # False because == distinguishes these types
-        print(frozen_cfg_1 == cfg_1)
-        # But eq_as_configdict() treats both as ConfigDict, so these are True:
-        print(frozen_cfg_1.eq_as_configdict(cfg_1))
-        print(cfg_1.eq_as_configdict(frozen_cfg_1))
-        ```
-        
-        ### Equality checking with lazy computation
-        
-        Equality checks see if the computed values are the same. Equality is satisfied
-        if two sets of computations are different as long as they result in the same
-        value.
-        
-        ```python
-        import ml_collections
-        
-        cfg_1 = ml_collections.ConfigDict()
-        cfg_1.a = 1
-        cfg_1.b = cfg_1.get_ref('a') + 2
-        
-        cfg_2 = ml_collections.ConfigDict()
-        cfg_2.a = 1
-        cfg_2.b = cfg_2.get_ref('a') * 3
-        
-        # True because all computed values are the same
-        print(cfg_1 == cfg_2)
-        ```
-        
-        ### Locking and copying
-        
-        Here is an example with `lock()` and `deepcopy()`:
-        
-        ```python
-        import copy
-        import ml_collections
-        
-        cfg = ml_collections.ConfigDict()
-        cfg.integer_field = 123
-        
-        # Locking prohibits the addition and deletion of new fields but allows
-        # modification of existing values.
-        cfg.lock()
-        try:
-          cfg.integer_field = 124  # Raises AttributeError and suggests valid field.
-        except AttributeError as e:
-          print(e)
-        with cfg.unlocked():
-          cfg.integer_field = 1555  # Works fine too.
-        
-        # Get a copy of the config dict.
-        new_cfg = copy.deepcopy(cfg)
-        new_cfg.integer_field = -123  # Works fine.
-        
-        print(cfg)
-        ```
-        
-        ### Dictionary attributes and initialization
-        
-        ```python
-        import ml_collections
-        
-        referenced_dict = {'inner_float': 3.14}
-        d = {
-            'referenced_dict_1': referenced_dict,
-            'referenced_dict_2': referenced_dict,
-            'list_containing_dict': [{'key': 'value'}],
-        }
-        
-        # We can initialize on a dictionary
-        cfg = ml_collections.ConfigDict(d)
-        
-        # Reference structure is preserved
-        print(id(cfg.referenced_dict_1) == id(cfg.referenced_dict_2))  # True
-        
-        # And the dict attributes have been converted to ConfigDict
-        print(type(cfg.referenced_dict_1))  # ConfigDict
-        
-        # However, the initialization does not look inside of lists, so dicts inside
-        # lists are not converted to ConfigDict
-        print(type(cfg.list_containing_dict[0]))  # dict
-        ```
-        
-        ### More Examples
-        
-        TODO(mohitreddy): Add links for examples.
-        
-        For more examples, take a look at these `ml_collections/config_dict/examples/`.
-        
-        For examples and gotchas specifically about initializing a ConfigDict, see
-        `ml_collections/config_dict/examples/config_dict_initialization.py`.
-        
-        ## Config Flags
-        
-        This library adds flag definitions to `absl.flags` to handle config files. It
-        does not wrap `absl.flags` so if using any standard flag definitions alongside
-        config file flags, users must also import `absl.flags`.
-        
-        Currently, this module adds two new flag types, namely `DEFINE_config_file`
-        which accepts a path to a Python file that generates a configuration, and
-        `DEFINE_config_dict` which accepts a configuration directly. Configurations are
-        dict-like structures (see [ConfigDict](#configdict)) whose nested elements
-        can be overridden using special command-line flags. See the examples below
-        for more details.
-        
-        ### Usage
-        
-        Use `ml_collections.config_flags` alongside `absl.flags`. For
-        example:
-        
-        `script.py`:
-        
-        ```python
-        from absl import app
-        from absl import flags
-        
-        from ml_collections.config_flags import config_flags
-        
-        FLAGS = flags.FLAGS
-        config_flags.DEFINE_config_file('my_config')
-        
-        def main(_):
-          print(FLAGS.my_config)
-        
-        if __name__ == '__main__':
-          app.run()
-        ```
-        
-        `config.py`:
-        
-        ```python
-        # Note that this is a valid Python script.
-        # get_config() can return an arbitrary dict-like object. However, it is advised
-        # to use ml_collections.ConfigDict.
-        # See ml_collections/config_dict/examples/config_dict_basic.py
-        
-        import ml_collections
-        
-        def get_config():
-          config = ml_collections.ConfigDict()
-          config.field1 = 1
-          config.field2 = 'tom'
-          config.nested = ml_collections.ConfigDict()
-          config.nested.field = 2.23
-          config.tuple = (1, 2, 3)
-          return config
-        ```
-        
-        Now, after running:
-        
-        ```bash
-        python script.py -- --my_config=config.py \
-                            --my_config.field1=8 \
-                            --my_config.nested.field=2.1 \
-                            --my_config.tuple='(1, 2, (1, 2))'
-        ```
-        
-        we get:
-        
-        ```
-        field1: 8
-        field2: tom
-        nested:
-          field: 2.1
-        tuple: !!python/tuple
-        - 1
-        - 2
-        - !!python/tuple
-          - 1
-          - 2
-        ```
-        
-        Usage of `DEFINE_config_dict` is similar to `DEFINE_config_file`, the main
-        difference is the configuration is defined in `script.py` instead of in a
-        separate file.
-        
-        `script.py`:
-        
-        ```python
-        from absl import app
-        from absl import flags
-        
-        import ml_collections
-        from ml_collections.config_flags import config_flags
-        
-        config = ml_collections.ConfigDict()
-        config.field1 = 1
-        config.field2 = 'tom'
-        config.nested = ml_collections.ConfigDict()
-        config.nested.field = 2.23
-        config.tuple = (1, 2, 3)
-        
-        FLAGS = flags.FLAGS
-        config_flags.DEFINE_config_dict('my_config', config)
-        
-        def main(_):
-          print(FLAGS.my_config)
-        
-        if __name__ == '__main__':
-          app.run()
-        ```
-        
-        `config_file` flags are compatible with the command-line flag syntax. All the
-        following options are supported for non-boolean values in configurations:
-        
-        *   `-(-)config.field=value`
-        *   `-(-)config.field value`
-        
-        Options for boolean values are slightly different:
-        
-        *   `-(-)config.boolean_field`: set boolean value to True.
-        *   `-(-)noconfig.boolean_field`: set boolean value to False.
-        *   `-(-)config.boolean_field=value`: `value` is `true`, `false`, `True` or
-            `False`.
-        
-        Note that `-(-)config.boolean_field value` is not supported.
-        
-        ### Parameterising the get_config() function
-        
-        It's sometimes useful to be able to pass parameters into `get_config`, and
-        change what is returned based on this configuration. One example is if you are
-        grid searching over parameters which have a different hierarchical structure -
-        the flag needs to be present in the resulting ConfigDict. It would be possible
-        to include the union of all possible leaf values in your ConfigDict,
-        but this produces a confusing config result as you have to remember which
-        parameters will actually have an effect and which won't.
-        
-        A better system is to pass some configuration, indicating which structure of
-        ConfigDict should be returned. An example is the following config file:
-        
-        ```python
-        import ml_collections
-        
-        def get_config(config_string):
-          possible_structures = {
-              'linear': ml_collections.ConfigDict({
-                  'model_constructor': 'snt.Linear',
-                  'model_config': ml_collections.ConfigDict({
-                      'output_size': 42,
-                  }),
-              'lstm': ml_collections.ConfigDict({
-                  'model_constructor': 'snt.LSTM',
-                  'model_config': ml_collections.ConfigDict({
-                      'hidden_size': 108,
-                  })
-              })
-          }
-        
-          return possible_structures[config_string]
-        ```
-        
-        The value of `config_string` will be anything that is to the right of the first
-        colon in the config file path, if one exists. If no colon exists, no value is
-        passed to `get_config` (producing a TypeError if `get_config` expects a value.)
-        
-        The above example can be run like:
-        
-        ```bash
-        python script.py -- --config=path_to_config.py:linear \
-                            --config.model_config.output_size=256
-        ```
-        
-        or like:
-        
-        ```bash
-        python script.py -- --config=path_to_config.py:lstm \
-                            --config.model_config.hidden_size=512
-        ```
-        
-        ### Additional features
-        
-        *   Loads any valid python script which defines `get_config()` function
-            returning any python object.
-        *   Automatic locking of the loaded object, if the loaded object defines a
-            callable `.lock()` method.
-        *   Supports command-line overriding of arbitrarily nested values in dict-like
-            objects (with key/attribute based getters/setters) of the following types:
-            *   `types.IntType` (integer)
-            *   `types.FloatType` (float)
-            *   `types.BooleanType` (bool)
-            *   `types.StringType` (string)
-            *   `types.TupleType` (tuple)
-        *   Overriding is type safe.
-        *   Overriding of `TupleType` can be done by passing in the `tuple` as a string
-            (see the example in the [Usage](#usage) section).
-        *   The overriding `tuple` object can be of a different size and have different
-            types than the original. Nested tuples are also supported.
-        
 Platform: UNKNOWN
 Classifier: Development Status :: 4 - Beta
 Classifier: Intended Audience :: Developers
@@ -613,3 +18,611 @@ Classifier: Topic :: Software Development :: Libraries
 Classifier: Topic :: Software Development :: Libraries :: Python Modules
 Requires-Python: >=2.6
 Description-Content-Type: text/markdown
+License-File: LICENSE
+License-File: AUTHORS
+
+# ML Collections
+
+ML Collections is a library of Python Collections designed for ML use cases.
+
+[![Documentation Status](https://readthedocs.org/projects/ml-collections/badge/?version=latest)](https://ml-collections.readthedocs.io/en/latest/?badge=latest)
+[![PyPI version](https://badge.fury.io/py/ml-collections.svg)](https://badge.fury.io/py/ml-collections)
+[![Build Status](https://github.com/google/ml_collections/workflows/Python%20package/badge.svg)](https://github.com/google/ml_collections/actions?query=workflow%3A%22Python+package%22)
+
+## ConfigDict
+
+The two classes called `ConfigDict` and `FrozenConfigDict` are "dict-like" data
+structures with dot access to nested elements. Together, they are supposed to be
+used as a main way of expressing configurations of experiments and models.
+
+This document describes example usage of `ConfigDict`, `FrozenConfigDict`,
+`FieldReference`.
+
+### Features
+
+*   Dot-based access to fields.
+*   Locking mechanism to prevent spelling mistakes.
+*   Lazy computation.
+*   FrozenConfigDict() class which is immutable and hashable.
+*   Type safety.
+*   "Did you mean" functionality.
+*   Human readable printing (with valid references and cycles), using valid YAML
+    format.
+*   Fields can be passed as keyword arguments using the `**` operator.
+*   There are two exceptions to the strong type-safety of the ConfigDict. `int`
+    values can be passed in to fields of type `float`. In such a case, the value
+    is type-converted to a `float` before being stored. Similarly, all string
+    types (including Unicode strings) can be stored in fields of type `str` or
+    `unicode`.
+
+### Basic Usage
+
+```python
+import ml_collections
+
+cfg = ml_collections.ConfigDict()
+cfg.float_field = 12.6
+cfg.integer_field = 123
+cfg.another_integer_field = 234
+cfg.nested = ml_collections.ConfigDict()
+cfg.nested.string_field = 'tom'
+
+print(cfg.integer_field)  # Prints 123.
+print(cfg['integer_field'])  # Prints 123 as well.
+
+try:
+  cfg.integer_field = 'tom'  # Raises TypeError as this field is an integer.
+except TypeError as e:
+  print(e)
+
+cfg.float_field = 12  # Works: `Int` types can be assigned to `Float`.
+cfg.nested.string_field = u'bob'  # `String` fields can store Unicode strings.
+
+print(cfg)
+```
+
+### FrozenConfigDict
+
+A `FrozenConfigDict`is an immutable, hashable type of `ConfigDict`:
+
+```python
+import ml_collections
+
+initial_dictionary = {
+    'int': 1,
+    'list': [1, 2],
+    'tuple': (1, 2, 3),
+    'set': {1, 2, 3, 4},
+    'dict_tuple_list': {'tuple_list': ([1, 2], 3)}
+}
+
+cfg = ml_collections.ConfigDict(initial_dictionary)
+frozen_dict = ml_collections.FrozenConfigDict(initial_dictionary)
+
+print(frozen_dict.tuple)  # Prints tuple (1, 2, 3)
+print(frozen_dict.list)  # Prints tuple (1, 2)
+print(frozen_dict.set)  # Prints frozenset {1, 2, 3, 4}
+print(frozen_dict.dict_tuple_list.tuple_list[0])  # Prints tuple (1, 2)
+
+frozen_cfg = ml_collections.FrozenConfigDict(cfg)
+print(frozen_cfg == frozen_dict)  # True
+print(hash(frozen_cfg) == hash(frozen_dict))  # True
+
+try:
+  frozen_dict.int = 2 # Raises TypeError as FrozenConfigDict is immutable.
+except AttributeError as e:
+  print(e)
+
+# Converting between `FrozenConfigDict` and `ConfigDict`:
+thawed_frozen_cfg = ml_collections.ConfigDict(frozen_dict)
+print(thawed_frozen_cfg == cfg)  # True
+frozen_cfg_to_cfg = frozen_dict.as_configdict()
+print(frozen_cfg_to_cfg == cfg)  # True
+```
+
+### FieldReferences and placeholders
+
+A `FieldReference` is useful for having multiple fields use the same value. It
+can also be used for [lazy computation](#lazy-computation).
+
+You can use `placeholder()` as a shortcut to create a `FieldReference` (field)
+with a `None` default value. This is useful if a program uses optional
+configuration fields.
+
+```python
+import ml_collections
+from ml_collections.config_dict import config_dict
+
+placeholder = ml_collections.FieldReference(0)
+cfg = ml_collections.ConfigDict()
+cfg.placeholder = placeholder
+cfg.optional = config_dict.placeholder(int)
+cfg.nested = ml_collections.ConfigDict()
+cfg.nested.placeholder = placeholder
+
+try:
+  cfg.optional = 'tom'  # Raises Type error as this field is an integer.
+except TypeError as e:
+  print(e)
+
+cfg.optional = 1555  # Works fine.
+cfg.placeholder = 1  # Changes the value of both placeholder and
+                     # nested.placeholder fields.
+
+print(cfg)
+```
+
+Note that the indirection provided by `FieldReference`s will be lost if accessed
+through a `ConfigDict`.
+
+```python
+import ml_collections
+
+placeholder = ml_collections.FieldReference(0)
+cfg.field1 = placeholder
+cfg.field2 = placeholder  # This field will be tied to cfg.field1.
+cfg.field3 = cfg.field1  # This will just be an int field initialized to 0.
+```
+
+### Lazy computation
+
+Using a `FieldReference` in a standard operation (addition, subtraction,
+multiplication, etc...) will return another `FieldReference` that points to the
+original's value. You can use `FieldReference.get()` to execute the operations
+and get the reference's computed value, and `FieldReference.set()` to change the
+original reference's value.
+
+```python
+import ml_collections
+
+ref = ml_collections.FieldReference(1)
+print(ref.get())  # Prints 1
+
+add_ten = ref.get() + 10  # ref.get() is an integer and so is add_ten
+add_ten_lazy = ref + 10  # add_ten_lazy is a FieldReference - NOT an integer
+
+print(add_ten)  # Prints 11
+print(add_ten_lazy.get())  # Prints 11 because ref's value is 1
+
+# Addition is lazily computed for FieldReferences so changing ref will change
+# the value that is used to compute add_ten.
+ref.set(5)
+print(add_ten)  # Prints 11
+print(add_ten_lazy.get())  # Prints 15 because ref's value is 5
+```
+
+If a `FieldReference` has `None` as its original value, or any operation has an
+argument of `None`, then the lazy computation will evaluate to `None`.
+
+We can also use fields in a `ConfigDict` in lazy computation. In this case a
+field will only be lazily evaluated if `ConfigDict.get_ref()` is used to get it.
+
+```python
+import ml_collections
+
+config = ml_collections.ConfigDict()
+config.reference_field = ml_collections.FieldReference(1)
+config.integer_field = 2
+config.float_field = 2.5
+
+# No lazy evaluatuations because we didn't use get_ref()
+config.no_lazy = config.integer_field * config.float_field
+
+# This will lazily evaluate ONLY config.integer_field
+config.lazy_integer = config.get_ref('integer_field') * config.float_field
+
+# This will lazily evaluate ONLY config.float_field
+config.lazy_float = config.integer_field * config.get_ref('float_field')
+
+# This will lazily evaluate BOTH config.integer_field and config.float_Field
+config.lazy_both = (config.get_ref('integer_field') *
+                    config.get_ref('float_field'))
+
+config.integer_field = 3
+print(config.no_lazy)  # Prints 5.0 - It uses integer_field's original value
+
+print(config.lazy_integer)  # Prints 7.5
+
+config.float_field = 3.5
+print(config.lazy_float)  # Prints 7.0
+print(config.lazy_both)  # Prints 10.5
+```
+
+#### Changing lazily computed values
+
+Lazily computed values in a ConfigDict can be overridden in the same way as
+regular values. The reference to the `FieldReference` used for the lazy
+computation will be lost and all computations downstream in the reference graph
+will use the new value.
+
+```python
+import ml_collections
+
+config = ml_collections.ConfigDict()
+config.reference = 1
+config.reference_0 = config.get_ref('reference') + 10
+config.reference_1 = config.get_ref('reference') + 20
+config.reference_1_0 = config.get_ref('reference_1') + 100
+
+print(config.reference)  # Prints 1.
+print(config.reference_0)  # Prints 11.
+print(config.reference_1)  # Prints 21.
+print(config.reference_1_0)  # Prints 121.
+
+config.reference_1 = 30
+
+print(config.reference)  # Prints 1 (unchanged).
+print(config.reference_0)  # Prints 11 (unchanged).
+print(config.reference_1)  # Prints 30.
+print(config.reference_1_0)  # Prints 130.
+```
+
+#### Cycles
+
+You cannot create cycles using references. Fortunately
+[the only way](#changing-lazily-computed-values) to create a cycle is by
+assigning a computed field to one that *is not* the result of computation. This
+is forbidden:
+
+```python
+import ml_collections
+from ml_collections.config_dict import config_dict
+
+config = ml_collections.ConfigDict()
+config.integer_field = 1
+config.bigger_integer_field = config.get_ref('integer_field') + 10
+
+try:
+  # Raises a MutabilityError because setting config.integer_field would
+  # cause a cycle.
+  config.integer_field = config.get_ref('bigger_integer_field') + 2
+except config_dict.MutabilityError as e:
+  print(e)
+```
+
+### Advanced usage
+
+Here are some more advanced examples showing lazy computation with different
+operators and data types.
+
+```python
+import ml_collections
+
+config = ml_collections.ConfigDict()
+config.float_field = 12.6
+config.integer_field = 123
+config.list_field = [0, 1, 2]
+
+config.float_multiply_field = config.get_ref('float_field') * 3
+print(config.float_multiply_field)  # Prints 37.8
+
+config.float_field = 10.0
+print(config.float_multiply_field)  # Prints 30.0
+
+config.longer_list_field = config.get_ref('list_field') + [3, 4, 5]
+print(config.longer_list_field)  # Prints [0, 1, 2, 3, 4, 5]
+
+config.list_field = [-1]
+print(config.longer_list_field)  # Prints [-1, 3, 4, 5]
+
+# Both operands can be references
+config.ref_subtraction = (
+    config.get_ref('float_field') - config.get_ref('integer_field'))
+print(config.ref_subtraction)  # Prints -113.0
+
+config.integer_field = 10
+print(config.ref_subtraction)  # Prints 0.0
+```
+
+### Equality checking
+
+You can use `==` and `.eq_as_configdict()` to check equality among `ConfigDict`
+and `FrozenConfigDict` objects.
+
+```python
+import ml_collections
+
+dict_1 = {'list': [1, 2]}
+dict_2 = {'list': (1, 2)}
+cfg_1 = ml_collections.ConfigDict(dict_1)
+frozen_cfg_1 = ml_collections.FrozenConfigDict(dict_1)
+frozen_cfg_2 = ml_collections.FrozenConfigDict(dict_2)
+
+# True because FrozenConfigDict converts lists to tuples
+print(frozen_cfg_1.items() == frozen_cfg_2.items())
+# False because == distinguishes the underlying difference
+print(frozen_cfg_1 == frozen_cfg_2)
+
+# False because == distinguishes these types
+print(frozen_cfg_1 == cfg_1)
+# But eq_as_configdict() treats both as ConfigDict, so these are True:
+print(frozen_cfg_1.eq_as_configdict(cfg_1))
+print(cfg_1.eq_as_configdict(frozen_cfg_1))
+```
+
+### Equality checking with lazy computation
+
+Equality checks see if the computed values are the same. Equality is satisfied
+if two sets of computations are different as long as they result in the same
+value.
+
+```python
+import ml_collections
+
+cfg_1 = ml_collections.ConfigDict()
+cfg_1.a = 1
+cfg_1.b = cfg_1.get_ref('a') + 2
+
+cfg_2 = ml_collections.ConfigDict()
+cfg_2.a = 1
+cfg_2.b = cfg_2.get_ref('a') * 3
+
+# True because all computed values are the same
+print(cfg_1 == cfg_2)
+```
+
+### Locking and copying
+
+Here is an example with `lock()` and `deepcopy()`:
+
+```python
+import copy
+import ml_collections
+
+cfg = ml_collections.ConfigDict()
+cfg.integer_field = 123
+
+# Locking prohibits the addition and deletion of new fields but allows
+# modification of existing values.
+cfg.lock()
+try:
+  cfg.integer_field = 124  # Raises AttributeError and suggests valid field.
+except AttributeError as e:
+  print(e)
+with cfg.unlocked():
+  cfg.integer_field = 1555  # Works fine too.
+
+# Get a copy of the config dict.
+new_cfg = copy.deepcopy(cfg)
+new_cfg.integer_field = -123  # Works fine.
+
+print(cfg)
+```
+
+### Dictionary attributes and initialization
+
+```python
+import ml_collections
+
+referenced_dict = {'inner_float': 3.14}
+d = {
+    'referenced_dict_1': referenced_dict,
+    'referenced_dict_2': referenced_dict,
+    'list_containing_dict': [{'key': 'value'}],
+}
+
+# We can initialize on a dictionary
+cfg = ml_collections.ConfigDict(d)
+
+# Reference structure is preserved
+print(id(cfg.referenced_dict_1) == id(cfg.referenced_dict_2))  # True
+
+# And the dict attributes have been converted to ConfigDict
+print(type(cfg.referenced_dict_1))  # ConfigDict
+
+# However, the initialization does not look inside of lists, so dicts inside
+# lists are not converted to ConfigDict
+print(type(cfg.list_containing_dict[0]))  # dict
+```
+
+### More Examples
+
+For more examples, take a look at
+[`ml_collections/config_dict/examples/`](https://github.com/google/ml_collections/tree/master/ml_collections/config_dict/examples)
+
+For examples and gotchas specifically about initializing a ConfigDict, see
+[`ml_collections/config_dict/examples/config_dict_initialization.py`](https://github.com/google/ml_collections/blob/master/ml_collections/config_dict/examples/config_dict_initialization.py).
+
+## Config Flags
+
+This library adds flag definitions to `absl.flags` to handle config files. It
+does not wrap `absl.flags` so if using any standard flag definitions alongside
+config file flags, users must also import `absl.flags`.
+
+Currently, this module adds two new flag types, namely `DEFINE_config_file`
+which accepts a path to a Python file that generates a configuration, and
+`DEFINE_config_dict` which accepts a configuration directly. Configurations are
+dict-like structures (see [ConfigDict](#configdict)) whose nested elements
+can be overridden using special command-line flags. See the examples below
+for more details.
+
+### Usage
+
+Use `ml_collections.config_flags` alongside `absl.flags`. For
+example:
+
+`script.py`:
+
+```python
+from absl import app
+from absl import flags
+
+from ml_collections.config_flags import config_flags
+
+FLAGS = flags.FLAGS
+config_flags.DEFINE_config_file('my_config')
+
+def main(_):
+  print(FLAGS.my_config)
+
+if __name__ == '__main__':
+  app.run(main)
+```
+
+`config.py`:
+
+```python
+# Note that this is a valid Python script.
+# get_config() can return an arbitrary dict-like object. However, it is advised
+# to use ml_collections.ConfigDict.
+# See ml_collections/config_dict/examples/config_dict_basic.py
+
+import ml_collections
+
+def get_config():
+  config = ml_collections.ConfigDict()
+  config.field1 = 1
+  config.field2 = 'tom'
+  config.nested = ml_collections.ConfigDict()
+  config.nested.field = 2.23
+  config.tuple = (1, 2, 3)
+  return config
+```
+
+Now, after running:
+
+```bash
+python script.py --my_config=config.py \
+                 --my_config.field1=8 \
+                 --my_config.nested.field=2.1 \
+                 --my_config.tuple='(1, 2, (1, 2))'
+```
+
+we get:
+
+```
+field1: 8
+field2: tom
+nested:
+  field: 2.1
+tuple: !!python/tuple
+- 1
+- 2
+- !!python/tuple
+  - 1
+  - 2
+```
+
+Usage of `DEFINE_config_dict` is similar to `DEFINE_config_file`, the main
+difference is the configuration is defined in `script.py` instead of in a
+separate file.
+
+`script.py`:
+
+```python
+from absl import app
+from absl import flags
+
+import ml_collections
+from ml_collections.config_flags import config_flags
+
+config = ml_collections.ConfigDict()
+config.field1 = 1
+config.field2 = 'tom'
+config.nested = ml_collections.ConfigDict()
+config.nested.field = 2.23
+config.tuple = (1, 2, 3)
+
+FLAGS = flags.FLAGS
+config_flags.DEFINE_config_dict('my_config', config)
+
+def main(_):
+  print(FLAGS.my_config)
+
+if __name__ == '__main__':
+  app.run()
+```
+
+`config_file` flags are compatible with the command-line flag syntax. All the
+following options are supported for non-boolean values in configurations:
+
+*   `-(-)config.field=value`
+*   `-(-)config.field value`
+
+Options for boolean values are slightly different:
+
+*   `-(-)config.boolean_field`: set boolean value to True.
+*   `-(-)noconfig.boolean_field`: set boolean value to False.
+*   `-(-)config.boolean_field=value`: `value` is `true`, `false`, `True` or
+    `False`.
+
+Note that `-(-)config.boolean_field value` is not supported.
+
+### Parameterising the get_config() function
+
+It's sometimes useful to be able to pass parameters into `get_config`, and
+change what is returned based on this configuration. One example is if you are
+grid searching over parameters which have a different hierarchical structure -
+the flag needs to be present in the resulting ConfigDict. It would be possible
+to include the union of all possible leaf values in your ConfigDict,
+but this produces a confusing config result as you have to remember which
+parameters will actually have an effect and which won't.
+
+A better system is to pass some configuration, indicating which structure of
+ConfigDict should be returned. An example is the following config file:
+
+```python
+import ml_collections
+
+def get_config(config_string):
+  possible_structures = {
+      'linear': ml_collections.ConfigDict({
+          'model_constructor': 'snt.Linear',
+          'model_config': ml_collections.ConfigDict({
+              'output_size': 42,
+          }),
+      'lstm': ml_collections.ConfigDict({
+          'model_constructor': 'snt.LSTM',
+          'model_config': ml_collections.ConfigDict({
+              'hidden_size': 108,
+          })
+      })
+  }
+
+  return possible_structures[config_string]
+```
+
+The value of `config_string` will be anything that is to the right of the first
+colon in the config file path, if one exists. If no colon exists, no value is
+passed to `get_config` (producing a TypeError if `get_config` expects a value.)
+
+The above example can be run like:
+
+```bash
+python script.py -- --config=path_to_config.py:linear \
+                    --config.model_config.output_size=256
+```
+
+or like:
+
+```bash
+python script.py -- --config=path_to_config.py:lstm \
+                    --config.model_config.hidden_size=512
+```
+
+### Additional features
+
+*   Loads any valid python script which defines `get_config()` function
+    returning any python object.
+*   Automatic locking of the loaded object, if the loaded object defines a
+    callable `.lock()` method.
+*   Supports command-line overriding of arbitrarily nested values in dict-like
+    objects (with key/attribute based getters/setters) of the following types:
+    *   `types.IntType` (integer)
+    *   `types.FloatType` (float)
+    *   `types.BooleanType` (bool)
+    *   `types.StringType` (string)
+    *   `types.TupleType` (tuple)
+*   Overriding is type safe.
+*   Overriding of `TupleType` can be done by passing in the `tuple` as a string
+    (see the example in the [Usage](#usage) section).
+*   The overriding `tuple` object can be of a different size and have different
+    types than the original. Nested tuples are also supported.
+
+## Authors
+*   Sergio Gómez Colmenarejo - sergomez@google.com
+*   Wojciech Marian Czarnecki - lejlot@google.com
+*   Nicholas Watters
+*   Mohit Reddy - mohitreddy@google.com
+
+
diff --git a/ml_collections.egg-info/SOURCES.txt b/ml_collections.egg-info/SOURCES.txt
index 1bbcf2e..07ab224 100644
--- a/ml_collections.egg-info/SOURCES.txt
+++ b/ml_collections.egg-info/SOURCES.txt
@@ -1,5 +1,11 @@
+AUTHORS
+LICENSE
+MANIFEST.in
 README.md
+requirements-test.txt
+requirements.txt
 setup.py
+docs/conf.py
 ml_collections/__init__.py
 ml_collections.egg-info/PKG-INFO
 ml_collections.egg-info/SOURCES.txt
@@ -25,12 +31,14 @@ ml_collections/config_flags/__init__.py
 ml_collections/config_flags/config_flags.py
 ml_collections/config_flags/tuple_parser.py
 ml_collections/config_flags/examples/config.py
+ml_collections/config_flags/examples/define_config_dataclass_basic.py
 ml_collections/config_flags/examples/define_config_dict_basic.py
 ml_collections/config_flags/examples/define_config_file_basic.py
 ml_collections/config_flags/examples/examples_test.py
 ml_collections/config_flags/examples/parameterised_config.py
 ml_collections/config_flags/tests/config_overriding_test.py
 ml_collections/config_flags/tests/configdict_config.py
+ml_collections/config_flags/tests/dataclass_overriding_test.py
 ml_collections/config_flags/tests/fieldreference_config.py
 ml_collections/config_flags/tests/ioerror_config.py
 ml_collections/config_flags/tests/mini_config.py
diff --git a/ml_collections.egg-info/requires.txt b/ml_collections.egg-info/requires.txt
index 6f289bb..32b75f6 100644
--- a/ml_collections.egg-info/requires.txt
+++ b/ml_collections.egg-info/requires.txt
@@ -1,7 +1,10 @@
-absl-py
 PyYAML
-six
+absl-py
 contextlib2
+six
 
 [:python_version < "3.5"]
 typing
+
+[:python_version < "3.7"]
+dataclasses
diff --git a/ml_collections.egg-info/top_level.txt b/ml_collections.egg-info/top_level.txt
index 50b72e9..759af71 100644
--- a/ml_collections.egg-info/top_level.txt
+++ b/ml_collections.egg-info/top_level.txt
@@ -1 +1,4 @@
+build
+dist
+docs
 ml_collections
diff --git a/ml_collections/__init__.py b/ml_collections/__init__.py
index f2096bb..1264616 100644
--- a/ml_collections/__init__.py
+++ b/ml_collections/__init__.py
@@ -1,4 +1,4 @@
-# Copyright 2020 The ML Collections Authors.
+# Copyright 2021 The ML Collections Authors.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
diff --git a/ml_collections/config_dict/__init__.py b/ml_collections/config_dict/__init__.py
index 5f448ed..a0dbc1e 100644
--- a/ml_collections/config_dict/__init__.py
+++ b/ml_collections/config_dict/__init__.py
@@ -1,4 +1,4 @@
-# Copyright 2020 The ML Collections Authors.
+# Copyright 2021 The ML Collections Authors.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
diff --git a/ml_collections/config_dict/config_dict.py b/ml_collections/config_dict/config_dict.py
index 2027e60..554b78e 100644
--- a/ml_collections/config_dict/config_dict.py
+++ b/ml_collections/config_dict/config_dict.py
@@ -1,4 +1,4 @@
-# Copyright 2020 The ML Collections Authors.
+# Copyright 2021 The ML Collections Authors.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -23,12 +23,14 @@ configurations of experiments and models.
 
 import abc
 import collections
+from collections import abc as collections_abc
 import contextlib
 import difflib
 import functools
 import inspect
 import json
 import operator
+from typing import Any, Mapping, Optional
 
 from absl import logging
 
@@ -62,6 +64,11 @@ class JSONDecodeError(Exception):
 _NoneType = type(None)
 
 
+def _is_callable_type(field_type):
+  """Tries to ensure: `_is_callable_type(type(obj)) == callable(obj)`."""
+  return any('__call__' in c.__dict__ for c in field_type.__mro__)
+
+
 def _is_type_safety_violation(value, field_type):
   """Helper function for type safety exceptions.
 
@@ -78,8 +85,11 @@ def _is_type_safety_violation(value, field_type):
   # Allow None to override and be overridden by any type.
   if value is None or field_type == _NoneType:
     return False
+  elif isinstance(value, field_type):
+    return False
   else:
-    return not isinstance(value, field_type)
+    # A callable can overridde a callable.
+    return not (callable(value) and _is_callable_type(field_type))
 
 
 def _safe_cast(value, field_type, type_safe=False):
@@ -157,30 +167,29 @@ class _Op(collections.namedtuple('_Op', ['fn', 'args'])):
 
 
 @functools.total_ordering
-class FieldReference(object):
+class FieldReference:
   """Reference to a configuration element.
 
-  Typed configuration element that can take a None default value. Example:
+  Typed configuration element that can take a None default value. Example::
 
-      from ml_collections import config_dict
+    from ml_collections import config_dict
 
-      cfg_field = config_dict.FieldReference(0)
-      cfg = config_dict.ConfigDict({
-          'optional': configdict.FieldReference(None, field_type=str)
-          'field': cfg_field,
-          'nested': {'field': cfg_field}
-      })
+    cfg_field = config_dict.FieldReference(0)
+    cfg = config_dict.ConfigDict({
+        'optional': configdict.FieldReference(None, field_type=str)
+        'field': cfg_field,
+        'nested': {'field': cfg_field}
+    })
 
-      with self.assertRaises(TypeError):
-        cfg.optional = 10  # Raises an error because it's defined as an
-                           # intfield.
+    with self.assertRaises(TypeError):
+      cfg.optional = 10  # Raises an error because it's defined as an
+                          # intfield.
 
-      cfg.field = 1  # Changes the value of both cfg.field and cfg.nested.field.
-      print(cfg)
+    cfg.field = 1  # Changes the value of both cfg.field and cfg.nested.field.
+    print(cfg)
 
-  This class also supports lazy computation. Example:
+  This class also supports lazy computation. Example::
 
-    ```python
     ref = config_dict.FieldReference(0)
 
     # Using ref in a standard operation returns another FieldReference. The new
@@ -193,7 +202,6 @@ class FieldReference(object):
 
     ref.set(-2)  # change ref's value again
     print(ref_plus_ten.get())  # Prints 8 because ref's value is -2
-    ```
   """
 
   def __init__(self, default, field_type=None, op=None, required=False):
@@ -387,6 +395,9 @@ class FieldReference(object):
   def identity(self):
     return self._apply_op(lambda x: x)
 
+  def attr(self, attr_name):
+    return self._apply_op(operator.attrgetter(attr_name))
+
   def __add__(self, other):
     return self._apply_op(operator.add, other)
 
@@ -409,10 +420,10 @@ class FieldReference(object):
     return self._apply_op(rmul)
 
   def __div__(self, other):
-    return self._apply_op(operator.div, other)
+    return self._apply_op(operator.truediv, other)
 
   def __rdiv__(self, other):
-    rdiv = functools.partial(operator.div, other)
+    rdiv = functools.partial(operator.truediv, other)
     return self._apply_op(rdiv)
 
   def __truediv__(self, other):
@@ -548,7 +559,7 @@ def _configdict_fill_seed(seed, initial_dictionary, visit_map=None):
     seed.__setattr__(key, value)
 
 
-class ConfigDict(object):
+class ConfigDict:
   # pylint: disable=line-too-long
   """Base class for configuration objects used in DeepMind.
 
@@ -558,7 +569,7 @@ class ConfigDict(object):
   - it has dot-based access as well as dict-style key access,
   - it is type safe (once a value is set one cannot change its type).
 
-  Typical usage eaxmple:
+  Typical usage example::
 
     from ml_collections import config_dict
 
@@ -571,23 +582,23 @@ class ConfigDict(object):
 
     print(cfg)
 
-  Config dictionaries can also be used to pass named arguments to functions:
+  Config dictionaries can also be used to pass named arguments to functions::
 
-      from ml_collections import config_dict
+    from ml_collections import config_dict
 
-      def print_point(x, y):
-        print "({},{})".format(x, y)
+    def print_point(x, y):
+      print "({},{})".format(x, y)
 
-      point = config_dict.ConfigDict()
-      point.x = 1
-      point.y = 2
-      print_point(**point)
+    point = config_dict.ConfigDict()
+    point.x = 1
+    point.y = 2
+    print_point(**point)
 
   Note that, depending on your use case, it may be easier to use the `create`
-  function in this package to construct a `ConfigDict`:
+  function in this package to construct a `ConfigDict`::
 
-     from ml_collections.config_dict import config_dict
-     point = config_dict.create(x=1, y=2)
+    from ml_collections.config_dict import config_dict
+    point = config_dict.create(x=1, y=2)
 
   Differently from standard `dicts`, `ConfigDicts` also have the nice property
   that iterating over them is deterministic, in a fashion similar to
@@ -595,20 +606,28 @@ class ConfigDict(object):
   """
   # pylint: enable=line-too-long
 
+  # Loosen the static type checking requirements.
+  _HAS_DYNAMIC_ATTRIBUTES = True
+
   def __init__(
-      self, initial_dictionary=None, type_safe=True, convert_dict=True):
+      self,
+      initial_dictionary: Optional[Mapping[str, Any]] = None,
+      type_safe: bool = True,
+      convert_dict: bool = True,
+  ):
     """Creates an instance of ConfigDict.
 
     Warning: In most cases, this faithfully reproduces the reference structure
     of initial_dictionary, even if initial_dictionary is self-referencing.
     However, unexpected behavior occurs if self-references are contained within
-    list, tuple, or custom types. For example:
-        d = {}
-        d['a'] = d
-        d['b'] = [d]
-        cd = ConfigDict(d)
-        cd.a    # refers to cd, type ConfigDict. Expected behavior.
-        cd.b    # refers to d, type dict. Unexpected behavior.
+    list, tuple, or custom types. For example::
+
+      d = {}
+      d['a'] = d
+      d['b'] = [d]
+      cd = ConfigDict(d)
+      cd.a    # refers to cd, type ConfigDict. Expected behavior.
+      cd.b    # refers to d, type dict. Unexpected behavior.
 
     Warning: FieldReference values may be changed. If initial_dictionary
     contains a FieldReference with a value of type dict or FrozenConfigDict,
@@ -616,16 +635,19 @@ class ConfigDict(object):
 
     Args:
       initial_dictionary: May be one of the following:
-          1) dict. In this case, all values of initial_dictionary that are
-             dictionaries are also be converted to ConfigDict. However,
-             dictionaries within values of non-dict type are untouched.
-          2) ConfigDict. In this case, all attributes are uncopied, and only the
-             top-level object (self) is re-addressed. This is the same behavior
-             as Python dict, list, and tuple.
-          3) FrozenConfigDict. In this case, initial_dictionary is converted to
-             a ConfigDict version of the initial dictionary for the
-             FrozenConfigDict (reversing any mutability changes FrozenConfigDict
-             made).
+
+        1) dict. In this case, all values of initial_dictionary that are
+        dictionaries are also be converted to ConfigDict. However,
+        dictionaries within values of non-dict type are untouched.
+
+        2) ConfigDict. In this case, all attributes are uncopied, and only the
+        top-level object (self) is re-addressed. This is the same behavior
+        as Python dict, list, and tuple.
+
+        3) FrozenConfigDict. In this case, initial_dictionary is converted to
+        a ConfigDict version of the initial dictionary for the
+        FrozenConfigDict (reversing any mutability changes FrozenConfigDict
+        made).
       type_safe: If set to True, once an attribute value is assigned, its type
           cannot be overridden without .ignore_type() context manager
           (default: True).
@@ -651,7 +673,7 @@ class ConfigDict(object):
                                           initial_dictionary.is_type_safe)
 
   @property
-  def is_type_safe(self):
+  def is_type_safe(self) -> bool:
     """Returns True if config dict is type safe."""
     return self._type_safe
 
@@ -660,7 +682,7 @@ class ConfigDict(object):
     """Returns True if it is converting dicts to ConfigDict automatically."""
     return self._convert_dict
 
-  def lock(self):
+  def lock(self) -> 'ConfigDict':
     """Locks object, preventing user from adding new fields.
 
     Returns:
@@ -680,11 +702,11 @@ class ConfigDict(object):
     return self
 
   @property
-  def is_locked(self):
+  def is_locked(self) -> bool:
     """Returns True if object is locked."""
     return self._locked
 
-  def unlock(self):
+  def unlock(self) -> 'ConfigDict':
     """Grants user the ability to add new fields to ConfigDict.
 
     In most cases, the unlocked() context manager should be preferred to the
@@ -701,7 +723,7 @@ class ConfigDict(object):
         element.unlock()
     return self
 
-  def get(self, key, default=None):
+  def get(self, key: str, default=None):
     """Returns value if key is present, or a user defined value otherwise."""
     try:
       return self[key]
@@ -725,19 +747,17 @@ class ConfigDict(object):
   def get_oneway_ref(self, key):
     """Returns a one-way FieldReference.
 
-    Example:
+    Example::
 
-    ```
-    cfg = collections.ConfigDict(dict(a=1))
-    cfg.b = cfg.get_oneway_ref('a')
+      cfg = ml_collections.ConfigDict(dict(a=1))
+      cfg.b = cfg.get_oneway_ref('a')
 
-    cfg.a = 2
-    print(cfg.b)  # 2
+      cfg.a = 2
+      print(cfg.b)  # 2
 
-    cfg.b = 3
-    print(cfg.a)  # 2 (would have been 3 if using get_ref())
-    print(cfg.b)  # 3
-    ```
+      cfg.b = 3
+      print(cfg.a)  # 2 (would have been 3 if using get_ref())
+      print(cfg.b)  # 3
 
     Args:
       key: Key for field we want to reference.
@@ -855,7 +875,7 @@ class ConfigDict(object):
                                                              request)
     return message
 
-  def __delitem__(self, key):
+  def __delitem__(self, key: str):
     if self.is_locked:
       raise KeyError('This ConfigDict is locked, you have to unlock it before '
                      'trying to delete a field.')
@@ -872,7 +892,7 @@ class ConfigDict(object):
     except KeyError as e:
       raise KeyError(self._generate_did_you_mean_message(key, str(e)))
 
-  def __getitem__(self, key):
+  def __getitem__(self, key: str):
     if '.' in key:
       # As per the check in __setitem__ above, keys cannot contain dots.
       # Hence, we can use dots to do recursive calls.
@@ -888,15 +908,25 @@ class ConfigDict(object):
     except KeyError as e:
       raise KeyError(self._generate_did_you_mean_message(key, str(e)))
 
-  def __contains__(self, key):
+  def __contains__(self, key: str):
     return key in self._fields
 
-  def __repr__(self):
-    return yaml.dump(self.to_dict(preserve_field_references=True),
-                     default_flow_style=False)
-
-  def __str__(self):
-    return yaml.dump(self.to_dict())
+  def __repr__(self) -> str:
+    # We want __repr__ to always run without throwing an exception,
+    # even if the config dict is not YAML serialisable.
+    try:
+      return yaml.dump(self.to_dict(preserve_field_references=True),
+                       default_flow_style=False)
+    except Exception:  # pylint: disable=broad-except
+      return repr(self.to_dict())
+
+  def __str__(self) -> str:
+    # We want __str__ to always run without throwing an exception,
+    # even if the config dict is not YAML serialisable.
+    try:
+      return yaml.dump(self.to_dict())
+    except Exception:  # pylint: disable=broad-except
+      return str(self.to_dict())
 
   def keys(self):
     """Returns the sorted list of all the keys defined in a config."""
@@ -963,11 +993,11 @@ class ConfigDict(object):
     """Type-invariant equals.
 
     This is like `__eq__`, except it does not distinguish `FrozenConfigDict`
-    from `ConfigDict`. For example:
+    from `ConfigDict`. For example::
 
-        cd = ConfigDict()
-        fcd = FrozenConfigDict()
-        fcd.eq_as_configdict(cd)  # Returns True
+      cd = ConfigDict()
+      fcd = FrozenConfigDict()
+      fcd.eq_as_configdict(cd)  # Returns True
 
     Args:
       other: Object to compare self to.
@@ -986,6 +1016,23 @@ class ConfigDict(object):
   def to_yaml(self, **kwargs):
     """Returns a YAML representation of the object.
 
+    ConfigDict serializes types of fields as well as the values of fields
+    themselves. Deserializing the YAML representation hence requires using
+    YAML's UnsafeLoader:
+
+    ```
+    yaml.load(cfg.to_yaml(), Loader=yaml.UnsafeLoader)
+    ```
+
+    or equivalently:
+
+    ```
+    yaml.unsafe_load(cfg.to_yaml())
+    ```
+
+    Please see the PyYAML documentation and https://msg.pyyaml.org/load for more
+    details on the consequences of this.
+
     Args:
       **kwargs: Keyword arguments for yaml.dump.
 
@@ -1143,6 +1190,8 @@ class ConfigDict(object):
     """
     visit_map = visit_map or {}
     config_dict_copy = self.__class__()
+    super(ConfigDict, config_dict_copy).__setattr__('_convert_dict',
+                                                    self.convert_dict)
     visit_map[id(self)] = config_dict_copy
 
     for key, value in six.iteritems(self._fields):
@@ -1204,9 +1253,9 @@ class ConfigDict(object):
       if isinstance(field, ConfigDict):
         managers.append(field.ignore_type)
       # Recursively add elements in nested collections.
-      elif isinstance(field, collections.Mapping):
+      elif isinstance(field, collections_abc.Mapping):
         fields.extend(six.iteritems(field))
-      elif isinstance(field, (collections.Sequence, collections.Set)):
+      elif isinstance(field, (collections_abc.Sequence, collections_abc.Set)):
         fields.extend(field)
 
     super(ConfigDict, self).__setattr__('_type_safe', False)
@@ -1260,7 +1309,7 @@ class ConfigDict(object):
       if isinstance(other, ConfigDict):
         iteritems_kwargs['preserve_field_references'] = True
 
-      for key, value in six.iteritems(other, **iteritems_kwargs):
+      for key, value in six.iteritems(other, **iteritems_kwargs):  # pytype: disable=wrong-keyword-args
         if key not in self:
           self[key] = value
         elif isinstance(self._fields[key], ConfigDict):
@@ -1294,55 +1343,55 @@ class ConfigDict(object):
   def update_from_flattened_dict(self, flattened_dict, strip_prefix=''):
     """In-place updates values taken from a flattened dict.
 
-    This allows a potentially nested source `ConfigDict` of the following form:
+    This allows a potentially nested source `ConfigDict` of the following form::
 
-    cfg = ConfigDict({
-        'a': 1,
-        'b': {
-            'c': {
-                'd': 2
-            }
-        }
-    })
+      cfg = ConfigDict({
+          'a': 1,
+          'b': {
+              'c': {
+                  'd': 2
+              }
+          }
+      })
 
     to be updated from a dict containing paths navigating to child items, of the
-    following form:
+    following form::
 
-    updates = {
-        'a': 2,
-        'b.c.d': 3
-    }
+      updates = {
+          'a': 2,
+          'b.c.d': 3
+      }
 
     This filters `paths_dict` to only contain paths starting with
     `strip_prefix` and strips the prefix when applying the update.
 
-    For example, consider we have the following values returned as flags:
-
-    flags = {
-        'flag1': x,
-        'flag2': y,
-        'config': 'some_file.py',
-        'config.a.b': 1,
-        'config.a.c': 2
-    }
-
-    config = ConfigDict({
-        'a': {
-            'b': 0,
-            'c': 0
-        }
-    })
+    For example, consider we have the following values returned as flags::
 
-    config.update_from_flattened_dict(flags, 'config.')
+      flags = {
+          'flag1': x,
+          'flag2': y,
+          'config': 'some_file.py',
+          'config.a.b': 1,
+          'config.a.c': 2
+      }
 
-    Then we will now have:
+      config = ConfigDict({
+          'a': {
+              'b': 0,
+              'c': 0
+          }
+      })
 
-    config = ConfigDict({
-        'a': {
-            'b': 1,
-            'c': 2
-        }
-    })
+      config.update_from_flattened_dict(flags, 'config.')
+
+    Then we will now have::
+
+      config = ConfigDict({
+          'a': {
+              'b': 1,
+              'c': 2
+          }
+      })
 
     Args:
       flattened_dict: A mapping (key path) -> value.
@@ -1618,13 +1667,17 @@ class FrozenConfigDict(ConfigDict):
 
     Args:
       initial_dictionary: May be one of the following:
-          1) dict. In this case all values of initial_dictionary that are
-             dictionaries are also converted to FrozenConfigDict. If there are
-             dictionaries contained in lists or tuples, an error is raised.
-          2) ConfigDict. In this case all ConfigDict attributes are also
-             converted to FrozenConfigDict.
-          3) FrozenConfigDict. In this case all attributes are uncopied, and
-             only the top-level object (self) is re-addressed.
+
+        1) dict. In this case all values of initial_dictionary that are
+        dictionaries are also converted to FrozenConfigDict. If there are
+        dictionaries contained in lists or tuples, an error is raised.
+
+        2) ConfigDict. In this case all ConfigDict attributes are also
+        converted to FrozenConfigDict.
+
+        3) FrozenConfigDict. In this case all attributes are uncopied, and
+        only the top-level object (self) is re-addressed.
+
       type_safe: See ConfigDict documentation. Note that this only matters
           if the FrozenConfigDict is converted to ConfigDict at some point.
     """
@@ -1836,23 +1889,23 @@ def create(**kwargs):
   """Creates a `ConfigDict` with the given named arguments as key-value pairs.
 
   This allows for simple dictionaries whose elements can be accessed directly
-  using field access:
+  using field access::
 
-      from ml_collections.config_dict import config_dict
-      point = config_dict.create(x=1, y=2)
-      print(point.x, point.y)
+    from ml_collections.config_dict import config_dict
+    point = config_dict.create(x=1, y=2)
+    print(point.x, point.y)
 
-  This is particularly useful for compactly writing nested configurations:
+  This is particularly useful for compactly writing nested configurations::
 
-      config = config_dict.create(
-          data=config_dict.create(
-              game='freeway',
-              frame_size=100),
-          model=config_dict.create(num_hidden=1000))
+    config = config_dict.create(
+      data=config_dict.create(
+        game='freeway',
+        frame_size=100),
+      model=config_dict.create(num_hidden=1000))
 
   The reason for the existence of this function is that it simplifies the
   code required for the majority of the use cases of `ConfigDict`, compared
-  to using either `ConfigDict` or `namedtuple`s. Examples of such use cases
+  to using either `ConfigDict` or `namedtuple's`. Examples of such use cases
   include training script configuration, and returning multiple named values.
 
   Args:
@@ -1868,11 +1921,11 @@ def create(**kwargs):
 def placeholder(field_type, required=False):
   """Defines an entry in a ConfigDict that has no value yet.
 
-  Example:
+  Example::
 
-      config = configdict.create(
-          batch_size = configdict.placeholder(int),
-          frame_shape = configdict.placeholder(tf.TensorShape))
+    config = configdict.create(
+        batch_size = configdict.placeholder(int),
+        frame_shape = configdict.placeholder(tf.TensorShape))
 
   Args:
     field_type: type of value.
@@ -1888,18 +1941,18 @@ def placeholder(field_type, required=False):
 def required_placeholder(field_type):
   """Defines an entry in a ConfigDict with unknown but required value.
 
-  Example:
+  Example::
 
-      config = configdict.create(
-          batch_size = configdict.required_placeholder(int))
+    config = configdict.create(
+        batch_size = configdict.required_placeholder(int))
 
-      try:
-        print(config.batch_size)
-      except RequiredValueError:
-        pass
+    try:
+      print(config.batch_size)
+    except RequiredValueError:
+      pass
 
-      config.batch_size = 10
-      print(config.batch_size)  # 10
+    config.batch_size = 10
+    print(config.batch_size)  # 10
 
   Args:
     field_type: type of value.
diff --git a/ml_collections/config_dict/examples/config.py b/ml_collections/config_dict/examples/config.py
index b57a68a..a1034d7 100644
--- a/ml_collections/config_dict/examples/config.py
+++ b/ml_collections/config_dict/examples/config.py
@@ -1,4 +1,4 @@
-# Copyright 2020 The ML Collections Authors.
+# Copyright 2021 The ML Collections Authors.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
diff --git a/ml_collections/config_dict/examples/config_dict_advanced.py b/ml_collections/config_dict/examples/config_dict_advanced.py
index 4e40cff..d8f0f04 100644
--- a/ml_collections/config_dict/examples/config_dict_advanced.py
+++ b/ml_collections/config_dict/examples/config_dict_advanced.py
@@ -1,4 +1,4 @@
-# Copyright 2020 The ML Collections Authors.
+# Copyright 2021 The ML Collections Authors.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -21,14 +21,10 @@ safety, iteration over fields, checking for a particular field, unpacking with
 """
 
 from absl import app
-from absl import flags
 from ml_collections.config_flags import config_flags
 import yaml
 
-
-FLAGS = flags.FLAGS
-
-config_flags.DEFINE_config_file(
+_CONFIG = config_flags.DEFINE_config_file(
     'my_config',
     default='ml_collections/config_dict/examples/config.py')
 
@@ -49,13 +45,13 @@ def print_section(name):
 def main(_):
   # Config is already loaded in FLAGS.my_config due to the logic hidden
   # in app.run().
-  config = FLAGS.my_config
+  config = _CONFIG.value
 
   print_section('Printing config.')
-  print(FLAGS.my_config)
+  print(config)
 
   # Config is of our type ConfigDict.
-  print('Type of the config {}'.format(type(FLAGS.my_config)))
+  print('Type of the config {}'.format(type(config)))
 
   # By default it is locked, thus you cannot add new fields.
   # This prevents you from misspelling your attribute name.
@@ -122,7 +118,7 @@ def main(_):
   # Note: __repr__ (not __str__) is the recommended representation, as it
   # preserves FieldReferences and placeholders.
   print_section('Loading dictionary from string representation.')
-  dictionary = yaml.load(repr(config))
+  dictionary = yaml.load(repr(config), yaml.UnsafeLoader)
   print('dict["object_reference"]["dict"]["dict"]["float"]={}'.format(
       dictionary['object_reference']['dict']['dict']['float']))
 
diff --git a/ml_collections/config_dict/examples/config_dict_basic.py b/ml_collections/config_dict/examples/config_dict_basic.py
index 87cfa37..00750af 100644
--- a/ml_collections/config_dict/examples/config_dict_basic.py
+++ b/ml_collections/config_dict/examples/config_dict_basic.py
@@ -1,4 +1,4 @@
-# Copyright 2020 The ML Collections Authors.
+# Copyright 2021 The ML Collections Authors.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
diff --git a/ml_collections/config_dict/examples/config_dict_initialization.py b/ml_collections/config_dict/examples/config_dict_initialization.py
index ee64dd5..501a4f1 100644
--- a/ml_collections/config_dict/examples/config_dict_initialization.py
+++ b/ml_collections/config_dict/examples/config_dict_initialization.py
@@ -1,4 +1,4 @@
-# Copyright 2020 The ML Collections Authors.
+# Copyright 2021 The ML Collections Authors.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
diff --git a/ml_collections/config_dict/examples/config_dict_lock.py b/ml_collections/config_dict/examples/config_dict_lock.py
index 47c8a88..a22be91 100644
--- a/ml_collections/config_dict/examples/config_dict_lock.py
+++ b/ml_collections/config_dict/examples/config_dict_lock.py
@@ -1,4 +1,4 @@
-# Copyright 2020 The ML Collections Authors.
+# Copyright 2021 The ML Collections Authors.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
diff --git a/ml_collections/config_dict/examples/config_dict_placeholder.py b/ml_collections/config_dict/examples/config_dict_placeholder.py
index a58904d..ce71d41 100644
--- a/ml_collections/config_dict/examples/config_dict_placeholder.py
+++ b/ml_collections/config_dict/examples/config_dict_placeholder.py
@@ -1,4 +1,4 @@
-# Copyright 2020 The ML Collections Authors.
+# Copyright 2021 The ML Collections Authors.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
diff --git a/ml_collections/config_dict/examples/examples_test.py b/ml_collections/config_dict/examples/examples_test.py
index 6133091..3c91cdd 100644
--- a/ml_collections/config_dict/examples/examples_test.py
+++ b/ml_collections/config_dict/examples/examples_test.py
@@ -1,4 +1,4 @@
-# Copyright 2020 The ML Collections Authors.
+# Copyright 2021 The ML Collections Authors.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
diff --git a/ml_collections/config_dict/examples/field_reference.py b/ml_collections/config_dict/examples/field_reference.py
index 5899c52..91e4394 100644
--- a/ml_collections/config_dict/examples/field_reference.py
+++ b/ml_collections/config_dict/examples/field_reference.py
@@ -1,4 +1,4 @@
-# Copyright 2020 The ML Collections Authors.
+# Copyright 2021 The ML Collections Authors.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
diff --git a/ml_collections/config_dict/examples/frozen_config_dict.py b/ml_collections/config_dict/examples/frozen_config_dict.py
index b500ca8..ea5ffdf 100644
--- a/ml_collections/config_dict/examples/frozen_config_dict.py
+++ b/ml_collections/config_dict/examples/frozen_config_dict.py
@@ -1,4 +1,4 @@
-# Copyright 2020 The ML Collections Authors.
+# Copyright 2021 The ML Collections Authors.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
diff --git a/ml_collections/config_dict/tests/config_dict_test.py b/ml_collections/config_dict/tests/config_dict_test.py
index 4b1fedf..482835b 100644
--- a/ml_collections/config_dict/tests/config_dict_test.py
+++ b/ml_collections/config_dict/tests/config_dict_test.py
@@ -1,4 +1,4 @@
-# Copyright 2020 The ML Collections Authors.
+# Copyright 2021 The ML Collections Authors.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -16,7 +16,8 @@
 """Tests for ml_collections.ConfigDict."""
 
 import abc
-import collections as python_collections
+from collections import abc as collections_abc
+import functools
 import json
 import pickle
 import sys
@@ -57,7 +58,7 @@ class _TestClass(six.with_metaclass(abc.ABCMeta, object)):
 _test_object = _TestClass()
 
 
-class _TestClassNoStr():  # pylint: disable=old-style-class
+class _TestClassNoStr():
   pass
 
 
@@ -290,6 +291,28 @@ class ConfigDictTest(parameterized.TestCase):
     self.assertEqual(cfg.long_field, int_value)
     self.assertIsInstance(cfg.long_field, expected)
 
+  def testOverrideCallable(self):
+    """Test that overriding a callable with a callable works."""
+
+    class SomeClass:
+
+      def __init__(self, x, power=1):
+        self.y = x**power
+
+      def factory(self, x):
+        return SomeClass(self.y + x)
+
+    fn1 = SomeClass
+    fn2 = lambda x: SomeClass(x, power=2)
+    fn3 = functools.partial(SomeClass, power=3)
+    fn4 = SomeClass(4.0).factory
+    cfg = ml_collections.ConfigDict()
+    for orig in [fn1, fn2, fn3, fn4]:
+      for new in [fn1, fn2, fn3, fn4]:
+        cfg.fn_field = orig
+        cfg.fn_field = new
+        self.assertEqual(cfg.fn_field, new)
+
   def testOverrideFieldReference(self):
     """Test overriding with FieldReference objects."""
     cfg = ml_collections.ConfigDict()
@@ -589,7 +612,7 @@ class ConfigDictTest(parameterized.TestCase):
       hash(cfg)
 
     # Ensure Python realizes ConfigDict is not hashable.
-    self.assertNotIsInstance(cfg, python_collections.Hashable)
+    self.assertNotIsInstance(cfg, collections_abc.Hashable)
 
   def testDidYouMeanFeature(self):
     """Tests 'did you mean' suggestions."""
@@ -675,7 +698,7 @@ class ConfigDictTest(parameterized.TestCase):
   def testDeYaml(self):
     """Tests YAML deserialization."""
     cfg = _get_test_config_dict()
-    deyamled = yaml.load(cfg.to_yaml())
+    deyamled = yaml.load(cfg.to_yaml(), yaml.UnsafeLoader)
     self.assertEqual(cfg, deyamled)
 
   def testJSONConversion(self):
@@ -723,7 +746,7 @@ class ConfigDictTest(parameterized.TestCase):
     cfg_dict.r1 = field
     cfg_dict.r2 = field
 
-    cfg_load = yaml.load(repr(cfg_dict))
+    cfg_load = yaml.load(repr(cfg_dict), yaml.UnsafeLoader)
 
     # Test FieldReferences are preserved
     cfg_load['r1'].set(2)
@@ -1317,6 +1340,21 @@ class ConfigDictUpdateTest(absltest.TestCase):
     self.assertIsInstance(cfg.a, dict)
     self.assertIsInstance(cfg.a['b'], dict)
 
+  def testConvertDictInCopyAndResolveReferences(self):
+    """Test conversion, or not, of dict in copy and resolve references."""
+    cfg = ml_collections.ConfigDict()
+    cfg.a = dict(b=dict(c=0))
+    copied_cfg = cfg.copy_and_resolve_references()
+    self.assertIsInstance(copied_cfg.a, ml_collections.ConfigDict)
+    self.assertIsInstance(copied_cfg.a.b, ml_collections.ConfigDict)
+
+    cfg = ml_collections.ConfigDict(convert_dict=False)
+    cfg.a = dict(b=dict(c=0))
+    copied_cfg = cfg.copy_and_resolve_references()
+    self.assertNotIsInstance(copied_cfg.a, ml_collections.ConfigDict)
+    self.assertIsInstance(copied_cfg.a, dict)
+    self.assertIsInstance(copied_cfg.a['b'], dict)
+
   def testConvertDictTypeCompat(self):
     """Test that automatic conversion to ConfigDict doesn't trigger type errors."""
     cfg = ml_collections.ConfigDict()
@@ -1331,7 +1369,7 @@ class ConfigDictUpdateTest(absltest.TestCase):
     This checks backward compatibility of deserialisation.
     """
     cfg = ml_collections.ConfigDict(dict(a=1))
-    self.assertTrue(yaml.load(cfg.to_yaml())._convert_dict)
+    self.assertTrue(yaml.load(cfg.to_yaml(), yaml.UnsafeLoader)._convert_dict)
 
   def testRecursiveRename(self):
     """Test recursive_rename.
diff --git a/ml_collections/config_dict/tests/field_reference_test.py b/ml_collections/config_dict/tests/field_reference_test.py
index cbe37e8..9f8d449 100644
--- a/ml_collections/config_dict/tests/field_reference_test.py
+++ b/ml_collections/config_dict/tests/field_reference_test.py
@@ -1,4 +1,4 @@
-# Copyright 2020 The ML Collections Authors.
+# Copyright 2021 The ML Collections Authors.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -532,6 +532,41 @@ class FieldReferenceTest(parameterized.TestCase):
     self._test_unary_operator(initial_value, operator.neg, true_value,
                               new_initial_value, new_true_value)
 
+  @parameterized.parameters(
+      {
+          'initial_value': config_dict.create(attribute=2),
+          'true_value': 2,
+          'new_initial_value': config_dict.create(attribute=3),
+          'new_true_value': 3,
+      },
+      {
+          'initial_value': config_dict.create(attribute={'a': 1}),
+          'true_value': config_dict.create(a=1),
+          'new_initial_value': config_dict.create(attribute={'b': 1}),
+          'new_true_value': config_dict.create(b=1),
+      },
+      {
+          'initial_value':
+              ml_collections.FieldReference(config_dict.create(attribute=2)),
+          'true_value':
+              ml_collections.FieldReference(2),
+          'new_initial_value':
+              config_dict.create(attribute=3),
+          'new_true_value':
+              3,
+      },
+      {
+          'initial_value': config_dict.placeholder(config_dict.ConfigDict),
+          'true_value': None,
+          'new_initial_value': config_dict.create(attribute=3),
+          'new_true_value': 3,
+      },
+  )
+  def testAttr(self, initial_value, true_value, new_initial_value,
+               new_true_value):
+    self._test_unary_operator(initial_value, lambda x: x.attr('attribute'),
+                              true_value, new_initial_value, new_true_value)
+
   @parameterized.parameters(
       {
           'initial_value': 3,
diff --git a/ml_collections/config_dict/tests/frozen_config_dict_test.py b/ml_collections/config_dict/tests/frozen_config_dict_test.py
index 156d8b5..ceb45da 100644
--- a/ml_collections/config_dict/tests/frozen_config_dict_test.py
+++ b/ml_collections/config_dict/tests/frozen_config_dict_test.py
@@ -1,4 +1,4 @@
-# Copyright 2020 The ML Collections Authors.
+# Copyright 2021 The ML Collections Authors.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -15,7 +15,7 @@
 # Lint as: python3
 """Tests for ml_collections.FrozenConfigDict."""
 
-import collections as python_collections
+from collections import abc as collections_abc
 import copy
 import pickle
 
@@ -357,7 +357,7 @@ class FrozenConfigDictTest(absltest.TestCase):
         hash(ml_collections.FrozenConfigDict(list_to_tuple)))
 
     # Ensure Python realizes FrozenConfigDict is hashable
-    self.assertIsInstance(_test_frozenconfigdict(), python_collections.Hashable)
+    self.assertIsInstance(_test_frozenconfigdict(), collections_abc.Hashable)
 
   def testUnhashableType(self):
     """Ensures __hash__() fails if FrozenConfigDict has unhashable value."""
diff --git a/ml_collections/config_flags/__init__.py b/ml_collections/config_flags/__init__.py
index 619e74b..14d619f 100644
--- a/ml_collections/config_flags/__init__.py
+++ b/ml_collections/config_flags/__init__.py
@@ -1,4 +1,4 @@
-# Copyright 2020 The ML Collections Authors.
+# Copyright 2021 The ML Collections Authors.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -15,7 +15,16 @@
 # Lint as: python 3
 """Config flags module."""
 
+from .config_flags import DEFINE_config_dataclass
 from .config_flags import DEFINE_config_dict
 from .config_flags import DEFINE_config_file
+from .config_flags import get_config_filename
+from .config_flags import get_override_values
 
-__all__ = ("DEFINE_config_dict", "DEFINE_config_file")
+__all__ = (
+    "DEFINE_config_dataclass",
+    "DEFINE_config_dict",
+    "DEFINE_config_file",
+    "get_config_filename",
+    "get_override_values",
+)
diff --git a/ml_collections/config_flags/config_flags.py b/ml_collections/config_flags/config_flags.py
index c8a5195..6f35848 100644
--- a/ml_collections/config_flags/config_flags.py
+++ b/ml_collections/config_flags/config_flags.py
@@ -1,4 +1,4 @@
-# Copyright 2020 The ML Collections Authors.
+# Copyright 2021 The ML Collections Authors.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -13,7 +13,7 @@
 # limitations under the License.
 
 # Lint as: python 3
-"""Complex configs commmand line parser."""
+"""Configuration commmand line parser."""
 
 import errno
 import imp
@@ -21,9 +21,11 @@ import os
 import re
 import sys
 import traceback
+from typing import Any, Dict, Generic, List, MutableMapping, Optional, Tuple, Type, TypeVar
 
 from absl import flags
 from absl import logging
+import dataclasses
 import ml_collections
 from ml_collections.config_flags import tuple_parser
 import six
@@ -75,12 +77,12 @@ class UnparsedFlagError(flags.Error):
 
 
 def DEFINE_config_file(  # pylint: disable=g-bad-name
-    name,
-    default=None,
-    help_string='path to config file.',
-    flag_values=FLAGS,
-    lock_config=True,
-    **kwargs):
+    name: str,
+    default: Optional[str] = None,
+    help_string: str = 'path to config file.',
+    flag_values: flags.FlagValues = FLAGS,
+    lock_config: bool = True,
+    **kwargs) -> flags.FlagHolder:
   r"""Defines flag for `ConfigDict` files compatible with absl flags.
 
   The flag's value should be a path to a valid python file which contains a
@@ -100,95 +102,78 @@ def DEFINE_config_file(  # pylint: disable=g-bad-name
 
   Typical usage example:
 
-  `script.py`:
+  `script.py`::
 
-  ```python
-  ...
-  from absl import flags
-  from ml_collections.config_flags import config_flags
+    from absl import flags
+    from ml_collections.config_flags import config_flags
 
-  FLAGS = flags.FLAGS
-  config_flags.DEFINE_config_file('my_config')
-  ...
+    FLAGS = flags.FLAGS
+    config_flags.DEFINE_config_file('my_config')
 
-  print(FLAGS.my_config)
-  ```
+    print(FLAGS.my_config)
 
-  `config.py`:
+  `config.py`::
 
-  ```python
-  def get_config():
-    return {
-        'field1': 1,
-        'field2': 'tom',
-        'nested': {
-            'field': 2.23,
-        },
-    }
-  ```
+    def get_config():
+      return {
+          'field1': 1,
+          'field2': 'tom',
+          'nested': {
+              'field': 2.23,
+          },
+      }
 
-  The following command:
+  The following command::
 
-  ```
-  python script.py -- --my_config=config.py
-                      --my_config.field1 8
-                      --my_config.nested.field=2.1
-  ```
+    python script.py -- --my_config=config.py
+                        --my_config.field1 8
+                        --my_config.nested.field=2.1
 
-  will print:
+  will print::
 
-  ```
-  {'field1': 8, 'field2': 'tom', 'nested': {'field': 2.1}}
-  ```
+    {'field1': 8, 'field2': 'tom', 'nested': {'field': 2.1}}
 
   It is possible to parameterise the get_config function, allowing it to
   return a differently structured result for different occasions. This is
   particularly useful when setting up hyperparameter sweeps across various
   network architectures.
 
-  `parameterised_config.py`:
-
-  ```python
-  def get_config(config_string):
-    possible_configs = {
-        'mlp': {
-            'constructor': 'snt.nets.MLP',
-            'config': {
-                'output_sizes': (128, 128, 1),
-            }
-        },
-        'lstm': {
-            'constructor': 'snt.LSTM',
-            'config': {
-                'hidden_size': 128,
-                'forget_bias': 1.0,
-            }
-        }
-    }
-    return possible_configs[config_string]
-  ```
+  `parameterised_config.py`::
+
+    def get_config(config_string):
+      possible_configs = {
+          'mlp': {
+              'constructor': 'snt.nets.MLP',
+              'config': {
+                  'output_sizes': (128, 128, 1),
+              }
+          },
+          'lstm': {
+              'constructor': 'snt.LSTM',
+              'config': {
+                  'hidden_size': 128,
+                  'forget_bias': 1.0,
+              }
+          }
+      }
+      return possible_configs[config_string]
 
   If a colon is present in the command line override for the config file,
   everything to the right of the colon is passed into the get_config function.
-  The following command lines will both function correctly:
+  The following command lines will both function correctly::
 
-  ```
-  python script.py -- --my_config=parameterised_config.py:mlp
-                      --my_config.config.output_sizes="(256,256,1)"
-  ```
+    python script.py -- --my_config=parameterised_config.py:mlp
+                        --my_config.config.output_sizes="(256,256,1)"
 
-  ```
-  python script.py -- --my_config=parameterised_config.py:lstm
-                      --my_config.config.hidden_size=256
-  ```
+
+    python script.py -- --my_config=parameterised_config.py:lstm
+                        --my_config.config.hidden_size=256
 
   The following will produce an error, as the hidden_size flag does not
-  exist when the "mlp" config_string is provided.
+  exist when the "mlp" config_string is provided::
 
-  ```
-  python script.py -- --my_config=parameterised_config.py:mlp
-                      --my_config.config.hidden_size=256
-  ```
+    python script.py -- --my_config=parameterised_config.py:mlp
+                        --my_config.config.hidden_size=256
 
   Args:
     name: Flag name, optionally including extra config after a colon.
@@ -200,6 +185,9 @@ def DEFINE_config_file(  # pylint: disable=g-bad-name
     lock_config: If set to True, loaded config will be locked through calling
         .lock() method on its instance (if it exists). (default: True)
     **kwargs: Optional keyword arguments passed to Flag constructor.
+
+  Returns:
+    a handle to defined flag.
   """
   parser = _ConfigFileParser(name=name, lock_config=lock_config)
   flag = _ConfigFlag(
@@ -214,17 +202,17 @@ def DEFINE_config_file(  # pylint: disable=g-bad-name
   # Get the module name for the frame at depth 1 in the call stack.
   module_name = sys._getframe(1).f_globals.get('__name__', None)  # pylint: disable=protected-access
   module_name = sys.argv[0] if module_name == '__main__' else module_name
-  flags.DEFINE_flag(flag, flag_values, module_name=module_name)
+  return flags.DEFINE_flag(flag, flag_values, module_name=module_name)
 
 
 def DEFINE_config_dict(  # pylint: disable=g-bad-name
-    name,
-    config,
-    help_string='ConfigDict instance.',
-    flag_values=FLAGS,
-    lock_config=True,
-    **kwargs):
-  """Defines flag for inline `ConfigDict`s compatible with absl flags.
+    name: str,
+    config: ml_collections.ConfigDict,
+    help_string: str = 'ConfigDict instance.',
+    flag_values: flags.FlagValues = FLAGS,
+    lock_config: bool = True,
+    **kwargs) -> flags.FlagHolder:
+  """Defines flag for inline `ConfigDict's` compatible with absl flags.
 
   Similar to `DEFINE_config_file` except the flag's value should be a
   `ConfigDict` instead of a path to a file containing a `ConfigDict`. After the
@@ -233,46 +221,39 @@ def DEFINE_config_dict(  # pylint: disable=g-bad-name
 
   Typical usage example:
 
-  `script.py`:
+  `script.py`::
 
-  ```python
-  ...
-  from absl import flags
+    from absl import flags
 
-  import ml_collections
-  from ml_collections.config_flags import config_flags
+    import ml_collections
+    from ml_collections.config_flags import config_flags
 
 
-  config = ml_collections.ConfigDict({
-      'field1': 1,
-      'field2': 'tom',
-      'nested': {
-          'field': 2.23,
-      }
-  })
+    config = ml_collections.ConfigDict({
+        'field1': 1,
+        'field2': 'tom',
+        'nested': {
+            'field': 2.23,
+        }
+    })
 
 
-  FLAGS = flags.FLAGS
-  config_flags.DEFINE_config_dict('my_config', config)
-  ...
+    FLAGS = flags.FLAGS
+    config_flags.DEFINE_config_dict('my_config', config)
+    ...
 
-  print(FLAGS.my_config)
-  ```
+    print(FLAGS.my_config)
 
-  The following command:
+  The following command::
 
-  ```
-  python script.py -- --my_config.field1 8
-                      --my_config.nested.field=2.1
-  ```
+    python script.py -- --my_config.field1 8
+                        --my_config.nested.field=2.1
 
-  will print:
+  will print::
 
-  ```
-  field1: 8
-  field2: tom
-  nested: {field: 2.1}
-  ```
+    field1: 8
+    field2: tom
+    nested: {field: 2.1}
 
   Args:
     name: Flag name.
@@ -284,6 +265,9 @@ def DEFINE_config_dict(  # pylint: disable=g-bad-name
     lock_config: If set to True, loaded config will be locked through calling
         .lock() method on its instance (if it exists). (default: True)
     **kwargs: Optional keyword arguments passed to Flag constructor.
+
+  Returns:
+    a handle to defined flag.
   """
   if not isinstance(config, ml_collections.ConfigDict):
     raise TypeError('config should be a ConfigDict')
@@ -300,10 +284,107 @@ def DEFINE_config_dict(  # pylint: disable=g-bad-name
   # Get the module name for the frame at depth 1 in the call stack.
   module_name = sys._getframe(1).f_globals.get('__name__', None)  # pylint: disable=protected-access
   module_name = sys.argv[0] if module_name == '__main__' else module_name
-  flags.DEFINE_flag(flag, flag_values, module_name=module_name)
+  return flags.DEFINE_flag(flag, flag_values, module_name=module_name)
+
+
+# Note that we would add a bound to constrain this to be a dataclass, except
+# that dataclasses don't have a specific base class, and structural typing for
+# attributes is currently (2021Q1) not supported in pytype (b/150927776).
+_T = TypeVar('_T')
+
+
+class _TypedFlagHolder(flags.FlagHolder, Generic[_T]):
+  """A typed wrapper for a FlagHolder."""
+
+  def __init__(self, flag: flags.FlagHolder, ctor: Type[_T]):
+    self._flag = flag
+    self._ctor = ctor
+
+  @property
+  def value(self) -> _T:
+    return self._ctor(**self._flag.value)
+
+  @property
+  def default(self) -> _T:
+    return self._ctor(**self._flag.default)
+
+  @property
+  def name(self) -> str:
+    return self._flag.name
+
+
+def DEFINE_config_dataclass(  # pylint: disable=invalid-name
+    name: str,
+    config: _T,
+    help_string: str = 'Configuration object. Must be a dataclass.',
+    flag_values: flags.FlagValues = FLAGS,
+    **kwargs,
+) -> _TypedFlagHolder[_T]:
+  """Defines a typed (dataclass) flag-overrideable configuration.
+
+  Similar to `DEFINE_config_dict` except `config` should be a `dataclass`.
+
+  Args:
+    name: Flag name.
+    config: A user-defined configuration object. Must be built via `dataclass`.
+    help_string: Help string to display when --helpfull is called.
+    flag_values: FlagValues instance used for parsing.
+    **kwargs: Optional keyword arguments passed to Flag constructor.
+  Returns:
+    A handle to the defined flag.
+  """
+
+  if not dataclasses.is_dataclass(config):
+    raise ValueError('Configuration object must be a `dataclass`.')
+
+  # Convert to configdict *without* recursing into leaf node(s).
+  # If our config contains dataclasses (or other types) as fields, we want to
+  # preserve them; dataclasses.asdict recursively turns all fields into dicts.
+  dictionary = {field.name: getattr(config, field.name)
+                for field in dataclasses.fields(config)}
+  config_dict = ml_collections.ConfigDict(initial_dictionary=dictionary)
+
+  # Define the flag.
+  config_flag = DEFINE_config_dict(
+      name,
+      config=config_dict,
+      help_string=help_string,
+      flag_values=flag_values,
+      **kwargs,
+  )
+
+  return _TypedFlagHolder(flag=config_flag, ctor=config.__class__)
+
+
+def get_config_filename(config_flag) -> str:  # pylint: disable=g-bad-name
+  """Returns the path to the config file given the config flag.
+
+  Args:
+    config_flag: The flag instance obtained from FLAGS, e.g. FLAGS['config'].
+
+  Returns:
+    the path to the config file.
+  """
+  if not is_config_flag(config_flag):
+    raise TypeError('expect a config flag, found {}'.format(type(config_flag)))
+  return config_flag.config_filename
+
+
+def get_override_values(config_flag) -> Dict[str, Any]:  # pylint: disable=g-bad-name
+  """Returns a flat dict containing overridden values from the config flag.
+
+  Args:
+    config_flag: The flag instance obtained from FLAGS, e.g. FLAGS['config'].
+
+  Returns:
+    a flat dict containing overridden values from the config flag.
+  """
+  if not is_config_flag(config_flag):
+    raise TypeError('expect a config flag, found {}'.format(type(config_flag)))
+  return config_flag.override_values
 
 
-class _IgnoreFileNotFoundAndCollectErrors(object):
+class _IgnoreFileNotFoundAndCollectErrors:
   """Helps recording "file not found" exceptions when loading config.
 
   Usage:
@@ -315,14 +396,14 @@ class _IgnoreFileNotFoundAndCollectErrors(object):
   """
 
   def __init__(self):
-    self._attempts = []  # type: List[Tuple[Tuple[Text, Text], IOError]]
+    self._attempts = []  # type: List[Tuple[Tuple[str, str], IOError]]
 
   def Attempt(self, description, path):
     """Creates a context manager that routes exceptions to this class."""
     self._current_attempt = (description, path)
     ignore_errors = self
 
-    class _ContextManager(object):
+    class _ContextManager:
 
       def __enter__(self):
         return self
@@ -346,7 +427,7 @@ class _IgnoreFileNotFoundAndCollectErrors(object):
         for attempt, e in self._attempts)
 
 
-def _LoadConfigModule(name, path):
+def _LoadConfigModule(name: str, path: str):
   """Loads a script from external file specified by path.
 
   Unprefixed path is looked for in the current working directory using
@@ -377,7 +458,7 @@ def _LoadConfigModule(name, path):
       name, ignoring_errors.DescribeAttempts()))
 
 
-class _ErrorConfig(object):
+class _ErrorConfig:
   """Dummy ConfigDict that raises an error on any attribute access."""
 
   def __init__(self, error):
@@ -531,7 +612,7 @@ class _ConfigFlag(flags.Flag):
     if self._IsConfigSpecified(sys.argv):
       self.default = default
     else:
-      super(_ConfigFlag, self)._set_default(default)
+      super(_ConfigFlag, self)._set_default(default)  # pytype: disable=attribute-error
     self.default_as_str = "'{}'".format(default)
 
   def _parse(self, argument):
@@ -593,7 +674,8 @@ class _ConfigFlag(flags.Flag):
 
     FLAGS['my_config'].config_filename
 
-    will output 'ml_collections/config_flags/tests/configdict_config.py'
+    will output
+    'ml_collections/config_flags/tests/configdict_config.py'
     ```
 
     Returns:
@@ -684,7 +766,7 @@ def is_config_flag(flag):  # pylint: disable=g-bad-name
   return isinstance(flag, _ConfigFlag)
 
 
-class _ConfigFieldParser(object):
+class _ConfigFieldParser(flags.ArgumentParser):
   """Parser with config update after parsing.
 
   This class-based wrapper creates a new object, which uses
@@ -693,7 +775,12 @@ class _ConfigFieldParser(object):
   the config object.
   """
 
-  def __init__(self, parser, path, config, override_values):
+  def __init__(
+      self,
+      parser: flags.ArgumentParser,
+      path: str,
+      config: ml_collections.ConfigDict,
+      override_values: MutableMapping[str, Any]):
     """Creates new parser with callback, using existing one to perform parsing.
 
     Args:
@@ -718,6 +805,13 @@ class _ConfigFieldParser(object):
     self._override_values[self._path] = value
     return value
 
+  def flag_type(self) -> str:
+    return self._parser.flag_type()
+
+  @property
+  def syntactic_help(self) -> str:
+    return self._parser.syntactic_help
+
 
 def _ExtractIndicesFromStep(step):
   """Separates identifier from indexes. Example: id[1][10] -> 'id', [1, 10]."""
@@ -782,7 +876,7 @@ def _TakeStep(current, step):
   return _AccessConfig(current, field, indices)
 
 
-def GetValue(path, config):
+def GetValue(path: str, config: ml_collections.ConfigDict):
   """Gets value of a single field."""
   current = config
   for step in path.split('.'):
@@ -817,7 +911,7 @@ def GetTypes(paths, config):
   return [GetType(path, config) for path in paths]
 
 
-def SetValue(path, config, value):
+def SetValue(path: str, config: ml_collections.ConfigDict, value):
   """Sets value of a single field."""
   current = config
   steps = path.split('.')
diff --git a/ml_collections/config_flags/examples/config.py b/ml_collections/config_flags/examples/config.py
index f647495..25a020e 100644
--- a/ml_collections/config_flags/examples/config.py
+++ b/ml_collections/config_flags/examples/config.py
@@ -1,4 +1,4 @@
-# Copyright 2020 The ML Collections Authors.
+# Copyright 2021 The ML Collections Authors.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
diff --git a/ml_collections/config_flags/examples/define_config_dataclass_basic.py b/ml_collections/config_flags/examples/define_config_dataclass_basic.py
new file mode 100644
index 0000000..a285bc9
--- /dev/null
+++ b/ml_collections/config_flags/examples/define_config_dataclass_basic.py
@@ -0,0 +1,54 @@
+# Copyright 2021 The ML Collections Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Lint as: python 3
+r"""Example of basic DEFINE_config_dataclass usage.
+
+To run this example:
+python define_config_dataclass_basic.py -- --my_config.field1=8 \
+  --my_config.nested.field=2.1 --my_config.tuple='(1, 2, (1, 2))'
+"""
+
+from typing import Any, Mapping, Sequence
+
+from absl import app
+
+import dataclasses
+from ml_collections.config_flags import config_flags
+
+
+@dataclasses.dataclass
+class MyConfig:
+  field1: int
+  field2: str
+  nested: Mapping[str, Any]
+  tuple: Sequence[int]
+
+
+config = MyConfig(
+    field1=1,
+    field2='tom',
+    nested={'field': 2.23},
+    tuple=(1, 2, 3),
+)
+
+_CONFIG = config_flags.DEFINE_config_dataclass('my_config', config)
+
+
+def main(_):
+  print(_CONFIG.value)
+
+
+if __name__ == '__main__':
+  app.run(main)
diff --git a/ml_collections/config_flags/examples/define_config_dict_basic.py b/ml_collections/config_flags/examples/define_config_dict_basic.py
index 984b1a3..eb9cd89 100644
--- a/ml_collections/config_flags/examples/define_config_dict_basic.py
+++ b/ml_collections/config_flags/examples/define_config_dict_basic.py
@@ -1,4 +1,4 @@
-# Copyright 2020 The ML Collections Authors.
+# Copyright 2021 The ML Collections Authors.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -13,7 +13,7 @@
 # limitations under the License.
 
 # Lint as: python 3
-r"""Example of basic DEFINE_flag_dict usage.
+r"""Example of basic DEFINE_config_dict usage.
 
 To run this example:
 python define_config_dict_basic.py -- --my_config_dict.field1=8 \
@@ -21,7 +21,6 @@ python define_config_dict_basic.py -- --my_config_dict.field1=8 \
 """
 
 from absl import app
-from absl import flags
 
 import ml_collections
 from ml_collections.config_flags import config_flags
@@ -33,12 +32,11 @@ config.nested = ml_collections.ConfigDict()
 config.nested.field = 2.23
 config.tuple = (1, 2, 3)
 
-FLAGS = flags.FLAGS
-config_flags.DEFINE_config_dict('my_config_dict', config)
+_CONFIG = config_flags.DEFINE_config_dict('my_config_dict', config)
 
 
 def main(_):
-  print(FLAGS.my_config_dict)
+  print(_CONFIG.value)
 
 
 if __name__ == '__main__':
diff --git a/ml_collections/config_flags/examples/define_config_file_basic.py b/ml_collections/config_flags/examples/define_config_file_basic.py
index 009489e..eeb68e0 100644
--- a/ml_collections/config_flags/examples/define_config_file_basic.py
+++ b/ml_collections/config_flags/examples/define_config_file_basic.py
@@ -1,4 +1,4 @@
-# Copyright 2020 The ML Collections Authors.
+# Copyright 2021 The ML Collections Authors.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -32,16 +32,14 @@ python define_config_dict_basic.py -- \
 # pylint: enable=line-too-long
 
 from absl import app
-from absl import flags
 
 from ml_collections.config_flags import config_flags
 
-FLAGS = flags.FLAGS
-config_flags.DEFINE_config_file('my_config')
+_CONFIG = config_flags.DEFINE_config_file('my_config')
 
 
 def main(_):
-  print(FLAGS.my_config)
+  print(_CONFIG.value)
 
 
 if __name__ == '__main__':
diff --git a/ml_collections/config_flags/examples/examples_test.py b/ml_collections/config_flags/examples/examples_test.py
index b9aac67..09d7a47 100644
--- a/ml_collections/config_flags/examples/examples_test.py
+++ b/ml_collections/config_flags/examples/examples_test.py
@@ -1,4 +1,4 @@
-# Copyright 2020 The ML Collections Authors.
+# Copyright 2021 The ML Collections Authors.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
diff --git a/ml_collections/config_flags/examples/parameterised_config.py b/ml_collections/config_flags/examples/parameterised_config.py
index d72c966..3a9383e 100644
--- a/ml_collections/config_flags/examples/parameterised_config.py
+++ b/ml_collections/config_flags/examples/parameterised_config.py
@@ -1,4 +1,4 @@
-# Copyright 2020 The ML Collections Authors.
+# Copyright 2021 The ML Collections Authors.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
diff --git a/ml_collections/config_flags/tests/config_overriding_test.py b/ml_collections/config_flags/tests/config_overriding_test.py
index 381e9f8..7249a5e 100644
--- a/ml_collections/config_flags/tests/config_overriding_test.py
+++ b/ml_collections/config_flags/tests/config_overriding_test.py
@@ -1,4 +1,4 @@
-# Copyright 2020 The ML Collections Authors.
+# Copyright 2021 The ML Collections Authors.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -132,7 +132,7 @@ class _ConfigFlagTestCase(object):
   def assert_equal_configs(self, config1, config2):
     """Checks if two configs are identical."""
     self.assert_subset_configs(config1, config2)
-    self.assert_subset_configs(config2, config1)
+    self.assert_subset_configs(config2, config1)  # pylint: disable=arguments-out-of-order
 
 
 class ConfigFileFlagTest(_ConfigFlagTestCase, parameterized.TestCase):
@@ -695,7 +695,8 @@ class ConfigFileFlagTest(_ConfigFlagTestCase, parameterized.TestCase):
                               _TEST_CONFIG_FILE, integer_override,
                               dictfloat_override))
 
-    config.update_from_flattened_dict(values['test_config'].override_values)
+    config.update_from_flattened_dict(
+        config_flags.get_override_values(values['test_config']))
     self.assertEqual(config['integer'], integer_override)
     self.assertEqual(config['float'], original_float)
     self.assertEqual(config['dict']['float'], dictfloat_override)
@@ -708,7 +709,8 @@ class ConfigFileFlagTest(_ConfigFlagTestCase, parameterized.TestCase):
   def testConfigPath(self, config_file):
     """Test access to saved config file path."""
     values = _parse_flags('./program --test_config={}'.format(config_file))
-    self.assertEqual(values['test_config'].config_filename, config_file)
+    self.assertEqual(config_flags.get_config_filename(values['test_config']),
+                     config_file)
 
 
 def _simple_config():
diff --git a/ml_collections/config_flags/tests/configdict_config.py b/ml_collections/config_flags/tests/configdict_config.py
index 60defc2..969c01d 100644
--- a/ml_collections/config_flags/tests/configdict_config.py
+++ b/ml_collections/config_flags/tests/configdict_config.py
@@ -1,4 +1,4 @@
-# Copyright 2020 The ML Collections Authors.
+# Copyright 2021 The ML Collections Authors.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
diff --git a/ml_collections/config_flags/tests/dataclass_overriding_test.py b/ml_collections/config_flags/tests/dataclass_overriding_test.py
new file mode 100644
index 0000000..8076d03
--- /dev/null
+++ b/ml_collections/config_flags/tests/dataclass_overriding_test.py
@@ -0,0 +1,86 @@
+# Copyright 2021 The ML Collections Authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+"""Tests for config_flags used in conjunction with DEFINE_config_dataclass."""
+
+import shlex
+import sys
+from typing import Mapping, Optional, Sequence
+
+from absl import flags
+from absl.testing import absltest
+import dataclasses
+from ml_collections import config_flags
+
+
+#####
+# Simple dummy configuration classes.
+@dataclasses.dataclass
+class MyModelConfig:
+  foo: int
+  bar: Sequence[str]
+  baz: Optional[Mapping[str, str]] = None
+
+
+@dataclasses.dataclass
+class MyConfig:
+  my_model: MyModelConfig
+  baseline_model: MyModelConfig
+
+
+_CONFIG = MyConfig(
+    my_model=MyModelConfig(
+        foo=3,
+        bar=['a', 'b'],
+        baz={'foo': 'bar'},
+    ),
+    baseline_model=MyModelConfig(
+        foo=55,
+        bar=['c', 'd'],
+    ),
+)
+
+# Define the flag.
+_CONFIG_FLAG = config_flags.DEFINE_config_dataclass('config', _CONFIG)
+
+
+class TypedConfigFlagsTest(absltest.TestCase):
+
+  def test_instance(self):
+    config = _CONFIG_FLAG.value
+    self.assertIsInstance(config, MyConfig)
+    self.assertEqual(config.my_model, _CONFIG.my_model)
+    self.assertEqual(_CONFIG, config)
+
+  def test_flag_overrides(self):
+
+    # Set up some flag overrides.
+    old_argv = list(sys.argv)
+    sys.argv = shlex.split(
+        './program foo.py --test_config.baseline_model.foo=99')
+    flag_values = flags.FlagValues()
+
+    # Define a config dataclass flag.
+    test_config = config_flags.DEFINE_config_dataclass(
+        'test_config', _CONFIG, flag_values=flag_values)
+
+    # Inject the flag overrides.
+    flag_values(sys.argv)
+    sys.argv = old_argv
+
+    # Did the value get overridden?
+    self.assertEqual(test_config.value.baseline_model.foo, 99)
+
+if __name__ == '__main__':
+  absltest.main()
diff --git a/ml_collections/config_flags/tests/fieldreference_config.py b/ml_collections/config_flags/tests/fieldreference_config.py
index 7c270ce..5954975 100644
--- a/ml_collections/config_flags/tests/fieldreference_config.py
+++ b/ml_collections/config_flags/tests/fieldreference_config.py
@@ -1,4 +1,4 @@
-# Copyright 2020 The ML Collections Authors.
+# Copyright 2021 The ML Collections Authors.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
diff --git a/ml_collections/config_flags/tests/ioerror_config.py b/ml_collections/config_flags/tests/ioerror_config.py
index 58d9280..c27d7f6 100644
--- a/ml_collections/config_flags/tests/ioerror_config.py
+++ b/ml_collections/config_flags/tests/ioerror_config.py
@@ -1,4 +1,4 @@
-# Copyright 2020 The ML Collections Authors.
+# Copyright 2021 The ML Collections Authors.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
diff --git a/ml_collections/config_flags/tests/mini_config.py b/ml_collections/config_flags/tests/mini_config.py
index 7c445f2..51377fe 100644
--- a/ml_collections/config_flags/tests/mini_config.py
+++ b/ml_collections/config_flags/tests/mini_config.py
@@ -1,4 +1,4 @@
-# Copyright 2020 The ML Collections Authors.
+# Copyright 2021 The ML Collections Authors.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
diff --git a/ml_collections/config_flags/tests/mock_config.py b/ml_collections/config_flags/tests/mock_config.py
index b3132e3..696f288 100644
--- a/ml_collections/config_flags/tests/mock_config.py
+++ b/ml_collections/config_flags/tests/mock_config.py
@@ -1,4 +1,4 @@
-# Copyright 2020 The ML Collections Authors.
+# Copyright 2021 The ML Collections Authors.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
diff --git a/ml_collections/config_flags/tests/parameterised_config.py b/ml_collections/config_flags/tests/parameterised_config.py
index a71edc5..df8ebfe 100644
--- a/ml_collections/config_flags/tests/parameterised_config.py
+++ b/ml_collections/config_flags/tests/parameterised_config.py
@@ -1,4 +1,4 @@
-# Copyright 2020 The ML Collections Authors.
+# Copyright 2021 The ML Collections Authors.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
diff --git a/ml_collections/config_flags/tests/typeerror_config.py b/ml_collections/config_flags/tests/typeerror_config.py
index c01ebcf..a392a6f 100644
--- a/ml_collections/config_flags/tests/typeerror_config.py
+++ b/ml_collections/config_flags/tests/typeerror_config.py
@@ -1,4 +1,4 @@
-# Copyright 2020 The ML Collections Authors.
+# Copyright 2021 The ML Collections Authors.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
diff --git a/ml_collections/config_flags/tests/valueerror_config.py b/ml_collections/config_flags/tests/valueerror_config.py
index 55a8520..126d187 100644
--- a/ml_collections/config_flags/tests/valueerror_config.py
+++ b/ml_collections/config_flags/tests/valueerror_config.py
@@ -1,4 +1,4 @@
-# Copyright 2020 The ML Collections Authors.
+# Copyright 2021 The ML Collections Authors.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
diff --git a/ml_collections/config_flags/tuple_parser.py b/ml_collections/config_flags/tuple_parser.py
index 08de218..6e7df3d 100644
--- a/ml_collections/config_flags/tuple_parser.py
+++ b/ml_collections/config_flags/tuple_parser.py
@@ -1,4 +1,4 @@
-# Copyright 2020 The ML Collections Authors.
+# Copyright 2021 The ML Collections Authors.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
diff --git a/requirements-test.txt b/requirements-test.txt
new file mode 100644
index 0000000..932a895
--- /dev/null
+++ b/requirements-test.txt
@@ -0,0 +1 @@
+mock
diff --git a/requirements.txt b/requirements.txt
new file mode 100644
index 0000000..0c9f81f
--- /dev/null
+++ b/requirements.txt
@@ -0,0 +1,6 @@
+absl-py
+PyYAML
+six
+contextlib2
+dataclasses;python_version<'3.7'
+typing;python_version<'3.5'
diff --git a/setup.py b/setup.py
index 14bf6e6..24af8e3 100644
--- a/setup.py
+++ b/setup.py
@@ -1,4 +1,4 @@
-# Copyright 2020 The ML Collections Authors.
+# Copyright 2021 The ML Collections Authors.
 #
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -24,7 +24,7 @@ def _parse_requirements(requirements_txt_path):
     return fp.read().splitlines()
 
 
-_VERSION = '0.1.0'
+_VERSION = '0.1.1'
 
 
 setup(

More details

Full run details