Skip to content

Commit

Permalink
Working version of the new update
Browse files Browse the repository at this point in the history
  • Loading branch information
morington committed Jun 27, 2024
1 parent 6188ce3 commit 9d4c280
Show file tree
Hide file tree
Showing 24 changed files with 573 additions and 371 deletions.
5 changes: 4 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -159,4 +159,7 @@ cython_debug/
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
.idea/

.pypirc
.pypirc

Added using Confhub
.secrets.*
5 changes: 5 additions & 0 deletions confhub/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
from confhub.core.fields import field
from confhub.core.block import BlockCore
from confhub.reader import Confhub

__all__ = ["field", "BlockCore", "Confhub"]
46 changes: 5 additions & 41 deletions confhub/__main__.py
Original file line number Diff line number Diff line change
@@ -1,49 +1,13 @@
import argparse

import structlog

import confhub.commands as commands
from confhub.setup_logger import SetupLogger, LoggerReg
from confhub.console import main
from confhub.__meta__ import __version__
from confhub.setup_logger import SetupLogger, LoggerReg

SetupLogger(
name_registration=[
LoggerReg(name="confhub_configuration", level=LoggerReg.Level.INFO),
LoggerReg(name="confhub", level=LoggerReg.Level.DEBUG),
],
default_development=True
developer_mode=True
)
logger: structlog.BoundLogger = structlog.getLogger('confhub_configuration')


class Config:
def __init__(self):
self.parser = argparse.ArgumentParser(description=f"Confhub v{__version__} help")
self.init_args()

self.__args = self.parser.parse_args()
logger.debug('Initializing config')

def parsing(self) -> None:
logger.debug('Argument handling..')

if self.__args.create:
commands.create(folder=self.__args.create)

logger.debug('Argument processing completed!')

def init_args(self):
# Добавляем аргументы
self.parser.add_argument(
'-c', '--create',
metavar="folder", type=str,
help='Generating configuration files'
)


def main() -> None:
config = Config()
config.parsing()


if __name__ == '__main__':
main()
main(prog='confhub', version=__version__)
74 changes: 74 additions & 0 deletions confhub/builder.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
from pathlib import Path
from typing import List, Any, Dict

import yaml
import structlog

from confhub.core.block import BlockCore
from confhub.core.fields import ConfigurationField

logger: structlog.BoundLogger = structlog.get_logger("confhub")


class ConfigurationBuilder:
def __init__(self, *blocks: BlockCore):
self.blocks = [*blocks]
self.datafiles: Dict[str, Any] = self.generate_filenames()

def generate_filenames(self) -> Dict[str, Any]:
_datafiles = {'settings': {}, '.secrets': {}}

def add_field_to_datafiles(field_name: str, field: ConfigurationField, parent_path: List[str]) -> None:
if field.secret:
target = _datafiles['.secrets']
elif field.filename:
if field.filename not in _datafiles:
_datafiles[field.filename] = {}
target = _datafiles[field.filename]
else:
target = _datafiles['settings']

current = target
for part in parent_path:
if part not in current:
current[part] = {}
current = current[part]

current[field_name] = field.get_default_value()

def process_block(_block: BlockCore, parent_path: List[str]) -> None:
current_path = parent_path + [_block.__block__]

for field_name, field in _block.__dict__.items():
if isinstance(field, ConfigurationField):
add_field_to_datafiles(field_name, field, current_path)
elif isinstance(field, BlockCore):
process_block(field, current_path)

for block in self.blocks:
process_block(block, [])

return _datafiles

def create_files(self, config_path: Path) -> None:
for filename, data in self.datafiles.items():
file_path = config_path / Path(f'{filename}.yml')

existing_file = None
if file_path.exists():
while existing_file is None:
result = input(f"The `{file_path}` file already exists, should I recreate it? [Y/n] ")

if result.lower() in ['y', 'n', '', ' ']:
if result.lower() == 'n':
existing_file = False
else:
existing_file = True

if existing_file:
with open(file_path, 'w', encoding='utf-8') as file:
yaml.dump(data, file, default_flow_style=False)

logger.info("Create file", path=file_path)
else:
logger.info('Skip...', path=file_path)
119 changes: 69 additions & 50 deletions confhub/commands.py
Original file line number Diff line number Diff line change
@@ -1,54 +1,73 @@
import os
from pathlib import Path
from typing import List

import structlog

import confhub.templates as templates

logger: structlog.BoundLogger = structlog.getLogger('confhub_configuration')


def create(folder: str) -> None:
def generate_configurations_files(select_folder: Path) -> None:
if not select_folder.exists():
select_folder.mkdir(parents=True, exist_ok=True)
logger.debug('Create folder', path=select_folder)

""" SECRETS CONFIGURATION FILE """
secret_file = folder_path / '.secrets.yml'
with open(secret_file, 'w', encoding='utf-8') as f:
f.write(templates.SECRETS)

""" SECRETS EXAMPLE CONFIGURATION FILE """
secret_file__example = folder_path / 'example__secrets.yml'
with open(secret_file__example, 'w', encoding='utf-8') as f:
f.write(templates.SECRETS_EXAMPLE)

""" SETTINGS CONFIGURATION FILE """
settings_file = folder_path / 'settings.yml'
with open(settings_file, 'w', encoding='utf-8') as f:
f.write(templates.SETTINGS)

logger.info('Loading configuration files', secret_file=secret_file, secret_file__example=secret_file__example, settings=settings_file)

current_dir = Path(os.getcwd())
folder_path = current_dir / Path(folder)
logger.debug('`create` function called', folder=folder_path, current_dir=current_dir)

if not folder_path.exists():
_q = True
while _q:
confirmation_creation_folder = input(f'\nDo you want to create a new folder at ({folder_path})? [Y/n]\n: ')
if not confirmation_creation_folder:
confirmation_creation_folder = 'y'

if confirmation_creation_folder.lower() in ['y', 'yes', 'д', 'да']:
_q = False
folder_path.mkdir(parents=True, exist_ok=True)
generate_configurations_files(select_folder=folder_path)
elif confirmation_creation_folder.lower() in ['n', 'no', 'н', 'нет']:
_q = False
logger.info('Generation canceled')
else:
generate_configurations_files(select_folder=folder_path)
logger.debug('Folder exists')
from confhub import templates, BlockCore
from confhub.builder import ConfigurationBuilder
from confhub.core.error import ConfhubError
from confhub.core.parsing import get_service_data
from confhub.utils.__models import get_models_from_path
from confhub.utils.gitignore import add_to_gitignore

logger: structlog.BoundLogger = structlog.getLogger('confhub')


def init(folder: str) -> None:
"""
Initializes the project in the confhub system, creating the specified folder and generating all the necessary information in it.
"""
if folder is None:
raise ConfhubError("Path not specified for initialization")

root_path = Path.cwd()
project_folder = root_path / folder

def create_folder_if_not_exists(path: Path) -> None:
if not path.exists():
path.mkdir(parents=True, exist_ok=True)

def write_file(path: Path, content: str) -> None:
with open(path, 'w', encoding='utf-8') as f:
f.write(content)

create_folder_if_not_exists(project_folder)

config_path = project_folder / 'config'
create_folder_if_not_exists(config_path)

models_file = project_folder / 'models.py'
if not models_file.exists():
write_file(models_file, templates.SAMPLE_MODELS)

service_file = root_path / '.service.yml'
service_content = templates.SERVICE.format(
models_path=models_file.relative_to(root_path),
configs_path=config_path.relative_to(root_path)
)
write_file(service_file, service_content)
add_to_gitignore('.service.*')

init_file = project_folder / '__init__.py'
write_file(init_file, templates.INIT_SAMPLE)

logger.info("Files have been successfully generated and are ready to use")


def generate_models() -> None:
"""
Receives models from a models file and generates a configuration based on them.
"""
service_data = get_service_data()

models: List[BlockCore] = get_models_from_path(data=service_data)

_config_path = service_data.get('configs_path')
if not _config_path or not isinstance(_config_path, str):
raise ValueError("Directory `config` not defined")

ConfigurationBuilder(*models).create_files(Path.cwd() / Path(_config_path))

add_to_gitignore('.secrets.*')

logger.info("Configuration successfully generated")
103 changes: 103 additions & 0 deletions confhub/console.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import inspect
from argparse import ArgumentParser, Namespace
from typing import Optional, Any, Sequence, Dict

import structlog

from confhub.core.error import confhub_error, ConfhubError
from confhub import commands
from confhub.utils.__inspect import inspect_get_full_arg_spec

logger: structlog.BoundLogger = structlog.getLogger('confhub')


class CommandLineInterface:
def __init__(self, prog: Optional[str] = None, version: Optional[str] = None) -> None:
self._generate_args(prog, version)

def _generate_args(self, prog: Optional[str] = None, version: Optional[str] = None) -> None:
def add_options(_parser: ArgumentParser, function_arguments: Dict[str, Any]):
kwargs_opts = {
"folder": dict(
kwargs=dict(
type=str,
help="Folder is specified",
),
),
}

for arg, metadata in function_arguments.items():
options = kwargs_opts.get(arg)

if options:
if metadata.get('is_optional'):
flags = options.get('flags')
if not flags:
raise ConfhubError(f"Optional flags not found for `{arg}`")

_parser.add_argument(*flags, **options.get('kwargs'))
else:
subparser.add_argument(arg, **options.get('kwargs'))

prog_version = f"{prog} v{version}"
parser = ArgumentParser(prog=prog, description=f"{prog_version} help")

parser.add_argument(
"--version", action="version", version=prog_version
)
parser.add_argument(
"--raiser",
action="store_true",
help="Raise a full stack trace on error",
)

subparsers = parser.add_subparsers()

for fn in [getattr(commands, n) for n in dir(commands)]:
if (
inspect.isfunction(fn)
and fn.__name__[0] != "_"
and fn.__module__ == "confhub.commands"
):
args: Dict[str, Any] = inspect_get_full_arg_spec(fn)

help_ = fn.__doc__
if help_:
help_text = []
for line in help_.split("\n"):
if not line.strip():
continue
else:
help_text.append(line.strip())
else:
help_text = []

subparser = subparsers.add_parser(fn.__name__, help=" ".join(help_text))
add_options(_parser=subparser, function_arguments=args)
subparser.set_defaults(cmd=(fn, args.keys()))
self.parser = parser

def run(self, options: Namespace) -> None:
fn, args = options.cmd

try:
fn(*[getattr(options, arg, None) for arg in args])
except Exception as e:
if options.raiser:
raise
else:
confhub_error(str(e), args=args)

def main(self, argv: Optional[Sequence[str]] = None) -> None:
options = self.parser.parse_args(argv)

if not hasattr(options, "cmd"):
# see http://bugs.python.org/issue9253, argparse
# behavior changed incompatibly in py3.3
self.parser.error("too few arguments")
else:
self.run(options)


def main(argv: Optional[Sequence[str]] = None, prog: Optional[str] = None, version: Optional[str] = None) -> None:
CommandLineInterface(prog=prog, version=version).main(argv=argv)
Empty file added confhub/core/__init__.py
Empty file.
24 changes: 24 additions & 0 deletions confhub/core/block.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from confhub.core.fields import ConfigurationField
from confhub.core.parsing import parsing_value


class BlockCore:
__block__ = None

@classmethod
def from_dict(cls, data: dict, development_mode: bool):
if data:
instance = cls()

for attr_name, attr_value in instance.__class__.__dict__.items():
if isinstance(attr_value, ConfigurationField):
value_str = data.get(attr_name)
if value_str is not None:
value = parsing_value(value_str, development_mode)
setattr(instance, attr_name, value)
else:
raise ValueError(f"The value for `{cls.__block__}.{attr_name}` could not be found, perhaps the file was not transferred")
elif isinstance(attr_value, BlockCore):
setattr(instance, attr_name, attr_value.from_dict(data.get(attr_value.__block__), development_mode))

return instance
Loading

0 comments on commit 9d4c280

Please sign in to comment.