r"""Class decorator that extends a given class to save its source code when pickled. Example: from torch_utils import persistence @persistence.persistent_class class MyNetwork(torch.nn.Module): def __init__(self, num_inputs, num_outputs):
(orig_class)
| 36 | #---------------------------------------------------------------------------- |
| 37 | |
| 38 | def persistent_class(orig_class): |
| 39 | r"""Class decorator that extends a given class to save its source code |
| 40 | when pickled. |
| 41 | |
| 42 | Example: |
| 43 | |
| 44 | from torch_utils import persistence |
| 45 | |
| 46 | @persistence.persistent_class |
| 47 | class MyNetwork(torch.nn.Module): |
| 48 | def __init__(self, num_inputs, num_outputs): |
| 49 | super().__init__() |
| 50 | self.fc = MyLayer(num_inputs, num_outputs) |
| 51 | ... |
| 52 | |
| 53 | @persistence.persistent_class |
| 54 | class MyLayer(torch.nn.Module): |
| 55 | ... |
| 56 | |
| 57 | When pickled, any instance of `MyNetwork` and `MyLayer` will save its |
| 58 | source code alongside other internal state (e.g., parameters, buffers, |
| 59 | and submodules). This way, any previously exported pickle will remain |
| 60 | usable even if the class definitions have been modified or are no |
| 61 | longer available. |
| 62 | |
| 63 | The decorator saves the source code of the entire Python module |
| 64 | containing the decorated class. It does *not* save the source code of |
| 65 | any imported modules. Thus, the imported modules must be available |
| 66 | during unpickling, also including `torch_utils.persistence` itself. |
| 67 | |
| 68 | It is ok to call functions defined in the same module from the |
| 69 | decorated class. However, if the decorated class depends on other |
| 70 | classes defined in the same module, they must be decorated as well. |
| 71 | This is illustrated in the above example in the case of `MyLayer`. |
| 72 | |
| 73 | It is also possible to employ the decorator just-in-time before |
| 74 | calling the constructor. For example: |
| 75 | |
| 76 | cls = MyLayer |
| 77 | if want_to_make_it_persistent: |
| 78 | cls = persistence.persistent_class(cls) |
| 79 | layer = cls(num_inputs, num_outputs) |
| 80 | |
| 81 | As an additional feature, the decorator also keeps track of the |
| 82 | arguments that were used to construct each instance of the decorated |
| 83 | class. The arguments can be queried via `obj.init_args` and |
| 84 | `obj.init_kwargs`, and they are automatically pickled alongside other |
| 85 | object state. This feature can be disabled on a per-instance basis |
| 86 | by setting `self._record_init_args = False` in the constructor. |
| 87 | |
| 88 | A typical use case is to first unpickle a previous instance of a |
| 89 | persistent class, and then upgrade it to use the latest version of |
| 90 | the source code: |
| 91 | |
| 92 | with open('old_pickle.pkl', 'rb') as f: |
| 93 | old_net = pickle.load(f) |
| 94 | new_net = MyNetwork(*old_obj.init_args, **old_obj.init_kwargs) |
| 95 | misc.copy_params_and_buffers(old_net, new_net, require_all=True) |
no test coverage detected