diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index b9d315b8ae..da48b1e8d8 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -65,6 +65,7 @@ "ms-python.vscode-pylance", "ms-python.python", "donjayamanne.python-environment-manager", + "njpwerner.autodocstring", "ms-toolsai.jupyter", "lextudio.restructuredtext", "trond-snekvik.simple-rst", diff --git a/.gitignore b/.gitignore index d75d46d5e1..7fcafa8859 100644 --- a/.gitignore +++ b/.gitignore @@ -153,6 +153,7 @@ junit/test-results.xml .doc-prereqs.*.build-stamp .pycodestyle.build-stamp .pycodestyle.*.build-stamp +.pydocstyle.*.build-stamp .pylint.build-stamp .pylint.*.build-stamp .pytest.build-stamp diff --git a/.pylintrc b/.pylintrc index cf0fbab3ab..e619ac4ea3 100644 --- a/.pylintrc +++ b/.pylintrc @@ -4,6 +4,10 @@ # Specify a score threshold to be exceeded before program exits with error. fail-under=9.7 +# Make sure public methods are documented. +# See Also: https://github.com/PyCQA/pydocstyle/issues/309#issuecomment-1426642147 +fail-on=C0116 + # Ignore pylint complaints about an upstream dependency. ignored-modules=ConfigSpace.hyperparameters diff --git a/.vscode/extensions.json b/.vscode/extensions.json index b195f7267b..f10818b0c5 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -5,6 +5,7 @@ "ms-python.vscode-pylance", "ms-python.python", "donjayamanne.python-environment-manager", + "njpwerner.autodocstring", "lextudio.restructuredtext", "trond-snekvik.simple-rst", "DavidAnson.vscode-markdownlint", // Linter for markdown files diff --git a/.vscode/settings.json b/.vscode/settings.json index 0653afa783..dc609e996b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -6,6 +6,7 @@ "python.linting.pycodestyleEnabled": true, "python.linting.pydocstyleEnabled": true, "python.testing.pytestEnabled": true, + "autoDocstring.docstringFormat": "numpy", "cSpell.ignoreWords": [ "arange", "astype", diff --git a/Makefile b/Makefile index 1bc3e2af1c..acf833b187 100644 --- a/Makefile +++ b/Makefile @@ -68,9 +68,8 @@ pydocstyle: conda-env .pydocstyle.${CONDA_ENV_NAME}.build-stamp .pydocstyle.${CONDA_ENV_NAME}.build-stamp: .conda-env.${CONDA_ENV_NAME}.build-stamp .pydocstyle.${CONDA_ENV_NAME}.build-stamp: $(PYTHON_FILES) setup.cfg # Check for decent pep8 doc style with pydocstyle. - # TODO: FIXME: Force this to break the build. - conda run -n ${CONDA_ENV_NAME} pydocstyle $(PYTHON_FILES) || true - #touch .pydocstyle.${CONDA_ENV_NAME}.build-stamp + conda run -n ${CONDA_ENV_NAME} pydocstyle $(PYTHON_FILES) + touch .pydocstyle.${CONDA_ENV_NAME}.build-stamp .PHONY: pylint pylint: conda-env .pylint.${CONDA_ENV_NAME}.build-stamp diff --git a/conda-envs/mlos_core-3.10.yml b/conda-envs/mlos_core-3.10.yml index 1bd1e23190..36a55941c3 100644 --- a/conda-envs/mlos_core-3.10.yml +++ b/conda-envs/mlos_core-3.10.yml @@ -10,6 +10,7 @@ dependencies: - pylint - pycodestyle - autopep8 + - pydocstyle - pytest - pytest-cov - pytest-forked diff --git a/conda-envs/mlos_core-3.11.yml b/conda-envs/mlos_core-3.11.yml index 4fc4724a0c..7ba5562cec 100644 --- a/conda-envs/mlos_core-3.11.yml +++ b/conda-envs/mlos_core-3.11.yml @@ -10,6 +10,7 @@ dependencies: - pylint - pycodestyle - autopep8 + - pydocstyle - pytest - pytest-cov - pytest-forked diff --git a/conda-envs/mlos_core-3.8.yml b/conda-envs/mlos_core-3.8.yml index 04ae99ef88..fb8c36521c 100644 --- a/conda-envs/mlos_core-3.8.yml +++ b/conda-envs/mlos_core-3.8.yml @@ -10,6 +10,7 @@ dependencies: - pylint - pycodestyle - autopep8 + - pydocstyle - pytest - pytest-cov - pytest-forked diff --git a/conda-envs/mlos_core-3.9.yml b/conda-envs/mlos_core-3.9.yml index 12541c330e..5b9722c550 100644 --- a/conda-envs/mlos_core-3.9.yml +++ b/conda-envs/mlos_core-3.9.yml @@ -10,6 +10,7 @@ dependencies: - pylint - pycodestyle - autopep8 + - pydocstyle - pytest - pytest-cov - pytest-forked diff --git a/conda-envs/mlos_core.yml b/conda-envs/mlos_core.yml index 5d33770e74..b79ef9be1e 100644 --- a/conda-envs/mlos_core.yml +++ b/conda-envs/mlos_core.yml @@ -10,6 +10,7 @@ dependencies: - pylint - pycodestyle - autopep8 + - pydocstyle - pytest - pytest-cov - pytest-forked diff --git a/conftest.py b/conftest.py index 7dfccdd850..4886460c63 100644 --- a/conftest.py +++ b/conftest.py @@ -13,7 +13,6 @@ def pytest_configure(config): # pylint: disable=unused-argument """ Add some additional (global) configuration steps for pytest. """ - # Workaround some issues loading emukit in certain environments. if os.environ.get('DISPLAY', None): import matplotlib # pylint: disable=import-outside-toplevel diff --git a/mlos_bench/examples/linux-kernel/create_new_grub_cfg.py b/mlos_bench/examples/linux-kernel/create_new_grub_cfg.py index e130af021f..da4b02be5a 100755 --- a/mlos_bench/examples/linux-kernel/create_new_grub_cfg.py +++ b/mlos_bench/examples/linux-kernel/create_new_grub_cfg.py @@ -1,6 +1,7 @@ #!/usr/bin/env python3 """ Python script to parse through JSON and create new config file. + This script will be run in the SCHEDULER. NEW_CFG will need to be copied over to the VM (/etc/default/grub.d). """ diff --git a/mlos_bench/mlos_bench/environment/local_env_fileshare.py b/mlos_bench/mlos_bench/environment/local_env_fileshare.py index 635c42c57e..5f45f102c8 100644 --- a/mlos_bench/mlos_bench/environment/local_env_fileshare.py +++ b/mlos_bench/mlos_bench/environment/local_env_fileshare.py @@ -41,7 +41,7 @@ class LocalFileShareEnv(LocalEnv): and the "const_args" sections. `LocalFileShareEnv` must also have at least some of the following parameters: {setup, upload, run, download, teardown, - dump_params_file, read_results_file} + dump_params_file, read_results_file} global_config : dict Free-format dictionary of global parameters (e.g., security credentials) to be mixed in into the "const_args" section of the local config. diff --git a/mlos_bench/mlos_bench/environment/status.py b/mlos_bench/mlos_bench/environment/status.py index 71f0a18f70..bfb7d8404a 100644 --- a/mlos_bench/mlos_bench/environment/status.py +++ b/mlos_bench/mlos_bench/environment/status.py @@ -9,6 +9,7 @@ class Status(enum.Enum): """ Enum for the status of the benchmark. """ + UNKNOWN = 0 PENDING = 1 READY = 2 diff --git a/mlos_core/mlos_core/optimizers/bayesian_optimizers.py b/mlos_core/mlos_core/optimizers/bayesian_optimizers.py index 90212cf1e6..0e8b154fc2 100644 --- a/mlos_core/mlos_core/optimizers/bayesian_optimizers.py +++ b/mlos_core/mlos_core/optimizers/bayesian_optimizers.py @@ -15,7 +15,7 @@ from mlos_core.spaces import configspace_to_skopt_space, configspace_to_emukit_s class BaseBayesianOptimizer(BaseOptimizer, metaclass=ABCMeta): - """Abstract base class defining the interface for Bayesian optimization. """ + """Abstract base class defining the interface for Bayesian optimization.""" @abstractmethod def surrogate_predict(self, configurations: pd.DataFrame, context: pd.DataFrame = None): @@ -57,6 +57,7 @@ class EmukitOptimizer(BaseBayesianOptimizer): space_adapter : BaseSpaceAdapter The space adapter class to employ for parameter space transformations. """ + def __init__(self, parameter_space: ConfigSpace.ConfigurationSpace, space_adapter: Optional[BaseSpaceAdapter] = None): super().__init__(parameter_space, space_adapter) self.emukit_parameter_space = configspace_to_emukit_space(self.optimizer_parameter_space) @@ -182,6 +183,7 @@ class SkoptOptimizer(BaseBayesianOptimizer): parameter_space : ConfigSpace.ConfigurationSpace The parameter space to optimize. """ + def __init__( self, parameter_space: ConfigSpace.ConfigurationSpace, diff --git a/mlos_core/mlos_core/optimizers/optimizer.py b/mlos_core/mlos_core/optimizers/optimizer.py index 0f1871bd13..36c0e2d1e3 100644 --- a/mlos_core/mlos_core/optimizers/optimizer.py +++ b/mlos_core/mlos_core/optimizers/optimizer.py @@ -23,6 +23,7 @@ class BaseOptimizer(metaclass=ABCMeta): space_adapter : BaseSpaceAdapter The space adapter class to employ for parameter space transformations. """ + def __init__(self, parameter_space: ConfigSpace.ConfigurationSpace, space_adapter: Optional[BaseSpaceAdapter] = None): self.parameter_space: ConfigSpace.ConfigurationSpace = parameter_space self.optimizer_parameter_space: ConfigSpace.ConfigurationSpace = \ diff --git a/mlos_core/mlos_core/optimizers/random_optimizer.py b/mlos_core/mlos_core/optimizers/random_optimizer.py index 3c1bb09f3b..3b2c9b7e0f 100644 --- a/mlos_core/mlos_core/optimizers/random_optimizer.py +++ b/mlos_core/mlos_core/optimizers/random_optimizer.py @@ -16,6 +16,7 @@ class RandomOptimizer(BaseOptimizer): parameter_space : ConfigSpace.ConfigurationSpace The parameter space to optimize. """ + def _register(self, configurations: pd.DataFrame, scores: pd.Series, context: pd.DataFrame = None): """Registers the given configurations and scores. diff --git a/mlos_core/mlos_core/runners.py b/mlos_core/mlos_core/runners.py deleted file mode 100644 index a0bad3178a..0000000000 --- a/mlos_core/mlos_core/runners.py +++ /dev/null @@ -1,48 +0,0 @@ -""" -Contains classes related to experiment execution runners. -These classes contain the policies for managing things like retries and failed -configs when interacting with the optimizer(s). -""" - -# TODO: Implement retry/failure handling logic. - - -class ExperimentRunner: - """Manages pending observations for parallel & asynchronous optimization.""" - def __init__(self, optimizer): - self.optimizer = optimizer - - def register(self, configurations, scores, context=None): - """Registers the given configurations and scores with the optimizer associated with this ExperimentRunner. - - Parameters - ---------- - configurations : pd.DataFrame - Dataframe of configurations / parameters. The columns are parameter names and the rows are the configurations. - - scores : pd.Series - Scores from running the configurations. The index is the same as the index of the configurations. - - context : pd.DataFrame - Not Yet Implemented. - """ - self.optimizer.register(configurations, scores, context) - - def suggest(self, context=None): - """Gets a new configuration suggestion from the optimizer associated - with this ExperimentRunner and automatically registers it as "pending", - under the assumption that it will be executed as an experiment trial. - - Parameters - ---------- - context : pd.DataFrame - Not Yet Implemented. - - Returns - ------- - configuration : pd.DataFrame - Pandas dataframe with a single row. Column names are the parameter names. - """ - configurations = self.optimizer.suggest(context) - self.optimizer.register_pending(configurations, context) - return configurations diff --git a/mlos_core/mlos_core/spaces/adapters/adapter.py b/mlos_core/mlos_core/spaces/adapters/adapter.py index a3154bcad0..1584d1e64f 100644 --- a/mlos_core/mlos_core/spaces/adapters/adapter.py +++ b/mlos_core/mlos_core/spaces/adapters/adapter.py @@ -16,6 +16,7 @@ class BaseSpaceAdapter(metaclass=ABCMeta): orig_parameter_space : ConfigSpace.ConfigurationSpace The original parameter space to explore. """ + def __init__(self, orig_parameter_space: ConfigSpace.ConfigurationSpace): self._orig_parameter_space: ConfigSpace.ConfigurationSpace = orig_parameter_space self._random_state = orig_parameter_space.random diff --git a/mlos_core/mlos_core/spaces/adapters/llamatune.py b/mlos_core/mlos_core/spaces/adapters/llamatune.py index adb93a5bff..b210618a1f 100644 --- a/mlos_core/mlos_core/spaces/adapters/llamatune.py +++ b/mlos_core/mlos_core/spaces/adapters/llamatune.py @@ -340,7 +340,7 @@ class LlamaTuneAdapter(BaseSpaceAdapter): # pylint: disable=too-many-instance- NOTE: This method is experimental, and there is currently no guarrantee that it works as expected. Raises - ------- + ------ RuntimeError: if reverse mapping computation fails. """ from scipy.linalg import pinv, LinAlgError # pylint: disable=import-outside-toplevel diff --git a/setup.cfg b/setup.cfg index ad6ed4baaa..848bce0b43 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,12 +1,28 @@ # vim: set ft=dosini: [pycodestyle] count = True +# E124: Closing bracket does not match indentation of opening bracket's line +# E128: Continuation line under-indented for visual indent +# E261: At least two spaces before inline comment +# E502: The backslash is redundant between brackets +# W503: Line break occurred before a binary operator +# W504: Line break occurred after a binary operator ignore = E124,E128,E261,E502,W503,W504 format = pylint +# See Also: .editorconfig, .pylintrc max-line-length = 132 show-source = True statistics = True [pydocstyle] -add_ignore = D200,D202,D401 +# D102: Missing docstring in public method (Avoids inheritence bug. Force checked in .pylintrc instead.) +# D105: Missing docstring in magic method +# D107: Missing docstring in __init__ +# D200: One-line docstring should fit on one line with quotes +# D401: First line should be in imperative mood +# We have many docstrings that are too long to fit on one line, so we ignore both of these two rules: +# D205: 1 blank line required between summary line and description +# D400: First line should end with a period +add_ignore = D102,D105,D107,D200,D401,D205,D400 match = .+(?