Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update pre-commits and pytest configuration #153

Merged
merged 6 commits into from
Jun 9, 2024
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 11 additions & 25 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
@@ -1,37 +1,23 @@
default_language_version:
python: python3.8
exclude: 'dev'

repos:
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: v4.4.0
rev: v4.5.0
hooks:
- id: check-merge-conflict # checks for files that contain merge conflict strings
- id: check-toml # checks toml files for parseable syntax
- id: debug-statements # checks for debugger imports and py37+ `breakpoint()` calls in python source
# - id: trailing-whitespace # needs more checks
# args: [--markdown-linebreak-ext=md]
# exclude: 'oommfc/tests/test_sample/.*'

- repo: https://github.com/pycqa/isort
rev: 5.12.0
- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.4.4
hooks:
- id: isort

- repo: https://github.com/nbQA-dev/nbQA
rev: 1.7.0
hooks:
- id: nbqa-isort # isort inside Jupyter notebooks

- repo: https://github.com/PyCQA/flake8
rev: 6.1.0
hooks:
- id: flake8
additional_dependencies: [flake8-rst-docstrings] #, flake8-docstrings]

- repo: https://github.com/psf/black
rev: 23.9.1
hooks:
- id: black-jupyter
# Run the linter.
- id: ruff
types_or: [python, pyi, jupyter]
args: [--fix, --exit-non-zero-on-fix]
# Run the formatter.
- id: ruff-format
types_or: [python, pyi, jupyter]

# - repo: https://github.com/codespell-project/codespell
# rev: v2.1.0
Expand Down
2 changes: 1 addition & 1 deletion oommfc/__init__.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
"""OOMMF calculator."""

import importlib.metadata

import pytest

import oommfc.oommf
import oommfc.scripts

from .compute import compute
from .delete import delete
from .drivers import Driver, HysteresisDriver, MinDriver, TimeDriver
Expand Down
2 changes: 1 addition & 1 deletion oommfc/compute.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def schedule_script(func, system):
msg = f"Computing the value of {func} is not supported."
raise ValueError(msg)

return 'Schedule "{}" archive Step 1\n'.format(output)
return f'Schedule "{output}" archive Step 1\n'


def compute(
Expand Down
2 changes: 1 addition & 1 deletion oommfc/drivers/driver.py
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ def write_mif(
compute=compute,
**kwargs,
)
with open(self._miffilename(system), "wt", encoding="utf-8") as miffile:
with open(self._miffilename(system), "w", encoding="utf-8") as miffile:
miffile.write(mif)

# Generate and save json info file for a drive (not compute).
Expand Down
1 change: 1 addition & 0 deletions oommfc/oommf/__init__.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
"""OOMMF driving utility"""

from .oommf import (
DockerOOMMFRunner,
ExeOOMMFRunner,
Expand Down
45 changes: 22 additions & 23 deletions oommfc/oommf/oommf.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,7 +158,7 @@ def status(self):
td.drive(system, t=1e-12, n=1, runner=self)
print("OOMMF found and running.")
return 0
except (EnvironmentError, RuntimeError):
except (OSError, RuntimeError):
print("Cannot find OOMMF.")
return 1

Expand Down Expand Up @@ -250,7 +250,7 @@ def __init__(self, oommf_tcl):

def errors(self):
errors_file = os.path.join(os.path.dirname(self.oommf_tcl), "boxsi.errors")
with open(errors_file, "r") as f:
with open(errors_file) as f:
errors = f.read()

return errors
Expand Down Expand Up @@ -285,13 +285,13 @@ def errors(self):
"oommf",
"boxsi.errors",
)
with open(errors_file, "r") as f:
with open(errors_file) as f:
errors = f.read()
return errors

except FileNotFoundError:
msg = "boxsi.errors cannot be retrieved."
raise EnvironmentError(msg)
raise OSError(msg) from None

def __repr__(self):
return f"ExeOOMMFRunner({self.oommf_exe})"
Expand Down Expand Up @@ -357,7 +357,7 @@ def _kill(self, targets=("all",), dry_run=False):

def errors(self):
msg = "boxsi.errors cannot be retrieved from Docker container."
raise EnvironmentError(msg)
raise OSError(msg)

def __repr__(self):
return f"DockerOOMMFRunner(docker_exe={self.docker_exe}, image={self.image})"
Expand Down Expand Up @@ -569,7 +569,7 @@ def autoselect_runner(self):

# If OOMMFRunner was not returned up to this point, we raise an
# exception.
raise EnvironmentError("Cannot find OOMMF.")
raise OSError("Cannot find OOMMF.")

def __repr__(self):
# avoid selecting a runner when calling __repr__
Expand Down Expand Up @@ -600,22 +600,21 @@ def overhead():
True

"""
with tempfile.TemporaryDirectory() as workingdir:
with uu.changedir(workingdir):
# Running OOMMF through oommfc.
system = mm.examples.macrospin()
td = oc.TimeDriver()
oommfc_start = time.time()
td.drive(system, t=1e-12, n=1)
oommfc_stop = time.time()
oommfc_time = oommfc_stop - oommfc_start

# Running OOMMF directly.
oommf_runner = oc.runner.runner
mifpath = pathlib.Path(f"{system.name}/drive-0/macrospin.mif").resolve()
oommf_start = time.time()
oommf_runner.call(str(mifpath))
oommf_stop = time.time()
oommf_time = oommf_stop - oommf_start
with tempfile.TemporaryDirectory() as workingdir, uu.changedir(workingdir):
# Running OOMMF through oommfc.
system = mm.examples.macrospin()
td = oc.TimeDriver()
oommfc_start = time.time()
td.drive(system, t=1e-12, n=1)
oommfc_stop = time.time()
oommfc_time = oommfc_stop - oommfc_start

# Running OOMMF directly.
oommf_runner = oc.runner.runner
mifpath = pathlib.Path(f"{system.name}/drive-0/macrospin.mif").resolve()
oommf_start = time.time()
oommf_runner.call(str(mifpath))
oommf_stop = time.time()
oommf_time = oommf_stop - oommf_start

return oommfc_time - oommf_time
5 changes: 1 addition & 4 deletions oommfc/scripts/driver.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,10 +75,7 @@ def driver_script(
driver.evolver.fixed_spins = resstr

# What is saved in output?
if output_step:
output_str = "Step"
else:
output_str = "Stage"
output_str = "Step" if output_step else "Stage"

mif += oc.scripts.evolver_script(driver.evolver)

Expand Down
25 changes: 11 additions & 14 deletions oommfc/scripts/energy.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import contextlib
import numbers
import warnings

Expand All @@ -22,10 +23,7 @@ def exchange_script(term, system):
mif += "}\n\n"

elif isinstance(term.A, dict):
if "default" in term.A.keys():
default_value = term.A["default"]
else:
default_value = 0
default_value = term.A.get("default", 0)
mif = "# Exchange6Ngbr\n"
mif += f"Specify Oxs_Exchange6Ngbr:{term.name} {{\n"
mif += f" default_A {default_value}\n"
Expand Down Expand Up @@ -61,7 +59,9 @@ def zeeman_script(term, system):
if isinstance(term.wave, str) or isinstance(term.func, str):
if isinstance(term.wave, str):
warnings.warn(
"Parameter `wave` is deprecated; use `func` instead.", FutureWarning
"Parameter `wave` is deprecated; use `func` instead.",
FutureWarning,
stacklevel=2,
)
if isinstance(term.H, (df.Field, dict)):
if term.wave == "sin" or term.func == "sin":
Expand Down Expand Up @@ -220,10 +220,8 @@ def zeeman_script(term, system):
mif += f'Specify {term.tcl_strings["energy"]}:{term.name} {{\n'
mif += f' script {term.tcl_strings["script_name"]}\n'
for key in ["type", "script_args"]:
try:
with contextlib.suppress(KeyError):
mif += f" {key} {term.tcl_strings[key]}\n"
except KeyError:
pass
if term.tcl_strings["energy"] == "Oxs_TransformZeeman":
mif += f" field {Hname}\n"
mif += "}\n\n"
Expand Down Expand Up @@ -264,14 +262,16 @@ def dmi_script(term, system):
elif (tcc := term.crystalclass) in ["D2d_x", "D2d_y", "D2d_z", "D2d"]:
if tcc == "D2d":
warnings.warn(
"Use of `D2d` is deprecated; use `D2d_z` instead.", FutureWarning
"Use of `D2d` is deprecated; use `D2d_z` instead.",
FutureWarning,
stacklevel=2,
)
tcc = "D2d_z"
oxs = f"Oxs_DMI_{tcc}"
elif (tcc := term.crystalclass) in ["Cnv_x", "Cnv_y", "Cnv_z", "Cnv"]:
if tcc == "Cnv":
msg = "Use of `Cnv` is deprecated; use `Cnv_z` instead."
warnings.warn(msg, FutureWarning)
warnings.warn(msg, FutureWarning, stacklevel=2)
tcc = "Cnv_z"
oxs = f"Oxs_DMI_{tcc}"

Expand All @@ -290,10 +290,7 @@ def dmi_script(term, system):
mif += "}\n\n"

elif isinstance(term.D, dict):
if "default" in term.D.keys():
default_value = term.D["default"]
else:
default_value = 0
default_value = term.D.get("default", 0)
mif += f" default_D {default_value}\n"
mif += " atlas :main_atlas\n"
mif += " D {\n"
Expand Down
16 changes: 8 additions & 8 deletions oommfc/scripts/evolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,20 +70,20 @@ def evolver_script(evolver, **kwargs):
mif += "}\n\n"

if isinstance(evolver, (oc.SpinXferEvolver, oc.Xf_ThermSpinXferEvolver)):
setattr(evolver, "J_profile", "TimeFunction")
setattr(evolver, "J_profile_args", "total_time")
evolver.J_profile = "TimeFunction"
evolver.J_profile_args = "total_time"
elif isinstance(evolver, oc.SpinTEvolver):
setattr(evolver, "u_profile", "TimeFunction")
setattr(evolver, "u_profile_args", "total_time")
evolver.u_profile = "TimeFunction"
evolver.u_profile_args = "total_time"
if hasattr(evolver, "tcl_strings") and isinstance(evolver.tcl_strings, dict):
print(evolver.tcl_strings)
mif += evolver.tcl_strings["script"]
if isinstance(evolver, (oc.SpinXferEvolver, oc.Xf_ThermSpinXferEvolver)):
setattr(evolver, "J_profile", evolver.tcl_strings["script_name"])
setattr(evolver, "J_profile_args", evolver.tcl_strings["script_args"])
evolver.J_profile = evolver.tcl_strings["script_name"]
evolver.J_profile_args = evolver.tcl_strings["script_args"]
elif isinstance(evolver, oc.SpinTEvolver):
setattr(evolver, "u_profile", evolver.tcl_strings["script_name"])
setattr(evolver, "u_profile_args", evolver.tcl_strings["script_args"])
evolver.u_profile = evolver.tcl_strings["script_name"]
evolver.u_profile_args = evolver.tcl_strings["script_args"]

# temperature cannot spacially vary

Expand Down
2 changes: 1 addition & 1 deletion oommfc/scripts/mesh.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ def mesh_script(mesh):
mif += oc.scripts.box_atlas(mesh.region.pmin, mesh.region.pmax, name="entire")
mif += "# MultiAtlas\n"
mif += "Specify Oxs_MultiAtlas:main_atlas {\n"
for name in mesh.subregions.keys():
for name in mesh.subregions:
mif += f" atlas :{name}_atlas\n"
mif += " atlas :entire_atlas\n"
mif += f" xrange {{ {mesh.region.pmin[0]} {mesh.region.pmax[0]} }}\n"
Expand Down
4 changes: 2 additions & 2 deletions oommfc/scripts/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ def setup_scalar_parameter(parameter, name):
# to avoid changing the dictionary used in the respective term
# in the system (from micromagneticmodel)
param = parameter.copy()
if "default" not in param.keys():
if "default" not in param:
param["default"] = 0
mif = atlas_scalar_field(param, f"{name}")
return mif, f"{name}"
Expand All @@ -102,7 +102,7 @@ def setup_vector_parameter(parameter, name):
return mif, f"{name}"

elif isinstance(parameter, dict):
if "default" not in parameter.keys():
if "default" not in parameter:
parameter["default"] = (0, 0, 0)
mif = atlas_vector_field(parameter, f"{name}")
return mif, f"{name}"
Expand Down
8 changes: 3 additions & 5 deletions oommfc/tests/test_oommf.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,7 @@ def check_runner(runner):

# Cleanup created files.
for f in os.listdir(dirname):
if f.endswith(".odt"):
os.remove(os.path.join(dirname, f))
elif f.endswith(".omf") and f.startswith("test_oommf-Oxs"):
if f.endswith(".odt") or f.endswith(".omf") and f.startswith("test_oommf-Oxs"):
os.remove(os.path.join(dirname, f))


Expand Down Expand Up @@ -120,7 +118,7 @@ def test_missing_oommf():
oc.runner.oommf_exe = "wrong_name"
oc.runner.docker_exe = "wrong_name"
with pytest.raises(EnvironmentError):
oc.runner.runner
oc.runner.runner # noqa: B018


def test_get_cached_runner(reset_runner, monkeypatch):
Expand All @@ -138,7 +136,7 @@ def test_get_cached_runner(reset_runner, monkeypatch):
oc.runner.cache_runner = False
oc.runner.docker_exe = "wrong_name" # ensure that we do not find docker
with pytest.raises(EnvironmentError):
oc.runner.runner
oc.runner.runner # noqa: B018

oc.runner.envvar = "OOMMFTCL"
if oommf_tcl_path():
Expand Down
44 changes: 39 additions & 5 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -61,15 +61,49 @@ repository = "https://github.com/ubermag/oommfc"



[tool.black]
experimental-string-processing = true

[tool.coverage.run]
omit = ["oommfc/tests/*"]

[tool.isort]
profile = "black"
skip_gitignore = true # ignores files listed in .gitignore
[tool.ruff.lint]
ignore-init-module-imports = true # do not remove unused imports in __init__ and warn instead
select = [
"B", # flake8-bugbear
"E", # pycodestyle
"F", # Pyflakes
"I", # isort
"SIM", # flake8-simplify
"UP", # pyupgrade
]
ignore = [
# conflict with other rules
"D203", # one-blank-line-before-class (conflicts with D204)
"D212", # multi-line-summary-first-line (conflicts with D213)
# conflict with formatter
"D206", # indent-with-spaces
"D300", # triple-single-quotes
"E111", # indentation-with-invalid-multiple
"E114", # indentation-with-invalid-multiple-comment
"E117", # over-indented
# conflict with Python 3.6 compatibility
"UP022", # replace-stdout-stderr
]

[tool.ruff.lint.isort]
known-local-folder = ["oommfc"]

[tool.ruff.lint.per-file-ignores]
"*.ipynb" = [
"B018", # "Found useless expression. Either assign it to a variable or remove it."; false positives when using implicit __repr__ in the notebook
"E501", # line too long
"F811", # 'redefined-while-unused'; many false positives in notebooks because ipywidgets decorated functions are not recognised
]

[tool.pytest.ini_options]
filterwarnings = [
"error",
"ignore:((.|\n)*)Sentinel is not a public part of the traitlets API((.|\n)*)", # dependency of k3d
]

[tool.setuptools.packages.find]
include = ["oommfc*"]
Expand Down
Loading
Loading