from typing import TYPE_CHECKING import aox.utils from aox.utils import get_current_directory if TYPE_CHECKING: from aox.challenge import Debugger # noqa: F401 dot_aox = get_current_directory() repo_root = dot_aox.parent sensitive_user_settings = aox.utils.load_module_from_path( dot_aox.joinpath('sensitive_user_settings.py')) AOC_SESSION_ID = sensitive_user_settings.AOC_SESSION_ID """ This is the `session` cookie, when you're logged in on the AOC website. You should copy it here verbatim. Do not put your session ID here, so that you don't run the risk of committing the file to Git! Put it in `sensitive_user_settings.py` in the same directory. """ CHALLENGES_ROOT = repo_root """ The directory where the challenges code is on. """ CHALLENGES_MODULE_NAME_ROOT = None """ The name of the module under which the challenges leave. If there is no parent module (or it is the root one) you can leave it as `None`.
from dataclasses import dataclass from pathlib import Path from aox.boilerplate import DefaultBoilerplate from aox.utils import get_current_directory current_directory = get_current_directory() @dataclass class DummyBoilerplate(DefaultBoilerplate): example_year_path = current_directory\ .joinpath('test_boilerplate_example_year') example_day_path: Path = example_year_path.joinpath('test_example_day') example_part_path: Path = example_day_path.joinpath('test_example_part.py') example_input_path: Path = example_day_path.joinpath('input.txt')
class Settings: is_missing: bool settings_directory: Path path: Path module: Module sensitive_users_path: Path aoc_session_id: Optional['str'] = field( default=None, metadata={ "module_attribute": "AOC_SESSION_ID", "warn": warnings.warn_falsy_attribute, }, ) challenges_root: Optional[Path] = field( default=Path(), metadata={ "module_attribute": "CHALLENGES_ROOT", "warn": warnings.warn_missing_path, }, ) challenges_module_name_root: Optional[str] = field( default=None, metadata={"module_attribute": "CHALLENGES_MODULE_NAME_ROOT"}, ) # noinspection PyUnresolvedReferences challenges_boilerplate: 'aox.boilerplate.BaseBoilerplate' = field( # noqa: F821, E501 default="aox.boilerplate.DefaultBoilerplate", metadata={ "module_attribute": "CHALLENGES_BOILERPLATE", "warn": warnings.error_on(warnings.warn_missing_instance), "validate_on_load": True, }, ) site_data_path: Optional[Path] = field( default=None, metadata={ "module_attribute": "SITE_DATA_PATH", "warn": warnings.warn_falsy_attribute, }, ) readme_path: Optional[Path] = field( default=Path().joinpath('README.md'), metadata={ "module_attribute": "README_PATH", "warn": warnings.warn_missing_path, }, ) extra_module_imports: List[str] = field( default_factory=list, metadata={ "module_attribute": "EXTRA_MODULE_IMPORTS", }, ) default_debugger_report_format: \ Optional[Callable[['Debugger', str], str]] = field( default=None, metadata={ "module_attribute": "DEFAULT_DEBUGGER_REPORT_FORMAT", "warn": warnings.warn_falsy_attribute, }, ) _warnings: Dict[str, Any] = field( default_factory=warnings.get_warnings_for_new_instance) DEFAULT_SETTINGS_DIRECTORY = Path('.aox') DEFAULT_PATH_NAME = 'user_settings.py' DEFAULT_SENSITIVE_USERS_PATH_NAME = 'sensitive_user_settings.py' EXAMPLE_SETTINGS_DIRECTORY = \ get_current_directory().joinpath('.example-aox') @classmethod def from_default(cls): return cls.from_settings_directory(cls.DEFAULT_SETTINGS_DIRECTORY) @classmethod def from_settings_directory(cls, settings_directory): path = settings_directory.joinpath(cls.DEFAULT_PATH_NAME) sensitive_users_path = settings_directory\ .joinpath(cls.DEFAULT_SENSITIVE_USERS_PATH_NAME) try: settings_module = load_module_from_path(path) except (ImportError, FileNotFoundError) as e: click.echo( f"Could not load {e_error('user_settings.py')} ({e}): using " f"default settings - use {e_suggest('aox init-settings')} to " f"create your settings file") settings_module = None return cls.from_settings_module( settings_module, settings_directory=settings_directory, path=path, sensitive_users_path=sensitive_users_path) @classmethod def from_settings_module(cls, settings_module, settings_directory, path, sensitive_users_path): return cls( is_missing=settings_module is None, settings_directory=settings_directory, path=path, module=settings_module, sensitive_users_path=sensitive_users_path, **{ _field.name: getattr( settings_module, _field.metadata['module_attribute'], ) for _field in fields(cls) if 'module_attribute' in _field.metadata and hasattr( settings_module, _field.metadata['module_attribute'], ) }, ) def __post_init__(self): self.validate() self.post_settings_init() def validate(self): validation_errors = self.get_validation_errors() if validation_errors: click.echo( f"Encountered {e_error('some errors')} while loading settings:" f"\n" + "\n".join( f" * {error}" for error in validation_errors )) raise InvalidSettingsError( "Settings were invalid", validation_errors) def get_validation_errors(self): validation_errors = [] for _field in fields(self): if not _field.metadata.get('validate_on_load', False): continue try: getattr(self, _field.name) except SettingsValidationError as e: validation_errors.append(e) return validation_errors def __getattribute__(self, item): value = super().__getattribute__(item) _warnings = super().__getattribute__('_warnings') if item in _warnings: warn, module_attribute = _warnings.pop(item) value = warn(self, item, module_attribute, value) setattr(self, item, value) return value def post_settings_init(self): self.load_extra_module_imports() def load_extra_module_imports(self): for module_name in self.extra_module_imports: import_module(module_name)