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

docs: add project guidelines #40

Merged
merged 1 commit into from
Nov 29, 2023
Merged
Show file tree
Hide file tree
Changes from all 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
2 changes: 1 addition & 1 deletion docs/development/environment.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ This document describes how to configure and use your development environment.

You must complete these steps once before you can start setting up the project itself:

1. Install [Python 3.10](https://www.python.org/downloads/).
1. Install [Python 3.11](https://www.python.org/downloads/).
2. Verify that `python` can be launched by running this command in a **new** terminal:
```shell
python --version
Expand Down
245 changes: 245 additions & 0 deletions docs/development/project_guidelines.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,245 @@
# Project Guidelines

This document describes general guidelines for the Safe-DS Stub Generator.

## Docstrings

The docstrings **should** use the [numpydoc](https://numpydoc.readthedocs.io/en/latest/format.html) format. The
descriptions **should not** start with "this" and **should** use imperative mood. Refer to the subsections below for
more details on how to document specific API elements.

!!! success "**DO**:"

```py
def add_ints(a: int, b: int) -> int:
"""Add two integers."""
return a + b
```

!!! failure "**DON'T**:"

```py
def add_ints(a: int, b: int) -> int:
"""This function adds two integers."""
return a + b
```

!!! failure "**DON'T**:"

```py
def add_ints(a: int, b: int) -> int:
"""Adds two integers."""
return a + b
```

!!! success "**DO**:"

```py
def __init__(self, data: Mapping[str, Any] | None = None) -> None:
"""
data : Mapping[str, Any] | None
"""
```

!!! failure "**DON'T**:"

```py
def __init__(self, data: Optional[Mapping[str, Any]] = None) -> None:
"""
data : Optional[Mapping[str, Any]]
"""
```

!!! failure "**DON'T**:"

```py
def __init__(self, data: Mapping[str, Any] | None = None) -> None:
"""
data : Optional[Mapping[str, Any]]
"""
```

### Modules

All modules should have

* a one-line description ([short summary][short-summary-section]),
* a longer description if needed ([extended summary][extended-summary-section]).

Example:

```py
"""Containers for tabular data."""
```

### Classes

All classes should have

* a one-line description ([short summary][short-summary-section]),
* a longer description if needed ([extended summary][extended-summary-section])
* a description of the parameters of their `__init__` method ([`Parameters` section][parameters-section]),
* examples that show how to use them correctly ([`Examples` section][examples-section]).

Example:

```py
"""
A row is a collection of named values.

Parameters
----------
data : Mapping[str, Any] | None
The data. If None, an empty row is created.

Examples
--------
>>> from safeds.data.tabular.containers import Row
>>> row = Row({"a": 1, "b": 2})
"""
```

### Functions

All functions should have

* a one-line description ([short summary][short-summary-section]),
* a longer description if needed ([extended summary][extended-summary-section])
* a description of their parameters ([`Parameters` section][parameters-section]),
* a description of their results ([`Returns` section][returns-section]),
* a description of any exceptions that may be raised and under which conditions that may
happen ([`Raises` section][raises-section]),
* a description of any warnings that may be issued and under which conditions that may
happen ([`Warns` section][warns-section]),
* examples that show how to use them correctly ([`Examples` section][examples-section]).

Example:

```py
"""
Return the value of a specified column.

Parameters
----------
column_name : str
The column name.

Returns
-------
value : Any
The column value.

Raises
------
UnknownColumnNameError
If the row does not contain the specified column.

Examples
--------
>>> from safeds.data.tabular.containers import Row
>>> row = Row({"a": 1, "b": 2})
>>> row.get_value("a")
1
"""
```

## Tests

We aim for 100% line coverage, so automated tests should be added for any new function.

### File structure

Tests belong in the [`tests`][tests-folder] folder. The file structure in the tests folder should mirror the file
structure of the [`src`][src-folder] folder.

### Naming

Names of test functions shall start with `test_should_` followed by a description of the expected behaviour,
e.g. `test_should_add_column`.

!!! success "**DO**:"

```py
def test_should_raise_if_less_than_or_equal_to_0(self, number_of_trees) -> None:
with pytest.raises(ValueError, match="The parameter 'number_of_trees' has to be greater than 0."):
...
```

!!! failure "**DON'T**:"

```py
def test_value_error(self, number_of_trees) -> None:
with pytest.raises(ValueError, match="The parameter 'number_of_trees' has to be greater than 0."):
...
```

### Parametrization

Tests should be parametrized using `@pytest.mark.parametrize`, even if there is only a single test case. This makes it
easier to add new test cases in the future. Test cases should be given descriptive IDs.

!!! success "**DO**:"

```py
@pytest.mark.parametrize("number_of_trees", [0, -1], ids=["zero", "negative"])
def test_should_raise_if_less_than_or_equal_to_0(self, number_of_trees) -> None:
with pytest.raises(ValueError, match="The parameter 'number_of_trees' has to be greater than 0."):
RandomForest(number_of_trees=number_of_trees)
```

!!! failure "**DON'T**:"

```py
def test_should_raise_if_less_than_0(self, number_of_trees) -> None:
with pytest.raises(ValueError, match="The parameter 'number_of_trees' has to be greater than 0."):
RandomForest(number_of_trees=-1)

def test_should_raise_if_equal_to_0(self, number_of_trees) -> None:
with pytest.raises(ValueError, match="The parameter 'number_of_trees' has to be greater than 0."):
RandomForest(number_of_trees=0)
```

## Code style

### Consistency

If there is more than one way to solve a particular task, check how it has been solved at other places in the codebase
and stick to that solution.

### Sort exported classes in `__init__.py`

Classes defined in a module that other classes shall be able to import must be defined in a list named `__all__` in the
module's `__init__.py` file. This list should be sorted alphabetically, to reduce the likelihood of merge conflicts when
adding new classes to it.

!!! success "**DO**:"

```py
__all__ = [
"Column",
"Row",
"Table",
"TaggedTable",
]
```

!!! failure "**DON'T**:"

```py
__all__ = [
"Table",
"TaggedTable",
"Column",
"Row",
]
```

[src-folder]: https://github.com/Safe-DS/Library/tree/main/src
[tests-folder]: https://github.com/Safe-DS/Library/tree/main/tests
[short-summary-section]: https://numpydoc.readthedocs.io/en/latest/format.html#short-summary
[extended-summary-section]: https://numpydoc.readthedocs.io/en/latest/format.html#extended-summary
[parameters-section]: https://numpydoc.readthedocs.io/en/latest/format.html#parameters
[returns-section]: https://numpydoc.readthedocs.io/en/latest/format.html#returns
[raises-section]: https://numpydoc.readthedocs.io/en/latest/format.html#raises
[warns-section]: https://numpydoc.readthedocs.io/en/latest/format.html#warns
[examples-section]: https://numpydoc.readthedocs.io/en/latest/format.html#examples
2 changes: 2 additions & 0 deletions mkdocs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ nav:
- Changelog: CHANGELOG.md
- Development:
- Environment: development/environment.md
- Project Guidelines: development/project_guidelines.md
- Contributing 🌐: https://github.com/Safe-DS/Stub-Generator/contribute

# Configuration of MkDocs & Material for MkDocs --------------------------------

Expand Down