Skip to content

Commit

Permalink
Added doctests (#207)
Browse files Browse the repository at this point in the history
* Fix `pycodestyle` and `pydocstyle` compliance

* Docstring fixes

* Fixed an incorrect exception in `_Settings.__missing__`
* Fixed a number of incorrect Sphinx domains
* Clarified a few docstrings

* Enabled doctest

* Removed a redundant assignment
  • Loading branch information
BvB93 committed Jun 24, 2020
1 parent 14f6c58 commit 6b5cbf7
Show file tree
Hide file tree
Showing 10 changed files with 61 additions and 33 deletions.
5 changes: 5 additions & 0 deletions conftest.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
"""A pytest ``conftest.py`` file."""

import os

collect_ignore = [os.path.join('src', 'qmflows', 'settings.py')]
1 change: 1 addition & 0 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
'sphinx.ext.githubpages',
'sphinx.ext.autosummary',
'sphinx.ext.napoleon',
'sphinx.ext.doctest',
'nbsphinx',
# Workaround for this issue: https://github.com/spatialaudio/nbsphinx/issues/24
'IPython.sphinxext.ipython_console_highlighting'
Expand Down
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ max-line-length = 100

[tool:pytest]
testpaths = src test
addopts = --pycodestyle --pydocstyle --tb=short --cov --cov-report xml --cov-report term --cov-report html
addopts = --pycodestyle --pydocstyle --tb=short --cov --doctest-modules --cov-report xml --cov-report term --cov-report html
markers = slow: A marker for slow tests requiring external quantum-chemical packages.

# Define `python setup.py build_sphinx`
Expand Down
11 changes: 8 additions & 3 deletions src/qmflows/backports.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,16 @@ class _NullContextBackup(AbstractContextManager):
Used as a stand-in for a normal context manager, when a particular
block of code is only sometimes used with a normal context manager:
.. testsetup:: python
>>> condition = False
.. code:: python
>>> cm = optional_cm if condition else nullcontext()
>>> with cm:
... ... # Perform operation, using optional_cm if condition is True
>>> cm = optional_cm if condition else nullcontext(1)
>>> with cm as f:
... print(f)
1
"""

Expand Down
8 changes: 4 additions & 4 deletions src/qmflows/cp2k_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -522,10 +522,10 @@ def prm_to_df(settings: MutableMapping) -> None:
>>> print(type(s.lennard_jones))
<class 'pandas.core.frame.DataFrame'>
>>> print(s)
lennard_jones: param unit Cs Cd O H
epsilon epsilon kcalmol 1.0 2.0 3.0 4.0
sigma sigma angstrom 1.0 2.0 3.0 4.0
>>> print(s) # doctest: +NORMALIZE_WHITESPACE
lennard_jones: param unit Cs Cd O H
epsilon epsilon kcalmol 1.0 2.0 3.0 4.0
sigma sigma angstrom 1.0 2.0 3.0 4.0
Parameters
Expand Down
25 changes: 16 additions & 9 deletions src/qmflows/packages/package_wrapper.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,23 +27,30 @@
For example:
.. testsetup:: python
>>> from scm.plams import from_smiles
>>> mol = from_smiles('C')
>>> settings = Settings()
.. code:: python
>>> from scm.plams import AMSJob, Molecule, Settings
>>> from qmflows import run, PackageWrapper
>>> from qmflows.packages.package_wrapper import ResultWrapper
>>> mol = Molecule(...)
>>> settings = Settings(...)
>>> mol = Molecule(...) # doctest: +SKIP
>>> settings = Settings(...) # doctest: +SKIP
>>> pkg = PackageWrapper(AMSJob)
>>> job = pkg(settings, mol, name='amsjob')
>>> result: ResultWrapper = run(job, ...)
>>> result: ResultWrapper = run(job)
>>> energy = result.get_energy() # Alias for AMSResults.get_energy()
>>> mol = result.get_molecule() # Alias for AMSResults.get_molecule()
>>> freq = result.get_frequencies() # Alias for AMSResults.get_frequencies()
>>> energy = result.get_energy() # doctest: +SKIP
>>> mol = result.get_molecule() # doctest: +SKIP
>>> freq = result.get_frequencies() # doctest: +SKIP
Index
Expand Down Expand Up @@ -144,7 +151,7 @@ class PackageWrapper(Package):
>>> from scm.plams import ADFJob, AMSJob
>>> from qmflows PackageWrapper, run
>>> from qmflows import PackageWrapper, run
>>> from qmflows.packages.package_wrapper import ResultWrapper
>>> from qmflows.packages.SCM import ADF_Result
Expand All @@ -153,8 +160,8 @@ class PackageWrapper(Package):
>>> pkg_ams = PackageWrapper(AMSJob)
# End up with two different Result instances
>>> result_adf: ADF_Result = run(pkg_adf(...), ...)
>>> result_ams: ResultWrapper = run(pkg_ams(...), ...)
>>> result_adf: ADF_Result = run(pkg_adf(...), ...) # doctest: +SKIP
>>> result_ams: ResultWrapper = run(pkg_ams(...), ...) # doctest: +SKIP
Attributes
----------
Expand Down
6 changes: 3 additions & 3 deletions src/qmflows/packages/packages.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,10 +108,10 @@ def __getattr__(self, prop: str) -> Any:
..code:: python
>>> from qmflows.packages.packages import Results
>>> from qmflows.packages.packages import Result
>>> result = Result(...)
>>> dipole = result.dipole
>>> result = Result(...) # doctest: +SKIP
>>> dipole = result.dipole # doctest: +SKIP
"""
is_private = prop.startswith('__') and prop.endswith('__')
Expand Down
6 changes: 3 additions & 3 deletions src/qmflows/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,9 @@ class Settings(plams.core.settings.Settings, ):
"""A subclass of :class:`plams.Settings<scm.plams.core.settings.Settings>`.
The difference with respect to plams' Settings are:
- :code:`settings['a.b']` is equivalent to :code:`settings['a']['b'] = settings.a.b`
- in :meth:`Settings.update`: :code:`settings.__block_replace = True` results in removal
- in :meth:`Settings.update`: :code:`settings.__block_replace = True` results in removal
of all existing key value pairs.
``__block_replace`` can be either in the updated settings or in the updating settings object.
"""
Expand All @@ -40,7 +40,7 @@ def __delitem__(self, name):

def copy(self):
"""Create a deep(-ish) copy of this instance.
All nested settings instances embedded within *self* are copied recursively;
all other objects set without copying.
"""
Expand Down
4 changes: 2 additions & 2 deletions src/qmflows/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,11 +95,11 @@ def delete_output(delete_db: Union[Callable, bool] = True,
>>> from qmflows.test_utils import delete_output
>>> @delete_output
>>> def test1(...):
... def test1(*args, **kwargs):
... ...
>>> @delete_output(delete_db=True, delete_workdir=False)
>>> def test2(...):
... def test2(*args, **kwargs):
... ...
Parameters
Expand Down
26 changes: 18 additions & 8 deletions src/qmflows/utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,18 @@ def to_runtime_error(func: Callable) -> Callable:
... raise Exception('error')
>>> @to_runtime_error
>>> def func2(settings, key, value, mol):
... def func2(settings, key, value, mol):
... raise Exception('error')
>>> func1(None, 'func1', None, None)
Exception('error')
Traceback (most recent call last):
...
Exception: error
>>> func1(None, 'func2', None, None)
Exception('"func2" section: error')
>>> func2(None, 'func2', None, None)
Traceback (most recent call last):
...
RuntimeError: 'func2' section: error
"""
@wraps(func)
Expand All @@ -71,18 +75,24 @@ def file_to_context(file: Union[int, PathLike, IO],
Examples
--------
.. testsetup:: python
>>> from pathlib import Path
>>> path_like = Path('test') / 'test_files' / 'mypy.ini'
.. code:: python
>>> from io import StringIO
>>> path_like = 'file_name.txt'
>>> path_like = 'file_name.txt' # doctest: +SKIP
>>> file_like = StringIO('this is a file-like object')
>>> context1 = file_to_context(path_like)
>>> context2 = file_to_context(file_like)
>>> with context1 as f1, with context2 as f2:
... ... # insert operations here
>>> with context1 as f1, context2 as f2:
... pass # insert operations here
Parameters
Expand Down Expand Up @@ -174,7 +184,7 @@ class InitRestart(AbstractContextManager):
.. code:: python
>>> path = "path/to/my/workdir"
>>> with InitRestart(path):
>>> with InitRestart(path): # doctest: +SKIP
... ... # Run any PLAMS Jobs here
Expand Down

0 comments on commit 6b5cbf7

Please sign in to comment.