From 625ee24ca0bafd1c98cc42b1fe3e62f3f90c60a9 Mon Sep 17 00:00:00 2001 From: OlofHarrysson Date: Fri, 8 Nov 2019 17:08:18 +0100 Subject: [PATCH 1/5] made it so that we dont inherit from anyfig.masterconfig but only decorate with the class --- anyfig/__init__.py | 2 +- anyfig/anyfig_setup.py | 40 +++++++++--------- anyfig/masterconfig.py | 6 +-- example/1_example.py | 9 ++-- example/2_example.py | 95 ++++++++++++++++++++++++++++++++++++++++-- example/otherfile.py | 7 ++++ 6 files changed, 123 insertions(+), 36 deletions(-) create mode 100644 example/otherfile.py diff --git a/anyfig/__init__.py b/anyfig/__init__.py index c9b5f5f..bfa2eda 100644 --- a/anyfig/__init__.py +++ b/anyfig/__init__.py @@ -1,2 +1,2 @@ from anyfig.masterconfig import MasterConfig, save_config, load_config -from anyfig.anyfig_setup import setup_config, parse_args, choose_config, get_available_configs, overwrite, config_class, print_source \ No newline at end of file +from anyfig.anyfig_setup import setup_config, parse_args, choose_config, overwrite, config_class, print_source \ No newline at end of file diff --git a/anyfig/anyfig_setup.py b/anyfig/anyfig_setup.py index 23946e6..be2d69d 100644 --- a/anyfig/anyfig_setup.py +++ b/anyfig/anyfig_setup.py @@ -5,6 +5,8 @@ import sys from .masterconfig import MasterConfig, is_anyfig_class +registered_config_classes = {} + def setup_config(default_config=None): config_str = parse_args(default_config) @@ -26,7 +28,7 @@ def parse_args(default_config): def choose_config(config_str): # Create config object - available_configs = get_available_configs() + available_configs = registered_config_classes err_msg = ( "Specify which config to use by either starting your python script with " "the input argument --config=YourConfigClass or set " @@ -35,7 +37,9 @@ def choose_config(config_str): raise RuntimeError(err_msg) try: + from types import MethodType config_class_ = available_configs[config_str] + config_obj = config_class_() except KeyError as e: err_msg = ( @@ -52,15 +56,6 @@ def choose_config(config_str): return config_obj -def get_available_configs(): - available_configs = {} - for name, obj in inspect.getmembers(sys.modules[__name__]): - if is_anyfig_class(obj): - available_configs[name] = obj - available_configs.pop('MasterConfig') - return available_configs - - def overwrite(config_obj): ''' Overwrites parameters with input flags. Function is needed for the convenience of specifying parameters via a combination of the config classes @@ -93,25 +88,28 @@ def config_class(func): class_name = func.__name__ module_name = sys.modules[__name__] + # Makes sure that nothing fishy is going on... err_msg = (f"Can't decorate '{class_name}' of type {type(func)}. " "Can only be used for classes") assert inspect.isclass(func), err_msg - err_msg = (f"Can't decorate '{class_name}' since it's not a sublass of " - "'chilliconfig.MasterConfig'") - assert issubclass(func, MasterConfig), err_msg - - err_msg = "The class name 'MasterConfig' is reserved" - assert class_name != 'MasterConfig', err_msg - err_msg = ( - f"The config class {class_name} has already been registered. " + f"The config class '{class_name}' has already been registered. " "Duplicated names aren't allowed. Either change the name or avoid " - "importing the duplicated classes at the same time") - assert not hasattr(module_name, class_name), err_msg + "importing the duplicated classes at the same time. " + f"The registered classes are '{registered_config_classes}'") + assert class_name not in registered_config_classes, err_msg + + # Transfers the functions from MasterConfig to config class + for name, member in inspect.getmembers(MasterConfig): + if inspect.isfunction(member): + setattr(func, name, member) - setattr(module_name, class_name, func) + # Manually transfers attributes from MasterConfig to config class + setattr(func, "_frozen", False) + setattr(func, "config_class", func.__name__) + registered_config_classes[class_name] = func return func diff --git a/anyfig/masterconfig.py b/anyfig/masterconfig.py index ec8218b..f1915f4 100644 --- a/anyfig/masterconfig.py +++ b/anyfig/masterconfig.py @@ -30,11 +30,7 @@ def save_config(obj, path): f.write(str(obj)) -class MasterConfig(ABC): - def __init__(self): - self._frozen: bool = False - self.config_class = type(self).__name__ - +class MasterConfig(): def frozen(self, freeze=True): self._frozen = freeze diff --git a/example/1_example.py b/example/1_example.py index e7a7775..8345de9 100644 --- a/example/1_example.py +++ b/example/1_example.py @@ -27,10 +27,9 @@ def __init__(self, x): @anyfig.config_class -class MainConfig(anyfig.MasterConfig): +class MainConfig(): def __init__(self): print("MAIN CONFIG SUPER") - super().__init__() # self.start_time = time.time() self.img_size = 100 self.classes = ['car', 'dog'] @@ -38,9 +37,8 @@ def __init__(self): @anyfig.config_class -class Train(anyfig.MasterConfig): +class Train(): def __init__(self): - super().__init__() self.name = 'oldname' self.transforms111 = Transformer(100) # self.freeze_config = False @@ -48,9 +46,8 @@ def __init__(self): @anyfig.config_class -class Config(anyfig.MasterConfig): +class Config(): def __init__(self): - super().__init__() self.name = 'oldname1111' self.transforms111 = Transformer(100) # self.freeze_config = False diff --git a/example/2_example.py b/example/2_example.py index 3284b0b..2a121d7 100644 --- a/example/2_example.py +++ b/example/2_example.py @@ -1,15 +1,104 @@ +# from .. import anyfig import anyfig import random +import otherfile +# config_class(FooConfig) + + +class RandomClass(): + def __init__(self): + self.seed = 0 + self.hej = 'wowowo' @anyfig.config_class -class FooConfig(anyfig.MasterConfig): +class FooConfig(RandomClass): + # class FooConfig(anyfig.MasterConfig): def __init__(self): + print("INIT FOOCONFIG") super().__init__() + # self.frozen = 123 self.experiment_note = 'Changed some stuff' self.seed = random.randint(0, 80085) + def myfunc(self): + pass + + +@anyfig.config_class +class MasterConfig(): + # class FooConfig(anyfig.MasterConfig): + def __init__(self): + print("INIT MASTER") + super().__init__() + self.experiment_note = 'Changed some stuff22' + self.seed = random.randint(0, 80085) + -config = anyfig.setup_config(default_config='FooConfig') +print("BEFORE OBJ CREATION") +# config = anyfig.setup_config(default_config='FooConfig') +config = anyfig.setup_config(default_config='MasterConfig') +print("AFTER OBJ CREATION") print(config) -print(config.seed) # Prints -1 \ No newline at end of file +print(config.seed) # Prints -1 + +# class A(): +# def __init__(self): +# self.name = 'A' +# print("I AM A") + +# class AA(): +# def __init__(self): +# self.name = 'AA' +# self.aa = 'aaa' +# print("I AM AA") + +# class B(A): +# # class B(): +# def __init__(self): + +# # class B(AA, A): +# # def __init__(self): +# # A.__init__(self) +# # super().__init__() +# self.name = 'B' + +# # class C(A): +# # def __init__(self): +# # self.name = 'C' + +# # B.__bases__ = (A, ) +# print(B.__bases__) +# qwe + +# b = B() +# print(b.name) +# print(b.__class__.__bases__) + +# class Friendly: +# def hello(self): +# print('Hello') + +# class Person: +# pass + +# print(Person.__bases__) +# p = Person() +# Person.__bases__ = (Friendly, ) +# p.hello() # prints "Hello" + +# class Fig(): +# def __init__(self): +# print("FIG INIT") + +# def extension(self): +# print("Some work...") + +# class Master(): +# def __init__(self): +# print("BASE INIT") + +# print(Master.__bases__) +# Master = type('Master', (Fig, ), {}) +# print(Master.__bases__) +# Master().extension() \ No newline at end of file diff --git a/example/otherfile.py b/example/otherfile.py new file mode 100644 index 0000000..450bc55 --- /dev/null +++ b/example/otherfile.py @@ -0,0 +1,7 @@ +import anyfig + + +@anyfig.config_class +class FooConfig2(): + def __init__(self): + self.experiment_note = 'Changed some stuff' From 5c3322e49d4e837c8621316322868fdeae10ec2c Mon Sep 17 00:00:00 2001 From: OlofHarrysson Date: Fri, 8 Nov 2019 18:51:36 +0100 Subject: [PATCH 2/5] made it so that one can overwrite functions in the config class that are transfered from masterconfig --- anyfig/anyfig_setup.py | 12 ++++-- anyfig/masterconfig.py | 2 +- example/2_example.py | 83 +++--------------------------------------- 3 files changed, 14 insertions(+), 83 deletions(-) diff --git a/anyfig/anyfig_setup.py b/anyfig/anyfig_setup.py index be2d69d..68acaf3 100644 --- a/anyfig/anyfig_setup.py +++ b/anyfig/anyfig_setup.py @@ -100,12 +100,16 @@ def config_class(func): f"The registered classes are '{registered_config_classes}'") assert class_name not in registered_config_classes, err_msg - # Transfers the functions from MasterConfig to config class - for name, member in inspect.getmembers(MasterConfig): - if inspect.isfunction(member): + # Config class functions + members = inspect.getmembers(func, inspect.isfunction) + members = {name: function for name, function in members} + + # Transfers functions from MasterConfig to config class + for name, member in inspect.getmembers(MasterConfig, inspect.isfunction): + if name not in members: # Only transfer not implemented functions setattr(func, name, member) - # Manually transfers attributes from MasterConfig to config class + # Manually add attributes to config class setattr(func, "_frozen", False) setattr(func, "config_class", func.__name__) diff --git a/anyfig/masterconfig.py b/anyfig/masterconfig.py index f1915f4..305866f 100644 --- a/anyfig/masterconfig.py +++ b/anyfig/masterconfig.py @@ -30,7 +30,7 @@ def save_config(obj, path): f.write(str(obj)) -class MasterConfig(): +class MasterConfig(ABC): def frozen(self, freeze=True): self._frozen = freeze diff --git a/example/2_example.py b/example/2_example.py index 2a121d7..be91a2c 100644 --- a/example/2_example.py +++ b/example/2_example.py @@ -1,8 +1,6 @@ -# from .. import anyfig import anyfig import random import otherfile -# config_class(FooConfig) class RandomClass(): @@ -13,7 +11,6 @@ def __init__(self): @anyfig.config_class class FooConfig(RandomClass): - # class FooConfig(anyfig.MasterConfig): def __init__(self): print("INIT FOOCONFIG") super().__init__() @@ -24,81 +21,11 @@ def __init__(self): def myfunc(self): pass - -@anyfig.config_class -class MasterConfig(): - # class FooConfig(anyfig.MasterConfig): - def __init__(self): - print("INIT MASTER") - super().__init__() - self.experiment_note = 'Changed some stuff22' - self.seed = random.randint(0, 80085) + # def __str__(self): + # return 'strannnng' -print("BEFORE OBJ CREATION") -# config = anyfig.setup_config(default_config='FooConfig') -config = anyfig.setup_config(default_config='MasterConfig') -print("AFTER OBJ CREATION") +config = anyfig.setup_config(default_config='FooConfig') print(config) -print(config.seed) # Prints -1 - -# class A(): -# def __init__(self): -# self.name = 'A' -# print("I AM A") - -# class AA(): -# def __init__(self): -# self.name = 'AA' -# self.aa = 'aaa' -# print("I AM AA") - -# class B(A): -# # class B(): -# def __init__(self): - -# # class B(AA, A): -# # def __init__(self): -# # A.__init__(self) -# # super().__init__() -# self.name = 'B' - -# # class C(A): -# # def __init__(self): -# # self.name = 'C' - -# # B.__bases__ = (A, ) -# print(B.__bases__) -# qwe - -# b = B() -# print(b.name) -# print(b.__class__.__bases__) - -# class Friendly: -# def hello(self): -# print('Hello') - -# class Person: -# pass - -# print(Person.__bases__) -# p = Person() -# Person.__bases__ = (Friendly, ) -# p.hello() # prints "Hello" - -# class Fig(): -# def __init__(self): -# print("FIG INIT") - -# def extension(self): -# print("Some work...") - -# class Master(): -# def __init__(self): -# print("BASE INIT") - -# print(Master.__bases__) -# Master = type('Master', (Fig, ), {}) -# print(Master.__bases__) -# Master().extension() \ No newline at end of file +# print(config.seed) # Prints -1 +# print(dir(config)) \ No newline at end of file From 7d198435b0f907689d7f4f1c1a1657e6fba7d994 Mon Sep 17 00:00:00 2001 From: OlofHarrysson Date: Fri, 8 Nov 2019 19:08:05 +0100 Subject: [PATCH 3/5] made some more error checks and messages. its foolproof now --- anyfig/anyfig_setup.py | 15 +++++++++------ example/2_example.py | 4 ++-- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/anyfig/anyfig_setup.py b/anyfig/anyfig_setup.py index 68acaf3..857066f 100644 --- a/anyfig/anyfig_setup.py +++ b/anyfig/anyfig_setup.py @@ -28,24 +28,27 @@ def parse_args(default_config): def choose_config(config_str): # Create config object - available_configs = registered_config_classes err_msg = ( "Specify which config to use by either starting your python script with " "the input argument --config=YourConfigClass or set " - "'default_config=YourConfigClass' in anyfigs setup_config method") + "'default_config=YourConfigClass' in anyfigs 'setup_config' method") if config_str == None: raise RuntimeError(err_msg) - try: - from types import MethodType - config_class_ = available_configs[config_str] + err_msg = ("There aren't any registered config classes. Decorate a class " + "with '@anyfig.config_class' and make sure that the class is " + "imported to the file where the function 'anyfig.setup_config' " + "is called from") + assert len(registered_config_classes), err_msg + try: + config_class_ = registered_config_classes[config_str] config_obj = config_class_() except KeyError as e: err_msg = ( f"Config class '{config_str}' wasn't found. Feel free to create " "it as a new config class or use one of the existing ones " - f"{list(available_configs)}") + f"{list(registered_config_classes)}") raise KeyError(err_msg) from e # Overwrite parameters via optional input flags diff --git a/example/2_example.py b/example/2_example.py index be91a2c..e053a95 100644 --- a/example/2_example.py +++ b/example/2_example.py @@ -1,6 +1,6 @@ import anyfig import random -import otherfile +# import otherfile class RandomClass(): @@ -9,7 +9,7 @@ def __init__(self): self.hej = 'wowowo' -@anyfig.config_class +# @anyfig.config_class class FooConfig(RandomClass): def __init__(self): print("INIT FOOCONFIG") From a887f1f4078213556e3e5a3ab40f617a8e81650b Mon Sep 17 00:00:00 2001 From: OlofHarrysson Date: Fri, 8 Nov 2019 19:27:04 +0100 Subject: [PATCH 4/5] time for release --- anyfig/masterconfig.py | 19 +++++++++++-------- example/2_example.py | 4 +--- notes.txt | 3 --- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/anyfig/masterconfig.py b/anyfig/masterconfig.py index 305866f..38ea125 100644 --- a/anyfig/masterconfig.py +++ b/anyfig/masterconfig.py @@ -4,6 +4,7 @@ from dataclasses import FrozenInstanceError import pickle from pathlib import Path +import copy def is_anyfig_class(obj): @@ -33,9 +34,12 @@ def save_config(obj, path): class MasterConfig(ABC): def frozen(self, freeze=True): self._frozen = freeze + return self def get_parameters(self): - return self.__dict__ + params = copy.deepcopy(self.__dict__) + params.pop('_frozen') + return params def __str__(self): str_ = "" @@ -57,13 +61,12 @@ def __str__(self): def __setattr__(self, name, value): # Raise error if frozen unless we're trying to unfreeze the config - if hasattr(self, '_frozen'): - if name == '_frozen': - pass - elif self._frozen: - err_msg = (f"Cannot set attribute '{name}'. Config object is frozen. " - "Unfreeze the config for a mutable config object") - raise FrozenInstanceError(err_msg) + if name == '_frozen': + pass + elif self._frozen: + err_msg = (f"Cannot set attribute '{name}'. Config object is frozen. " + "Unfreeze the config for a mutable config object") + raise FrozenInstanceError(err_msg) # Check for reserved names name_taken_msg = f"The attribute '{name}' can't be assigned to config '{type(self).__name__}' since it already has a method by that name" diff --git a/example/2_example.py b/example/2_example.py index e053a95..206fa5e 100644 --- a/example/2_example.py +++ b/example/2_example.py @@ -9,7 +9,7 @@ def __init__(self): self.hej = 'wowowo' -# @anyfig.config_class +@anyfig.config_class class FooConfig(RandomClass): def __init__(self): print("INIT FOOCONFIG") @@ -27,5 +27,3 @@ def myfunc(self): config = anyfig.setup_config(default_config='FooConfig') print(config) -# print(config.seed) # Prints -1 -# print(dir(config)) \ No newline at end of file diff --git a/notes.txt b/notes.txt index 153aa88..3d88216 100644 --- a/notes.txt +++ b/notes.txt @@ -1,14 +1,11 @@ Would like to be able to use dataclasses structure. Its possible now but the type hinting is needed, and its not enforced so just seems bad to use. -Can do I want to remove the inheritence from anyfig.MasterConfig? It looks neater but might be a bit harder for people to grasp what is happening. I don't want it to inherit form masterConfig and then when people inherit other shit it breaks. - make it so __repr__ is ambigious Should have a function to register the config object in the anyfig module which can later simply be imported anywhere. Singleton idea. No more passing around configs - Do we want to let users be able to create several configs? The fire/argparse doesn't work then. Unless we hook into that and divides it... But not for v1 Mark classes / properties as final? For properties, it would be cool to just From de538660b99deb1831e1aa665b6a54f064130092 Mon Sep 17 00:00:00 2001 From: OlofHarrysson Date: Mon, 11 Nov 2019 07:38:44 +0100 Subject: [PATCH 5/5] thoguht Id make a new release --- README.md | 9 +++------ example/2_example.py | 2 +- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index c6346f7..f899b22 100644 --- a/README.md +++ b/README.md @@ -21,9 +21,8 @@ import anyfig import random @anyfig.config_class -class FooConfig(anyfig.MasterConfig): +class FooConfig(): def __init__(self): - super().__init__() # Config-parameters goes as attributes self.experiment_note = 'Changed some stuff' self.seed = random.randint(0, 80085) @@ -47,9 +46,8 @@ import anyfig import random @anyfig.config_class -class FooConfig(anyfig.MasterConfig): +class FooConfig(): def __init__(self): - super().__init__() self.experiment_note = 'Changed some stuff' self.seed = random.randint(0, 80085) @@ -75,9 +73,8 @@ import anyfig import random @anyfig.config_class -class FooConfig(anyfig.MasterConfig): +class FooConfig(): def __init__(self): - super().__init__() self.experiment_note = 'Changed some stuff' self.seed = random.randint(0, 80085) diff --git a/example/2_example.py b/example/2_example.py index 206fa5e..7b7697c 100644 --- a/example/2_example.py +++ b/example/2_example.py @@ -10,7 +10,7 @@ def __init__(self): @anyfig.config_class -class FooConfig(RandomClass): +class FooConfig2(RandomClass): def __init__(self): print("INIT FOOCONFIG") super().__init__()