### Overview
Effortlessly transition from YAML configuration to Python object management with the Simple Config Powered Code package.
### Installation
```bash
pip install simple-copoco
```
### Why don't you just use OmegaConf + Hydra?
OmegaConf + Hydra duet is fantastic and great (also literally). So there are some reasons to search for alternatives:
1. Simplicity: The simple-copoco utility is designed to be lightweight and easy to use. If you have a project with straightforward configuration needs, simple-copoco provides an intuitive and easy to learn way to manage objects via configuration without the need for additional complexity.
2. Integration of configuration with object registration: simple-copoco offers features for object registration, allowing you to register and manage classes dynamically. If your project involves dynamic class instantiation based on configuration, simple-copoco provides a convenient way to achieve this.
3. Grid Configurations: simple-copoco includes a GridManager for handling grids of YAML configurations, which can be useful for scenarios where you need to explore multiple configuration combinations in you experiments (whatever you do).
### Getting Started
**Handling YAML Configurations**
Consider the following YAML configuration:
```yaml
berries:
color: blue
amount: 5
coconuts:
amount: 1
```
In Python:
```python
from simple_copoco import ConfigManager
# config path
config_path = '~/path/to/config.yaml'
# optional, possibly wide, general config with many defaults
config_template_path = '~/path/to/template.yaml'
# Option 1: template will be updated with provided config
cfg_manager = ConfigManager(config_path, config_template_path)
# Option 2: template doesn't exist
cfg_manager = ConfigManager(config_path)
my_config = cfg_manager.cfg
# access config attributes
how_many_berries_to_buy = my_config.berries.amount
what_color_of_berries_to_buy = my_config.berries.color
# unpack config attributes, like it is a dictionary
do_something_with_beries(**my_config.berries)
# dump config
cfg_manager.save_to_disk('~/where/to/save.yaml')
```
**Managing Grids of YAML Configurations**
Consider the following YAML grid:
```yaml
berries:
color: [red, green, yellow]
amount: [5, 10, 15]
coconuts:
amount: [1, 3]
```
In Python:
```python
from simple_copoco import GridManager
# Specify paths
grid_path = '~/path/to/grid.yaml'
config_path = '~/path/to/config.yaml'
config_template_path = '~/path/to/template.yaml' # optional
# Grid manager will create all possible combinations of settings from grid.yaml
grid_manager = GridManager(grid_path, config_path, config_template_path)
# Grid manager is a generator
first_config_manager = grid_manager.next()
second_config_manager = grid_manager.next()
# Total number of config managers
total_of_config_managers = len(grid_manager)
first_config = first_config_manager.cfg
```
**Object Registration**
```python
from torch import nn
from simple_copoco import Register, register_as, RegistrableMixin
@register_as('utility_layer')
class CustomLayer(nn.Module):
def __init__(self):
super(CustomLayer, self).__init__()
...
register = Register(nn.Module, include_parent=False)
# register.buit_ins -> Dictionary of all classes inheriting
# directly or indirectly from nn.Module that weren't registered
# with @register_as.
# register.utility_layer -> Dictionary of classes inheriting from
# nn.Module registered as "utility_layer"
# another use case, when submodules of a package are not being imported
# with their parent. It happens, when the package has complex directory tree
# and uses blank __init__.py files.
from .. import custom_layers # custom_modules has some submodules
register = Register(nn.Module, custom_layers)
@register_as('classifier_head')
class CustomHead(nn.Module):
def __init__(self):
super(CustomHead, self).__init__()
...
# You can do the same stuff with functions :)
# Registered function is stored in the register as a wrapped object,
# but can be called the normal way.
@register_as('functions')
def some_interesting_function(a, b, c):
return a * b * c
assert register.functions['some_interesting_function'](1, 2, 3) == 6 # True
# Update the register with the current namespace
register.update_register()
# register.classifier_head -> dict of all the classes in the namespace
# inheriting from nn.Module registered as "classifier_head"
# Add a class (that has been registered after Register initialisation) to the
# register manually
register.extend(CustomHead)
class AnyClass:
...
def some_func():
...
# Register anything
register.extend('other_classes', 'AnyClass', AnyClass)
# name in the register doesn't need to the same
register.extend('functions', 'some_function', some_func)
# Register hint :)
register = Register(RegistrableMixin)
# registers anything within the namespace that inherits from RegistrableMixin
# because @register_as() injects RegistrableMixin as a parent class
# (but this inheritance will be ignored in children if passes_on_children == False).
```
**Putting It All Together**
Consider the following configuration:
```yaml
core:
type: OtherModule
params:
a: 1
b: 2
head:
type: CustomHead
params:
x: 1
y: 1
```
In Python:
```python
from torch.nn import Module
from simple_copoco import Register, register_as
from simple_copoco import ConfigManager
@register_as('core', passes_on_children=True)
class CustomModule(Module):
...
class OtherModule(CustomModule):
...
@register_as('head')
class CustomHead(Module):
...
register = Register(Module)
cfg_man = ConfigManager('path/to/some_config.yaml', 'path/to/config_template.yaml')
cfg = cfg_man.cfg
# Create dynamic code based on configuration.
# Attributes are accessible by dot notation, but you can use ** to unpack them,
# just like with dictionaries.
# It makes experimentation with different architectures and many sets of parameters a breeze.
class MemesClassifier(Module):
def __init__(self, cfg, register):
self.model_core = register.core[cfg.core.type](**cfg.core.params)
self.classifier_head = register.head[cfg.head.type](**cfg.head.params)
...
```
Raw data
{
"_id": null,
"home_page": "https://github.com/jakubgajski/simple_copoco",
"name": "simple-copoco",
"maintainer": null,
"docs_url": null,
"requires_python": "<4.0,>=3.11",
"maintainer_email": null,
"keywords": "configuration, dynamic code, experiments, yaml",
"author": "Jakub Gajski",
"author_email": "jakub.gajski@gmail.com",
"download_url": "https://files.pythonhosted.org/packages/58/2f/e71ba3f53226e8683e8272bc99e325f9895a0c7c84663b17412085a86f61/simple_copoco-0.2.1.tar.gz",
"platform": null,
"description": "### Overview\n\nEffortlessly transition from YAML configuration to Python object management with the Simple Config Powered Code package.\n\n### Installation\n```bash\npip install simple-copoco\n```\n\n### Why don't you just use OmegaConf + Hydra? \nOmegaConf + Hydra duet is fantastic and great (also literally). So there are some reasons to search for alternatives: \n\n1. Simplicity: The simple-copoco utility is designed to be lightweight and easy to use. If you have a project with straightforward configuration needs, simple-copoco provides an intuitive and easy to learn way to manage objects via configuration without the need for additional complexity.\n2. Integration of configuration with object registration: simple-copoco offers features for object registration, allowing you to register and manage classes dynamically. If your project involves dynamic class instantiation based on configuration, simple-copoco provides a convenient way to achieve this.\n3. Grid Configurations: simple-copoco includes a GridManager for handling grids of YAML configurations, which can be useful for scenarios where you need to explore multiple configuration combinations in you experiments (whatever you do).\n\n### Getting Started\n**Handling YAML Configurations** \nConsider the following YAML configuration:\n```yaml\nberries:\n color: blue\n amount: 5\ncoconuts:\n amount: 1\n```\nIn Python:\n\n```python\nfrom simple_copoco import ConfigManager\n\n# config path\nconfig_path = '~/path/to/config.yaml'\n\n# optional, possibly wide, general config with many defaults\nconfig_template_path = '~/path/to/template.yaml'\n\n# Option 1: template will be updated with provided config\ncfg_manager = ConfigManager(config_path, config_template_path)\n# Option 2: template doesn't exist\ncfg_manager = ConfigManager(config_path)\n\nmy_config = cfg_manager.cfg\n\n# access config attributes\nhow_many_berries_to_buy = my_config.berries.amount\nwhat_color_of_berries_to_buy = my_config.berries.color\n\n# unpack config attributes, like it is a dictionary\ndo_something_with_beries(**my_config.berries)\n\n# dump config\ncfg_manager.save_to_disk('~/where/to/save.yaml')\n```\n**Managing Grids of YAML Configurations** \nConsider the following YAML grid:\n```yaml\nberries:\n color: [red, green, yellow]\n amount: [5, 10, 15]\ncoconuts:\n amount: [1, 3]\n```\nIn Python:\n```python\nfrom simple_copoco import GridManager\n\n# Specify paths\ngrid_path = '~/path/to/grid.yaml'\nconfig_path = '~/path/to/config.yaml'\nconfig_template_path = '~/path/to/template.yaml' # optional\n\n# Grid manager will create all possible combinations of settings from grid.yaml\ngrid_manager = GridManager(grid_path, config_path, config_template_path)\n\n# Grid manager is a generator\nfirst_config_manager = grid_manager.next()\nsecond_config_manager = grid_manager.next()\n\n# Total number of config managers\ntotal_of_config_managers = len(grid_manager)\n\nfirst_config = first_config_manager.cfg\n```\n**Object Registration**\n```python\nfrom torch import nn\nfrom simple_copoco import Register, register_as, RegistrableMixin\n\n@register_as('utility_layer')\nclass CustomLayer(nn.Module):\n def __init__(self):\n super(CustomLayer, self).__init__()\n ...\n\nregister = Register(nn.Module, include_parent=False)\n# register.buit_ins -> Dictionary of all classes inheriting\n# directly or indirectly from nn.Module that weren't registered\n# with @register_as.\n# register.utility_layer -> Dictionary of classes inheriting from\n# nn.Module registered as \"utility_layer\"\n\n# another use case, when submodules of a package are not being imported\n# with their parent. It happens, when the package has complex directory tree\n# and uses blank __init__.py files.\nfrom .. import custom_layers # custom_modules has some submodules\n\nregister = Register(nn.Module, custom_layers)\n\n@register_as('classifier_head')\nclass CustomHead(nn.Module):\n def __init__(self):\n super(CustomHead, self).__init__()\n ...\n\n# You can do the same stuff with functions :)\n# Registered function is stored in the register as a wrapped object,\n# but can be called the normal way.\n@register_as('functions')\ndef some_interesting_function(a, b, c):\n return a * b * c\n\nassert register.functions['some_interesting_function'](1, 2, 3) == 6 # True\n\n# Update the register with the current namespace\nregister.update_register()\n# register.classifier_head -> dict of all the classes in the namespace\n# inheriting from nn.Module registered as \"classifier_head\"\n\n# Add a class (that has been registered after Register initialisation) to the\n# register manually\nregister.extend(CustomHead)\n\nclass AnyClass:\n ...\n\ndef some_func():\n ...\n\n# Register anything\nregister.extend('other_classes', 'AnyClass', AnyClass)\n# name in the register doesn't need to the same\nregister.extend('functions', 'some_function', some_func)\n \n# Register hint :) \nregister = Register(RegistrableMixin) \n# registers anything within the namespace that inherits from RegistrableMixin\n# because @register_as() injects RegistrableMixin as a parent class\n# (but this inheritance will be ignored in children if passes_on_children == False).\n```\n**Putting It All Together** \nConsider the following configuration:\n```yaml\ncore:\n type: OtherModule\n params:\n a: 1\n b: 2\nhead:\n type: CustomHead\n params:\n x: 1\n y: 1\n```\nIn Python:\n```python\nfrom torch.nn import Module\nfrom simple_copoco import Register, register_as\nfrom simple_copoco import ConfigManager\n\n\n@register_as('core', passes_on_children=True)\nclass CustomModule(Module):\n ...\n\nclass OtherModule(CustomModule):\n ...\n\n@register_as('head')\nclass CustomHead(Module):\n ...\n\nregister = Register(Module)\ncfg_man = ConfigManager('path/to/some_config.yaml', 'path/to/config_template.yaml')\ncfg = cfg_man.cfg\n\n# Create dynamic code based on configuration.\n# Attributes are accessible by dot notation, but you can use ** to unpack them,\n# just like with dictionaries.\n# It makes experimentation with different architectures and many sets of parameters a breeze.\n\nclass MemesClassifier(Module):\n \n def __init__(self, cfg, register):\n self.model_core = register.core[cfg.core.type](**cfg.core.params)\n self.classifier_head = register.head[cfg.head.type](**cfg.head.params)\n \n ...\n```\n",
"bugtrack_url": null,
"license": "MIT",
"summary": "Effortlessly transition from YAML configuration to Python object management with the Simple Config Powered Code package.",
"version": "0.2.1",
"project_urls": {
"Homepage": "https://github.com/jakubgajski/simple_copoco",
"Repository": "https://github.com/jakubgajski/simple_copoco"
},
"split_keywords": [
"configuration",
" dynamic code",
" experiments",
" yaml"
],
"urls": [
{
"comment_text": "",
"digests": {
"blake2b_256": "6f02bf40bf44c22662d30273b0f259232eb1e9da54b317214eb78a1a3f789fe8",
"md5": "a6906d827b61748569ff644d971a2ba2",
"sha256": "455045d0625804ce0dbddaa167f78f1c31d442f19946cc9c3209a69e70b0753a"
},
"downloads": -1,
"filename": "simple_copoco-0.2.1-py3-none-any.whl",
"has_sig": false,
"md5_digest": "a6906d827b61748569ff644d971a2ba2",
"packagetype": "bdist_wheel",
"python_version": "py3",
"requires_python": "<4.0,>=3.11",
"size": 10986,
"upload_time": "2024-03-20T11:39:26",
"upload_time_iso_8601": "2024-03-20T11:39:26.397848Z",
"url": "https://files.pythonhosted.org/packages/6f/02/bf40bf44c22662d30273b0f259232eb1e9da54b317214eb78a1a3f789fe8/simple_copoco-0.2.1-py3-none-any.whl",
"yanked": false,
"yanked_reason": null
},
{
"comment_text": "",
"digests": {
"blake2b_256": "582fe71ba3f53226e8683e8272bc99e325f9895a0c7c84663b17412085a86f61",
"md5": "2b26e8c962a38c3dc9343481e8430550",
"sha256": "327602e4e276172afbc39a9fe828bd0ac0e68f8704a27628906d85a7971cb2eb"
},
"downloads": -1,
"filename": "simple_copoco-0.2.1.tar.gz",
"has_sig": false,
"md5_digest": "2b26e8c962a38c3dc9343481e8430550",
"packagetype": "sdist",
"python_version": "source",
"requires_python": "<4.0,>=3.11",
"size": 11850,
"upload_time": "2024-03-20T11:39:29",
"upload_time_iso_8601": "2024-03-20T11:39:29.481349Z",
"url": "https://files.pythonhosted.org/packages/58/2f/e71ba3f53226e8683e8272bc99e325f9895a0c7c84663b17412085a86f61/simple_copoco-0.2.1.tar.gz",
"yanked": false,
"yanked_reason": null
}
],
"upload_time": "2024-03-20 11:39:29",
"github": true,
"gitlab": false,
"bitbucket": false,
"codeberg": false,
"github_user": "jakubgajski",
"github_project": "simple_copoco",
"travis_ci": false,
"coveralls": false,
"github_actions": false,
"lcname": "simple-copoco"
}