Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
{
"mypy.enabled": true
"mypy.enabled": true,
"[python]": {
"editor.defaultFormatter": "ms-python.black-formatter",
"editor.formatOnSave": true
}
}
65 changes: 0 additions & 65 deletions LICENSE.txt

This file was deleted.

56 changes: 34 additions & 22 deletions comprehensiveconfig/__init__.py
Original file line number Diff line number Diff line change
@@ -1,14 +1,25 @@
from abc import ABCMeta
import os
from typing import Any, Self, Type, Union

from . import configio
from . import spec
from . import validators
from .json import JsonWriter
from .toml import TomlWriter


class _ConfigSpecMeta(type):
def autoloaded[T: "ConfigSpec"](cls: type[T]) -> T:
"""Acts as a temporary fix to issues with autoloaded configuration classes in
certain type checkers. This was originally intended to fix mypy typechecking,
but mypy doesn't actually check class decorators."""
if not cls._AUTO_LOAD:
raise ValueError("Decorator expects an autoloaded configuration class.")
if cls._INST is None:
raise ValueError("Instance was not loaded somehow.")
return cls


class _ConfigSpecMeta(spec.ConfigSectionMeta):
"""handles getting of attributes"""

_WRITER: Type[configio.ConfigurationWriter] | None
Expand All @@ -21,24 +32,20 @@ def __new__(
cls,
name,
bases,
attrs,
namespace,
default_file: str | None = None, # automatically load a specific file
writer=None,
create_file: bool = False, # create the default file if not exists
auto_load: bool = True,
**kwargs,
):
cls._DEFAULT_FILE = default_file
cls._WRITER = writer
cls._INST = None
cls._CREATE_FILE = create_file
cls._AUTO_LOAD = auto_load
return super().__new__(cls, name, bases, attrs)

def __get__(self, instance, owner):
if self._INST is None:
return self
return self._INST
namespace["_INST"] = None
namespace["_DEFAULT_FILE"] = default_file
namespace["_WRITER"] = writer
namespace["_CREATE_FILE"] = create_file
namespace["_AUTO_LOAD"] = auto_load

return super().__new__(cls, name, bases, namespace)

def __getattribute__(self, name):
"""get attributes from active instance if available"""
Expand All @@ -57,11 +64,7 @@ def __setattr__(self, name, value):
return super().__setattr__(name, value)


class _ConfigSpecABCMeta(spec.ConfigurationFieldABCMeta, _ConfigSpecMeta):
"""A combination of ABCMeta and config spec meta"""


class ConfigSpec(spec.Section, metaclass=_ConfigSpecABCMeta):
class ConfigSpec(spec.Section, metaclass=_ConfigSpecMeta):

@classmethod
def __init_subclass__(
Expand Down Expand Up @@ -95,7 +98,7 @@ def __init__(self, value: dict[str, Any] | None = None, /):
super().__init__(value or self._default_value)

@classmethod
def load(cls, file=None, writer=None, /) -> Self:
def load(cls, file=None, writer=None, /, create_file: bool = False) -> Self:
file = file or cls._DEFAULT_FILE
writer = writer or cls._WRITER

Expand All @@ -104,9 +107,18 @@ def load(cls, file=None, writer=None, /) -> Self:
if file is None:
raise Exception("No file specified")

if cls._CREATE_FILE:
exists = os.path.exists(file)
if not exists:
inst = cls()
writer.dump(cls._DEFAULT_FILE, inst)
return inst

return cls(writer.load(file))

def save(self, file=None, writer=None, /):
def save(
self, file=None, writer: Type[configio.ConfigurationWriter] | None = None, /
):
file = file or self._DEFAULT_FILE
writer = writer or self._WRITER

Expand Down Expand Up @@ -145,8 +157,8 @@ def reset_global(cls):
__all__ = [
"ConfigSpec",
"_ConfigSpecMeta",
"_ConfigSpecABCMeta",
"spec",
"validators",
"configio",
"JsonWriter",
"TomlWriter",
Expand Down
3 changes: 3 additions & 0 deletions comprehensiveconfig/json.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from datetime import datetime
import json
from pathlib import Path
from typing import Any
from . import configio
from . import spec
Expand Down Expand Up @@ -31,6 +32,8 @@ def dump_value(cls, node: spec.AnyConfigField, value):
return value.name
case spec.ConfigEnum(_, False):
return value.value
case Path():
return str(value)
case str() | int() | float() | bool() | datetime() | dict() | None:
return value
case _:
Expand Down
Loading
Loading