-
If you're a first time contributor:
- Go to the epcc-reframe github and click the fork button to create your own copy of the project.
- Clone the project on your computer with
git clone /~https://github.com/your-username/epcc-reframe.git
- Change directory:
cd epcc-reframe
. - Add the upstream repository:
git remote add upstream /~https://github.com/EPCCed/epcc-reframe.git
git remote -v
will now show two remote repositories, one nameupstream
, which refers to theEPCCed
repository, and another namedorigin
, which is your personal fork.- You can pull the latest changes from upstream with:
git checkout main && git pull upstream main
-
Develop your contribution:
- Create a branch for the feature you want to work on.
- Commit changes locally as you progress (with
git add
andgit commit
). Please use descriptive commit messages. - Make sure your code follows the syntax guidelines detailed below.
- If you're developing a new test, make sure you include the
maintainer
parameter in the class.
-
Submit your contribution:
- Push your changes back to your fork on GitHub with:
git push origin <branch-name>
. - Go to GitHub, the new branch will show up with a green "Pull Request" button.
- Make sure to write clear title and messages, then click the button to submit it.
- If the PR relates to any open issues, you can reference them in the message body (with
closes
,fixes
, or justxref
followed by the issue number).
- Push your changes back to your fork on GitHub with:
-
Review process:
- Someone will review the code you submit, either with inline comments or overall comments on the PR.
- To update to your PR (based on these comments or not), make changes on your local repository, on the same branch as before, and these will also be pushed to the PR.
This repository follows the PEP8 guidelines with a few exceptions, based mostly on perceptions on aesthetics of the code.
- E501: The 79 character limit can lead developers to use shortened, cryptic variable names, especially in situations with nested modules, classes, functions, etc.
- W503: Has been mostly deprecated, but some tools still flag it as "bad syntax".
-
Use a code checker:
- pylint: a Python static code analysis tool.
- flake8: a tool that glues together
pycodestyle
,pyflakes
, andmccabe
to check the style and quality of Python code. - flake8-pyproject: Because flake8 refuses to adopt the use of the
pyproject.toml
file. - vim-flake8: a flake8 plugin for Vim.
- elpy: an emacs python plugin that integrates with flake8, black, and others.
-
Use an auto-formatter:
- black: An opinionated auto-formatter.
Follow the Python naming conventions, in summary:
Modules should be named all-lowercase, with underscores used where it improves readability.
For applications with more than one test, one module should contain the basic setup for that application, and each test should then be a different module (named for the type of system or test that it performs) that imports the base one. For example:
tests/apps/lammps/
├── dipole_large.py
├── ethanol.py
├── lammps.py
└── src
├── data.ethanol
├── in.ethanol
└── in_2048.dipole
Classes, including Type Variables, should be named using CamelCase. Classes that define tests should have the application name as prefix, and those that setup the application should have the suffix "Base". When different classes for CPU/GPU tests are needed, add the corresponding suffix. Exceptions should include the suffix "Error"
Examples:
class LAMMPSBase(rfm.RunOnlyRegressionTest)
pass
class LAMMPSEthanol(LAMMPSBase)
pass
class LAMMPSEthanolCPU(LAMMPSEthanol)
pass
class LAMMPSETHANOLGPU(LAMMPSEthanol)
pass
Method, function, and variable names should be lowercase, with words separated by underscores.
Examples:
executable = "lmp"
keep_files = ["log.lammps"]
strict_check = True
def assert_finished(self):
pass
Constants should be declared at the module level, and use all-caps with underscores separating words.
Example:
PI = 3.14159265359
Avoid having a dictionary key share a name with the dictionary or a key in a nested dictionary:
Examples of what to avoid:
performance = {"performance": [1, 0.01, -0.01, "ns/day"]}
extra_resources = {"qos": {"qos": "standard"}}
Modules, classes, and functions should have triple-quote delimited doc-strings with a short explanation of the intent of the module/class/function. Where applicable, method/function parameter should be listed and have their expected types detailed.
Module example:
"""Base module for LAMMPS tests"""
Class example:
"""LAMMPS Ethanol test for performance testing -- CPU nodes"""
Function example:
"""
Extracts performance value to compare with reference value.
Returns a float.
"""
Python 3 introduced Type/Function annotations. They allow anyone to know, at a quick glance, what type a variable, or a function parameter is. Their use is recommended for functions that return a value, rather than modifying an object, and when declaring an "empty" variable.
For example:
time_limit: str = None
n_nodes: int = None
valid_systems: list[str] = []
env_vars: dict[str, str] = {}
reference: dict[str, dict[str, tuple[float, float, float, str]]]
def extract_energy(self) -> float:
"""Extracts value of system energy for correctness check"""
return sn.extractsingle(
r"\s+11000\s+\S+\s+\S+\s+(?P<energy>\S+)",
self.keep_files[0],
"energy",
float,
item=-1,
)