Giter Club home page Giter Club logo

betterconf's Introduction

Minimalistic Python library for your configs.

Betterconf (better config) is a Python library for project configuration management. It allows you define your config like a regular Python class.

Features:

  • Easy to hack.
  • Less boilerplate.
  • Minimal code to do big things.

Installation

I recommend you to use poetry:

poetry add betterconf

However, you can use pip:

pip install betterconf

How to?

This is a guide written for betterconf 3.x, earlier versions can work differently

Try to write a simple config:

from betterconf import field, Config

class MyConfig(Config):
    my_var = field("my_var")

cfg = MyConfig()
print(cfg.my_var)

Try to run:

my_var=1 python our_file.py

With default values:

from betterconf import field, Config

class MyConfig(Config):
    my_var = field("my_var", default="hello world")
    my_second_var = field("my_second_var", default=lambda: "hi") # can be callable!

cfg = MyConfig()
print(cfg.my_var)
print(cfg.my_second_var)
# hello world
# hi

Override values when it's needed (for an example: test cases)

from betterconf import field, Config

class MyConfig(Config):
    my_var = field("my_var", default="hello world")

cfg = MyConfig(my_var="WOW!")
print(cfg.my_var)
# WOW!

By default, betterconf gets all values from os.environ but sometimes we need more. You can create own field's value provider in minutes:

from betterconf import field, Config
from betterconf.config import AbstractProvider

class NameProvider(AbstractProvider):
    def get(self, name: str):
        return name

class Cfg(Config):
    my_var = field("my_var", provider=NameProvider())

cfg = Cfg()
print(cfg.my_var)
# my_var

You can specify the class' default provider manually:

from betterconf import field, Config
from betterconf.config import AbstractProvider

class FancyNameProvider(AbstractProvider):
    def get(self, name: str) -> str:
        return f"fancy_{name}"
    
class Cfg(Config):
    _provider_ = FancyNameProvider()
    
    val1 = field("val1")
    val2 = field("val2")
    
cfg = Cfg()
print(cfg.val1, cfg.val2)
# fancy_val1, fancy_val2

# or you can change the provider at initialization moment
cfg = Cfg(_provider_=FancyNameProvider())
...

# However, this won't work with nested configs; their provider will be as it's set in `_provider_` field

Also, we can cast our values to python objects (or just manipulate them):

from betterconf import field, Config
# out of the box we have `to_bool` and `to_int`
from betterconf.caster import to_bool, to_int, AbstractCaster


class DashToDotCaster(AbstractCaster):
    def cast(self, val: str):
        return val.replace("-", ".")

to_dot = DashToDotCaster()

class Cfg(Config):
    integer = field("integer", caster=to_int)
    boolean = field("boolean", caster=to_bool)
    dots = field("dashes", caster=to_dot)

cfg = Cfg()
print(cfg.integer, cfg.boolean, cfg.dots)
# -500, True, hello.world
integer=-500 boolean=true dashes=hello-world python our_file.py

Sometimes we need to reference one field value in another one.

from betterconf import Config, field, reference_field
from betterconf.caster import to_int

class MyConfig(Config):
    money = field("MONEY_VAR", caster=to_int)
    name = field("NAME_VAR")
    
    money_if_a_lot: int = reference_field(money, func=lambda m: m * 1000)
    greeting: str = reference_field(money, name, func=lambda m, n: f"Hello, my name is {n} and I'm rich for {m}")
    

There is a builtin support for constant fields and getting field's value on-fly at any place of code:

from betterconf import value, constant_field, Config
from betterconf.caster import to_int

class Constants(Config):
    admin_id: int = constant_field(1)
    timezone: str = constant_field("UTC+3")
    
debug: bool = value("DEBUG", default=True)
print("Current status of debbuging is ", debug)

# betterconf can be used as a casting framework!
val: int = value(default="123", caster=to_int)
assert val == 123

License

This project is licensed under MIT License.

See LICENSE for details.

Made with ❤️ by prostomarkeloff and our contributors.

betterconf's People

Contributors

jieggii avatar prostomarkeloff avatar rmuskovets avatar znbiz avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

betterconf's Issues

Why are you using inheritance?

Why are you using inheritance?
It is bad that the base class modifies the derived.
A class decorator or a metaclass is more suitable for this task.

As an example, you could look into the dataclass module and use type annotations for better syntax, such:

@better_config 
class Config:
      int_var: int        # APP_INT_VAR from env
      float_var: float
      bool_var: bool
      special_var = Field("SomeSpecialNameWithoutPrefix", type=int)


config = Config(prefix="APP_")

config.int_var   # This We have a hit in IDE

partial field for default provider mutable field is not initiazed

class ConstantProvider(AbstractProvider):
    def get(self, name: str) -> typing.Any:
        return None


constant_field = partial(field, name="omit", provider=ConstantProvider())

class Config(BConfig):
     some_field = constant_field(default=1)
     some_default_dict_field = constant_field(default={"some_key":  some_field})
     
Config = Config()

Executable:

print(Config.some_default_dict_field["some_key"] * 1)

Expected behaviour:

1

Got this:

TypeError: unsupported operand type(s) for *: 'Field' and 'int'

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.