-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
24 changed files
with
573 additions
and
371 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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__) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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 |
Oops, something went wrong.