simple-copoco


Namesimple-copoco JSON
Version 0.2.1 PyPI version JSON
download
home_pagehttps://github.com/jakubgajski/simple_copoco
SummaryEffortlessly transition from YAML configuration to Python object management with the Simple Config Powered Code package.
upload_time2024-03-20 11:39:29
maintainerNone
docs_urlNone
authorJakub Gajski
requires_python<4.0,>=3.11
licenseMIT
keywords configuration dynamic code experiments yaml
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            ### 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"
}
        
Elapsed time: 0.21406s