diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3b41e12 --- /dev/null +++ b/.gitignore @@ -0,0 +1,126 @@ +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000..c8e3a2f --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,39 @@ +# See https://pre-commit.com for more information +# See https://pre-commit.com/hooks.html for more hooks +repos: +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.6.0 + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-shebang-scripts-are-executable + - id: check-toml + - id: check-yaml + - id: check-added-large-files + - id: debug-statements + language_version: python3 + +- repo: https://github.com/astral-sh/ruff-pre-commit + # Ruff version. + rev: v0.5.7 + hooks: + # Run the linter. + - id: ruff + types_or: [ python, pyi, jupyter ] + args: [ --fix ] + # Run the formatter. + - id: ruff-format + types_or: [ python, pyi, jupyter ] + +- repo: https://github.com/asottile/pyupgrade + rev: v3.17.0 + hooks: + - id: pyupgrade + +- repo: https://github.com/pre-commit/mirrors-mypy + rev: v1.11.1 + hooks: + - id: mypy + +default_language_version: + python: python3.12 diff --git a/README.md b/README.md new file mode 100644 index 0000000..a4ca234 --- /dev/null +++ b/README.md @@ -0,0 +1 @@ +## MindLess Molecule GENerator diff --git a/environment.yml b/environment.yml new file mode 100644 index 0000000..4e1db69 --- /dev/null +++ b/environment.yml @@ -0,0 +1,13 @@ +# conda env create -f environment.yaml +name: mlmgen +channels: + - conda-forge +dependencies: + - ruff + - coverage + - numpy + - pre-commit + - pytest + - tox + - pip: + - covdefaults diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..499b707 --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,54 @@ +[build-system] +requires = ["wheel", "setuptools>=64", "setuptools_scm>=8"] +build-backend = "setuptools.build_meta" + +[project] +name = "mlmgen" +authors = [ + { name = "Marcel Müller", email = "marcel.mueller@thch.uni-bonn.de" }, +] +description = "MindLess Molecule GENerator" +readme = "README.md" +requires-python = ">=3.8" +license = { file = "LICENSE.md" } +classifiers = [ + "License :: OSI Approved :: MIT License", + "Programming Language :: Python :: 3 :: Only", + "Programming Language :: Python :: 3.8", + "Programming Language :: Python :: 3.9", + "Programming Language :: Python :: 3.10", + "Programming Language :: Python :: 3.11", + "Programming Language :: Python :: 3.12", + "Topic :: Scientific/Engineering", + "Typing :: Typed", +] +dependencies = ["numpy"] +dynamic = ["version"] + +[project.optional-dependencies] +dev = [ + "ruff", + "covdefaults", + "coverage", + "pre-commit", + "pytest", + "tox", + "setuptools_scm>=8", +] + +[project.scripts] +mlmgen = "mlmgen:console_entry_point" + +[tool.setuptools_scm] +version_file = "src/mlmgen/__version__.py" + +[tool.pytest.ini_options] +testpaths = ["test"] +pythonpath = ["src"] + +[tool.coverage.run] +plugins = ["covdefaults"] +source = ["./src"] + +[tool.coverage.report] +fail_under = 50 diff --git a/src/mlmgen/__init__.py b/src/mlmgen/__init__.py new file mode 100644 index 0000000..a1e18cd --- /dev/null +++ b/src/mlmgen/__init__.py @@ -0,0 +1,12 @@ +""" +Squarer +======= + +Dummy command line tool to square a number. +""" + +from .__version__ import __version__ +from .cli import console_entry_point +from .mymath import square_a_number as square + +__all__ = ["__version__", "console_entry_point", "square"] diff --git a/src/mlmgen/__main__.py b/src/mlmgen/__main__.py new file mode 100644 index 0000000..3a0878f --- /dev/null +++ b/src/mlmgen/__main__.py @@ -0,0 +1,9 @@ +""" +Entry point for command line interface via `python -m `. +""" + +from .cli import console_entry_point + +if __name__ == "__main__": + # print("Hello from __main__.py") + raise SystemExit(console_entry_point()) diff --git a/src/mlmgen/__version__.py b/src/mlmgen/__version__.py new file mode 100644 index 0000000..c134c07 --- /dev/null +++ b/src/mlmgen/__version__.py @@ -0,0 +1,17 @@ +# file generated by setuptools_scm +# don't change, don't track in version control +TYPE_CHECKING = False +if TYPE_CHECKING: + from typing import Tuple, Union + + VERSION_TUPLE = Tuple[Union[int, str], ...] +else: + VERSION_TUPLE = object + +version: str +__version__: str +__version_tuple__: VERSION_TUPLE +version_tuple: VERSION_TUPLE + +__version__ = version = "0.1.dev26+g7432299.d20240810" +__version_tuple__ = version_tuple = (0, 1, "dev26", "g7432299.d20240810") diff --git a/src/mlmgen/cli/__init__.py b/src/mlmgen/cli/__init__.py new file mode 100644 index 0000000..50f7ccf --- /dev/null +++ b/src/mlmgen/cli/__init__.py @@ -0,0 +1,10 @@ +""" +Command line interface +====================== + +This module contains functionality for the CLI. +""" + +from .entrypoint import console_entry_point + +__all__ = ["console_entry_point"] diff --git a/src/mlmgen/cli/entrypoint.py b/src/mlmgen/cli/entrypoint.py new file mode 100644 index 0000000..ba8c087 --- /dev/null +++ b/src/mlmgen/cli/entrypoint.py @@ -0,0 +1,22 @@ +""" +Entrypoint for command line interface. +""" + +from __future__ import annotations + +import argparse +from collections.abc import Sequence + +from ..mymath import square_a_number as square + + +def console_entry_point(argv: Sequence[str] | None = None) -> int: + # get command line argument + parser = argparse.ArgumentParser() + parser.add_argument("number", type=float, help="Number to square.") + args = parser.parse_args(argv) + + # print result + print(square(args.number)) + + return 0 diff --git a/src/mlmgen/mymath/__init__.py b/src/mlmgen/mymath/__init__.py new file mode 100644 index 0000000..a372fc0 --- /dev/null +++ b/src/mlmgen/mymath/__init__.py @@ -0,0 +1,10 @@ +""" +Math +==== + +This module contains all mathematical functions. +""" + +from .calc import square_a_number + +__all__ = ["square_a_number"] diff --git a/src/mlmgen/mymath/calc.py b/src/mlmgen/mymath/calc.py new file mode 100644 index 0000000..85cfb85 --- /dev/null +++ b/src/mlmgen/mymath/calc.py @@ -0,0 +1,12 @@ +""" +Mathematical functions. +""" + +from __future__ import annotations + + +def square_a_number(a: float | int) -> float | int: + if not isinstance(a, (float, int)): + raise TypeError("Float or int expected.") + + return a * a diff --git a/test/__init__.py b/test/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/conftest.py b/test/conftest.py new file mode 100644 index 0000000..3273d1f --- /dev/null +++ b/test/conftest.py @@ -0,0 +1,10 @@ +""" +Setup for pytest. +""" + +import numpy as np + +# import pytest + +np.random.seed(0) +np.set_printoptions(precision=16) diff --git a/test/test_cli/__init__.py b/test/test_cli/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/test_cli/test_entrypoint.py b/test/test_cli/test_entrypoint.py new file mode 100644 index 0000000..dd6ab2f --- /dev/null +++ b/test/test_cli/test_entrypoint.py @@ -0,0 +1,16 @@ +""" +Test program call from command line. +""" + +import pytest + +from mlmgen.cli import console_entry_point + + +def test_entrypoint(capsys: pytest.CaptureFixture) -> None: + # pylint: disable=too-many-function-args + console_entry_point(["2.0"]) + + out, err = capsys.readouterr() + assert out == "4.0\n" + assert err == "" diff --git a/test/test_mymath/__init__.py b/test/test_mymath/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/test/test_mymath/test_square.py b/test/test_mymath/test_square.py new file mode 100644 index 0000000..4913e8d --- /dev/null +++ b/test/test_mymath/test_square.py @@ -0,0 +1,28 @@ +""" +Test the squaring function. +""" + +from __future__ import annotations + +import numpy as np +import pytest + +from mlmgen.mymath import square_a_number + + +@pytest.mark.parametrize("value", [1.0, 2, -3.0]) +def test_squarer(value: int | float) -> None: + expected = value * value + actual = square_a_number(value) + + assert pytest.approx(expected) == actual + + +def test_squarer_fail() -> None: + with pytest.raises(TypeError): + square_a_number("2") # type: ignore + + +def test_dummy() -> None: + # show effect of `conftest.py` by setting printoptions + print(np.array([1.0 / 3.0])) diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..a5f4db0 --- /dev/null +++ b/tox.ini @@ -0,0 +1,13 @@ +[tox] +envlist = py{312} + +[testenv] +deps = + covdefaults + coverage + pytest +commands = + coverage erase + coverage run -m pytest -svv {posargs:test} + coverage report -m + coverage xml -o coverage.xml